Netscape DevEdge

Skip to: [content] [navigation]

Creating a Toolbar for Netscape 7.x

Introduction

Netscape's user interface since the 6.x series has been defined by XUL (XML User-interface Language), which is a cross-platform XML dialect. CSS is used to style the XUL interface, and JavaScript is used to provide interaction: Rollovers, event handlers, and other behavior are all typically defined in scripts.

This tutorial demonstrates how to use XUL and JavaScript to write a toolbar that can interact with a website, and how to add that toolbar to Netscape 7.x. The website used in this example is the Netscape DevEdge site.

For further reading about XUL, we recommend XulPlanet.com's XUL Tutorial. Throughout this tutorial, we will link to XulPlanet for more detailed information. The best book on this topic, O'Reilly's Creating Applications with Mozilla, also makes its content available for free online at mozdev.org in typical Mozilla fashion.

How a browser window works

Netscape 7's user interface is written in XUL. The main browser window is defined in the file navigator.xul. You can actually load the browser UI inside Netscape 7.x by loading the following url: chrome://navigator/content/navigator.xul. Note that the loaded UI works like the normal browser, you can browse sites using this second, embedded user interface just as you would the regular one.

Figure 1 : navigator.xul's toolbars

Netscape 7.x's Toolbars

Structure of navigator.xul's user interface:

  <window>
    <toolbox id="navigator-toolbox">
      <menubar id="main-menubar"/>
      <toolbar id="NavigationBar"/>
      <toolbar id="PersonalToolbar"/>
    </toolbox>
  </window>

The root window element contains the XUL that defines the main navigator window. A direct child of it is a toolbox, which contains the menubar and the two toolbars (the navigation toolbar and the personal toolbar). We want to add the new DevEdge toolbar underneath the personal toolbar.

Building the Toolbar

We will now build our toolbar step by step. As we build our toolbar, each step will link to a XUL file showing the current progress.

DevEdge has several sections that we want our DevEdge toolbar to allow easy access to, such as ViewSource (for articles), Toolbox (examples, tools and references to aid Web developers), and others.

A toolbar can contain any XUL element, but it often uses a special variant of the button element called toolbarbutton, which is designed to fit better inside a toolbar. A toolbarbutton's text is set via the label attribute, which in the case of our toolbar is "DevEdge". You can also have an image inside the toolbarbutton by pointing to the image via the image attribute. The oncommand attribute gets executed whenever the toolbarbutton is pressed. When pressed, the button sends the user to http://devedge.netscape.com.

Figure 2 : Toolbarbutton - view source | view example | toolbarbutton reference

A simple toolbar example

XUL:

  <toolbar id="devedgetoolbar">
    <toolbarbutton label="DevEdge" 
       oncommand="devedgetoolbarLoadPage('http://devedge.netscape.com')"/>
  </toolbar>
JavaScript:
  function devedgetoolbarLoadPage(url){
    // change the current location
    window.content.document.location.href=url;
  }

Since toolbar space is limited, having one button per DevEdge section is not practical. We can use the available space much better if the toolbarbutton opens a drop down menu. This can be achieved by setting the type attribute of toolbarbutton to "menu" and defining a menu inside of the toolbarbutton. Popup menus in XUL are defined by the menupopup tag, and can contain menuitem and menuseparator children.

When you set the type attribute of a toolbarbutton to "menu", a small arrow pointing downwards is automatically placed next to the label.

Figure 3 : Toolbarbutton with dropdown - view source | view example

A Simple toolbar with a dropdown menu

XUL:

  <toolbarbutton label="DevEdge" type="menu">
    <menupopup>
      <menuitem label="DevEdge Home" 
         oncommand="devedgetoolbarLoadPage('http://devedge.netscape.com')"/>
      <menuitem label="DevEdge ViewSource" 
         oncommand="devedgetoolbarLoadPage('http://devedge.netscape.com/viewsource')"/>
      <menuseparator/>
      <menuitem label="DevEdge Help" 
         oncommand="devedgetoolbarLoadPage('http://devedge.netscape.com/help')"/>
    </menupopup>
  </toolbarbutton>

JavaScript:
  function devedgetoolbarLoadPage(url){
    // change the current location
    window.content.document.location.href=url;
  }

Of the numerous attributes on a menuitem that deal with events, such has onclick, onkeypress, onmousedown and oncommand. oncommand is the right event to use for loading a page, as it is triggered by both clicks and keystrokes.

We will also add the ability to search Google to the toolbar. For this, we need an input field, which in XUL is represented by the textbox element. textbox has a label attribute where default text can be specified (e.g., "Search for...."). We can clear the text when the user clicks into the textbox element by using its onfocus event handler. In that event handler, the "this" JavaScript object is used to refer the selected element and reset the value to the empty string: this.value = ''

Since we want to start a search when the user presses the enter key, we can use the onkeypress attribute to handle keystrokes from the textbox. Inside the onkeypress, we'll need some javascript to check which key was actually been pressed, for which the event object and its keyCode attribute are perfect tools. In the case of the enter key, keyCode's value will be the number 13.

Figure 4 : Textbox - view source | view example | textbox reference

A textbox in the toolbar

XUL:

  <toolbar id="devedgetoolbar">
    ...
    <textbox value="Search for...." onfocus="this.value=''"
       onkeypress="if(event.keyCode == 13){devedgetoolbarSearch();}" />
  </toolbar>

One enhancement we'll also add is a menulist element that displays a history for search via a drop down menu in the search box. Just like the toolbar button mentioned above, the drop down menu is defined inside the menulist element by the menupopup element. By default, a menulist works like a normal dropdown and does not allow user input, but by setting the editable attribute to true, you can make it work like a textbox with an arrow on its right side to show/hide the dropdown.

Figure 5 : Menulist - view source | view example | menulist reference

A menulist in the toolbar containing 2 items

XUL:

  <toolbar id="devedgetoolbar">
    ...
    <menulist editable="true" minwidth="160">
      <menupopup>
        <menuitem label="CSS"/>
        <menuitem label="DOM"/>
      </menupopup>        
    </menulist>
  </toolbar>

In order to provide a history of previous search terms, we need to add them to the dropdown. A menulist offers several methods like appendItem() and insertItemAt()to add/remove elements from its dropdown menu. Since we want to place newer search terms above older ones (rather than sticking the most recent on the end), we'll use insertItemAt(0, searchTerm), as the following example shows:

Figure 6 : Dynamic adding to a Menulist - view source | view example

XUL:

  <menulist id="devedgetoolbarSearchText" editable="true" minwidth="160"
     onkeypress="if(event.keyCode == 13){devedgetoolbarSearch(this.value);}" 
     minwidth="160">
    <menupopup />
  </menulist>
JavaScript:
  function devedgetoolbarSearch(searchTerm){

    // We want to add it to the top of the drop down, so index 0.
    mySearchMenuList.insertItemAt(0,searchTerm);
  }

Now that we have the basic Devedge toolbar done in XUL, we will describe how to get the toolbar into an Netscape 7.x browser.

Final look:
A complex toolbar

Overlays

In order to get a toolbar and other new XUL into the browser's user interface, you have to change your XUL file into an overlay. An overlay is a special XUL file that gets inserted into another XUL document. Overlay files typically overlay their new interface at run-time, which is what we'll be doing here. For the DevEdge Toolbar, we will name our xul file devedgetoolbarOverlay.xul.

Figure 7 : Basic Overlay Structure - overlay reference

XUL:

  <?xml version="1.0"?>
  <overlay id="devedgeToolbarOverlay" 
     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    ...

    <toolbox id="navigator-toolbox">
      <toolbar id="devedgetoolbar" ... >
        <!-- toolbar's XUL content goes here -->
      </toolbar>
    </toolbox>

  </overlay>

The toolbox with the id "navigator-toolbox" is part of navigator.xul, the XUL file that describes the main browser user interface. By using the same ID in the overlay, we instruct Netscape to add any new children of our toolbox to the toolbox in navigator.xul. We do the same to put a new entry into the View -> Show/Hide menu.

In addition to this run-time overlay process, the overlay file needs to be registered during the installation process in order to take effect.

Installation

Netscape 7.x uses XPInstall for cross-platform installation of packages. A .xpi file is basically a .zip file that consists of a javascript installer script and the files you want to install.

The DevEdge toolbar consists of three files - devedgetoolbarOverlay.xul, devedgetoolbarOverlay.js and contents.rdf (more on this file later).

Figure 8 : DevEdge Toolbar Directory Listing

devedgetoolbar\
  install.js
  content\
    devedgetoolbarOverlay.xul
    devedgetoolbarOverlay.js
    contents.rdf

While an .xpi is used for the packaging, the content to be installed is usually inside the .xpi as a .jar file. A jar file again is nothing more than a renamed ZIP file. In the devedgetoolbar\ directory, we create a devedgetoolbar.jar file from the content\ subdirectory.

Figure 9 : devedgetoolbar.jar added with contents of content/

devedgetoolbar\
  install.js
  devedgetoolbar.jar
    contains:
      content\
        devedgetoolbarOverlay.xul
        devedgetoolbarOverlay.js
        contents.rdf

We now combine the devedgetoolbar.jar file with the install.js file.

Figure 10 : final devedgetoolbar.xpi created

devedgetoolbar\
devedgetoolbar.xpi
  contains:
    install.js
    devedgetoolbar.jar

contents.rdf is a manifest file used to describe the relationship between a package (our toolbar) and a component. Our contents.rdf serves two purposes - first to add the DevEdge Toolbar package to the package list, and then to register our .xul file as an overlay for the main navigator window (navigator.xul).

Figure 11 : Registering the DevEdge Toolbar package in contents.rdf

RDF:

  <RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
       xmlns:chrome="http://www.mozilla.org/rdf/chrome#">

    <RDF:Seq about="urn:mozilla:package:root">
      <RDF:li resource="urn:mozilla:package:devedgetoolbar"/>
    </RDF:Seq>

    <RDF:Description about="urn:mozilla:package:devedgetoolbar" 
       chrome:displayName="DevEdge Toolbar" chrome:author="doron@netscape.com" 
       chrome:name="devedgetoolbar">
    </RDF:Description>

    ...

  </RDF:RDF>

In the above source code, our RDF adds the devedgetoolbar package to the package root list, and then defines an RDF:Description with more detailed information about the package. Next, we add our .xul file to the overlays sequence and state that we want to be overlaid onto navigator.xul:

Figure 12 : Adding the DevEdge Toolbar to the overlay registry in contents.rdf

RDF:

      
  <RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
           xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
    ...

    <RDF:Seq about="urn:mozilla:overlays">
      <RDF:li resource="chrome://navigator/content/navigator.xul"/>
    </RDF:Seq>

    <RDF:Seq about="chrome://navigator/content/navigator.xul">
      <RDF:li>chrome://devedgetoolbar/content/devedgetoolbarOverlay.xul</RDF:li>
    </RDF:Seq>
     
  </RDF:RDF>

Finally we take a look at install.js, which takes care of the installation and registration of our overlay. In order to simplify the customizing of the install.js script, the first section (Figure 13) defines the package-specific variables.

Figure 13 : Package-specific variables in install.js

JavaScript:

  // User defined constants
  //has to be the same as the package name in contents.rdf      
  const myProductName = "devedgetoolbar"; 
  const myProductRegKey = "/Netscape/devedgetoolbar";
  const myProductRegVersion = "0.0.1";
  const myJarFileName = "devedgetoolbar.jar";

  ...

The install.js initializes the installation using the initInstall() command. We accompany most installation commands with a logComment() method that logs the return value of the specific installation command. The actual install.log file where everything is logged is located in your Netscape top level directory.

Figure 14 : Initialize the install in install.js

JavaScript:

  ...
  // Installation Script - no user modifications needed
    
  var err = initInstall(myProductName, myProductRegKey, myProductRegVersion);
  logComment("initInstall: " + err);

  ...

Next, install.js copies the devedgetoolbar.jar file, which contains the content of the toolbar, to the chrome folder. First it gets the chrome folder by using getFolder(). It then sets the chrome folder as the default package folder using setPackageFolder(). Copying the .jar file to the chrome directory (now the default) is done by addFile().

Figure 15 : Get the chrome folder and copy the .jar file into it

JavaScript:

  ...
  fChrome = getFolder("Chrome");
  setPackageFolder(fChrome);
    
  err = addFile(myJarFileName)             
  logComment("addFile() returned: " + err);
  ...

After it copies the .jar file to the chrome directory, install.js registers it so that the contents.rdf that we defined get processed and our devedgetoolbarOverlay.xul gets added to the overlay list for navigator.xul. This is done by using the registerChrome() method. Finally, if there have been no errors (retrieved via getLastError), install.js performs the install. If not, it cancels the installation.

Figure 16 : Register the package and perform the actual installation

JavaScript:

  ...
  regErr = registerChrome(PACKAGE | DELAYED_CHROME, getFolder(fChrome,myJarFileName), "content/");
  logComment("regChrome returned: " + regErr);

  if (0 == getLastError())
    performInstall();
  else
    cancelInstall(err);

The final install.js looks as follows:

Figure 17 : final install.js

  // User defined constants
  const myProductName = "devedgetoolbar";
  const myProductRegKey = "/Netscape/devedgetoolbar";
  const myProductRegVersion = "0.0.1";
  const myJarFileName = "devedgetoolbar.jar";

  // Installation Script - no user modifications needed
    
  var err = initInstall(myProductName, myProductRegKey, myProductRegVersion);
  logComment("initInstall: " + err);
 
  fChrome = getFolder("Chrome");
  setPackageFolder(fChrome);
      
  err = addFile(myJarFileName)
  logComment("addFile() returned: " + err);
 
  regErr = registerChrome(PACKAGE | DELAYED_CHROME, getFolder(fChrome,myJarFileName), "content/");
  logComment("regChrome returned: " + regErr);

  if (0 == getLastError())
    performInstall();
  else
    cancelInstall(err);

For furthur reading about XPInstall, DevEdge has a XPInstall manual available.

To get the browser to process the .xpi file, the user has to either click on a link to the file or you can use the below function to initiate the installation. The XPIInstall function takes the file location (as a string) and the name of the package, which will be shown to the user in the XPInstall prompt. Note that the user will have to restart the browser to see the toolbar.

Figure 18 : Launch the XPI

JavaScript:

  function XPIInstall(file, title) {
    var xpi = new Object();
    xpi[title] = file;
    InstallTrigger.install(xpi);
  }

Install the DevEdge Toolbar

Customizing

All the files used in the final DevEdge Toolbar can be found here. The DevEdge toolbar is written to be easily customized.

devedgetoolbarOverlay.js has three constants that need to be changed.
myToolbarName - the name of the Toolbar. Used for the Uninstall dialog.
myToolbarId - id of the toolbar element.
myMenulistId - id of the menulist element used for search.

The file implements the devedgetoolbar object (which gets extended with methods by the devedgetoolbar.prototype lines), which should be renamed when creating a new toolbar to avoid collisions with other toolbars.

Line 10 of devedgetoolbarOverlay.xul creates a new devedgetoolbar object and uses that object for various tasks, such as loading a page via devedgetoolbar.LoadPage().

Uninstall

A toolbar should also be uninstallable. We provide a Uninstall method, which uses the global myToolbarId to identify the toolbar and then removes the toolbar, the entry in the View -> Show/Hide Menu, removes the overlay entry in overlays.rdf and the entries in chrome.rdf. We will not go into detail how the uninstall code works, as it should work for any toolbar without changes.

Where to go from here

While the DevEdge Toolbar is easily customized, customizing it to your website's need will probably require adding new features to the toolbar. Once the toolbar lives in chrome, it has cross domain access privileges, so retrieving data (in XML form) from an external server via Webservices such as SOAP or XMLHttpRequest is one possibility. The resources section below should provide furthur reading to implement such features. A followup article to this one will talk about using css to style the toolbar, retrieving remote data and storing data locally.

Resources

XUL:
- XulPlanet.com
- XulPlanet.com XUL Tutorial
- O'Reilly's Creating Applications with Mozilla, also available online at mozdev.org.
- Remote XUL Tutorial
- Creating a Mozilla Extension

XPInstall:
- XPInstall manual

A+R