Adding a Toolbar Button in a Bootstrapped Firefox Extension

14 December 2012 by Matthew Gertner - Category: Technical Articles

Adding a toolbar button in a Firefox extension is simple. Since the Firefox user interface is described using XUL, it is enough to inject a snippet of XUL into the “toolbar palette” in browser.xul. Traditionally this would be done with a XUL overlay. Experienced XUL developers know that a button added like this won’t show up in the toolbar unless it is added explicitly by the user (using View/Toolbars/Customize…). This can be solved by adding the button to the toolbar manually and saving the toolbar’s “currentset”, which contains the list of buttons it contains. This should be done on first run only to ensure that the user’s settings are preserved if they remove the button later.

But what if you are writing a bootstrapped extension? XUL overlays are not supported, so you have to add the button to the toolbar palette by hand. Coordinating the toolbar palette, the visible toolbar and the toolbar’s persistent current set isn’t rocket science, but it is a bit tricky. You want to make sure that the button is added to the toolbar on first run and is visible on subsequent runs. At the same time, it should be possible to remove it and add it back using the toolbar customization feature.

After some trial and error, as well as plundering Dmitry Gutov‘s nicely written Replace Bookmark¬†extension for ideas, I came up with the following function:

This function ensures that the button is always added to the palette (i.e. what the overlay would normally do). On first run, it adds it to the far-right of the toolbar. On subsequent runs, it inserts the button manually into the correct position if it is in the currentset. This is the only bit that is substantially different from the behavior in a traditional extension. I suspect this is necessary because the button is not in the palette yet at the point when Firefox processes the currentset and adds the buttons automatically.

Remember that you should remove the button from the palette and toolbar manually when the extension is disabled or uninstalled to avoid memory leaks.


« - »
COMMENTS
  • http://adblockplus.org/blog/ Wladimir Palant

    Unfortunately, there is a catch. If the user disables your extension and customizes the toolbar while it is disabled your toolbar button will be gone forever. Which is why I use a more complicated approach in Adblock Plus – but unfortunately it doesn’t work reliably either. Toolbar buttons are really a pain, especially when your extension is restartless…

    • Matthew Gertner

      What do you mean by “gone forever”? I add it explicitly to the palette so it will be there after the extension is reenabled. It’s true that it won’t be added back into the toolbar. I suppose that could be fixed by e.g. setting a pref before disabling (a teeny bit messy) or always re-adding the button when the extension is reenabled (a teeny bit rude).

      • http://adblockplus.org/blog/ Wladimir Palant

        Right, it will still be in palette – but most people won’t know how to add it back. Especially since they likely didn’t customize the toolbar themselves, typically it was some other extension persisting the currentset attribute. Also, persisting currentset attribute at random times didn’t go well with SDK-based add-ons (don’t remember the details any more).

        My solution was to stay clear of the currentset attribute that has too much code messing with it already and use my own preference to store button location and state. Unfortunate complication: generic elements like separators change their ID each time the toolbar is customized. Plus there is https://bugzilla.mozilla.org/show_bug.cgi?id=575500 which makes sure that the button might be displayed in the wrong position even though you insert it correctly.

        • Matthew Gertner

          So what’s wrong with setting firstRun to true if the extension is being enabled so that the button is added back into the toolbar? This might not cover every edge case but it’s probably good enough for real-world use.

  • Mook

    There’s also document.loadOverlay (in nsIDOMXULDocument), but that has its own set of issues – I believe you never get notified if the load fails, for example, plus it doesn’t remove the overlays (for when you want to unload things in bootstrapped addons).