How we're breaking some extensions in the near future

You may have read some reports about how we’re re-implementing the bulk of the extension manager in Firefox. It’s been a long running project (something like a year since I first really started planning how to do it). Things are finally started to come together and all being well we are likely to look at landing the first pieces of this on the trunk nightlies in as little as a weeks time. I’ll be up front, this isn’t going to be a perfect landing. There may be some thing that are missing and other bits where the user experience isn’t as perfect as it will be finally. Of course there may also be bugs we have to rush to fix. Despite all this we feel that we’re about at the point where exposing it to the hands of thousands of nightly testers is the best way forward. Your eyes spot things that we miss, even things that may seem obvious to you and you’re vital to us getting these sorts of features polished and really just how they should be before they get released to the world at large in a Firefox release.

One thing I wanted to mention is how this will affect the extensions themselves. In many cases as you might hope there will be no change at all, the extensions will continue to work as they did on previous trunk builds. Those that do anything to the add-ons manager UI will see obvious problems since that UI has changed considerably. There is also a set of extensions that are likely to see issues because of API changes. This is pretty much normal for any feature but I thought I’d mention two common uses that extension developers are going to have to change in their code.

# Accessing file and version information for extensions

The extension manager interface as it used to exist is gone with the new code. Some extensions would have used this to access their extension’s version and packaged files. There is a replacement so it mostly just means changing code to something similar to this:

Components.utils.import("resource://gre/modules/AddonManager.jsm");

AddonManager.getAddonByID("my-addon@foo.com", function(addon) {
  alert("My extension's version is " + addon.version);
  alert("Did I remember to include that file.txt file in my XPI? " +
        addon.hasResource("file.txt") ? "YES!" : "No :(");
  alert("Let's pretend I did, it's available from the URL " + addon.getResourceURL("file.txt"));
});

Hopefully to developers who used the old APIs this will look a lot nicer than it used to, I certainly prefer it. There are a couple of things to note. The old API would give you an nsIFile directly to play with. The new API gives you a URL. It is currently possible to get an nsIFile from this (as it is a file:// url), however in the future we may be no longer extracting the files out of an XPI for an extension so it is worth trying to just use the URL where you can. We might add a simple method for getting say an input stream to the files as well.

The second thing to note is that this API is asynchronous. As performance becomes more and more of an issue for the platform we have to start moving APIs to be asynchronous to allow file accesses to happen on background threads leaving the UI responsive while we wait for data. In most cases this probably won’t cause developers much of a problem, unfortunately it is difficult to make this appear to operate synchronously safely. People may know the trick of spinning an event loop while you wait for data, that is kind of unsafe since it can allow unexpected reeentrancy into code that isn’t anticipating it.

This change to an asynchronous API brings up the other main change that I am sad to have to make but cannot see a way around.

# FUEL has to change

FUEL was designed to be a simple stable wrapper around the nitty gritty world of parts of XPCOM and XUL. It has I believed served fairly well in that purpose though in retrospect there are some things it might have been nice to have different about it. The problem right now though is that FUEL has wrappers around the old extension manager and they are synchronous. They simply cannot stay and work with the new extension manage, which means we have to change a part of the FUEL API slightly. This is unfortunate but I’ve tried to make the change as low impact as possible, there is in fact just a single change. Previously to get an add-ons version you might have done:

alert(Application.extensions.get("my-addon@foo.com").version);

Now you will have to do:

Application.getExtensions(function(extensions) {
  alert(extensions.get("my-addon@foo.com").version);
});

Again it switches from synchronous to asynchronous, but that is really the only change here. The extensions object passed into the callback function is basically the same as the current Application.extensions object, only a slight difference in that it is a snapshot of the list of extensions at the time of retrieval. That doesn’t make a great deal of difference for normal extensions as they cannot install or uninstall without a restart but it may have an effect if you are looking at the new restartless add-ons, in which case you can just get a new copy of the extensions object.