June 8, 2014

Creating sidebar add-on for the Firefox with Australis UI

I'm creating simple add-on for Mozilla Firefox and even I did such add-ons before I run into the troubles. It's because of new Firefox UI called Australis and because I want this add-on to be bootstrapped (so it doesn't require browser restart to complete the installation).
After a short googling I found CustomizableUI.jsm and I finally got the working solution.
So here is my bootstrap.js which adds the sidebar:

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const { classes: CC, interfaces: CI, utils: CU } = Components;
const self = {
    id: "tagssidebar@ondrejd.info",
    name: "TagsSidebar",
    path: {
        chrome: "chrome://tagssidebar/"
const prefPrefix = "extensions." + self.id + ".";


var gStylesSrv;
var gStylesURI;

// Bootstrap API: install(aData, aReason)
function install(aData, aReason) {
    if (aReason == ADDON_INSTALL) {
        // Set default preferences
        let prefs = Services.prefs.getBranch(prefPrefix);
        prefs.setBoolPref("showInSidebar", true);
        prefs.setBoolPref("showTagCloud", false);
        prefs.setBoolPref("showFavicons", false);
} // end install(aData, aReason)

// Bootstrap API: uninstall(aData, aReason)
function uninstall(aData, aReason) {
    if (aReason == ADDON_UNINSTALL) {
        // Remove default preferences
} // end uninstall(aData, aReason)

// Bootstrap API: startup(aData, aReason)
function startup(aData, aReason) {
    // Create new toolbar button
        id: "tagssidebar-toolbarbutton",
        defaultArea: CustomizableUI.AREA_NAVBAR,
        label: "TagsSidebar",
        tooltiptext: "Toggle visibility of TagsSidebar.",
        type: "button", 
        onBeforeCreated: function(aDocument) {
            // We need to add more UI than the toolbarbutton (broadcaster, key and menuitem):
            // Broadcaster
            let bcset = aDocument.getElementById("mainBroadcasterSet");
            if (bcset) {
                let bc = aDocument.createElementNS(XUL_NS, "broadcaster");
                bc.setAttribute("autoCheck", "false");
                bc.setAttribute("group", "sidebar");
                bc.setAttribute("id", "viewSidebar_tagssidebar");
                bc.setAttribute("oncommand", "toggleSidebar('viewSidebar_tagssidebar');");
                bc.setAttribute("sidebarurl", "chrome://tagssidebar/content/sidebar.xul");
                bc.setAttribute("sidebartitle", "TagsSidebar");
                bc.setAttribute("type", "checkbox");
            // Key
            let keyset = aDocument.getElementById("mainKeyset");
            if (keyset) {
                let key = aDocument.createElementNS(XUL_NS, "key");
                key.setAttribute("command", "viewSidebar_tagssidebar");
                key.setAttribute("id", "key_openSidebar_tagssidebar");
                key.setAttribute("key", "t");
                key.setAttribute("modifiers", "accel,shift");
            // Menuitem
            let mp = aDocument.getElementById("viewSidebarMenu");
            if (mp) {
                let mi = aDocument.createElementNS(XUL_NS, "menuitem");
                mi.setAttribute("key", "key_openSidebar_tagssidebar");
                mi.setAttribute("label", "TagsSidebar");
                mi.setAttribute("observes", "viewSidebar_tagssidebar");
                mi.setAttribute("tooltiptext", "Toggle visibility of TagsSidebar!");
        onCreated: function(aNode) {
            // Add ID of broadcaster we created before as a command:
            aNode.setAttribute("command", "viewSidebar_tagssidebar");
            return aNode;

    // Load stylesheet
    gStylesSrv = CC["@mozilla.org/content/style-sheet-service;1"].getService(CI.nsIStyleSheetService);
    gStylesURI = Services.io.newURI(self.path.chrome + "skin/overlay.css", null, null);
    gStylesSrv.loadAndRegisterSheet(gStylesURI, gStylesSrv.USER_SHEET); 
} // end startup(aData, aReason)

// Bootstrap API: shutdown(aData, aReason)
function shutdown(aData, aReason) {
    if (aReason == APP_SHUTDOWN) return;

    // Destroy our widget

    // Unload stylesheet
    if (gStylesSrv.sheetRegistered(gStylesURI, gStylesSrv.USER_SHEET)) {
        gStylesSrv.unregisterSheet(gStylesURI, gStylesSrv.USER_SHEET);
} // end shutdown(aData, aReason)

No comments: