Chapter 6: Scripting the Toolbar

Firefox extensions are usually driven by JavaScript, a programming language that is fairly easy to learn. As you will soon find out, the code that our toolbar extension will use is very straightforward. We will make fairly heavy use of the Firefox chrome’s DOM, which allows us to access the individual elements of our toolbar.

Before we write any code, there is one very important point you should be aware of. Just as the id attributes for our XUL elements needed to be unique across the entire browser system, so to must our JavaScript variables and functions be unique. All JavaScript included in a browser overlay is global, hence this unique name constraint. Once upon a time, it was an acceptable practice to prefix variable and function names with a "unique" string. In fact, this tutorial used to recommend that method. That practice is now frowned upon, however, so through the course of this chapter, we’ll learn one way to handle things cleanly.

Let’s first create our JavaScript file. Place it inside of the content folder, and name it tuttoolbar.js. Our resulting file structure now looks like this:

+- TutToolbar/
   +- install.rdf
   +- chrome.manifest
   +- chrome/
      +- content/
         +- tuttoolbar.xul
         +- tuttoolbar.js
      +- skin/
         +- combined.png
         +- gripper.png
         +- images.png
         +- main.png
         +- web.png
         +- tuttoolbar.css

Before we place any code within this file, let’s first tell our XUL markup how to use our JavaScript.

Tying XUL to JavaScript

Our XUL markup (stored in the tuttoolbar.xul file) needs to be told where to find the corresponding JavaScript. We can tell it where to look by making use of the script element:

<script type="application/x-javascript"
        src="chrome://tuttoolbar/content/tuttoolbar.js" />

This element should be placed inside the overlay element in our XUL markup. The type attribute indicates that we are pointing to a JavaScript file, and src attribute simply specifies the chrome path to our JavaScript file (that we just created). Let’s take a look at what our XUL overlay looks like after we insert this element: [View XUL Overlay Revision 7].

Structuring Our JavaScript

In order to keep the global namespace clean from our function and variable names, we must place all of our variables and functions in a JavaScript "object". This will provide us access to everything we need while keeping things nice and neat. Here’s an example object, using what’s called "literal notation":

var sampleObject = {
    g_MyVariable: "value",
    g_SettingOne: false,
    g_SettingTwo: true,
    g_SampleArrayVariable: new Array("apple", "banana", "orange"),

    SomeFunction: function(param1, param2)
    {
        // Do something clever here
    },

    AnotherFunction: function()
    {
        // More clever stuff
    }
};

There are several very important things to note in this sample code:

  1. Everything in our object is wrapped with a pair of curly braces, followed by a semicolon.
  2. Variables within the object are not declared with the var keyword. Instead, we make them properties of our object variable using the colon operator as shown above.
  3. All variables and functions are separated by commas.
  4. Functions are declared a little differently. The order is: your function name, a colon, the function keyword, an open parenthesis, a parameter list (if there are to be any), and a close parenthesis.

We will be using this object-style format in the JavaScript we create for this tutorial. Our object’s name will be objTutorialToolbar. Let’s jump in!

Adding Functionality to the Buttons

Remember the oncommand attribute we supplied for each toolbar button back in our XUL markup? That attribute is how we specify what code to execute when the command event gets fired (i.e. the user activates the toolbar button). We could have used the onclick event instead, but it doesn’t respond to the keyboard; oncommand responds to both the keyboard and the mouse. Let’s take a look back at the value we supplied for this attribute for our web search button:

oncommand="objTutorialToolbar.Search(event, 'web')"

This value shows a call to the Search() function (via the objTutorialToolbar object), passing in two values: the event that generated this function call, and our own custom value that indicates what type of search to do (in this case, a web search). We won’t be making use of the event parameter at the moment; we’ve simply included it here for future use.

Because the code is rather lengthy, I have decided not to show it within the text of this article. Instead, I will present the code to you in sample code files, which you can examine at your leisure. The code is well commented, so I won’t go into a great deal of explanation on what each line does. Let’s take a look at the first sample file now (copy the contents of this file and paste them into the JavaScript file you created moments ago): [View JavaScript Revision 1].

Start up Firefox (with your development profile), enter a search term into our toolbar’s search box, and click one of the web search buttons. The code should execute and the Google search results page should appear! This dynamic development stuff is pretty handy!

A Special Note About Button-Menu Buttons

Before we move on to the next topic, one very important note must be made concerning "button-menu" style buttons. Recall that this type of button provides both a clickable button as well as a popup menu (the forward and backward navigation buttons are prime examples). Also recall that the toolbarbutton sub-element of this button style, as well as each individual menuitem element, contains an oncommand attribute. When the toolbar button portion is activated, the code specified in the toolbarbutton element’s oncommand attribute gets executed, just as you would expect. But what happens when the user activates one of the menuitem elements?

Not only does the menuitem element’s oncommand event fire, the toolbarbutton element’s event also fires! So two events fire at once, even though you only wanted one of them to be triggered. Because the toolbarbutton‘s event fires last, its code is what gets executed. In other words, the menuitem‘s event never gets a chance. So how can we fix this problem? Well, truth be told, we already have. Let’s look back at the markup for one of our menuitem elements (the interesting piece is highlighted):

<menuitem id="TutTB-Combined-Image" label="Image Search"
          class="menuitem-iconic" tooltiptext="Search Images"
          oncommand="objTutorialToolbar.Search(event, 'image'); event.stopPropagation();" />

By making use of the DOM function stopPropagation(), we are able to prevent the oncommand event from propagating up the DOM tree. This effect is why the toolbarbutton‘s code was always getting executed. To make a long story short, always keep the following rule in mind: when creating a "button-menu" type toolbar button, always add event.stopPropagation() to the oncommand attribute of each menuitem element.

Adding Functionality to the Search Box

Now that we have the search buttons working, we need to add some capability to the search box itself. The user should be able to enter a search term and press the [Enter] key to execute a search. How can we do that? Well, let’s look back at our XUL markup to see what function we anticipated on using:

<toolbaritem id="TutTB-SearchTerms-TBItem" persist="width">
    <menulist id="TutTB-SearchTerms" editable="true" flex="1"
              minwidth="100" width="250"
              onkeypress="objTutorialToolbar.KeyHandler(event);">
        <menupopup id="TutTB-SearchTermsMenu" onpopupshowing="objTutorialToolbar.Populate()" />
    </menulist>
</toolbaritem>

This time, instead of using the oncommand event, we rely on the onkeypress event. This event gets fired each time the user presses a key inside of the search box. In this example, we are making a call to the KeyHandler() function, which is surprisingly simple:

KeyHandler: function(event)
{
    if(event.keyCode == event.DOM_VK_RETURN)
        this.Search(event, 'web');
}

This function simply checks to see if the key that was pressed is the [Enter] key. If it is, it calls the Search() function that we just looked at. If the [Enter] key wasn’t pressed, the function simply does nothing. Let’s add this function to our JavaScript file: [View JavaScript Revision 2].

Again, start Firefox with your development profile, type some search terms into the toolbar’s search box, and press the [Enter] key on your keyboard. A web search should take place, just like clicking the button before. Our extension has really come to life!

Dynamically Populating a Menu

Being able to dynamically populate a menu with menu items is surprisingly useful. Thankfully, this feature is quite easy to implement. For our tutorial toolbar, we will dynamically add some items to the search box drop-down menu. Let’s again look back at the XUL markup we used to create our search box:

<toolbaritem id="TutTB-SearchTerms-TBItem" persist="width">
    <menulist id="TutTB-SearchTerms" editable="true" flex="1"
              minwidth="100" width="250"
              onkeypress="objTutorialToolbar.KeyHandler(event);">
        <menupopup id="TutTB-SearchTermsMenu" onpopupshowing="objTutorialToolbar.Populate()" />
    </menulist>
</toolbaritem>

Note the highlighted onpopupshowing attribute in the menupopup element. We have specified that a function named objTutorialToolbar.Populate() should be executed each time the popup menu is about to be displayed. This function will be responsible for creating our dynamic menu items. Let’s take a look at that function’s code (again, the code is well commented to explain exactly what’s going on): [View Populate() Code].

This example function does not show how to instruct each menuitem element to execute some code when it is selected. To tell each menuitem what code it should execute, we simply make an additional call to the setAttribute() function, passing in the event to be handled and the code to be executed. Here is an example of handling the oncommand event:

tempItem.setAttribute("oncommand", "objTutorialToolbar.SomeFunction()");

Let’s add the Populate() function to our JavaScript file and take a look at the results:
[View JavaScript Revision 3]. That’s all there is to dynamic menu population!

Dynamically Adding Toolbar Buttons

(This section is being provided for informational purposes only; it does not appear in the example toolbar’s source code).

Adding dynamic toolbar buttons to a toolbar is just as easy as dynamically populating a menu, and it’s done in a very similar fashion. We first need a container to hold our dynamic buttons. The toolbaritem element is the perfect fit for this, and the markup would look like the following:

<toolbaritem id="TutTB-DynButtonContainer" />

Now that this container is available to us, we can use it to add our dynamic toolbarbutton elements. Let’s examine a sample function that we could use to add dynamic buttons:

AddDynamicButtons: function()
{
    // Get the toolbaritem "container" that we added to our XUL markup
    var container = document.getElementById("TutTB-DynButtonContainer");

    // Remove all of the existing buttons
    for(i=container.childNodes.length; i > 0; i--) {
        container.removeChild(container.childNodes[0]);
    }

    // Add 5 dynamic buttons
    for(var i=0; i<5; i++) {
        var tempButton = null;
        tempButton = document.createElement("toolbarbutton");
        tempButton.setAttribute("label", "Button " + i);
        tempButton.setAttribute("tooltiptext", "Button " + i);
        tempButton.setAttribute("oncommand", "objTutorialToolbar.SomeFunction()");
        container.appendChild(tempButton);
    }
}

This function looks very similar to the one we used to populate our dynamic menu. We remove all of the existing dynamic buttons from the container (the way shown here is different that we used in the dynamic menu code), then we create the new buttons to add to the container, appending them as we go.

Optional Programming Exercise: Try to use this general idea to add "search word buttons" to our example toolbar. As the user types search words into the search word box, dynamically add buttons to the end of the toolbar, one button for each word the user types. Here are a few hints to get you started:

  1. You’ll need to add a toolbaritem "container" to the end of our toolbar’s XUL markup (right before the toolbarspring element is a good choice). Make sure to give it a unique ID.
  2. Make use of the oninput event in the menulist (search box) element. This event gets fired as the user types text into the search box (which is just what we want).
  3. The JavaScript function you call will need to do the following: (a) Obtain the search words from the search box, (b) remove all previously created dynamic buttons, (c) split up the search words based on whitespace, and (d) create a button for each individual word.

This is (almost) exactly how I handle this feature in Googlebar Lite.

Disabling and Enabling Buttons

(This section is being provided for informational purposes only; it does not appear in the example toolbar’s source code).

You may occasionally want to dynamically disable or enable a toolbar button. For example, the highlighter button in Googlebar Lite is disabled when no search words are present in the search box, and enabled when there are search words.

The following example shows how to create a menu item that toggles the disabled status of a normal toolbar button. Again, this isn’t the most practical example in the world, but it demonstrates how the effect is achieved. Here is the markup for our menu item:

<menuitem label="Toggle Web Search Button"
          tooltiptext="Toggle Web Search Button"
          oncommand="objTutorialToolbar.ToggleWebSearchButton()" />

And here is the code for ToggleWebSearchButton() function:

ToggleWebSearchButton: function()
{
    var button = document.getElementById("TutTB-Web-Button");
    var value = button.disabled;
    if(value == true)
        button.disabled = false;
    else
        button.disabled = true;
}

We first get the toolbar button we are interested in by using its ID value and the getElementById() function. Once we have the button, we can get its current state (enabled or disabled) using the disabled property of the button object. If the value is true, we set it to false, and vice-versa. Firefox takes care of disabling the button for us, so that it is no longer clickable (note that the button’s image may not appeared grayed out: we must rely on skinning for that).

Dynamically Showing and Hiding Buttons

(This section is being provided for informational purposes only; it does not appear in the example toolbar’s source code).

Googlebar Lite allows the user to choose the buttons they wish to see on their toolbar. This means that the toolbar must be able to dynamically show or hide the available buttons. Here is a snippet of JavaScript code that will accomplish exactly what we want:

var TB_Web = document.getElementById("TutTB-Web-Button");
TB_Web.setAttribute("hidden", !ShowWebButton);

We first get the toolbar button element itself using the getElementById() function. Then we make a call to the setAttribute() function to change the value of the hidden attribute. You’ll note that I set the value of the hidden attribute to the opposite value of a variable I have called ShowWebButton. This variable is simply a boolean flag, telling us whether or not the user wants to see the "web search" button. The actual value stored in this variable comes from reading the user’s preference for the "Show Web Search Button" option. Read on to see how we can do that.

Reading and Storing User Preferences

(This section is being provided for informational purposes only; it does not appear in the example toolbar’s source code).

In order to read a stored preference, we need to get access to the Firefox preferences service interface. We do so by creating a variable like the following (note that this code has been formatted to fit this site’s layout):

PrefService: Components.classes["@mozilla.org/preferences-service;1"].
    getService(Components.interfaces.nsIPrefService);

Once we have access to the preferences interface, we can obtain our extension’s preferences branch (i.e. the location in the preferences "registry" where our extension’s settings are kept):

PrefBranch: this.PrefService.getBranch("tuttoolbar.");

Let us assume that all of our example toolbar’s preferences begin with the text tuttoolbar. (note the trailing period). As a result, we must pass this string into getBranch() (a built-in Firefox function). This function gives us access to our extension’s preferences, so we can finally obtain the value for an individual option. Here is how we can obtain the value for the "Show Web Search Button" option we mentioned a moment ago:

if(this.PrefBranch.prefHasUserValue("show.button.web"))
    this.ShowWebButton = this.PrefBranch.getBoolPref("show.button.web");
else
{
    this.PrefBranch.setBoolPref("show.button.web", false);
    this.ShowWebButton = false;
}

We first test to see if the preference exists in the preferences tree. If it does, we use the getBoolPref() function to read its value (storing it in the variable ShowWebButton). If the preference does not exist, we set the default value (both in the preferences tree and in our global variable).

So storing a preference is just as easy as reading one. You simply supply the name of the preference to set, and then the value to use:

this.PrefBranch.setBoolPref("show.button.web", this.ShowWebButton);

Reading and writing preferences is a handy way to store your toolbar’s settings. Most extensions utilize this feature, so there are certainly plenty of working examples out there for you to examine. In addition, an excellent expanded tutorial on preferences is available at the Mozilla Developer’s Center.