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:
...
resource://myaddonpackage/content/icon.png
resource://myaddonpackage/content/options.xul
...
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.
Do DTD files work for resource:// URLs?
@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.
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 😉
@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
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.
@Nils – Great information! Thanks for sharing. I’ll at least update my post to add the removal of the resource: alias.
@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.
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
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).
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
Thanks for writing this up; this removes one of the major roadblocks I was hitting in trying to restartless-ify some of my extensions.
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
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?
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
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?