Friday, January 27, 2012

Add-ons Sync Prefs: A Restartless Add-on for SeaMonkey 2.8

Ever since I ported Sync to SeaMonkey (2.1), the list of supported engines had been fixed: Bookmarks, History, Passwords, Preferences, and Tabs. With Firefox 11, the Sync developers added a new engine capable of syncing add-ons (or more precisely, which add-ons are installed). Since the Sync back-end is shared code, SeaMonkey 2.8 will include it, too. But while Firefox 11 will include UI for activating the new Add-ons engine, SeaMonkey 2.8 will lack it. This is because the Firefox changes landed pretty close to the l10n freeze so we were not able to port them in time. SeaMonkey 2.9 will include the UI changes, but why wait so long?

On January 31st, SeaMonkey 2.7 will be released (well, that's the plan). A few days later, the first beta of SeaMonkey 2.8 will follow. People like me who are on the beta channel will upgrade directly from 2.7b5 (ETA today) to 2.8b1. This is when my main working environment will be ready for Add-ons Sync.

Now I was wondering what the SeaMonkey developers could do to allow users of SeaMonkey 2.8 (on aurora, beta or release) to enable/disable the Add-ons Sync engine through preferences UI. Well, the answer is simple: Provide an add-on that adds the corresponding option!

I decided early on to give it a try myself and create that add-on. I also felt that users were probably more likely to install such an add-on if it didn't interrupt their work flow (i.e. not require a restart of the application, which in the case of SeaMonkey is often used for both browsing and mail) and took care to not break anything if it was installed on an incompatible version of SeaMonkey (say, 2.7 or 2.9). The first requirement could be solved through writing a restartless add-on, the latter by adding explicit checks to the code.

I quickly found some documentation for restartless add-ons on MDN (though they are called "bootstrapped" there). Unlike classic add-ons (which rely on XUL overlays and usually consist of many files and levels), a restartless add-on can be as simple as two files: The install manifest (install.rdf) and the code, written in JavaScript (bootstrap.js).

I guess for someone new to Mozilla-specific code, starting from scratch like this could be hard. During the development I faced several issues that you would probably not have seen with a classic XUL overlay approach. The Add-ons SDK might have been a better alternative if I had intended to write an add-on for Firefox, but since this one is SeaMonkey-only and given the requirements, there was not much of a choice.

The main issue I faced was actually in shared XBL code (preferences.xml): The constructor and destructor of preference elements throw if the element in question is not inside a preferences element. What is probably always the case with XUL (including overlays) is not true for elements dynamically added or removed through JavaScript (cloneNode/removeChild). In the end I had to replace cloneNode by createElement and stop using removeChild (i.e. the preference element is left in the preferences window until it is closed; if the add-on is disabled while the window is still open, the preference element just gets disabled).

Another surprise was that JavaScript errors inside bootstrap.js are reported as warnings in the Error Console. So if you enable the Errors filter, you won't see these warnings and assume everything is alright, while in fact the add-on does not work at all! Brilliant. :-(

Anyway, I was finally able to finish the add-on (with friendly help from Neil Rashbrook), upload it to AMO, and get it fully reviewed. :-) Here it is.

Note: If you look at the source code you might see that the prefs handling (user vs. default prefs) is not perfect yet. This is something that both Neil and Kris Maglione (AMO reviewer) pointed out, but which I deliberately disregarded for the first version of the add-on.