Firefox WebExtension toolbar buttons to toggle sidebars

Opening your own sidebar with a browserAction button

You can have a browserAction toolbar button which opens your own sidebar. You can do this by defining both a sidebar_action and a browser_action in your manifest.json.

When this question was asked, it wasn't possible to combine both a sidebar_action and a browser_action in a manifest.json. I'm not sure when that actually changed, but as of this point (tested on FF71), you can have both.

The following is example code which will do this:

manifest.json:

{
    "manifest_version": 2,
    "name": "Button opens sidebar",
    "description": "Open the sidebar with a toolbar button.",
    "version": "1.0",

    "browser_action": {
        "default_icon": "icon.png",
        "default_title": "Open Sidebar"
    },
    "background": {
        "scripts":[
            "background.js"
        ]
    },
    "sidebar_action": {
        "default_icon": "icon.png",
        "default_title": "Example Sidebar",
        "default_panel": "sidebar.html",
        "open_at_install": false
    }
}

background.js:

//The manifest.json entry must be a file in your extension, but you can change it to
//  an external site in your background.js:
browser.sidebarAction.setPanel({panel: 'https://example.org/'});
//Open the sidebar when the browserAction button is clicked.
browser.browserAction.onClicked.addListener(() => {
  browser.sidebarAction.open();
});

sidebar.html

<html>
  <body>
    <div>Example text</div>
  </body>
</html>

Toggling the sidebar open/closed

I tried implementing a naive implementation of the button actually toggling the sidebar:

background.js

browser.browserAction.onClicked.addListener(() => {
    browser.sidebarAction.isOpen({}).then((isOpen) =>  {
        if (isOpen) {
            browser.sidebarAction.close();
        } else {
            browser.sidebarAction.open();
        }
    });
});

Unfortunately, this fails with the error:

Error: sidebarAction.open may only be called from a user input handler

Use a second browserAction onClicked

You can take advantage of the fact that a sidebar that's showing HTML from within your extension is running in the background context, by just adding an additional browserAction.onClicked listener in the sidebar which closes the sidebar.

background.js

browser.browserAction.onClicked.addListener(() => {
    browser.sidebarAction.open();
});

sidebar.js

browser.browserAction.onClicked.addListener(() => {
    browser.sidebarAction.close();
});

sidebar.html

<html>
  <head>
    <script src="sidebar.js"></script>
  </head>
  <body>
    <div>Example text</div>
  </body>
</html>

Event listeners are executed in the order they are added, so the one added by the sidebar.js, which closes the sidebar, is run after the one in background.js, which opens the sidebar. Thus, when the sidebar is open, it gets closed upon the browserAction button being clicked.

Can't open the actual Bookmark or History sidebar

What you specifically desire, opening the actual Bookmarks or History sidebar, is not possible with WebExtensions. What you could do is implement similar sidebars yourself.

I did try changing the sidebar URL to a chrome:// URL:

browser.sidebarAction.setPanel({panel: 'chrome://browser/content/aboutDialog.xul'});

Unfortunately, that results in an error:

Error: Access denied for URL chrome://browser/content/aboutDialog.xul

If you desire some way to programmatically open the actual Bookmark or history sidebars, then you will need to file a bug requesting the functionality. Alternately, you can also create a WebExtension Experiment which adds the functionality to the WebExtensions API when the WebExtension Experiment is installed. WebExtension Experiments do not automatically get integrated into Firefox, but there is the possibility that it can be.


Firefox ⩾ 73

Since Firefox 73, you can simply use:

browser.browserAction.onClicked.addListener(() => {
    browser.sidebarAction.toggle();
});

Documentation on MDN

Firefox < 73

If you need to support older Firefox versions, especially ESR, you can keep track of what sidebar is open. This is the method I developed for one of my addons and the only one I know that works correctly with several windows.

In your background script:

let openedSidebarWindows = {};
browser.runtime.onConnect.addListener(port => onConnect(port));
browser.browserAction.onClicked.addListener(tab => this.onClick(tab));

function onConnect(port) {
  const windowId = parseInt(port.name);
  openedSidebarWindows[windowId] = port;
  port.onDisconnect.addListener(port => {
    delete openedSidebarWindows[parseInt(port.name)];
  });
}

function onClick({ windowId }) {
  if (openedSidebarWindows[windowId] !== undefined) {
    browser.sidebarAction.close();
  } else {
    browser.sidebarAction.open();
  }
}

In your sidebar script:

browser.runtime.connect({ name: windowId.toString() });