Fooling with XUL
March 15, 2000
Table of Contents |
•The Finished Product |
With the interest and excitement surrounding Mozilla's approaching beta release, I decided it was time to put it through its paces. For a while now we have heard and seen presentations of XUL, the Extensible User Interface Language, which forms an integral part of Mozilla. Delegates at XTech 2000 were clearly excited by the project and its promise for delivering a richer user interface on the Web.
As far as buzzword-compliance goes, Mozilla boasts a host of W3C specifications, including XML, DOM, CSS, and RDF. I wanted to find out how all these bits fit together in reality, and how practical client-side development with Mozilla actually is. I attacked the problem in my favorite way, by just jumping in with an idea I wanted to implement.
One of the fun parts of the Mozilla browser is the sidebar. Working in a similar way to the so-called "Explorer" bars in Microsoft's browser, the sidebar allows for handy access to components such as bookmarks, search, and "What's Related?" I decided that a shortcut interface to the XMLhack site would be a reasonable-sized learning project to undertake.
The Finished Product
Working from back to front, I'll show the finished product and then demonstrate how I got there. The image on the right shows a list of articles with icons, which when double-clicked, cause the browser to navigate to the complete article on the web site.
The first step was to learn how to construct the user interface. Of course, I could have done this in straight HTML rather than using XUL, but I wanted to use the experience to learn about the Mozilla browser, and also for the user to feel that the component was more tightly integrated with the sidebar. Fortunately, XUL is one of the better-documented parts of the Mozilla browser, and I used Neil Deakin's XUL Tutorial as my starting point. This is an excellent document, although still a work in progress.
It is beyond the scope of this article to present a XUL tutorial, but I will mention
the
key concepts that are required. User interface descriptions in XUL usually span four
files:
the layout (.xul
) file, the CSS style sheet, scripts, and localization
information. For my experiments, I simply used the default styles and omitted localization,
so I only had to consider two files: the .xul
file itself, and a file for my
JavaScript.
Creating the User Interface
Working through the XUL tutorial, I grasped the fundamentals of creating an interface
in
XUL. Each .xul
file describes a window that contains widgets. The
minimal XUL file looks like this:
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="xmlhack-window" title="XMLhack Headlines"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
>
</window>
Mozilla makes extensive use of XML Namespaces, hence the somewhat baroque URI denoting
that
window
and its contents have the XUL default namespace. The browser can
interpret elements from other vocabularies (such as HTML and RDF) within XUL documents.
These can be included in XUL descriptions by using namespace-qualified names.
Another thing of note to point out is the chrome://
URL scheme. This scheme
allows Mozilla to find localized resources for its user interface. If you look in
your
Mozilla install directory, you will find the chrome
directory, beneath which
are the XUL descriptions for the user interface. Mozilla interprets the chrome URL
to find
the actual file it wants, adjusting for such things as the current locale.
So, to get going, I added some simple widgets to my skeleton user interface, following
the
"Find Files" example
from the XUL tutorial. I placed the XUL file on my web server, pointed Mozilla at
http://localhost/test.xul
and got back the text contents of the file in the
browser, rather than a cool user interface. Oh dear.
It turns out that a little bit of configuration to your web server is required in
order to
get Mozilla to recognise XUL files. In particular, XUL currently uses the Internet
Media
Type text/xul
. I simply added this line to my Apache's mime.types
file, which fixed the problem:
text/xul xul
That issue resolved, I continued with the tutorial and had soon created a pleasing user interface. Not everything worked quite as smoothly in the browser as it did in the tutorial though! Some of the widgets aren't properly implemented yet in Mozilla, and it can vary from platform to platform. For instance, I was able to get context pop-up menus ("right-click" menus) working on Windows, but not on Linux.
Layout Mechanism
Arrangement of widgets in a XUL interface is done primarily by use of boxes. Readers au fait with the TeX typesetting system (or many other layout systems) will already be familiar with the box method. Boxes can have their contents stacked either horizontally or vertically. Layouts can be constructed by a combination of these boxes.
Each UI element has a flex attribute, which governs how it will stretch to fill the
space available. There is also a spring
element, which is an empty box you can
use to put space in between other UI elements. This example shows how two pushbutton
widgets
and a spacer would be written:
<titledbutton class="push" value="OK"
style="min-width: 100px;" flex="1"/>
<spring flex="1"/>
<titledbutton class="push" value="Cancel"
style="min-width: 100px;" flex="1"/>
As the window containing these elements was expanded, each button and the spring would
all
enlarge to consume the new space in equal measure, owing to their having identical
flex
values. Note the use of CSS styles to specify minimum widget
dimensions.
It's much more interesting to play with this for yourself than to have me recount all the ins and outs, so I recommend pursuing the exercises in the XUL Tutorial.
Adding Behavior
A collection of widgets is all very well, but not very useful without something actually
happening when they are activated. This is achieved through the use of JavaScript.
In a
similar way to HTML, a XUL document is exposed to the browser via a DOM, which can
be
scripted. Event handlers such as onClick
can be specified as attributes of
interface elements, much as in HTML.
Although script can be included inline in XUL with the <html:script>
element, the documentation advises against it. Instead, script should be stored in
a
separate file, and referenced like this:
<html:script src="myScript.js"/>
It is relatively simple to get started with writing scripts. Here's a quick example. Imagine a window with this button inside:
<titledbutton class="push" value="Hello" onclick="sayHello();"/>
... and this JavaScript:
function sayHello() { alert("Hello, World!"); }
More complex operations can be achieved through the use of the DOM. XUL provides two
extension functions to the XML DOM: getElementById
and
getElementsByAttribute
(see Introduction to XUL on
Mozilla.org). Extensive use of the ID
attribute on user interface elements is
made within interface descriptions, in order to facilitate easy scripting and modification
of the interface. The interface's DOM can be modified by scripts, enabling such things
as
the dynamic alteration of style and content of widgets.
Table of Contents |
•The Finished Product |
Adding Data to the Interface
I had reached the point of defining what I wanted my interface to look like. I needed to use the tree widget to show my list of headlines and so roughed out an interface description. You can download the full XUL file here, but here's an extract demonstrating the tree widget:
<tree style="height: 100%; width: 100%" flex="2">
<treehead>
<treerow>
<treecell value="Headlines"/>
</treerow>
</treehead>
<treecol/>
<treechildren>
<treeitem>
<treerow>
<treecell
src="chrome://bookmarks/skin/bookmark-item.gif"
value="Story 1"/>
</treerow>
</treeitem>
...
</treechildren>
</tree>
Notice that I used the browser's bookmark icon at this point instead of my own. This
is one
of the big advantages of Mozilla's open approach: the actual interface components
themselves
can be used for learning. In particular the
chrome/bookmarks/content/default/bm-panel.xul
file proved a great source of
information in my experiment. This is the interface description for the sidebar bookmarks
panel.
However, I did not want to create the .xul
file each time with different
contents; rather I wanted to be able to merge a data file with the XUL layout, in
much the
same way I would merge XML with XSLT to generate HTML. The way Mozilla handles this
is to
use RDF datasources. Arbitrary data, described in RDF, can be used to populate user
interface elements.
Yes, I said "RDF." For one reason or another this specification has acquired a reputation as being unwieldy and difficult to understand. The good news is, you don't need to understand everything in order to be able to use it. I started from the point of an example RDF file, and with a little RDF knowledge, created what I needed for my purposes.
So here's the RDF primer: RDF is basically a way of describing things. The "things" are known as resources. A resource is associated with a URI. Here's an example description of me:
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ORN="http://www.oreillynet.com/rdf#">
<RDF:Description about="urn:oreillynet:editors:edd">
<ORN:Email>edd@xml.com</ORN:Email>
<ORN:Site>http://xml.com/</ORN:Site>
</RDF:Description>
</RDF:RDF>
RDF provides constructs such as Description
, Seq
(sequence), and
Alt
(alternatives) to make describing resources easier and more portable. In
the above example, I simply invented the http://www.oreillynet.com/rdf
namespace to contain my Email
and Site
properties. In practice
there are some established vocabularies for metadata, such as Dublin Core, which you can use to improve the interoperability of your descriptions
(see also Using
the Dublin Core with RDF).
For my XMLhack project, the next task was to create an RDF description of all the headlines on the front of the web site. I constructed a small vocabulary to describe each story. Here's an extract for one story:
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:s="http://xmlhack.com/stories/rdf#">
<RDF:Description about="urn:xmlhack:story:207">
<s:title>Oracle adds new functionality to XSQL Pages</s:title>
<s:time>08:00, 10 Jan 2000</s:time>
<s:author>Edd Dumbill</s:author>
<s:url>http://xmlhack.com/read.php?item=207</s:url>
<s:icon>http://xmlhack.com/images/s_tools.gif</s:icon>
</RDF:Description>
...
</RDF:RDF>
You will note again that I could have profitably used elements from the Dublin Core to describe things such as "title," but as the sole intention of this particular RDF is to populate my user interface I preferred to take the simplest route.
Having described each story, I also wanted to describe a resource that would be my
"headlines" list. For this, I used an RDF sequence I gave the ID headlines
:
<RDF:Seq ID="headlines">
<RDF:li resource="urn:xmlhack:story:207"/>
<RDF:li resource="urn:xmlhack:story:206"/>
...
</RDF:Seq>
XMLhack is based on PHP and MySQL, so I had little difficulty writing the PHP script to generate this RDF. The remaining problem was inserting the data into the XUL.
Datasources and Templates
From within a XUL file, you can associate an RDF data source with a user interface item:
<tree id="xmlhack-tree"
datasources="http://myserver.com/headlines.rdf"
ref="http://myserver.com/headlines.rdf#headlines"
style="height: 100%; width: 100%">
The datasources
attribute signifies the source of the data, and
ref
says where in the data source I want to get data from. Here I indicate I
want to read the sequence of headlines.
The template
and rule
elements are then used to merge the data
with the interface:
<template>
<rule>
<treechildren>
<treeitem uri="rdf:*">
<treerow>
<treecell
src="rdf:http://xmlhack.com/stories/rdf#icon"
value="rdf:http://xmlhack.com/stories/rdf#title"
s:url="rdf:http://xmlhack.com/stories/rdf#url"/>
</treerow>
</treeitem>
</treechildren>
</rule>
</template>
This code replaces everything below the <treecol/>
from my interface
mockup above. Its purpose is quite self explanatory: the uri
attribute matches
each resource URI in the headlines list, making that the starting point. Thereafter,
the
rdf:
-style attribute values select properties of the resource: we described
these properties in the individual story descriptions earlier. Deeper understanding
of what
is going on can be gleaned from reading the RDF Syntax Recommendation from the W3C.
One subtlety here that I didn't find explicitly stated in the XUL documentation is the security model. Much as with Java applets, you can only retrieve data sources served from the same place as the XUL. For instance, this means you can't have a filesystem data source in a XUL description served by a web server. I found this out the hard way.
Making it Work
From studying the template above, you will note I added the s:url
attribute to
my tree cell. As the full XUL file shows, I
earlier bound the s:
prefix to another namespace of my own. This is so I could
communicate to my JavaScript the URL to which the browser ought to navigate when it
is
double-clicked. To tie in the script, I added this attribute to the tree
element:
onclick="return clicked(event, event.target);"
Again, I must acknowledge the value of the existing browser chrome files for providing
me
with shortcuts to write this script. Here's the definition of clicked()
:
function clicked(event, item) {
if (event.clickCount == 2 && event.button == 1) {
var tURL=item.getAttribute('s:url');
if (window.content == null) {
window.openDialog(
"chrome://navigator/content/navigator.xul",
"_blank", "chrome,all,dialog=no", tURL );
} else {
window.content.location = tURL;
}
return(true);
}
return(false);
}
On a double-click from the left mouse button, this script navigates to the destination URL, opening a new window if needed.
The final piece of the jigsaw is to allow the user to add the XMLhack panel to their sidebar. A short piece of Mozilla-specific JavaScript suffices:
<a href="javascript:sidebar.addPanel('<?xmlhack?>',
'http://xmlhack.com/mozPanel.xul','')">Add XMLhack
Panel To Sidebar</a>
Here's the link for you to try yourself: Add XMLhack Panel To Sidebar. I've tested this in M14 and later nightly builds of Mozilla. On Windows you may need to open and close the sidebar to get it to display properly.
Conclusions
Apart from having a lot of fun, what did I discover playing with Mozilla? (I will lay aside the obvious fact that there are numerous bugs, as the browser has not even reached beta release stage yet.)
Mozilla could mean widespread RDFI am of the opinion that what RDF needs is a "killer-app." Although lauded by many (especially Tim Berners-Lee), it has failed to break through in the same way XML has. Arbitrary data can be integrated into Mozilla interfaces via RDF with ease: the smallest part of my project was writing the RDF serialization of the data. Mozilla could well turn out to be a huge popularizer of RDF, leading to many sources of RDF information on the Web.
Mozilla provides a richer interface to online applicationsThe richer widget set provided by XUL compares well with those available for years in desktop applications. HTML has historically been woefully poor in its user interface elements, contributing to a Web in which "read" is much more common than "write." Mozilla could provide a truly cross-platform, network-aware framework for applications.
One proof of this is that the browser user interface itself is largely written in XUL and Javascript (and consequently the source code is a good place to explore how this stuff works!).
Having been involved in constructing online applications for several years, mainly in web content management, I'm excited by the prospect of more efficient web-enabled interfaces that are truly integrated with the browser. XUL delivers where Java never quite managed to.
Mozilla needs supportMicrosoft's Internet Explorer is still ahead of Mozilla in terms of functionality, and way ahead in terms of adoption. Several otherwise free-spirited developers tell me that they use IE5 simply because it is the better browser and their altruism has run out of steam. Mozilla certainly faces tough challenges. However, it's important that it get the support of the XML and open source communities, for these reasons among others:
- Faithful standards implementation: Mozilla provides the opportunity for 100% implementations of W3C standards in a production environment. While not a "crowd-puller," this is important to ensure the long term openness of client-side web technologies.
- Keeping the web open in a world of online applications: such applications are on the increase, and represent the future in both Internet and intranet situations. Current HTML can barely support the required flexibility in the user interface. Unless an open alternative is available, online applications will increasingly become browser or platform-specific.
- Encouraging the "semantic web": more widespread use of RDF will enhance the prospects of a more "intelligent" web, where automated processes can be used to aid users in finding and processing information.
Details on participating in the Mozilla project can be found at Mozilla.org.