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. Through the course of this chapter, we’ll learn one way to handle this 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/
          |-- image_sheet.png
          |-- gripper.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 will 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: function name, a colon, the function keyword, an open parenthesis, a parameter list (if there are to be any), a close parenthesis, and the body of the function.

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 several of the toolbar buttons 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, I’d like to point out a few things about “button-menu” style buttons. Recall that this type of button provides both a clickable button as well as a popup menu. In our XUL markup for this button, the toolbarbutton element had an oncommand attribute. When the toolbar button portion is activated, the code specified in the oncommand attribute gets executed, just as you would expect. But what about our menuitem elements? They didn’t have an oncommand attribute, so what happens when one of them is activated?

The answer lies in the parent menupopup element. We can exploit the fact that events “bubble” up the DOM tree to save us some effort. By letting the parent of our menuitem elements handle any events, we only need to place the handler in one location. This is an extremely useful strategy to use when many child nodes are expected. Let’s take a quick look back at the parent menupopup element:

<menupopup
    oncommand="objTutorialToolbar.CombinedSearch(event); event.stopPropagation();">

When a menuitem element is activated, the code in this parent element’s oncommand attribute gets run (because the “command” event bubbled up the DOM tree). If you examine the JavaScript code again, you’ll see that the CombinedSearch() function determines whether or not a menuitem element was the source of the event. If so, it gets the value of the searchType attribute for the activating element, then passes that along to our Search() routine.

It is very important to note that even if you provide a handler for an event at a parent node, the event continues to bubble up the tree! As a direct result of this, the parent of our menupopup element (a toolbarbutton in our case) will also execute any code it has in its oncommand attribute. This is a real problem for us, since the menupopup element’s parent toolbarbutton is hard-wired to do a web search. How do we fix this problem? By using the event.stopPropagation(); routine (already in our code). This built-in function will stop the given event from bubbling up any higher in the tree.

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.

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. However, you can simply employ the same trick we used previously on our “menu-button” style buttons. Put the event handler in the parent element (the menupopup in our example) and have it handle each child appropriately.

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(var 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:
    1. Obtain the search words from the search box
    2. Remove all previously created dynamic buttons
    3. Split up the search words based on whitespace
    4. 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"
          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("extensions.tuttoolbar.");

Let us assume that all of our example toolbar’s preferences begin with the text extensions.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).

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 Network.