Bootstrapped extensions – Loading a stylesheet for the Firefox/SeaMonkey toolbar customization dialog

(This has all become a bit moot with Australis, but since SeaMonkey isn’t going to use that this info is still somewhat relevant)

For non-bootstrap extensions injecting stylesheets is simple, you just add it to the chrome.manifest file for each XUL document you want to overlay. This makes it easy to specify style info for your extension button when it is in the customization dialog (e.g. which icon it should use). For bootstrap extensions you can’t do this as the “style” directive isn’t allowed in a bootstrapped extension’s chrome.manifest. Instead you have to use the loadSheet() method on the window containing the xul file to inject the stylesheet.

This is fairly easy to do for the main browser window:

var uri = Services.io.newURI("chrome://sixornot/skin/base.css", null, null);
win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
   .getInterface(Components.interfaces.nsIDOMWindowUtils).loadSheet(uri, 1);

Where “win” is a reference to the window to inject the stylesheet into (which will be an instance of “chrome://browser/content/browser.xul” on Firefox, and “chrome://navigator/content/navigator.xul” on SeaMonkey).

In theory doing the same for the customize toolbar panel would just involve grabbing a reference to that window (which is contained within an iframe with ID “customizeToolbarSheetIFrame”) and injecting the stylesheet there. This is made a little more complicated because the content of that iframe (and indeed, on Firefox the iframe itself) doesn’t exist before the user opens that panel to customize their toolbars. In the non-bootstrap extension case the registration via chrome.manifest would mean that for *any* instance of “chrome://global/content/customizeToolbar.xul” that gets created our stylesheet would be injected. For our bootstrapped extension we have to watch for these instances being created and inject our stylesheet.

In the case of the customize toolbar panel we can watch for the events “beforecustomization” and “aftercustomization”. These are fired when the panel opens and closes respectively.

var on_beforecustomization = function (evt) {                                                
    var iframe = win.document.getElementById("customizeToolbarSheetIFrame");                 
    if (iframe) {                                                                            
        win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)                                                                                                      
           .getInterface(Components.interfaces.nsIDOMWindowUtils).loadSheet(uri, 1);
    }                                                                                     
};                                                                                           
var on_aftercustomization = function (evt) {                                                 
    var iframe = win.document.getElementById("customizeToolbarSheetIFrame");                 
    if (iframe) {                                                                            
        win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)                                                                                                      
           .getInterface(Components.interfaces.nsIDOMWindowUtils).removeSheet(uri, 1);
    }                                                                                  
};  

win.addEventListener("beforecustomization", on_beforecustomization, false);                  
win.addEventListener("aftercustomization", on_aftercustomization, false);

Experimentation reveals that the iframe is available at the point “beforecustomization” is fired – “aftercustomization” provides an opportunity to clean up the injected stylesheet (although in practice since the customizeToolbar.xul gets unloaded when the panel closes, this isn’t strictly necessary).

As a fun little bonus, Firefox (pre-Australis) and SeaMonkey have different requirements for your toolbarbutton’s styles. Pre-Australis there were two settings for toolbar icon sizes, small and large. Firefox only uses 16px square icons for both, except on Linux where “large” uses 24px square icons. SeaMonkey uses both 16px and 24px icons. In the customize palette Firefox (pre-Australis) displays a mock-up of the toolbarbutton – and so you need the 16px icon to display properly there. SeaMonkey (and Firefox with Australis) both use 32px icons in their toolbar palettes.

Fun.