| <h1>External Content</h1> |
| |
| |
| <p> |
| The <a href="app_architecture.html#security">Chrome Apps security model</a> disallows |
| external content in iframes and |
| the use of inline scripting and <code>eval()</code>. |
| You can override these restrictions, |
| but your external content must be isolated from the app. |
| </p> |
| |
| <p> |
| Isolated content cannot directly |
| access the app's data or any of the APIs. |
| Use cross-origin XMLHttpRequests |
| and post-messaging to communicate between the event page and sandboxed content |
| and indirectly access the APIs. |
| </p> |
| |
| <p class="note"> |
| <b>API Sample: </b> |
| Want to play with the code? |
| Check out the |
| <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/sandbox">sandbox</a> sample. |
| </p> |
| |
| <h2 id="external">Referencing external resources</h2> |
| |
| <p> |
| The <a href="contentSecurityPolicy.html">Content Security Policy</a> used by apps disallows |
| the use of many kinds of remote URLs, so you can't directly reference external |
| images, stylesheets, or fonts from an app page. Instead, you can use use |
| cross-origin XMLHttpRequests to fetch these resources, |
| and then serve them via <code>blob:</code> URLs. |
| </p> |
| |
| <h3 id="manifest">Manifest requirement</h3> |
| |
| <p> |
| To be able to do cross-origin XMLHttpRequests, you'll need to add a permission |
| for the remote URL's host: |
| </p> |
| |
| <pre data-filename="manifest.json"> |
| "permissions": [ |
| "...", |
| "https://supersweetdomainbutnotcspfriendly.com/" |
| ] |
| </pre> |
| |
| <h3 id="cross-origin">Cross-origin XMLHttpRequest</h3> |
| |
| <p> |
| Fetch the remote URL into the app and serve its contents as a <code>blob:</code> |
| URL: |
| </p> |
| |
| <pre> |
| var xhr = new XMLHttpRequest(); |
| xhr.open('GET', 'https://supersweetdomainbutnotcspfriendly.com/image.png', true); |
| xhr.responseType = 'blob'; |
| xhr.onload = function(e) { |
| var img = document.createElement('img'); |
| img.src = window.webkitURL.createObjectURL(this.response); |
| document.body.appendChild(img); |
| }; |
| |
| xhr.send(); |
| </pre> |
| |
| <p>You may want to <a href="offline_apps.html#saving-locally">save</a> |
| these resources locally, so that they are available offline.</p> |
| |
| <h2 id="webview">Embed external web pages</h2> |
| |
| <p class="note"> |
| <b>API Sample: </b> |
| Want to play with the code? Check out the |
| <a href="https://github.com/GoogleChrome/chrome-app-samples/tree/master/browser">browser</a> |
| sample. |
| </p> |
| |
| <p> |
| The <code>webview</code> tag allows you to embed external web content in your |
| app, for example, a web page. It replaces iframes that point to remote URLs, |
| which are disabled inside Chrome Apps. Unlike iframes, the |
| <code>webview</code> tag runs in a separate process. This means that an exploit |
| inside of it will still be isolated and won't be able to gain elevated |
| privileges. Further, since its storage (cookies, etc.) is isolated from the app, |
| there is no way for the web content to access any of the app's data. |
| </p> |
| |
| <h3 id="webview_element">Add webview element</h3> |
| |
| <p> |
| Your <code>webview</code> element must include the URL to the source content |
| and specify its dimensions. |
| </p> |
| |
| <pre data-filename="browser.html"> |
| <webview src="http://news.google.com/" width="640" height="480"></webview> |
| </pre> |
| |
| <h3 id="properties">Update properties</h3> |
| |
| <p> |
| To dynamically change the <code>src</code>, <code>width</code> and |
| <code>height</code> properties of a <code>webview</code> tag, you can either |
| set those properties directly on the JavaScript object, or use the |
| <code>setAttribute</code> DOM function. |
| </p> |
| |
| <pre data-filename="browser.js"> |
| document.querySelector('#mywebview').src = |
| 'http://blog.chromium.org/'; |
| // or |
| document.querySelector('#mywebview').setAttribute( |
| 'src', 'http://blog.chromium.org/'); |
| </pre> |
| |
| <h2 id="sandboxing">Sandbox local content</h2> |
| |
| <p> |
| Sandboxing allows specified pages |
| to be served in a sandboxed, unique origin. |
| These pages are then exempt from their Content Security Policy. |
| Sandboxed pages can use iframes, inline scripting, |
| and <code>eval()</code>. |
| Check out the manifest field description for |
| <a href="manifest/sandbox.html">sandbox</a>. |
| </p> |
| |
| <p> |
| It's a trade-off though: |
| sandboxed pages can't use the chrome.* APIs. |
| If you need to do things like <code>eval()</code>, |
| go this route to be exempt from CSP, |
| but you won't be able to use the cool new stuff. |
| </p> |
| |
| <h3 id="inline_scripts">Use inline scripts in sandbox</h3> |
| |
| <p> |
| Here's a sample sandboxed page |
| which uses an inline script and <code>eval()</code>: |
| </p> |
| |
| <pre data-filename="sandboxed.html"> |
| <html> |
| <body> |
| <h1>Woot</h1> |
| <script> |
| document.write('I am an inline script.<br>'); |
| eval('document.write(\'I am an eval-ed inline script.\');'); |
| </script> |
| </body> |
| </html> |
| </pre> |
| |
| <h3 id="include_sandbox">Include sandbox in manifest</h3> |
| |
| <p> |
| You need to include the <code>sandbox</code> field in the manifest |
| and list the app pages to be served in a sandbox: |
| </p> |
| |
| <pre data-filename="manifest.json"> |
| "sandbox": { |
| "pages": ["sandboxed.html"] |
| } |
| </pre> |
| |
| <h3 id="opening_sandbox">Opening a sandboxed page in a window</h3> |
| |
| <p> |
| Just like any other app pages, |
| you can create a window that the sandboxed page opens in. |
| Here's a sample that creates two windows, |
| one for the main app window that isn't sandboxed, |
| and one for the sandboxed page: |
| </p> |
| |
| <p class="warning"> |
| NOTE: |
| <a href="https://code.google.com/p/chromium/issues/detail?id=154662">issue 154662</a> |
| is a bug when using sandbox pages to create windows. |
| The effect is that an error "Uncaught TypeError: Cannot call method |
| 'initializeAppWindow' of undefined" is output to the developer console |
| and that the app.window.create call does not call the callback function |
| with a window object. However, the sandbox page is created as a new window. |
| </p> |
| |
| <pre data-filename="background.js"> |
| chrome.app.runtime.onLaunched.addListener(function() { |
| chrome.app.window.create('window.html', { |
| 'bounds': { |
| 'width': 400, |
| 'height': 400, |
| 'left': 0, |
| 'top': 0 |
| } |
| }); |
| |
| chrome.app.window.create('sandboxed.html', { |
| 'bounds': { |
| 'width': 400, |
| 'height': 400, |
| 'left': 400, |
| 'top': 0 |
| } |
| }); |
| }); |
| </pre> |
| |
| <h3 id="embedding_sandbox">Embedding a sandboxed page in an app page</h3> |
| |
| <p>Sandboxed pages can also be embedded within another app page |
| using an <code>iframe</code>:</p> |
| |
| <pre data-filename="window.html"> |
| <!DOCTYPE html> |
| <html> |
| <head> |
| </head> |
| <body> |
| <p>I am normal app window.</p> |
| |
| <iframe src="sandboxed.html" width="300" height="200"></iframe> |
| </body> |
| </html> |
| </pre> |
| |
| |
| <h2 id="postMessage">Sending messages to sandboxed pages</h2> |
| |
| <p> |
| There are two parts to sending a message: |
| you need to post a message from the sender page/window, |
| and listen for messages on the receiving page/window. |
| </p> |
| |
| <h3 id="post_message">Post message</h3> |
| |
| <p> |
| You can use <code>postMessage</code> to communicate |
| between your app and sandboxed content. |
| Here's a sample background script |
| that posts a message to the sandboxed page it |
| opens: |
| </p> |
| |
| <pre data-filename="background.js"> |
| var myWin = null; |
| |
| chrome.app.runtime.onLaunched.addListener(function() { |
| chrome.app.window.create('sandboxed.html', { |
| 'bounds': { |
| 'width': 400, |
| 'height': 400 |
| } |
| }, function(win) { |
| myWin = win; |
| myWin.contentWindow.postMessage('Just wanted to say hey.', '*'); |
| }); |
| }); |
| </pre> |
| |
| <p> |
| Generally speaking on the web, |
| you want to specify the exact origin |
| from where the message is sent. |
| Chrome Apps have no access |
| to the unique origin of sandboxed content, |
| so you can only whitelist all origins |
| as acceptable origins ('*'). |
| On the receiving end, |
| you generally want to check the origin; |
| but since Chrome Apps content is contained, |
| it isn't necessary. |
| To find out more, |
| see <a href="https://developer.mozilla.org/en/DOM/window.postMessage">window.postMessage</a>. |
| </p> |
| |
| <h3 id="listen_message">Listen for message</h3> |
| |
| <p> |
| Here's a sample message receiver |
| that gets added to your sandboxed page: |
| </p> |
| |
| <pre data-filename="sandboxed.html"> |
| var messageHandler = function(e) { |
| console.log('Background script says hello.', e.data); |
| }; |
| |
| window.addEventListener('message', messageHandler); |
| </pre> |
| |
| <p class="backtotop"><a href="#top">Back to top</a></p> |