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.
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.
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.
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.
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.
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:
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:
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.
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).
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.
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.
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).
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:
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.
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.
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().
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.
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:
// 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.
JavaScript:
function XPIInstall(file, title) {
var xpi = new Object();
xpi[title] = file;
InstallTrigger.install(xpi);
}
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
