Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 <h1 id="lab_8_web_resources">Lab 8 - Web Resources</h1> | 1 <h1 id="lab_8_web_resources">Access Web Resources</h1> |
| 2 | 2 |
| 3 <p>Chrome apps have a strict <a href="http://developer.chrome.com/trunk/apps/app _csp.html">Content Security Policy</a> which will not let the user execute code or load resources that are hosted remotely.</p> | 3 <p>Chrome packaged apps have a strict |
| 4 <a href="http://developer.chrome.com/trunk/apps/app_csp.html">Content Security P olicy</a> | |
| 5 which will not let the user execute code or load resources that are hosted remot ely.</p> | |
| 4 | 6 |
| 5 <p>Many applications, however, need to be able to load and display content from a remote location. A News Reader, for example, needs to display remote content i nline or load and show images from a remote URL.</p> | 7 <p>Many applications, however, need to be able to load and display content from a remote location. A News Reader, for example, needs to display remote content i nline or load and show images from a remote URL.</p> |
| 6 | 8 |
| 7 <h2 id="you_should_also_read">You should also read</h2> | 9 <h2 id="loading_external_web_content_into_an_element">Load external web content< /h2> |
| 8 | |
| 9 <ul> | |
| 10 <li><a href="http://developer.chrome.com/apps/app_external.html">Embed Content</ a> in Chrome app docs</li> | |
| 11 </ul> | |
| 12 | |
| 13 <h2 id="loading_external_web_content_into_an_element">Loading external web conte nt into an element</h2> | |
| 14 | 10 |
| 15 <p>Sites on the internet are inherently a security risk and rendering arbitrary web pages directly into your application with elevated privileges would be a pot ential source of exploits.</p> | 11 <p>Sites on the internet are inherently a security risk and rendering arbitrary web pages directly into your application with elevated privileges would be a pot ential source of exploits.</p> |
| 16 | 12 |
| 17 <p>Chrome apps offer developers the ability to securely render third-party conte nt in the <code><webview></code> tag. A WebView is like an iframe that you can control with greater flexibility and added security. | 13 <p>Chrome packaged apps offer developers the ability |
| 14 to securely render third-party content in the <code><webview></code> tag. | |
| 15 A <a href="http://developer.chrome.com/trunk/apps/webview_tag.html">WebView</a> | |
| 16 is like an iframe that you can control with greater flexibility and added securi ty. | |
| 18 It runs in a separate sandboxed process and can't communicate directly with the application.</p> | 17 It runs in a separate sandboxed process and can't communicate directly with the application.</p> |
| 19 | 18 |
| 20 <p class="note"><b>Tip:</b> The WebView has a very simple API. From your app y ou can:</p> | 19 <p>The WebView has a very simple API. |
| 20 From your app you can:</p> | |
| 21 | 21 |
| 22 <ul> | 22 <ul> |
| 23 <li> Change the URL of the WebView.</li> | 23 <li> Change the URL of the WebView.</li> |
| 24 <li> Navigate forwards and backward, stop loading and reload.</li> | 24 <li> Navigate forwards and backward, stop loading and reload.</li> |
| 25 <li> Check if the WebView has finished loading and if it is possible, go back an d forward in the history stack.</li> | 25 <li> Check if the WebView has finished loading and if it is possible, go back an d forward in the history stack.</li> |
| 26 </ul></p> | 26 </ul> |
| 27 | 27 |
| 28 <p>We will change our code to render the content of URLs dropped in the drag-and -drop operations in a WebView when the user clicks on a link.</p> | 28 <p>We will change our code to render the content of URLs dropped in the drag-and -drop operations in a WebView when the user clicks on a link.</p> |
| 29 | 29 |
| 30 <ol> | 30 <h3 id="manifest">Update manifest</h3> |
| 31 <li><p>Request a new permission, "webview", in <a href="https://github .com/GoogleChrome/chrome-app-codelab/blob/master/lab8_webresources/angularjs/1_w ebview/manifest.json">manifest.json</a>: | 31 |
| 32 <p>Request a new permission, "webview", in the manifest. | |
| 33 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/1_webview/manifest.json">Angular JS manifest.json</a> and | |
| 34 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/1_webview/manifest.json">JavaScript manifest.json</a> are t he same: | |
| 35 </p> | |
| 36 | |
| 32 <pre> | 37 <pre> |
| 33 "permissions": ["storage", "webview"] | 38 "permissions": ["storage", "webview"] |
| 34 </pre></p></li> | 39 </pre> |
| 35 <li><p>Add a WebView tag and a link to <a href="https://github.com/GoogleChrome/ chrome-app-codelab/blob/master/lab8_webresources/angularjs/1_webview/index.html" >index.html</a>: | 40 |
| 36 <pre> | 41 <h3 id="view">Update view</h3> |
| 42 | |
| 43 <p>Add a WebView tag and a link to the view: | |
| 44 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/1_webview/index.html">AngularJS index.html</a> or | |
| 45 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/1_webview/index.html">JavaScript index.html</a>: | |
| 46 </p> | |
| 47 | |
| 48 <tabs data-group="source"> | |
| 49 | |
| 50 <header tabindex="0" data-value="angular">Angular</header> | |
| 51 <header tabindex="0" data-value="js">JavaScript</header> | |
| 52 | |
| 53 <content> | |
| 54 <pre> | |
| 37 <!-- in TODO item: --> | 55 <!-- in TODO item: --> |
| 38 <a ng-show="todo.uri" href="" ng-click="showUri(todo .uri)">(view url)</a> | 56 <a ng-show="todo.uri" href="" ng-click="showUri(todo .uri)">(view url)</a> |
| 39 <!-- at the bottom, below the end of body: --> | 57 <!-- at the bottom, below the end of body: --> |
| 40 <webview></webview> | 58 <webview></webview> |
| 41 </pre></p></li> | 59 </pre> |
| 42 <li><p>Set an appropriate width and height to the webview tag in <a href="https: //github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_webresources/angul arjs/1_webview/todo.css">todo.css</a> (it has zero size by default): | 60 </content> |
| 61 <content> | |
| 62 <pre> | |
| 63 <webview></webview> | |
| 64 <a style="display: none;" href="">(view url)</a> | |
| 65 </pre> | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
As in the Angular snippet, needs some comments to
mkearney1
2013/04/10 17:59:58
Done.
| |
| 66 </content> | |
| 67 | |
| 68 </tabs> | |
| 69 | |
| 70 <h3 id="css">Update stylesheet</h3> | |
| 71 | |
| 72 <p>Set an appropriate width and height to the webview tag in | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
<code> on webview. Check other occurrences
mkearney1
2013/04/10 17:59:58
Done.
| |
| 73 the style sheet (it has a zero size by default). | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
remove the "it has a zero size by default" because
mkearney1
2013/04/10 17:59:58
Done.
| |
| 74 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/1_webview/todo.css">AngularJS todo.css</a> and | |
| 75 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/1_webview/todo.css">JavaScript todo.css</a> are the same. | |
| 76 </p> | |
| 77 | |
| 43 <pre> | 78 <pre> |
| 44 webview { | 79 webview { |
| 45 width: 100%; | 80 width: 100%; |
| 46 height: 200px; | 81 height: 200px; |
| 47 } | 82 } |
| 48 </pre></p></li> | 83 </pre> |
| 49 <li><p>Thanks to AngularJS, we now only need to add the <code>showUri</code> met hod to our <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/mast er/lab8_webresources/angularjs/1_webview/controller.js">controller.js</a> and we 're done: | 84 |
| 50 <pre> | 85 <h3 id="controller">Update controller</h3> |
| 86 | |
| 87 <p>We now only need to add a method to our controller: | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
add a method to our AngularJS controller or an eve
mkearney1
2013/04/10 17:59:58
Done.
| |
| 88 <code>showUri</code> method in | |
| 89 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/1_webview/controller.js">AngularJS controller.js</a> | |
| 90 and <code>showURL</code> method in | |
| 91 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/1_webview/controller.js">JavaScript controller.js</a>. | |
| 92 </p> | |
| 93 | |
| 94 <tabs data-group="source"> | |
| 95 | |
| 96 <header tabindex="0" data-value="angular">Angular</header> | |
| 97 <header tabindex="0" data-value="js">JavaScript</header> | |
| 98 | |
| 99 <content> | |
| 100 <pre> | |
| 51 $scope.showUri = function(uri) { | 101 $scope.showUri = function(uri) { |
| 52 var webview=document.querySelector("webview"); | 102 var webview=document.querySelector("webview"); |
| 53 webview.src=uri; | 103 webview.src=uri; |
| 54 }; | 104 }; |
| 55 </pre></p></li> | 105 </pre> |
| 56 </ol> | 106 </content> |
| 107 <content> | |
| 108 <pre> | |
| 109 if(/^http:\/\/|https:\/\//.test(todoObj.text)) { | |
| 110 var showUrl = el.querySelector('a'); | |
| 111 showUrl.addEventListener('click', function(e) { | |
| 112 e.preventDefault(); | |
| 113 var webview=document.querySelector("webview"); | |
| 114 webview.src=todoObj.text; | |
| 115 }); | |
| 116 showUrl.style.display = 'inline'; | |
| 117 } | |
| 118 </pre> | |
| 119 </content> | |
| 120 | |
| 121 </tabs> | |
| 122 | |
| 123 <h3 id="results">Check the results</h3> | |
| 57 | 124 |
| 58 <p>To test, open the app, right-click, and select Reload App. | 125 <p>To test, open the app, right-click, and select Reload App. |
| 59 You should be able to click on the "view url" link on any dropped URL Todo item, and the corresponding web page will show in the webview. | 126 You should be able to click on the "view url" link |
| 60 If it's not showing, inspect the page and check if you set the webview size appropriately.</p> | 127 on any dropped URL Todo item, and the corresponding web page will show in the we bview. |
| 61 | 128 If it's not showing, |
| 62 <p class="note"><b>Note:</b> If you get stuck and want to see the app in action , go to <code>chrome://extensions</code>, | 129 inspect the page and check if you set the webview size appropriately.</p> |
| 63 load the unpacked <a href="https://github.com/GoogleChrome/chrome-app-codelab/tr ee/master/lab8_webresources/angularjs/1_webview">1_webview</a>, and launch the a pp from a new tab.</p> | 130 |
| 64 | 131 <p>If you get stuck and want to see the app in action, |
| 65 <h2 id="loading_external_images">Loading external images</h2> | 132 go to <code>chrome://extensions</code>, |
| 133 load the unpacked | |
| 134 <a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab8_web resources/angularjs/1_webview">AngularJS 1_webview</a> or | |
| 135 <a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab8_web resources/javascript/1_webview">JavaScript 1_webview</a>, | |
| 136 and launch the app from a new tab.</p> | |
| 137 | |
| 138 <h2 id="loading_external_images">Load external images</h2> | |
| 66 | 139 |
| 67 <p>If you try to add an <code><img></code> tag to your <code>index.html</c ode>, and point its <code>src</code> attribute to any site on the web, the follo wing exception is thrown in the console and the image isn't loaded:</p> | 140 <p>If you try to add an <code><img></code> tag to your <code>index.html</c ode>, and point its <code>src</code> attribute to any site on the web, the follo wing exception is thrown in the console and the image isn't loaded:</p> |
| 68 | 141 |
| 69 <p class="note"><b></b> Refused to load the image 'http://angularjs.org/img/ AngularJS-large.png' because it violates the following Content Security Poli cy directive: "img-src 'self' data: chrome-extension-resource:" ;.</p> | 142 <p class="note"><b></b> Refused to load the image 'http://angularjs.org/img/ AngularJS-large.png' because it violates the following Content Security Poli cy directive: "img-src 'self' data: chrome-extension-resource:" ;.</p> |
| 70 | 143 |
| 71 <p>Chrome apps cannot load any external resource directly in the DOM, because of the <a href="http://developer.chrome.com/apps/app_csp.html">CSP restrictions</a >.</p> | 144 <p>Chrome packaged apps cannot load any external resource directly in the DOM, b ecause of the <a href="http://developer.chrome.com/apps/app_csp.html">CSP restri ctions</a>.</p> |
| 72 | 145 |
| 73 <p>To avoid this restriction, you can use XHR requests, grab the blob correspond ing to the remote file and use it appropriately. | 146 <p>To avoid this restriction, you can use XHR requests, grab the blob correspond ing to the remote file and use it appropriately. |
| 74 For example, <code><img></code> tags can use a blob URL. | 147 For example, <code><img></code> tags can use a blob URL. |
| 75 Let's change our application to show a small icon in the Todo list if the dr opped URL represents an image:</p> | 148 Let's change our application to show a small icon in the Todo list if the dr opped URL represents an image.</p> |
| 76 | 149 |
| 77 <ol> | 150 <h3 id="manifest2">Update manifest</h3> |
| 78 <li><p>Before you start firing XHR requests, you must request permissions. | 151 |
| 79 Since we want to allow users to drag and drop images from any server, we need to request permission to XHR to any URL. | 152 <p>Before you start firing XHR requests, you must request permissions. |
| 80 Change <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/l ab8_webresources/angularjs/2_loading_resources/manifest.json">manifest.json</a>: | 153 Since we want to allow users to drag and drop images from any server, |
| 154 we need to request permission to XHR to any URL. | |
| 155 Change | |
| 156 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/2_loading_resources/manifest.json">AngularJS manifest.json</ a> or | |
| 157 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/2_loading_resources/manifest.json">JavaScript manifest.json </a>: | |
| 158 </p> | |
| 159 | |
| 81 <pre> | 160 <pre> |
| 82 "permissions": ["storage", "webview", "<al l_urls>"] | 161 "permissions": ["storage", "webview", "<al l_urls>"] |
| 83 </pre></p></li> | 162 </pre> |
| 84 <li><p>Add to your project a placeholder image <img src="https://github.com/Goog leChrome/chrome-app-codelab/raw/master/lab8_webresources/angularjs/2_loading_res ources/loading.gif" alt="loading.gif"> that will be shown while we are loading t he proper image.</p></li> | 163 |
| 85 <li><p>Add the <code><img></code> tag to the Todo item on the <a href="htt ps://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_webresources/an gularjs/2_loading_resources/index.html">index.html</a>: | 164 <h3 id="image">Add image</h3> |
| 86 <pre> | 165 |
| 87 <img style="max-height: 48px; max-width: 120px;" ng-show="todo .validImage" ng-src="{{todo.imageUrl}}">&l t;/img> | 166 <p>Add to your project a placeholder image |
| 88 </pre> | 167 <img src="https://github.com/GoogleChrome/chrome-app-codelab/raw/master/lab8_web resources/angularjs/2_loading_resources/loading.gif" alt="loading.gif"> |
| 89 As you will see soon, this element is only shown when the validImage attribute o f the Todo item is true.</p></li> | 168 that will be shown while we are loading the proper image.</p> |
| 90 <li><p>Add the method loadImage (either in <a href="https://github.com/GoogleChr ome/chrome-app-codelab/blob/master/lab8_webresources/angularjs/2_loading_resourc es/controller.js">controller.js</a> or in a <a href="https://github.com/GoogleCh rome/chrome-app-codelab/blob/master/lab8_webresources/angularjs/2_loading_resour ces/loader.js">separate script file</a> as we did), that will start a XHR reques t and execute a callback with a Blob URL: | 169 |
| 170 <p>Then add the <code><img></code> tag to the Todo item on the view: | |
| 171 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/2_loading_resources/index.html">AngularJS index.html</a> or | |
| 172 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/2_loading_resources/index.html">JavaScript index_html</a>: | |
| 173 </p> | |
| 174 | |
| 175 <tabs data-group="source"> | |
| 176 | |
| 177 <header tabindex="0" data-value="angular">Angular</header> | |
| 178 <header tabindex="0" data-value="js">JavaScript</header> | |
| 179 | |
| 180 <content> | |
| 181 <pre> | |
| 182 <img style="max-height: 48px; max-width: 120px;" ng-show="todo .validImage" | |
| 183 ng-src="{{todo.imageUrl}}"></img> | |
| 184 </pre> | |
| 185 </content> | |
| 186 <content> | |
| 187 <pre> | |
| 188 <img style="max-height: 48px; max-width: 120px;"</img> | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
missing a ">" after img
mkearney1
2013/04/10 17:59:58
Done.
| |
| 189 </pre> | |
| 190 </content> | |
| 191 | |
| 192 </tabs> | |
| 193 | |
| 194 <p> | |
| 195 In the | |
| 196 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/2_loading_resources/controller.js">JavaScript controller.js </a>, | |
| 197 add a constant for the placeholder image: | |
| 198 </p> | |
| 199 | |
| 200 <pre> | |
| 201 const PLACEHOLDER_IMAGE = "loading.gif"; | |
| 202 </pre> | |
| 203 | |
| 204 <p> | |
| 205 As you will see soon, | |
| 206 this element is only shown when the <code>validImage</code> attribute of the Tod o item is true. | |
| 207 </p> | |
| 208 | |
| 209 <h3 id="controller2">Update controller</h3> | |
| 210 | |
| 211 <p>Add the method <code>loadImage</code> to a new script file | |
| 212 that will start a XHR request and execute a callback with a Blob URL. | |
| 213 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/2_loading_resources/loader.js">AngularJS loader.js</a> and | |
| 214 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/2_loading_resources/loader.js">JavaScript loader.js</a> are the same: | |
| 215 </p> | |
| 216 | |
| 91 <pre> | 217 <pre> |
| 92 var loadImage = function(uri, callback) { | 218 var loadImage = function(uri, callback) { |
| 93 var xhr = new XMLHttpRequest(); | 219 var xhr = new XMLHttpRequest(); |
| 94 xhr.responseType = 'blob'; | 220 xhr.responseType = 'blob'; |
| 95 xhr.onload = function() { | 221 xhr.onload = function() { |
| 96 callback(window.webkitURL.createObjectURL(xhr.response), uri); | 222 callback(window.webkitURL.createObjectURL(xhr.response), uri); |
| 97 } | 223 } |
| 98 xhr.open('GET', uri, true); | 224 xhr.open('GET', uri, true); |
| 99 xhr.send(); | 225 xhr.send(); |
| 100 } | 226 } |
| 101 </pre></p></li> | 227 </pre> |
| 102 <li><p>In the <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/m aster/lab8_webresources/angularjs/2_loading_resources/controller.js">controller. js</a>, add a new method that will search the scope.todolist looking for images that are not loaded yet: | 228 |
| 103 <pre> | 229 <p>In the |
| 230 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/2_loading_resources/controller.js">AngularJS controller.js</ a> or | |
| 231 <a href="">JavaScript controller.js</a>, | |
| 232 add a new method that will search the Todo list looking for images that are not loaded yet: | |
| 233 </p> | |
| 234 | |
| 235 <tabs data-group="source"> | |
| 236 | |
| 237 <header tabindex="0" data-value="angular">Angular</header> | |
| 238 <header tabindex="0" data-value="js">JavaScript</header> | |
| 239 | |
| 240 <content> | |
| 241 <pre> | |
| 104 // for each image with no imageUrl, start a new loader | 242 // for each image with no imageUrl, start a new loader |
| 105 $scope.loadImages = function() { | 243 $scope.loadImages = function() { |
| 106 for (var i=0; i<$scope.todos.length; i++) { | 244 for (var i=0; i<$scope.todos.length; i++) { |
| 107 var todo=$scope.todos[i]; | 245 var todo=$scope.todos[i]; |
| 108 if (todo.validImage && todo.imageUrl===PLACEHOLDER_IMAGE) { | 246 if (todo.validImage && todo.imageUrl===PLACEHOLDER_IMAGE) { |
| 109 loadImage(todo.uri, function(blob_uri, requested_uri) { | 247 loadImage(todo.uri, function(blob_uri, requested_uri) { |
| 110 $scope.$apply(function(scope) { | 248 $scope.$apply(function(scope) { |
| 111 for (var k=0; k<scope.todos.length; k++) { | 249 for (var k=0; k<scope.todos.length; k++) { |
| 112 if (scope.todos[k].uri==requested_uri) { | 250 if (scope.todos[k].uri==requested_uri) { |
| 113 scope.todos[k].imageUrl = blob_uri; | 251 scope.todos[k].imageUrl = blob_uri; |
| 114 } | 252 } |
| 115 } | 253 } |
| 116 }); | 254 }); |
| 117 }); | 255 }); |
| 118 } | 256 } |
| 119 } | 257 } |
| 120 }; | 258 }; |
| 121 </pre></p></li> | 259 </pre> |
| 122 <li><p>In the <code>controller.js</code>, <code>drop()</code> method, change the handling of URIs to appropriately detect a valid image. For simplicity sake, we only tested for png and jpg extensions. Feel free to have a better coverage in your code. | 260 </content> |
| 261 <content> | |
| 262 <pre> | |
| 263 var maybeStartImageLoader = function(el, todo) { | |
| 264 var img = el.querySelector('img'); | |
| 265 if (todo['extras'] && todo.extras.validImage && todo.extras.imageUrl) { | |
| 266 if (todo.extras.imageUrl===PLACEHOLDER_IMAGE) { | |
| 267 img.src = PLACEHOLDER_IMAGE; | |
| 268 img.style.display = 'inline'; | |
| 269 window.loadImage(todo.extras.uri, function(blob_uri, requested_uri) { | |
| 270 todo.extras.imageUrl = blob_uri; | |
| 271 img.src = blob_uri; | |
| 272 }); | |
| 273 } else { | |
| 274 img.src = todo.extras.imageUrl; | |
| 275 img.style.display = 'inline'; | |
| 276 } | |
| 277 } else { | |
| 278 img.style.display = 'none'; | |
| 279 } | |
| 280 }; | |
| 281 </pre> | |
| 282 </content> | |
| 283 | |
| 284 </tabs> | |
| 285 | |
| 286 <p> | |
| 287 If writing your app in JavaScript, | |
| 288 you will need to call the <code>maybeStartImageLoader</code> function | |
| 289 in the | |
| 290 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/2_loading_resources/controller.js">JavaScript controller.js </a> | |
| 291 to update the Todo list from the model: | |
| 292 </p> | |
| 293 | |
| 123 <pre> | 294 <pre> |
| 295 var updateTodo = function(model) { | |
| 296 var todoElement = list.querySelector('li[data-id="'+model.id+'"]'); | |
| 297 if (todoElement) { | |
| 298 var checkbox = todoElement.querySelector('input[type="checkbox"]'); | |
| 299 var desc = todoElement.querySelector('span'); | |
| 300 checkbox.checked = model.isDone; | |
| 301 desc.innerText = model.text; | |
| 302 desc.className = "done-"+model.isDone; | |
| 303 | |
| 304 // load image, if this ToDo has image data | |
| 305 maybeStartImageLoader(todoElement, model); | |
| 306 } | |
| 307 } | |
| 308 </pre> | |
| 309 | |
| 310 <p>Then in the | |
| 311 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/angularjs/2_loading_resources/controller.js">AngularJS controller.js</ a> or | |
| 312 <a href="https://github.com/GoogleChrome/chrome-app-codelab/blob/master/lab8_web resources/javascript/2_loading_resources/controller.js">JavaScript.controller.js </a>, | |
| 313 <code>drop()</code> method, | |
| 314 change the handling of URIs to appropriately detect a valid image. | |
| 315 For simplicity sake, we only tested for png and jpg extensions. | |
| 316 Feel free to have a better coverage in your code. | |
| 317 </p> | |
| 318 | |
| 319 <tabs data-group="source"> | |
| 320 | |
| 321 <header tabindex="0" data-value="angular">Angular</header> | |
| 322 <header tabindex="0" data-value="js">JavaScript</header> | |
| 323 | |
| 324 <content> | |
| 325 <pre> | |
| 124 var uri=e.dataTransfer.getData("text/uri-list"); | 326 var uri=e.dataTransfer.getData("text/uri-list"); |
| 125 var todo = {text:uri, done:false, uri: uri}; | 327 var todo = {text:uri, done:false, uri: uri}; |
| 126 if (/.png$/.test(uri) || /.jpg$/.test(uri)) { | 328 if (/.png$/.test(uri) || /.jpg$/.test(uri)) { |
| 127 hasImage = true; | 329 hasImage = true; |
| 128 todo.validImage = true; | 330 todo.validImage = true; |
| 129 todo.imageUrl = PLACEHOLDER_IMAGE; | 331 todo.imageUrl = PLACEHOLDER_IMAGE; |
| 130 } | 332 } |
| 131 newTodos.push(todo); | 333 newTodos.push(todo); |
| 132 | 334 |
| 133 // [...] inside the $apply method, before save(), call the loadImages method: | 335 // [...] inside the $apply method, before save(), call the loadImages method: |
| 134 $scope.loadImages(); | 336 $scope.loadImages(); |
| 135 </pre></p></li><li><p>And, finally, we will change the load method to reset the Blob URLs, since Blob URLs don't span through sessions. | 337 </pre> |
| 136 Setting Todo's imageUrls to the PLACEHOLDER_IMAGE will force the loadImages method to request them again: | 338 </content> |
| 137 <pre> | 339 <content> |
| 340 <pre> | |
| 341 var uri = e.dataTransfer.getData("text/uri-list"); | |
| 342 var extras = { uri: uri }; | |
| 343 if (/\.png$/.test(uri) || /\.jpg$/.test(uri)) { | |
| 344 hasImage = true; | |
| 345 extras.validImage = true; | |
| 346 extras.imageUrl = PLACEHOLDER_IMAGE; | |
| 347 } | |
| 348 model.addTodo(uri, false, extras); | |
| 349 </pre> | |
| 350 </content> | |
| 351 | |
| 352 </tabs> | |
| 353 | |
| 354 <p>And, finally, we will change the load method to reset the Blob URLs, | |
| 355 since Blob URLs don't span through sessions. | |
| 356 Setting Todo's imageUrls to the PLACEHOLDER_IMAGE | |
| 357 will force the loadImages method to request them again: | |
| 358 </p> | |
| 359 | |
| 360 <tabs data-group="source"> | |
| 361 | |
| 362 <header tabindex="0" data-value="angular">Angular</header> | |
| 363 <header tabindex="0" data-value="js">JavaScript</header> | |
| 364 | |
| 365 <content> | |
| 366 <pre> | |
| 138 // If there is saved data in storage, use it. Otherwise, bootstrap with sample t odos | 367 // If there is saved data in storage, use it. Otherwise, bootstrap with sample t odos |
| 139 $scope.load = function(value) { | 368 $scope.load = function(value) { |
| 140 if (value && value.todolist) { | 369 if (value && value.todolist) { |
| 141 // ObjectURLs are revoked when the document is removed from memory, | 370 // ObjectURLs are revoked when the document is removed from memory, |
| 142 // so we need to reload all images. | 371 // so we need to reload all images. |
| 143 for (var i=0; i<value.todolist.length; i++) { | 372 for (var i=0; i<value.todolist.length; i++) { |
| 144 value.todolist[i].imageUrl = PLACEHOLDER_IMAGE; | 373 value.todolist[i].imageUrl = PLACEHOLDER_IMAGE; |
| 145 } | 374 } |
| 146 $scope.todos = value.todolist; | 375 $scope.todos = value.todolist; |
| 147 $scope.loadImages(); | 376 $scope.loadImages(); |
| 148 } else { | 377 } else { |
| 149 $scope.todos = [ | 378 $scope.todos = [ |
| 150 {text:'learn angular', done:true}, | 379 {text:'learn angular', done:true}, |
| 151 {text:'build an angular app', done:false}]; | 380 {text:'build an angular app', done:false}]; |
| 152 } | 381 } |
| 153 } | 382 } |
| 154 </pre></p></li> | 383 </pre> |
| 155 </ol> | 384 </content> |
| 385 <content> | |
| 386 <pre> | |
| 387 /** | |
| 388 * Listen to changes in the model and trigger the appropriate changes in the vie w | |
| 389 **/ | |
| 390 model.addListener(function(model, changeType, param) { | |
| 391 if ( changeType === 'reset' ) { | |
| 392 // let's invalidate all Blob URLs, since their lifetime is tied to the docum ent's lifetime | |
| 393 for (var id in model.todos) { | |
| 394 if (model.todos[id].extras && model.todos[id].extras.validImage) { | |
| 395 model.todos[id].extras.imageUrl = PLACEHOLDER_IMAGE; | |
| 396 } | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 if ( changeType === 'removed' || changeType === 'archived' || changeType === ' reset') { | |
| 401 redrawUI(model); | |
| 402 } else if ( changeType === 'added' ) { | |
| 403 drawTodo(model.todos[param], list); | |
| 404 } else if ( changeType === 'stateChanged') { | |
| 405 updateTodo(model.todos[param]); | |
| 406 } | |
| 407 storageSave(); | |
| 408 updateCounters(model); | |
| 409 }); | |
| 410 </pre> | |
| 411 </content> | |
| 412 | |
| 413 </tabs> | |
| 414 | |
| 415 <h3 id="results2">Check the results</h3> | |
| 156 | 416 |
| 157 <p>To test, open the app, right-click, and select Reload App. | 417 <p>To test, open the app, right-click, and select Reload App. |
| 158 Go to <a href="https://www.google.com/imghp?hl=en&tab=wi&authuser=0">Goo gle images</a>, search for and select an image, | 418 Go to |
| 419 <a href="https://www.google.com/imghp?hl=en&tab=wi&authuser=0">Google im ages</a>, | |
| 420 search for and select an image, | |
| 159 then drag and drop the image into the Todo list app. | 421 then drag and drop the image into the Todo list app. |
| 160 Assuming no mistakes were made, you should now have a thumbnail of every image U RL dropped into the Todo list app.</p> | 422 Assuming no mistakes were made, |
| 423 you should now have a thumbnail of every image URL dropped into the Todo list ap p.</p> | |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
Add "Notice that with this change we are not handl
mkearney1
2013/04/10 17:59:58
Done.
| |
| 161 | 424 |
| 162 <p class="note"><b>Note:</b> If you get stuck and want to see the app in action , go to <code>chrome://extensions</code>, | 425 <p>If you get stuck and want to see the app in action, |
| 163 load the unpacked <a href="https://github.com/GoogleChrome/chrome-app-codelab/tr ee/master/lab8_webresources/angularjs/2_loading_resources">2_loading_resources</ a>, and launch the app from a new tab.</p> | 426 go to <code>chrome://extensions</code>, load the unpacked |
| 427 <a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab8_web resources/angularjs/2_loading_resources">AngularJS 2_loading_resources</a> or, | |
| 428 <a href="https://github.com/GoogleChrome/chrome-app-codelab/tree/master/lab8_web resources/javascript/2_loading_resources">JavaScript 2_loading_resources</a> | |
| 429 and launch the app from a new tab.</p> | |
| 164 | 430 |
| 165 <p>The <code>loadImage()</code> method above is not the best solution for this p roblem, because it doesn't handle errors correctly and it could cache images in a local filesystem. | 431 <p>The <code>loadImage()</code> method above is not the best solution for this p roblem, because it doesn't handle errors correctly and it could cache images in a local filesystem. |
| 166 We are working on a library that will be much more robust and easier to use.</p> | 432 We are working on a library that will be much more robust and easier to use.</p> |
|
Renato Mangini (chromium)
2013/04/09 17:32:32
Link to the apps-resource-loader, and change the t
mkearney1
2013/04/10 17:59:58
Done.
| |
| 167 | 433 |
| 168 <h1 id="takeaways_">Takeaways:</h1> | 434 <h2 id="takeaways_">Takeaways</h2> |
| 169 | 435 |
| 170 <ul> | 436 <ul> |
| 171 <li><p>The <code><webview></code> tag allows you to have a controlled brow ser inside your app. | 437 <li><p>The <code><webview></code> tag allows you to have a controlled brow ser inside your app. |
| 172 You can use it if you have part of your application that is not CSP compatible a nd you don't have resources to migrate it immediately, for example. | 438 You can use it if you have part of your application that is not CSP compatible a nd you don't have resources to migrate it immediately, for example. |
| 173 One feature we didn't mention here is that WebViews can communicate with you r app and vice-versa using asynchronous <a href="https://developer.chrome.com/tr unk/apps/app_external.html#postMessage">postMessages</a>.</p></li> | 439 One feature we didn't mention here is that WebViews can communicate with you r app and vice-versa using asynchronous <a href="https://developer.chrome.com/tr unk/apps/app_external.html#postMessage">postMessages</a>.</p></li> |
| 174 <li><p>Loading resources like images from the web is not straightforward compare d to a standard web page. | 440 <li><p>Loading resources like images from the web is not straightforward compare d to a standard web page. |
| 175 But it's not too different from traditional native platforms, where you need to handle the resource download and, only when it is correctly downloaded, you can render it in the UI. We have also developed <a href="https://github.com/Goog leChrome/apps-resource-loader">a sample library</a> to asynchronously handle res ource loading from XHR calls. Feel free to use it in your projects.</p></li> | 441 But it's not too different from traditional native platforms, where you need to handle the resource download and, only when it is correctly downloaded, you can render it in the UI. We have also developed <a href="https://github.com/Goog leChrome/apps-resource-loader">a sample library</a> to asynchronously handle res ource loading from XHR calls. Feel free to use it in your projects.</p></li> |
| 176 </ul> | 442 </ul> |
| 177 | 443 |
| 178 <h1 id="what_39_s_next_">What's next?</h1> | 444 <h2 id="you_should_also_read">You should also read</h2> |
| 179 | 445 |
| 180 <p>In <a href="app_codelab9_multipleviews.html">lab9_multipleviews</a>, | 446 <ul> |
| 181 you will see how an app can have multiple windows that talk to each other and th e event page directly.</p> | 447 <li><a href="http://developer.chrome.com/trunk/apps/webview_tag.html">Webview Ta b API</a> reference</li> |
| 448 <li><a href="http://developer.chrome.com/apps/app_external.html">Embed Content</ a> tutorial</li> | |
| 449 </ul> | |
| 450 | |
| 451 <h2 id="what_39_s_next_">What's next?</h2> | |
| 452 | |
| 453 <p>In <a href="app_codelab_10_publishing.html">8 - Publish App</a>, | |
| 454 we finish off with instructions on how to publish your app in the Chrome Web Sto re.</p> | |
| OLD | NEW |