Restartless Add-ons – More Resources

After playing around with bootstrap (restartless) add-ons a bit more, I came to the point where I needed some extra resources in the add-on. For images, I could have stuck with the data: URI approach I mentioned previously, but I also wanted to add an options UI for my add-on. Turns out it was pretty simple. Thanks to Vlad for getting the ball rolling.

The trick I am using now is to programmatically add a resource: alias for my add-on. A resource: alias creates a handy way for you to create URIs pointing to resources, like scripts and images, in your add-on bundle. It’s pretty simple to add a resource: URI pointing to the add-on’s root folder or XPI bundle. Here is the code snippet taken from my bootstrap.js file:


function startup(aData, aReason) {
  let resource = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
  let alias = Services.io.newFileURI(aData.installPath);
  if (!aData.installPath.isDirectory())
    alias = Services.io.newURI("jar:" + alias.spec + "!/", null, null);
  resource.setSubstitution("myaddonpackage", alias);
  ...
}

function shutdown(aData, aReason) {
  if (aReason == APP_SHUTDOWN) return;

  let resource = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
  resource.setSubstitution("myaddonpackage", null);
  ...

The isDirectory check is needed because it is possible for add-ons to be installed in folders or XPI bundles, which require the jar: URI syntax. The code adds a resource alias, after which you can use URIs like this:


resource://myaddonpackage/images/someimage.png
resource://myaddonpackage/content/somescript.js

In these examples, images and content are folders inside your add-on bundle. Now you can add URI-based resources to your install.rdf like this:


  ...
  <em:iconURL>resource://myaddonpackage/content/icon.png</em:iconURL>
  <em:optionsURL>resource://myaddonpackage/content/options.xul</em:optionsURL>
  ...

Yes, the em:optionsURL works just fine for providing an options UI in your add-on. I hope this tip gives you more help converting your traditional add-on to a restartless add-on.

Updated: I added the removal of the resource: alias on shutdown based on feedback from Nils in comments. Check out his link to some great bootstrap add-on information too. The part about stale caches after an add-on update presents a painful problem. Waldimir has also previously posted about the limitations of bootstrapped add-ons.

16 Comments

  1. Wladimir Palant said,

    January 22, 2011 @ 4:49 pm

    Do DTD files work for resource:// URLs?

  2. Tweets that mention Mark Finkle’s Weblog » Restartless Add-ons – More Resources -- Topsy.com said,

    January 22, 2011 @ 5:03 pm

    […] This post was mentioned on Twitter by Mark Finkle and Planet Mozilla, sin3rss. sin3rss said: Restartless Add-ons – More Resources: After playing around with bootstrap (restartless) add-ons a bit more, I ca… http://bit.ly/fcj4hn […]

  3. Mark Finkle said,

    January 22, 2011 @ 6:30 pm

    @Wladimir – No. DTD files do not seem to load from resource: URLs. Looks like we are failing here:
    http://mxr.mozilla.org/mozilla-central/source/parser/htmlparser/src/nsExpatDriver.cpp#776

    Even if it did work, the automatic detection of the current locale would not work. That only works for chrome: URLs, iirc.

    Looks like we’d need some gettext like solution for l10n.

  4. Mook said,

    January 22, 2011 @ 7:38 pm

    Or some sane way to register chrome urls, i.e. fixing bug 564667. I just did https://github.com/mook/uploadscreenshot/blob/master/bootstrap.js – which works, but most definitely isn’t a sane way ;)

  5. Mark Finkle said,

    January 22, 2011 @ 8:17 pm

    @Mook – It might be easier to use a DirectoryServiceProvider and respond to the NS_CHROME_MANIFESTS_FILE_LIST. Then, force a manifest check using nsIChromeRegistry.checkForNewChrome()

    Note: I used this in XULExplorer, but have not tried it since the chrome manifest system was overhauled.

    http://viewvc.svn.mozilla.org/vc/projects/xul-explorer/trunk/chrome/content/explorer.js?revision=20121&view=co

  6. Nils Maier said,

    January 22, 2011 @ 10:33 pm

    Your code isn’t correct unfortunately.
    The XUL/XPC cache will be used, and hence when you update the addon in session, the resources will return stale cache entries from your previous addon version.
    Please see e.g. the section about js modules, which should apply to XUL as well.
    http://maglione-k.users.sourceforge.net/bootstrapped.xhtml
    You must register your resource substitution under different package names for each version.
    Memory leaks and growing on-disk xul cache still included… Jetpack basically does the same, btw.

    Don’t forget to close your xul windows (your prefwindow) on |shutdown|.

    Registering chrome would have the same limitations + you cannot unregister the chrome again.

  7. Mark Finkle said,

    January 23, 2011 @ 12:21 am

    @Nils – Great information! Thanks for sharing. I’ll at least update my post to add the removal of the resource: alias.

  8. Mark Finkle said,

    January 23, 2011 @ 1:02 am

    @Nils – When you mention caching issues related to XUL, by what means is the XUL getting cached? In Fennec, for example, we load the options UI by doing an XHR GET on the XUL (resource://foo/bar.xul in this case). Then we append the DOM nodes from the XHR.responseXML to the main document DOM. I don’t think the XUL cache comes into play in that scenario. We do the same thing for importDialog:

    http://mxr.mozilla.org/mobile-browser/source/chrome/content/bindings/extensions.xml#101
    http://mxr.mozilla.org/mobile-browser/source/chrome/content/browser.js#2305

    In these cases, removing the resource: alias when shutting down and adding it back when the new version updates might be fine.

  9. KWierso said,

    January 23, 2011 @ 1:11 am

    I’m getting the following error when I try to do this:
    Warning: WARN addons.xpi: Exception running bootstrap method startup on jid0-QVXoZN2I0qxeM6fXf1FoJU0cO7I@jetpack: ReferenceError: Services is not defined
    Source File: resource://gre/modules/XPIProvider.jsm -> file:///C:/Users/admin/AppData/Roaming/Mozilla/Firefox/Profiles/zwexmy17.x64prof/extensions/jid0-QVXoZN2I0qxeM6fXf1FoJU0cO7I@jetpack/bootstrap.js
    Line: 142

  10. KWierso said,

    January 23, 2011 @ 2:47 am

    Followup to my previous comment for anyone who cares (aside from mfinkle, who helped me with this on IRC):

    You need to import Services.jsm in bootstrap.js to solve that error.

    Beyond that, apparently you can’t use XUL in this with Firefox 4,triggers the Disabled Remote XUL error page when you try to load the resource (eg, click the Options button for the addon), as it doesn’t have Fennec’s XHR GET behavior to work around this.

    I filed https://bugzilla.mozilla.org/show_bug.cgi?id=628089 in the hopes that this can be worked around (or more likely explained away and marked invalid).

  11. Edward Lee said,

    January 23, 2011 @ 11:42 am

    You can use getResourceURI to get a URI without assuming the files are located in a jar.

    https://github.com/Mardak/restartless/commit/ae8f8e7a54c4ad2c484f5666cb38f09e7af98791#L0R107

  12. Chris Finke said,

    January 24, 2011 @ 12:56 am

    Thanks for writing this up; this removes one of the major roadblocks I was hitting in trying to restartless-ify some of my extensions.

  13. John J Barton said,

    January 24, 2011 @ 7:37 pm

    When I try statements like:
    Components.utils.import(“resource://firebug/firebug-trace-service.js”);

    in bootstrap.js I get exceptions. Is this supposed to work?

    jjb

  14. Mark Finkle said,

    January 24, 2011 @ 11:50 pm

    I use Components.utils.import(“resource://gre/modules/Services.jsm”); without any trouble. I have seen Components.utils.import(“resource://gre/modules/XPCOMUtils.jsm”); used too.

    What kind of exceptions? I assume the bootstrapped add-on using Firebug and Firebug is already installed?

  15. johnjbarton said,

    January 25, 2011 @ 1:13 am

    Yes, I can load Services.jsm. I can’t load any of mine,even ones that I load in other JS code and which load into the browser.

    Error: bootstrap 0 ERROR [Exception… “Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIXPCComponents_Utils.import]” nsresult: “0x80070057 (NS_ERROR_ILLEGAL_VALUE)” location: “JS frame :: resource://gre/modules/XPIProvider.jsm -> file:///C:/jjb/eclipse/fbugWorkspace/fbug/extensions/moduleloader/branches/moduleloader0.1/bootstrap.js :: :: line 6″ data: no]
    Source File: resource://gre/modules/XPIProvider.jsm -> file:///C:/jjb/eclipse/fbugWorkspace/fbug/extensions/moduleloader/branches/moduleloader0.1/bootstrap.js
    Line: 11

  16. Maria De Souza said,

    January 28, 2011 @ 7:09 pm

    I followed your method and could successfully load my images provided they are placed as is in my xpi.

    Is it possible to create the resource: alias if the image and js files are packaged in a jar file within my extension xpi?

    This is my directory structure:
    SampleExtension.xpi
    Install.rdf
    chrome.manifest
    bootstrap.js
    chrome\tb.jar

    How will I go about creating the resource: alias to access my images and javascript files located within tb.jar?

RSS feed for comments on this post