The Mozilla project team very early decided that it would standardize core development in C++, but that it would create a component system, XPCOM (Cross Platform Component Object Model), for sharing core and extension functionality across platforms. It also developed a means of designing user interfaces for such components in a platform-neutral language it called XUL (XML User Interface Language), pronounced "zool". Not much is special about the use of XML in XUL: The team could have come up with a completely new syntax, and it's best to think of XUL as just another programming language that happens to use XML for basic syntax. XUL does take advantage of existing Web technologies such as ECMAcript (most popularly known in its JavaScript implementation), DOM, and CSS, so it should be fairly easy for Web developers to learn. A companion language, XBL (eXtensible Bindings Language), is used to control dynamic behavior of XUL elements. XUL and XBL are powerful enough to develop a full browser or other full, cross-platform applications based on Mozilla components, but for most developers XUL is used more modestly to extend or manipulate the user interface of an existing browser or other application (a use case for which XUL was carefully designed). This is most often the case when you are developing a browser extension, and the most attractive target for most Mozilla browser extensions is Firefox, Mozilla's very popular stand-alone browser. Firefox is built in XUL and JavaScript. It is a demonstration of the power of XUL, but it is also very open for extension using XUL.
This article puts together the basic pieces you'll need to create most extensions with user-interface features—XUL, CSS, JavaScript, and DOM. It isn't an introduction to XUL—you can find some such introductions linked in Resources. It assumes you understand the basic concepts of the language and focuses on showing you how to put it to work. It also assumes basic knowledge of JavaScript, DOM, and CSS.
One of the great things about XUL is that Mozilla browsers treat it, for the most part, like any other Web content. This means you can rapidly try things out and deploy things during the development phase, before worrying about proper packaging into applications or full extensions. Several useful tools are available for such rapid deployment—Ted Mielczarek's Extension Developer's Extension (see Resources) includes many useful tools such as the Live XUL Editor which allows you to edit and preview XUL online. He also offers a very simple XUL page on his Web site that allows you to do much the same (barring some security restrictions applied to all XUL loaded over the Web). For the most part, I prefer a more direct approach to the XUL development cycle: editing in a good text editor, especially one that provides features to ease some of the pain of authoring XML, and then I simply load the XUL file directly into a Firefox tab. I edit again to make any tweaks or fixes, and reload the page for that file. Listing 1 (stats.xul) is a simple XUL example that demonstrates some of the language's power. It allows you to enter a URL and view the resulting Web page in one panel and some simple statistics about the page in another.
Listing 1 (stats.xul). Simple XUL example
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="statswindow" title="View Web page stats">
<vbox flex="1">
<hbox id="toppanel">
<label value="Enter a URL:"/>
<textbox id="url" flex="1" value="http://www.ibm.com/developerworks/web"/>
<button label="Go!"/>
</hbox>
<description value="Current page:"/>
<hbox flex="1">
<iframe id="contentview" src="http://www.ibm.com/developerworks/web" flex="2"/>
<vbox>
<groupbox>
<caption label="Stats"/>
<grid flex="1">
<columns>
<column flex="1"/>
<column flex="1"/>
</columns>
<rows>
<row>
<label value="Word count"/>
<textbox class="count" id="wordcount" value="N/A" readonly="true"/>
</row>
<row>
<label value="Character count"/>
<textbox class="count" id="charcount" value="N/A" readonly="true"/>
</row>
<row>
<label value="Element count"/>
<textbox class="count" id="elemcount" value="N/A" readonly="true"/>
</row>
</rows>
</grid>
</groupbox>
<spacer flex="1"/>
</vbox>
</hbox>
</vbox>
</window>
|
The chrome://global/skin/ stylesheet declaration makes the browser use the default global.css from your chrome package, representing either the built-in Firefox look and feel or some variation if you have a skin installed. The window uses a set of nested boxes to organize content, but I try to avoid too much fiddling with presentation (I'll get to that in the next section) and stick mostly to the logical grouping of UI elements. The iframe element, which is similar to the HTML element of the same name, creates a content frame into which the Web page of interest is rendered, defaulting to http://www.ibm.com/developerworks/web, which is also the value placed in the
url textbox. These values are just matched so the default
view makes sense to the user and have nothing connecting them just yet (I'll get to
that in the later section). A groupbox comes in handy for organizing the stats entries, each of which is a read-only text box defaulting to a dummy value for now. The result is a simple, static window that, thanks to the power of iframe, still offers an impressive result. Save it in a file "stats.xul" and load this into any Mozilla browser window. You can use a "file:" URL, or the "Open File" menu in Firefox. You should get a display similar to figure 1.
Figure 1. Firefox browser window with Listing 1 loaded
You can do some manipulation of positioning of UI elements in XUL, but for the most part the fine details of how a window looks are a matter of style that is better left to a stylesheet. Using CSS for XUL allows you to control the usual properties—colors, fonts, spacing, and sizing—familiar from CSS for HTML or other XML. Listing 2 (stats.css) shows some declarations for tweaking the presentation of Listing 1.
Listing 2 (stats.css). CSS to be applied to Listing 1
window {
font-family: arial, "lucida console", sans-serif;
}
description {
padding: 5px;
color: blue;
}
#toppanel {
padding: 5px;
}
#contentview {
padding: 5px;
border: thin solid black;
}
.count {
width: 5em;
}
|
You can see the rules using ID, class, and element selectors matching parts of Listing 1. You can apply this stylesheet by adding an additional declaration at the top of the XUL file, as in Listing 3, which only shows the first four lines of the file after updating to add the new CSS declaration.
Listing 3. Updated first few lines of Listing 1 with added stylesheet declaration
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="stats.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
Notice that I didn't remove the declaration for chrome://global/skin/. You almost always want to do the same, and the browser will combine the global skin with your more specific stylesheet. Figure 2 shows the updated XUL window with a stylesheet, loaded into Firefox.
Figure 2. Firefox browser with XUL window using CSS
Bringing things to life with script
The last step in developing this demo XUL application is to make the controls work. We do this by binding the control elements to behavior. XUL has a sister language, XBL, which allows you to perform much of this binding using declarations, reducing the amount of required script. But in this article the binding will be through event attributes that reference JavaScript functions.
Listing 4 (stats-styled-scripted.xul). Listing 1 updated with CSS and script declarations, as well as script hook attributes
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="stats.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="statswindow" title="View Web page stats">
<script type="application/x-javascript" src="stats.js"/>
<vbox flex="1">
<hbox>
<label value="Enter a URL:"/>
<textbox id="url" flex="1" value="http://www.ibm.com/developerworks/web"/>
<button label="Go!" oncommand="change_url(event)"/>
</hbox>
<description value="Current page:"/>
<hbox flex="1">
<iframe id="contentview" src="http://www.ibm.com/developerworks/web" flex="2"/>
<vbox>
<groupbox>
<caption label="Stats"/>
<grid flex="1">
<columns>
<column flex="1"/>
<column flex="1"/>
</columns>
<rows>
<row>
<label value="Word count"/>
<textbox class="count" id="wordcount" value="N/A" readonly="true"/>
</row>
<row>
<label value="Character count"/>
<textbox class="count" id="charcount" value="N/A" readonly="true"/>
</row>
<row>
<label value="Element count"/>
<textbox class="count" id="elemcount" value="N/A" readonly="true"/>
</row>
</rows>
</grid>
</groupbox>
<spacer flex="1"/>
</vbox>
</hbox>
</vbox>
</window>
|
Notice the added script element within window, and the added attribute on the "Go!" button—
oncommand="change_url(event)". Listing 5 is the referenced JavaScript file "stats.js".
Listing 5 (stats.js). JavaScript code to handle "Go!" button
//Invoked in response to a click on the "Go!" button
function change_url(event)
{
//Variables for convenient access to specific elements in the XUL
var urlbox = document.getElementById("url");
var contentview = document.getElementById("contentview");
var wordcountbox = document.getElementById("wordcount");
var charcountbox = document.getElementById("charcount");
var elemcountbox = document.getElementById("elemcount");
//Setting this attribute, happens to change the visible contents of the panel
contentview.setAttribute("src", urlbox.value);
//Fake up the update code for now, to allow running in Firefox
wordcountbox.previousSibling.value += " (fake)";
wordcountbox.value = "1000";
charcountbox.previousSibling.value += " (fake)";
charcountbox.value = "100";
elemcountbox.previousSibling.value += " (fake)";
elemcountbox.value = "10";
}
|
Listing 4 binds the function change_url to a click of the "Go!" button. This function receives the event details for the click, though they are not used in this case. The global document object allows you to use DOM to access the various XUL elements, in this case by using
getElementById to find an element by its unique ID. XUL
elements of course have XML attributes, but they also have object properties, and sometimes even though attributes and properties have similar names, they behave differently when queried or manipulated. For example, I get the text within the URL text box using code such as urlbox.value rather than
urlbox.getAttribute("value"). The latter would always read
the original content of the text box ("http://www.ibm.com/developerworks/web")
regardless of what the user had entered since loading the page. Similarly, if you
wanted to update the visible content of such a text box in code, you should write urlbox.value = newvalue rather than urlbox.setAttribute("value", newvalue). You can see this principle in play in the code that updates the count text boxes at the bottom of the function. These nuances are all covered in the XUL reference documentation, so be sure to read carefully about any XUL element you use.
The attribute versus property warning also applies to the line contentview.setAttribute("src", urlbox.value);. The more correct way to go to a new URL would be:
contentview.contentDocument.location.href = urlbox.value;.
But there is a problem with that line. Firefox's strict security model for XUL
documents means that if you try to access some sensitive object attributes,
including contentview.contentDocument, your JavaScript
will throw an exception. You'd need to use the Error Console window to see the
error, which will even then be something vague and generic such as "Error: uncaught exception: Permission denied to create wrapper for object of class UnnamedClass". This is the main problem with the direct means of experimenting with XUL. In the next article I'll show you how to turn the raw materials from this article into a proper Firefox extension, which avoids security limitations through proper chrome registration. For now, the code changes the unrestricted src attribute of contentview (which happens to give a visible result), and makes dummy changes to the count text boxes to show you the basic setup of scripting in XUL.
You've seen how naturally XUL mixes with the basic building blocks of Web development. XUL has a wealth of UI elements and each one has a wealth of attributes and properties, adding up to a generous toolbox for building applications and extensions. Loading XUL directly into the browser is a handy way to rapidly prototype and storyboard XUL applications, but once you start manipulating things with script you may run into unpredictable difficulty. Luckily you have the option of XULRunner, a stand-alone tool created by Mozilla developers. XULRunner can run XUL files outside of any browser, thus avoiding the stringent security model. It's definitely a developer's tool, and can be a pain to get working, especially on some platforms, but it's an important option for developing and deploying XUL projects. In the next article I'll show you how to package the pieces from this one into a Firefox extension, with updates to the script to take advantage of the lowered security barriers.
Learn
-
The Mozilla Development Center hosts a very comprehensive XUL Tutorial, which is especially useful because of its many links to the reference guide for XUL elements.
-
Sort through several technologies comparable to XUL in Technology options for Rich Internet Applications, by Mr. Vaibhav V. Gadge.
- Learn how to use CSS to style XML, including XUL in the tutorial
"Use Cascading Stylesheets
to display XML, part 1" (developerWorks, November 2004). Continue with "Use Cascading Stylesheets to
display XML, part 2" (February 2005), which covers advanced topics for the use of CSS to style XML in browsers.
-
Give XULRunner a try, but
expect a bit of wrestling. See "My XULRunner on Mac recipe" for details on using XULRunner on Mac OS X.
-
Expand your site development skills with articles and tutorials that specialize
in Web technologies in the developerWorks Web development zone.
Get products and technologies
-
Check out Ted Mielczarek's Extension Developer's Extension for Firefox, which bundles many useful tools such as the Live XUL Editor. He also offers limited live XUL editing on his site.
Discuss
-
Participate in developerWorks
blogs and get involved in the developerWorks community.

Uche Ogbuji is Partner at Zepheira, LLC, a solutions firm specializing in the next generation of Web technologies. Mr. Ogbuji is lead developer of 4Suite, an open source platform for XML, RDF and knowledge-management applications and lead developer of the Versa RDF query language. He is a Computer Engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. You can find more about Mr. Ogbuji at his Weblog Copia.



