Frame script loading and lifetime

Loading frame scripts

To load a frame script use the loadFrameScript() function.

This line of code loads a frame script into the currently selected tab. The frame script just writes  "foo" to the command line:

// chrome script
var mm = gBrowser.selectedBrowser.messageManager;
mm.loadFrameScript('data:,dump("foo\\n")', true);

loadFrameScript() takes two mandatory parameters:

  • A URL that points to the frame script you want to load
  • A boolean flag, allowDelayedLoad

Note that if the message manager is a global frame message manager or a window message manager , then loadFrameScript() may load the script multiple times, once into each applicable frame.

chrome: URLs

Extension developers will usually use a chrome:// URL to refer to the frame scripts.

To define the mapping between a chrome:// URL and a frame script packaged with the extension, use a "chrome.manifest" file to register a chrome URL:

// chrome.manifest
content my-e10s-extension chrome/content/
// chrome script
mm.loadFrameScript("chrome://my-e10s-extension/content/content.js", true);

allowDelayedLoad

If the message manager is a global frame message manager or a window message manager then:

  • If allowDelayedLoad is true, the frame script will be loaded into any new frame, which has opened after the loadFrameScript() call. For example:

    var mm = window.messageManager;
    mm.loadFrameScript("chrome://my-e10s-extension/content/frame-script.js", true);

    The script will be loaded into all tabs currently open in this window, and into any new tabs opened afterwards.

  • If allowDelayedLoad is false, then the script will only be loaded into frames that are open when the call was made.

If the message manager is a browser message manager, you should always pass true here. Because a browser message manager only ever corresponds to a single browser tab, its loadFrameScript() function will only ever load the frame script into that one tab. So passing allowDelayedLoad is just a way to ensure that the script gets loaded correctly, in case the tab is not ready when you make the call.

If you use allowDelayedLoad , you can cancel it by using removeDelayedFrameScript:

var mm = window.messageManager;
mm.removeDelayedFrameScript("chrome://my-e10s-extension/content/frame-script.js");

This means that we will stop loading the script into new tabs. Note that this function will not remove any scripts that have already been loaded earlier.

Frame script lifetime

Frame scripts are loaded as soon as loadFrameScript() is called. If you've set allowDelayedLoad, the script is loaded into a new tab as soon as it is created.

Frame scripts are associated with a browser tab and not with a page. So once you load them, they will stay loaded until the tab is closed, even if you reload the document or navigate. If you want to limit a script to the lifetime of a page you can create a Sandbox instead, with the current content page as prototype.

If you want a frame script to do something whenever a new document is loaded, you'll need to listen for an appropriate DOM event, generally DOMWindowCreated, DOMContentLoaded, or load.

Unloading frame scripts

Frame scripts are automatically unloaded when the tab hosting them is closed. There is currently no way to unload them once you have loaded them, other than closing the tab they were loaded into.

To listen for an event when your frame script is unloaded (due to tab close for instance) , you must set the third argument of addMessageListener to true, for example from bootstrap.js:

Services.mm.addMessageListener(
    'my-addon-id',
    {
        receiveMessage: function() {
            console.log('incoming message from frame script:', aMsg.data);
        }
    },
    true // must set this argument to true, otherwise sending message from framescript will not work during and after the unload event on the ContentMessageManager triggers
);

and then in your frame script listen for the unload event of the message manager (which is the global this), and then send a message. If you did not set the third argument to true in bootstrap.js on Services.mm.addMessageListener, then send this message during and after unloading event will do nothing.

var gContentFrameMessageManager = this;

addEventListener('unload', function(aEvent) {
    if (aEvent.target == gContentFrameMessageManager) {
        sendAsyncMessage('my-addon-id', 'framescript-died'); // if you did not set third argument of `Services.mm.addMessageListener` to `true`, then this will fail to send a message
    }
}, false);

Note about unload during uninstallation/upgrade

When your add-on is uninstalled or disabled, you should:

  • If you have used allowDelayedLoad, you can cancel it by calling removeDelayedFrameScript to ensure that the frame script is not loaded into any new tabs.
  • Disable any frame scripts that are already loaded. There is no mechanism to unload frame scripts that are already loaded. So you need to send a message to your frame scripts telling them to disable themselves (for example, by undoing any changes they've made or removing any event listeners).

There is a bug in non-e10s where this order is not true. In e10s framescripts work fine on updating. For non-e10s waiting for Bug 1202125 - framescripts are not backwards loaded in message order in non-e10s.

Note: you might think that there is a race condition here due to the asynchronous nature of the message passing:

  • Your add-on is disabled for an upgrade.
  • Your add-on broadcasts "disable" to your frame scripts.
  • Your add-on is upgraded, and the new code loads new frame scripts.
  • The new frame scripts receive the "disable" message and stop working.

In fact, the message manager guarantees that loadFrameScript and broadcastAsyncMessage are guaranteed to affect frame scripts in the order that they are called, so in this case "disable" will be received and consumed before the new frame scripts are loaded.

At the moment, frame scripts are cached until the browser restarts: this problem is tracked as bug 1051238. This is especially a problem for restartless add-ons, because when a new version of the add-on is installed, the old frame scripts will not be unloaded. The workaround here is to randomize the frame script's URL, for example by appending "?" + Math.random() to it.

Document Tags and Contributors

 Contributors to this page: Makyen, The_8472, rolfedh, PushpitaPikuDey, Noitidart, wbamberg
 Last updated by: Makyen,