Google Chrome extensions using KnockoutJS
Attempting to use KnockoutJS within a Google Chrome extension will swiftly result in the following error message.
Uncaught Error: Unable to parse bindings. Message: Error: Code generation from strings disallowed for this context;
This is due to Chrome's Content Security Policy
for extensions. Essentially, it blocks the use of eval
and
new Function
, which means features like Knockout's template
engine and data-bindings won't work.
Sandboxing your application
The solution to using KnockoutJS in a Google Chrome extension is to sandbox the extension's HTML page and javascript. This isolates them from the rest of the extension and allows the use of previously restricted features.
To sandbox an HTML page, we need to mention it in the extension's manifest.json
file.
Here's an example manifest that sandboxes a file named app.html
.
{ "name": "Knockout Chrome Extension", "version": "0.1", "manifest_version": 2, "description": "An example extension that uses KnockoutJS", "sandbox": { "pages": ["app.html"] }, ... }
The app.html
page can now reference knockout.js
and your other scripts. These referenced files are then also sandboxed.
However, it can no longer be loaded directly into your extension's browser action pop-up, or be displayed in a tab.
Loading the sandboxed application
A sandboxed HTML page needs to be loaded from a non-sandboxed page, using an <iframe>
.
For example, the extension can have a browser action pop-up,
specified by the following part of the manifest.json
:
{ ... "browser_action": { "default_popup": "popup.html", "default_title": "Knockout Chrome Extension", "default_icon": { "19": "icon19.png", "38": "icon38.png" } }, ... }
The popup.html
page serves as a container for the sandboxed application page.
<!DOCTYPE html> <html> <head> <script src="popup.js"></script> <head> <body style="padding: 0"> <iframe src="app.html" id="app" seamless></iframe> <body> <html>
Note the handy HTML5 seamless
attribute on the <iframe>
tag.
This removes all borders and scrollbars.
Communicating with the application
Anything loaded by a sandboxed page, such as scripts, are also sandboxed. This means your application script won't be able to use any of the Chrome APIs.
Scripts loaded by the un-sandboxed popup.html
do have access
to the Chrome APIs. Therefore, if your application needs to interact with
Chrome, it has to get the un-sandboxed script to do so.
To communicate between sandboxed and un-sandboxed windows, you have to use messages.
Here's a snippet from popup.js
, loaded by popup.html
,
which calls a Chrome API and then posts a message to the application.
var appWindow = document.getElementById("app").contentWindow; chrome.topSites.get(function(topSites) { appWindow.postMessage({ displayLinks: topSites }, "*"); });
MDN has great documentation about the window.postMessage method.
In the application script, we can listen for this message:
var app = { links: ko.observableArray() }; window.addEventListener( "message", function(event) { if (event.data.displayLinks) { app.links(event.data.displayLinks); } }, false ); ko.applyBindings(app);
Similarly, the application can post messages to the un-sandboxed script via
the parent
window property. For example:
window.parent.postMessage({ example: true }, "*")
Full sample code
To get the full sample code for a simple extension that uses KnockoutJS, join my web development mailing list. I'll give you access to a GitHub repository containing code samples, which you can copy and use in your own projects.