Exchanging Data Between Chrome and Content

Scenario: a chrome and a content application are aware of their respective existence and wish to communicate. Communication should be able to flow both ways.

One possibility for chrome→content communication is for chrome to invoke Javascript functions defined in content. However, this would only work for DOM objects and properties (and for good reason), unless XPCNativeWrappers are disabled.

One possibility for chrome←content communication is for content to ask the user to grant it expanded privileges and then invoke chrome functions by itself. This opens a door much wider than necessary, increases the coupling between the remote and the local side, and nags the user.

Another possibility is described here.

Let there be two invisible <div> elements in content: <div id="for-chrome"> and <div id="for-content">.

Code living in content writes what it wants to be sent to chrome into <div id="for-chrome">; code living in chrome writes what it wants to be sent to content to <div id="for-content">; both register event listeners that tell them when the <div> they’re interested in gets new data.

Example of content XHTML, content.html:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Sample</title>
    <script type="text/javascript" src="content.js"/>
  </head>
  <body onload="init(event);">
    <div id="for-content"/>
    <div id="for-chrome"/>
  </body>
</html>

Example of content Javascript, content.js:


function init(event) {
    document.getElementById('for-content').addEventListener(
        'DOMNodeInserted', function(event) {
            receivedFromChrome(event.target.textContent);
        }, false);
}

function receivedFromChrome(data) {
    alert('Content received: ' + data);
}

function sendToChrome(data) {
    document.getElementById('for-chrome').textContent = data;
}

Example of chrome Javascript:


content.document.getElementById('for-chrome').addEventListener(
    'DOMNodeInserted', function(event) {
        receivedFromContent(event.target.textContent);
    }, false);

function sendToContent(data) {
    document.getElementById('for-content').textContent = data;
}

Going a step further and sending/expecting XML data is easy and very low overhead, courtesy of E4X. content.js then becomes:


function init(event) {
    document.getElementById('for-content').addEventListener(
        'DOMNodeInserted', function(event) {
            receivedFromChrome(new XML(event.target.textContent));
        }, false);
}

function receivedFromChrome(data) {
    alert('Content received: ' + data.toXMLString());
}

function sendToChrome(data) {
    document.getElementById('for-chrome').textContent =
        typeof(data) == 'xml' ? data.toXMLString() : data;
}

chrome.js:


content.document.getElementById('for-chrome').addEventListener(
    'DOMNodeInserted', function(event) {
        receivedFromContent(new XML(event.target.textContent));
    }, false);

function sendToContent(data) {
    document.getElementById('for-content').textContent =
        typeof(data) == 'xml' ? data.toXMLString() : data;
}


Where will one want this? Probably, in scenarios where the chrome application is expecting data from the content application that could come at any time, not just as a result of a chrome-initiated query, and one doesn’t want to sign scripts or nag users with requests for extra content privileges. The communication channel is still opt-in, although it’s the chrome code (which is trusted already) that opens it by registering the event listener, and it’s a much narrower channel with regard to security: an attacker would have to get hold of the content application and to craft data specific to the chrome-content protocol and the chrome code handling the protocol would have to contain security holes in the first place.

Share
  • Trackbacks are closed
  • Comments (1)
Comments are closed.