Skip to content Skip to sidebar Skip to footer

Customevent.detail "tainted"?

I'm developing a Chrome extension to add convenience to a website. I have access to the page's DOM, but I also need to interact with the 'first-party' JS on that page, which I cann

Solution 1:

What I found out

I initially thought this was a security feature, mostly because Firefox behaves that way.

In ran an equivalent test in Firefox by putting the event listener in a separate file that could be loaded via mozIJSSubScriptLoader:

test.js:

(function()
{
    window.addEventListener('xyz', function(ev)
    {
        console.log('after dispatch:');
        console.log(ev.detail);
    });
})();

firefox.js:

(function()
{
    var mozIJSSubScriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
    window.addEventListener('load', functionload(event)
    {
        window.removeEventListener('load', load);
        window.gBrowser.addEventListener('DOMContentLoaded', function(event)
        {
            mozIJSSubScriptLoader.loadSubScript('chrome://my-extension/content/test.js', window.content, 'UTF-8');
            // JSON-serializable datavar e = newCustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger' } });
            console.log('before dispatch:')
            console.log(e.detail);
            window.content.dispatchEvent(e);
            // non-JSON-serializable data
            e = newCustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger', func: function(){} } });
            console.log('before dispatch:');
            console.log(e.detail);
            window.content.dispatchEvent(e);
        });
    });
})();

Result:

console log

(Note that the error occurs twice.)

So in Firefox it doesn't even matter what detail contains - as long as it comes from an extension, the page is not allowed to access it. Looks like a security feature to me.

The reason I put the above in a quote is because this is somewhat different in Chrome!

After some deeper investigation it looks like although the extension and the page share the DOM tree, they exist in two different contexts. I don't know whether this is actually a security feature or just a technical consequence, but this, of course, has the consequence that only clonable objects can be passed back and forth.

What puzzles me though is the fact that the operation silently fails, when, according to the HTML standard, §2.7.5 (structured clone), the entire operation should fail with an error:

↪ If input is another native object type (e.g. Error, Function) ↪ If input is a host object (e.g. a DOM node)             Throw a DataCloneError exception and abort the overall structured clone algorithm.

Workaround

I ended up using a fairly easy (although not so pretty) workaround: In Chrome, there's no equivalent to mozIJSSubScriptLoader, but you're allowed to append <script> tags to a page from within your extension (you're not allowed to do that in FF). Together with chrome.extension.getURL, that can be used to run a JS file packaged with the extension in the context of the page:

(function()
{
    var script = document.createElement('script');
    script.src = chrome.extension.getURL('extension.js');
    document.head.appendChild(script);
})();

Of course that requires that

"web_accessible_resources":["extension.js"]

is set in manifest.json, which isn't pretty, but shouldn't be an actual problem.

The drawback of this is, of course, that from within extension.js you no longer have access to any chrome API your extension has access to, but in my case I didn't need that. It wouldn't be too difficult to set up a proxy via CustomEvent for that though, as the biggest part of the Chrome API only requires and returns data that is clonable.

Post a Comment for "Customevent.detail "tainted"?"