Skip to content

Instantly share code, notes, and snippets.

@lkuper
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lkuper/3672f5f49f431f90e912 to your computer and use it in GitHub Desktop.
Save lkuper/3672f5f49f431f90e912 to your computer and use it in GitHub Desktop.
Components.utils.import('resource://gre/modules/Services.jsm');
// Code that listens for a custom DOM event. This is how we
// implement communication between unprivileged (web page) and
// privileged (extension) JS code.
function load(win) {
let document = win.document;
document.addEventListener("TestExtensionCustomEvent", function(e) {
win.alert("Hello from privileged code! Event received!");
}, false, true);
}
function unload(win) {
let document = win.document;
document.removeEventListener("TestExtensionCustomEvent", arguments.callee, false);
}
// Listener to make sure that stuff happens in future windows. Not to
// be confused with our custom event listener, above.
var listener = {
onOpenWindow: function(aWindow) {
// Wait for the window to finish loading
let win = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowInternal);
win.addEventListener("UIReady", function(aEvent) {
win.removeEventListener(aEvent.name, arguments.callee, false);
load(win);
}, false);
},
// Unused:
onCloseWindow: function(aWindow) { },
onWindowTitleChange: function(aWindow, aTitle) { }
};
function startup(data, reason) {
// Load in existing windows.
let enumerator = Services.wm.getEnumerator("navigator:browser");
while(enumerator.hasMoreElements()) {
let win = enumerator.getNext();
load(win);
}
// Load in future windows.
Services.wm.addListener(listener);
}
function shutdown(data, reason) {
Services.wm.removeListener(listener);
if (reason == APP_SHUTDOWN)
return;
let enumerator = Services.wm.getEnumerator("navigator:browser");
while(enumerator.hasMoreElements()) {
let win = enumerator.getNext();
unload(win);
}
}
function install(data, reason) {
}
function uninstall(data, reason) {
}
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<!-- This is the extension ID. It has to be in the format of an
email address, but it need not actually be an email address!
-->
<em:id>test-extension@example.com</em:id>
<em:version>0.1</em:version>
<em:name>Test Extension</em:name>
<em:description>
An example extension for test purposes.
</em:description>
<em:creator>Lindsey Kuper</em:creator>
<em:unpack>true</em:unpack>
<em:type>2</em:type>
<em:targetApplication>
<Description>
<!-- Firefox's application ID. -->
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<!-- I have no idea what the correct minimum version is. 1.5 is a guess. -->
<em:minVersion>1.5</em:minVersion>
<!-- 32 is the current release of Firefox, as of today. -->
<em:maxVersion>32.*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Mark this extension as restartless. -->
<em:bootstrap>true</em:bootstrap>
</Description>
</RDF>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Extension test page</title>
<script type="text/javascript">
function run() {
alert("Hello from unprivileged code!");
var element = document.createElement("TestExtensionCustomElement");
// If we wanted to pass arguments, we could do so here by setting
// attributes on a custom element, as described in
// https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
document.documentElement.appendChild(element);
var evt = document.createEvent("Events");
evt.initEvent("TestExtensionCustomEvent", true, false);
element.dispatchEvent(evt);
}
</script>
</head>
<body>
<div><button type="button" onclick="run();">Click me</button></div>
</body>
</html>
@lkuper
Copy link
Author

lkuper commented Oct 9, 2014

Who knows things about restartless Firefox extensions and wants to help me out?

What I'm trying to do is implement communication between a web page and the extension using something like the trick described on https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Interaction_between_privileged_and_non-privileged_pages . However, although that seemed to work fine when I had a traditional, non-restartless extension that used a XUL overlay, it doesn't seem to work anymore. What I see when I click the button on testpage.html is the "Hello from unprivileged code!" alert, followed by nothing. The startup() function doesn't actually seem to run at all. No errors show up in the browser console. Anyone know what's up? Is there someplace other than startup() where I should be creating the event listener?

@mbrubeck
Copy link

mbrubeck commented Oct 9, 2014

You can't access document inside of your startup function because it isn't running in any particular window. Instead, you can use the window manager service to run a function in all current and future browser windows (example).

This is different from a XUL overlay-based add-on, which is automatically loaded once per browser window.

@lkuper
Copy link
Author

lkuper commented Oct 9, 2014

@mbrubeck Thanks, that makes sense. Here's a stab at doing it that way, but I'm getting the same results as before. Any idea what might be going wrong?

Cu.import('resource://gre/modules/Services.jsm');

// Code that listens for a custom DOM event.  This is how we
// implement communication between unprivileged (web page) and
// privileged (extension) JS code.
function load(win) {
    let document = win.document;
    document.addEventListener("TestExtensionCustomEvent", function(e) {
        alert("Hello from privileged code! Event received!");
    }, false);
}

function unload(win) {
    let document = win.document;
    document.removeEventListener("TestExtensionCustomEvent", arguments.callee, false);
}

// Listener to make sure that stuff happens in future windows.  Not to
// be confused with our custom event listener, above.
var listener = {
    onOpenWindow: function(aWindow) {
        // Wait for the window to finish loading
        let win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal);
        win.addEventListener("UIReady", function(aEvent) {
            win.removeEventListener(aEvent.name, arguments.callee, false);
            load(win);
        }, false);
    },

    // Unused:
    onCloseWindow: function(aWindow) { },
    onWindowTitleChange: function(aWindow, aTitle) { }
};

function startup(data, reason) {
    // Load in existing windows.
    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while(enumerator.hasMoreElements()) {
        let win = enumerator.getNext();
        load(win);
    }

    // Load in future windows.
    Services.wm.addListener(listener);
}

function shutdown(data, reason) {
    Services.wm.removeListener(listener);
    if (reason == APP_SHUTDOWN)
        return;

    let enumerator = Services.wm.getEnumerator("navigator:browser");
    while(enumerator.hasMoreElements()) {
        let win = enumerator.getNext();
        unload(win);
    }
}

function install(data, reason) {

}

function uninstall(data, reason) {

}

@mbrubeck
Copy link

mbrubeck commented Oct 9, 2014

If there's a problem there, I haven't managed to spot it yet...

@lkuper
Copy link
Author

lkuper commented Oct 9, 2014

Got it working with @mbrubeck's help and updated the gist! The problems with the version in my comment above:

  • can't use Cu and Ci, have to spell out Components.utils and Components.interfaces
  • alert() by itself won't work here
  • and, most importantly, I forgot the extra true argument to addEventListener that makes it listen for events coming from unprivileged web page code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment