Adding Toolbars and Toolbar Buttons

Adding a new toolbar

Adding new toolbars to Firefox is easy, but adding them the wrong way is very easy as well.

Toolbars in Firefox are very customizable. Some users don't like extra toolbars, or they want to rearrange toolbar buttons to their liking, possibly merging multiple toolbars in the process. Firefox allows all of this by default, and if you don't pay attention to the details we describe here, your toolbar may not be as easy to customize as the rest.

The first thing you need to do is add your buttons to the toolbar palette. The toolbarpalette is a collection of all toolbar buttons and toolbar items in Firefox, including those added by extensions. To add your buttons, all you need to do is overlay the palette in your main browser overlay.

<overlay id="xulschoolhello-browser-overlay"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <toolbarpalette id="BrowserToolbarPalette">
    <toolbarbutton id="xulschoolhello-hello-world-button"
      class="toolbarbutton-1 chromeclass-toolbar-additional"
      label="&xulschoolhello.helloWorld.label;"
      tooltiptext="&xulschoolhello.helloWorld.tooltip;"
      oncommand="XULSchoolChrome.BrowserOverlay.doSomething(event);" />
    <!-- More buttons here. -->
  </toolbarpalette>
  <!-- More overlay stuff. -->
</overlay>

One detail that is easy to overlook is the fact that the toolbarpalette element is outside of the window element. If you put the toolbarpalette element inside the window element in your overlay, some weird errors will begin to happen.

Always set the label and tooltiptext attributes of a toolbarbutton. In the case of a toolbaritem element (discussed later), use the title attribute instead of label. Tooltips are very important for users that want to know what a button does before clicking it.

Setting the image for a toolbar button is done with CSS:

#xulschoolhello-hello-world-button {
  list-style-image: url("chrome://xulschoolhello/skin/hello-world.png");
}

It's not really that simple to set the image for a toolbar button, because we need to consider the appearance of the button on different systems, and also consider the different button states, but we'll get into that further ahead.

The CSS file with your toolbar styles needs to be included in the overlay file, as you would expect, but also in the chrome.manifest file. This is very important because the toolbar customization dialog won't work correctly without this. The way to include the file in the manifest is to add this line:

style chrome://global/content/customizeToolbar.xul chrome://xulschoolhello/skin/toolbar.css

If you are using XBL bindings (explained way ahead) for your toolbar items, you'll have to include the CSS files for those as well, each in a new line like the one above.

We now have code that adds one or more buttons to the toolbar palette. The user can now use the Customize Dialog to add the buttons to the current Firefox toolbars. In most cases this is not what you want as default behavior, because it would be very hard for the user to discover your buttons. Keep in mind most users don't know how to customize toolbars in Firefox.

Let's add our own toolbar. This is done in the overlay as well.

<window id="main-window">
  <toolbox id="navigator-toolbox">
    <toolbar id="xulschoolhello-toolbar" toolbarname="&xulschoolhello.toolbarName.label;"
      accesskey="&xulschoolhello.toolbar.accesskey;"
      customizable="true" mode="icons" context="toolbar-context-menu"
      defaultset="xulschoolhello-hello-world-button"
      insertbefore="PersonalToolbar" />
  </toolbox>
</window>

(Note for Mac OS X: <window id="main-window"> and </window> are not required)

Our toolbar is added as a child of the toolbox element in the main browser window. The toolbox is the collection of toolbars in Firefox, including the main menu bar. Let's look at the attributes we used:

  • The toolbarname is the name that will appear on the View > Toolbars menulist. This menu allows hiding and showing any toolbar.
  • The customizable attribute determines if the user can customize the toolbar. You should normally set this to true, unless you have strong reasons not to want users changing your toolbar.
  • The mode attribute is set to icons, which is the usual value. This can be changed in the Customize dialog by the user.
  • The context attribute should also be set if you want a customizable toolbar. It points to the id of a popup element that holds the customization options that are displayed when the user right-clicks on the toolbar. This popup is already part of the main window, so it doesn't need to be implemented again.
  • The defaultset attribute lists the ids of the icons you want to include on your toolbar by default. It's a comma-separated list of ids, and it can also include other special values: spacer, separator and spring. spacer represents an empty button space, separator a vertical separation line and spring a flexible empty area that stretches.
  • Finally, the insertbefore attribute places our toolbar above the Bookmarks Toolbar. This is a matter of personal preference, but the Mac OS theme seems to be designed so that the Bookmarks Toolbar is always the last one (it has a lighter color than the rest). It also makes sense from a usability perspective, since bookmarks should be very easy to access for the user.

That's it for the basics. With this knowledge you should be able to create simple toolbars and toolbar buttons you can add to Firefox. Now we'll look deeper into the details of toolbars so that you can make great toolbars.

Toolbar buttons

There are several types of buttons and elements you can add to a toolbar depending on your needs. The toolbarbutton element has the type attribute that allows you to change the behavior of the button in many ways. The menu and menu-button types allow you to create buttons that open popup menus beneath them. See the Back/Forward buttons in the main Firefox toolbar for an example. Menu toolbar buttons are handy when you want to include many options in your toolbar and you don't want it to be too cluttered. The other types, checkbox and radio are useful when you have buttons that change state when the user clicks on them. Read more about this in the type attribute specification.

Keep in mind some users have small screens with low resolution. If you shrink the Firefox window, you'll notice that the content resizes until it reaches its minimum size and begin to be cropped (cut), making UI elements disappear. You should test that your toolbar resizes gracefully and doesn't force Firefox to crop content before it's strictly necessary.

When you need something more elaborate than a button in your toolbar, you can use the toolbaritem element instead. This element is nothing more than a wrapper, so you can have whatever XUL content you want in it. However, you should keep in mind that odd-looking toolbars are likely to confuse and annoy users. Use custom toolbar items sparingly.

Toolbar button icons

Creating the icons for toolbar buttons is one of the most difficult tasks when making extensions. It is not too hard to come up with some graphics for the buttons, but it can be hard to make them blend in with Firefox on all operating systems. The way icons are handled in Firefox changed considerably after Firefox 4, so supporting older versions is not mentioned here (you can check this document's change history for older revisions, though).

These are the current icon sets for Firefox:

Windows

Toolbar-win.png

Mac OS X (Lion and above)

Toolbar-mac-lion.png

Mac OS X

Toolbar-mac.png

Linux (Gnome)

Toolbar-gnome.png

Toolbar-gnome-small.png

Note: the images above are probably not distributable under the same CC license, unlike the rest of this material.

Here are the key similarities and differences between the 3 themes:

  • All systems use small icons that are 16x16 pixels. Only the Gnome theme has a set of large icons that are 24x24 pixels.
  • Only a few icons have different states (rows). The disabled state for most buttons is handled with CSS.
  • The icon sets have different paddings for their icons. The one for Windows has 19x19 pixel frames, while the Mac OS ones are 20x20 pixels. This doesn't really matter as long as the contents are 16x16 and they are centered in the frame.
  • Mac OS X Lion has icons with a lighter shade.
  • On Linux, icons are 24x24 pixels by default. Since Firefox is a GTK application, it uses GTK icons whenever possible. The icons in the image are the ones that aren't provided by GTK. GTK icons are accessed using special URLs, like this one: moz-icon://stock/gtk-go-back?size=menu. You can enter this URL in Firefox on Linux, and the corresponding icon will be displayed. This is also the reason this is the only theme that still has colorful icons for its buttons.

Now let's look at the CSS work involved in a toolbar that works on the aforementioned systems. If you want your toolbar to look right on all systems, you should consider having different icon sets for each. You should also consider using a graphic designer for this work, as it takes a lot of attention to detail to make good icons.

You can have a different skin directory for each operating system using Manifest Flags in the chrome.manifest file:

skin xulschoolhello classic/1.0 skin/unix/
skin xulschoolhello classic/1.0 skin/win/    os=WINNT
skin xulschoolhello classic/1.0 skin/mac/    os=Darwin

The osversion flag can be used in case we wanted to have different icons for Mac OS X Lion and above, and others for older systems. In this example we won't bother.

There is a separate skin directory for each system, with the Unix theme as the default (as most other systems are Unix-based). This makes it easy to keep the themes separate and to make changes to one of them without having to worry about the rest. On the other hand, it is often the case that there are multiple images and CSS sheets that are the same for all systems. For example, your extension logo icon will probably be the same. Having multiple copies of these files can be wasteful, so you may want to have a "common" directory.

Image files use the most space in an extension package, by far. Most extensions are a few hundred kilobytes in size or smaller. If your file is getting too big, you should look into optimizing your images.

Given the way manifest files work, we have found that the best solution is to have a separate package name for OS-specific skin files.

skin xulschoolhello    classic/1.0 skin/all/
skin xulschoolhello-os classic/1.0 skin/unix/
skin xulschoolhello-os classic/1.0 skin/win/    os=WINNT
skin xulschoolhello-os classic/1.0 skin/mac/    os=Darwin

All we did here is add a new entry for "common" styles that points to the all directory. The OS-specific entries now use a different package name: xulschoolhello-os. Now you just need to be careful about when to use chrome://xulschoolhello/skin/ and when to use chrome://xulschoolhello-os/skin/. It's a bit hacky, but it works well.

As for the image files themselves, you may be wondering why it is that all icons are included in a single file instead of having one file for every icon. One reason is that it would be complicated to manage that many files, and it becomes more likely that changes made to some of the files lead to an inconsistent appearance that is not obvious by looking at the individual files. It is easier to be able to edit all icons in one go. There is also a performance gain from using a single file. To get the region that corresponds to a specific state of an icon, the -moz-image-region CSS property is used.

Here are some examples of how the CSS for a toolbarbutton would look like on the 3 major platforms. This assumes that you've set the class xs-hw-toolbarbutton to all of your buttons.

Windows:

/* The second and third selectors at the bottom are necessary to prevent
   conflicts with installed themes. */
toolbarbutton.xulschoolhello-toolbarbutton,
window:not([active="true"]) toolbarbutton.xulschoolhello-toolbarbutton,
toolbar[iconsize="small"] toolbarbutton.xulschoolhello-toolbarbutton {
  list-style-image: url("chrome://xulschoolhello-os/skin/toolbar.png");
}

#xulschoolhello-hello-world-button {
  -moz-image-region: rect(0px, 16px, 16px, 0px);
}

Mac OS X:

/* The second and third selectors at the bottom are necessary to prevent
   conflicts with installed themes. */
toolbarbutton.xulschoolhello-toolbarbutton,
window:not([active="true"]) toolbarbutton.xulschoolhello-toolbarbutton,
toolbar[iconsize="small"] toolbarbutton.xulschoolhello-toolbarbutton {
  list-style-image: url("chrome://xulschoolhello-os/skin/toolbar.png");
}

#xulschoolhello-hello-world-button {
  -moz-image-region: rect(0px, 16px, 16px, 0px);
}

Linux:

/* The second and third selectors at the bottom are necessary to prevent
   conflicts with installed themes. */
toolbarbutton.xulschoolhello-toolbarbutton,
window:not([active="true"]) toolbarbutton.xulschoolhello-toolbarbutton {
  list-style-image: url("chrome://xulschoolhello-os/skin/toolbar-large.png");
}

#xulschoolhello-hello-world-button {
  -moz-image-region: rect(0px, 24px, 24px, 0px);
}

toolbar[iconsize="small"] #xulschoolhello-hello-world-button {
  list-style-image: url("chrome://xulschoolhello-os/skin/toolbar.png");
  -moz-image-region: rect(0px, 16px, 16px, 0px);
}

Several CSS rules apply by default to all toolbar buttons. These add the button-like look you want. If for some reason you want to override these styles (not recommended), you'll need the following rule:

-moz-appearance: none;

-moz-appearance can be used in many cases where you want to strip the native look out of an element. This will save you a lot of time trying to remove all the CSS rules that give the buttons a native look. You'll probably still need to override a couple other CSS rules to get a completely plain look.

The Customize Toolbars Dialog

Firefox has the option to customize its toolbars. We've already mentioned this before, and if you follow our recommendations, then you shouldn't have many problems making your toolbar compatible with the Customize Toolbars dialog. The dialog can be opened from View > Toolbars > Customize..., or by right-clicking on the main toolbar (or any toolbar with the correct context attribute value) and clicking on the Customize option.

Other than what we have stated before, you should take into account the controls at the bottom of the Customize Toolbars dialog. You should test your toolbar buttons and items under all combinations of Icons / Icons and text / Text, Use Small Icons, and having your icons in different toolbars. You should also test that the Reset to Defaults button works correctly. Adding elements to your toolbar that are not toolbarbutton or toolbaritem will cause it to fail. Make sure your icons look OK while the Customize Dialog is open, and after clicking on the OK button. If you use XBL bindings, make sure everything works normally after customizing toolbars. All of this is very important to test because, when the dialog is opened, Firefox changes the DOM of the toolbar, adding wrapper elements that allow the customization. This tends to break very specific CSS, and XBL bindings lose their internal state when moved around the DOM.

Adding toolbar buttons to existing toolbars

Finally, there is the very common case where you just want to add one button to the main toolbar. In this case you still need to add the button to the palette using an overlay. In order to add your button to the main toolbar on first run, you'll have to use Javascript code. Keep in mind that you shouldn't assume anything about the location (or presence!) of any specific buttons; remember users could have moved them or removed them altogether. The Toolbar Code Snippets page has a code sample you can use to do this.

Remember to validate if your button is already present, to prevent duplicates. It's also a good idea to set a preference that indicates that you added your button already, so that it can be removed permanently if the user chooses to. Another option is to use FUEL's firstRun property, which also relies on a preference.

Firefox 3

let extension = Application.extensions.get(YOUR_EXTENSION_ID);

if (extension.firstRun) {
  // add button here.
}

Firefox 4

Application.getExtensions(function (extensions) {
    let extension = extensions.get(YOUR_EXTENSION_ID);

    if (extension.firstRun) {
      // add button here.
    }
})

Both

function firstRun(extensions) {
    let extension = extensions.get(YOUR_EXTENSION_ID);

    if (extension.firstRun) {
      // add button here.
    }
}

if (Application.extensions)
    firstRun(Application.extensions);
else
    Application.getExtensions(firstRun);

The FUEL library currently only works on Firefox 3 and above.

This tutorial was kindly donated to Mozilla by Appcoast.