blob: 2d707679d4b248b6863ec912ebe0b26e07476767 [file] [log] [blame]
<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">
&lt;webview src="http://news.google.com/" width="640" height="480">&lt;/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">
&lt;html>
&lt;body>
&lt;h1>Woot&lt;/h1>
&lt;script>
document.write('I am an inline script.&lt;br>');
eval('document.write(\'I am an eval-ed inline script.\');');
&lt;/script>
&lt;/body>
&lt;/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">
&lt;!DOCTYPE html>
&lt;html>
&lt;head>
&lt;/head>
&lt;body>
&lt;p>I am normal app window.&lt;/p>
&lt;iframe src="sandboxed.html" width="300" height="200">&lt;/iframe>
&lt;/body>
&lt;/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>