Chapter 3: Introduction to XUL—How to build a more intuitive UI

Draft
This page is not complete.

Note: If you want to contribute to this document please follow the guidelines on the Contribute page.

This document was authored by Hiroshi Shimoda of Clear Code Inc. and was originally published in Japanese for the Firefox Developers Conference Summer 2007. Shimoda-san is a co-author of Firefox 3 Hacks (O'Reilly Japan, 2008).

Before you learn how to develop extensions, let's learn about XUL, the XML-based User-interface Language, which is one of the building blocks for extensions.

Introduction

An overview of XUL

XUL is an XML-based language, and was developed to be the GUI markup language for the Mozilla browser. There are earlier experiments going back a long way in developing user interfaces using a combination of HTML and scripting languages, and XUL could be considered an evolutionary step from that. Similar approaches are found in XAML, which is used in Windows Vista, and Flex, which is used in Adobe Flash. Like web pages, which display the same regardless of platform, applications marked up in XUL will work the same in any environment where Firefox runs.

Because HTML was originally conceived as a language for marking up documents, specifically web pages, it is inevitably lacking in functionality for marking up applications. XUL, on the other hand, was conceived from the ground up as a markup language for user interfaces, and makes it possible to insert UI components with sophisticated features just by writing tags, without any particular scripting.

Unlike languages with formal specifications that have been standardized by bodies like the W3C, XUL currently does not have an explicit specification.

Note: Although there is a specification document, its markup is based on implementations and markup as of 2001, and current XUL differs from it in many aspects. For the latest XUL specifications, please see the XUL Reference on MDC and the XUL page in the Mozilla wiki.

For each element that I explain in this chapter, I will illustrate it with a source code example. You can type these examples up and open them in Firefox to see how they behave and appear. You can download a file with all the source code examples from: FIXME: Attache the translated tarball - Paul Task !.

XUL display methods

XUL is used almost exclusively in Mozilla applications like Firefox and Thunderbird, and extensions for them, but other web browsers based on Firefox or the Gecko engine, and even web-based content also used XUL. For example, there is the Mozilla Amazon Browser, which helps with shopping at Amazon, and the Presentation Method in XUL, a tool for writing and displaying presentations

To try out the code samples in this chapter, save them as text files with .xul extensions and drag and drop them into the Firefox browser window.

FIXME: Explain how setting this option and use next listings

If we want to run Firefox displaying none of its GUI and only the contents of a certain XUL file, we can launch Firefox and set the option: -chrome file_URL.xul

Another way, as shown in Listing 1, is to use the window.openDialog() method, which can be used only within a XUL window. This is used by extensions to open a separate window.

window.openDialog('another.xul', '_blank','chrome,all,dialog=no'); 

Listing 1: Opening a window without the Firefox GUI

XUL as an XML application

Listing 2 shows an example of a GUI definition file marked up in XUL (a "XUL document"). The root element in XUL is generally the "window" element. The namespace URI is :

http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul

If the character encoding is UTF-8, both the encoding specifier and the XML declaration can be omitted. But for the samples in this chapter, please include them.

Because a XUL document is also an XML document, it can contain XHTML, SVG, MathML, and other elements; if you use external entity references, the XML document's contents are modularized; combined with other XML-related technologies, such as DOM, XLink, or XSLT, you can use it for a number of different applications.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <!-- Contents go here. -->
</window>

Listing 2: General structure of a XUL document

Even with the internationalization of Firefox and extensions, this kind of XML function is being used.

Reading in style sheets

XUL uses CSS to specify the appearance of its elements. Again, since XUL is a type of XML, stylesheets are read in using the xml-stylesheet processing instruction.

On line 2 of Listing 2, we’re reading in the theme’s standard style sheet. By referring to a special style sheet within chrome://global/skin/, we can make label and button sizes, window background color, etc, match the currently selected theme in Firefox. We can also read in a stylesheet that we had set up ourselves.

The XUL box model

In principle, XUL lays out all UI components using combinations of two kinds of boxes: horizontal and vertical. As shown in Listing 3, elements can be laid out horizontally using the hbox (horizontal box) element or vertically using the vbox (vertical box) element. When this file is opened in Firefox, it is displayed as shown in Figure 1.

<label value="Horizontal layout"/>
<hbox>
  <button label="horizontal1"/>
  <button label="horizontal2"/>
</hbox>
<label value="Vertical layout"/>
<vbox>
  <button label="vertical1"/>
  <button label="vertical2"/>
</vbox>
<label value="mixed"/>
<hbox>
  <button label="mixed1"/>
  <vbox>
    <button label="mixed2"/>
    <button label="mixed3"/>
  </vbox>
  <button label="mixed4"/>
</hbox>

Listing 3: Horizontal and vertical boxes

Figure 1: Output of Listing 3

There is also a grid element, which can be used for layouts similar to those achieved using the HTML table element, a stack element for layering other elements, and so on. All on-screen widgets are laid out using these boxes, making complex interface designs possible.

Common attributes

Before we look at the various interface objects, let's look at some of their common attributes, especially the frequently used ones.

id and class

The attributes id and class fill the same role that the XHTML attributes of the same names fill. id is used to define a unique name for an element, and class is used to classify elements; both of these provide convenient ways to refer to elements in CSS and JavaScript. There are also special kinds of element that only come into play after being referenced by another element.

orient

Whether a box's contents will be vertically or horizontally arrayed depends on the elements in their initial state. You can explicitly set or change this layout using the orient attribute, with options for horizontal and vertical.

align and pack

The align and pack attributes both specify the layout of elements within a box. They can both take the values start (top or left), center, end (bottom or right), or stretch (extend this element to match the element with the greatest height or width).

The align attribute is defined as operating in the axis perpendicular to the orient attribute, while the pack attribute operates along the same axis as the orient attribute. Figure 2 shows how setting align="center" pack="start" on two elements will result in completely different output with the only difference being the value for orient.

<box flex="1" align="end" pack="end">
  <button label="Happy"/>
  <button label="Sad"/>
</box>

Listing 2: How align and pack respond to orient

Figure 2: Output from listing 2

flex

Elements ordinarily have fixed height and width. The flex attribute indicates that an element should be expanded to take up all of a window's height or width.

flex takes a positive integer as a value, which is a growth multiple on the axis of the parent element's orient attribute. For example, flex="1" indicates that the element should extend to fill that axis; if there are two elements with flex="1" in a row, they are adjusted to be of equal size. You can assign flex="2" (or higher values) as a size multiplier for elements. In Listing 4, the second label will be displayed twice as big as the first (Figure 3).

<hbox>
  <label value="label1" flex="1" style="border: 1px solid;"/>
  <label value="label2" flex="2" style="border: 1px solid;"/>
</hbox>

Listing 4: Growing with flex

Figure 3: Output of Listing 4

ordinal

Within a XUL box, elements will ordinarily be laid out following their order of appearance in the source code (laid out left to right or top to bottom). Use the ordinal attribute to alter their order. The ordinal attribute takes positive integer values, which are used to order the box’s layout—in the example in Listing 5, the buttons would be laid out in the order button3, button2, button1 (Figure 4).

If multiple elements have the same ordinal value, they will be laid out in the same relative order they appear in the source code. The default value for ordinal is 1.

<vbox>
  <button label="button1" ordinal="3"/>
  <button label="button2" ordinal="2"/>
  <button label="button3" ordinal="1"/>
</vbox>

Listing 5: Changing order with ordinal

Figure 4: Output of Listing 5

box size

You can set the size of XUL elements explicitly using the width and height attributes, as shown in Listing 6. If you are creating elements that can grow using the flex attribute, you can also set minimums and maximums using minwidth, minheight, maxwidth, and maxheight. These all use pixels as the unit of size.

<button label="Button" width="200" height="100"/>

Listing 6: Setting the size of a button

Also, as shown in Listing 7, you can embed CSS inline into elements via the style attribute, which allows you to set sizes using units other than pixels.

<button label="Button" style="min-width: 10em;"/>

Listing 7: Setting the size of a button using CSS markup

hidden and collapsed

The hidden and collapsed attributes act as switches to turn off the display of elements.

Setting hidden="true" disables display of that element. This has the same effect as setting display: none in CSS. You would use this to set a hidden or absent state, for example items that aren't displayed in contextual menus.

Setting collapsed="true" sets an element's height and width to 0, but the element is treated as being present. This is equivalent to setting visibility: collapse in CSS. Use this to "roll up" sidebars that aren't currently in use.

disabled

While not appropriate for all elements, you can use the disabled attribute to temporarily disable input to an element that is normally operable by the user. Typically, elements with the disabled="true" attribute will appear as either transparent or pale gray.

Note: With attributes like hidden, collapsed, and disabled, which take Boolean values, setting disabled="false" in XUL can have unintended consequences. This is because there may be cases where a script or CSS rule will be written in such a way that they treat the value as true unconditionally if any value has been set. So when changing the attribute’s value using a DOM function for example, do not use setAttribute('disabled', false) — instead, use removeAttribute('disabled').
tooltiptext

Use the tooltiptext attribute to display a brief explanatory tooltip above the element. The text entered as the value for this attribute is what gets displayed as the tooltip.

persist

The persist attribute is an easy way to record and store a XUL element’s state after it has been changed by a user operation. Enter the names of the other attributes whose values you want to store as a space-delimited ASCII string into the value for persist; the next time that XUL document is opened, the saved values will automatically be restored4. The settings system, which we will explore in Chapter 4, makes it possible to save simple states without any complex scripts.

Note: These values are stored in localstore.rdf, inside the user profile.

In order for the persist attribute to record the states of other elements, each of those elements must have its id set.

Widgets that can be used in XUL

Root elements

XUL documents use different root elements for different purposes. In this section, we’ll look at three typical types of root element: the window, page, and dialog elements.

Root elements use the windowtype attribute as an arbitrary identifier for the type of window. For example, Firefox uses the windowtype navigator:browser for its browser window and Browser:Preferences for its options dialog. Using methods we will cover in Chapter 4, we will see how windows can be acquired using these values as keys.

A window's dimensions and location on screen can be specified using the attributes width, height, screenX, and screenY (all using pixels as units). With the previously discussed persist attribute, you can easily store a window's size and location. Firefox uses this method to store its own window sizes and locations as well.

General root elements

window

The window element that has appeared in examples so far is a root element used to define an ordinary window. This displays the same kind of window used for the Firefox browser window, the bookmark manager window, and many other windows. You can generally use the window element as your root element.

page

For sidebar panels and other XUL documents that are opened within inline frames, use the page element as the root element. Apart from having a different intended purpose than the window element, it is functionally no different.

Root elements for dialog windows

dialog

Use the dialog element when creating options dialogs, confirmation dialogs, etc. This element takes a number of attributes, and can easily be made to display controls (buttons, etc) using widgets and layouts native to whatever platform it is running on.

For example, Windows places the OK button on the left and the Cancel button on the right, while Mac OS X has the opposite layout. This is what Firefox itself uses for bookmark properties and other dialogs.

Buttons used in dialog windows

A dialog element will display some number of buttons at its bottom. As shown in Table 1, there are four types of buttons that can be displayed, and you can set the names of the buttons you want to display as a comma-delimited list in the value of the buttons attribute.

FIXME: Make the table cleaner

Button name Description
accept The OK button.
cancel The cancel button.
help The help button.
disclosure Either a disclosure triangle or a button. This is used to allow the user to toggle the display of additional information.

Table 1: Types of buttons that can be displayed in the dialog element

Additionally, there are two special button names, extra1 and extra2. The labels for these buttons are set using the buttonlabelextra1 and buttonlabelextra2 attributes on the root element, which take arbitrary strings as their values.

The action taken when pressing one of these buttons is defined by an event handler with the name ondialog<button name>. If these event handlers are not defined, pressing either the accept button or cancel button will simply close that dialog. Listing 8 shows a simple dialog example, and Figure 5 shows its output.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/"?>
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    title="My Dialog" buttons="accept,cancel"
    ondialogaccept="saveValues(); window.close();"
    ondialogcancel="window.close();">
  <checkbox label="My Option"/>
</dialog>

Listing 8: A dialog

Figure 5: Output from Listing 8

Note: The functions behind the dialog elements discussed here require "XPConnect privileges," which are discussed in Chapter 4, so this example will only run correctly if it can run as Firefox code itself or installed extension code. Please be aware that if you attempt to open this sample directly in Firefox, it will not run correctly.

Hierarchically structured dropdown menus are the part of the user interface used for frequently accessed functions in an application or web service. In the past, a combination of HTML and JavaScript was used to produce this sort of complex UI structure, but in XUL, it can be handled easily just by writing tags.

Creating the menu

Listing 9 shows how the menu element and its related elements are combined. This produces the output seen in Figure 6.

<menubar>
  <menu label="Menu 1">
    <menupopup>
      <menuitem label="Item 1"/>
      <menuitem label="Item 2"/>
      <menuseparator/>
      <menuitem label="Item 3"/>
      <menu label="Submenu">
        <menupopup>
          <menuitem label="Item 4"/>
          <menuitem label="Item 5"/>
        </menupopup>
      </menu>
    </menupopup>
  </menu>
</menubar>

Listing 9: A menu definition

Figure 6: Output from Listing 9

Each item in a menu is marked up with the menuitem tag. Use the menuseparator element to insert a separator bar that groups items together. Both menu and menuitem elements take the label attribute to set their labels.

Insert menu elements into a menubar element to create multiple menus. You can easily create hierarchical menus by inserting menupopup and menu elements.

You can even display icons in menus by adding class="menuitem-iconic" to a menuitem element, along with a src attribute that gives an image URI. Figure 7 shows an example of how this is displayed.

Figure 7: Menu items with icons

Executing commands when selecting menu items

Much like dynamic HTML, event handlers are used to execute a command when a menu item is selected. To respond to mouse and keyboard inputs in HTML, the onclick event handler is typically used to respond to mouse clicks, and the onkeypress event handler for keyboard input.

XUL can also use these event handlers, but XUL also offers the oncommand special event handler to deal with actions that often have specific meanings, such as selection by a left-click (or right-click on systems set up as left-handed) on the mouse or selection by the Enter key. Listing 10 shows an example of the oncommand event handler in use. Apart from menuitem elements, it can be used with buttons and other input controls.

<menuitem label="Open project page" oncommand="loadURI(this.value)"
      value="http://mozilla.org/"/> 

Listing 10: The oncommand event handler

Because the Gecko engine implements DOM Level 2 event handlers, you can define dynamic event listeners such as the one in Listing 11.

var item = document.getElementById('menu-item-custom');
function handleCommandEvent(aEvent) {
  alert('OK');
  item.removeEventListener('command', handleCommandEvent, false);
  item.parentNode.removeChild(item);
}

item.addEventListener('command', handleCommandEvent, false);

Listing 11: Additions and deletions using a dynamic event listener

Special menu items

Much like input elements in HTML, menuitem elements can operate like checkboxes and radio buttons by setting their type attributes.

checkbox

Adding type="checkbox" to a menuitem element will check that when it is selected, and uncheck it if it is selected again. For an example of menu items with checkboxes, see the View menu in Firefox, with items to show or hide the toolbar and sidebar. When one has been checked, then the checked="true" attribute is set.

radio button

Assigning type="radio" to multiple menuitem elements and setting them to have the same name attribute groups them so that selecting one deselects all the others, much like radio buttons in HTML. For an example of this kind of menu item, see the Character Encoding submenu of the View menu in Firefox. The selected radio button has the selected="true" attribute set.

Contextual menus

The context attribute is used to display a contextual menu or shortcut menu, that is, a custom menu that will appear when right-clicking on an element.

Earlier we placed the menupopup child element inside a menu element; here, we use it outside the menu element. Instead, the menupopup element is a direct child of the root element and we invoke it using its id attribute, which we set as the value for the context attribute on any other XUL element. When we right-click on that XUL element, we reference that menupopup element by its id, and display its contents as a contextual menu. Listing 12 shows an example.

<button label="Send" oncommand="send();" context="button-context"/>
<menupopup id="button-context">
  <menuitem label="Send with new tab" oncommand="sendInNewTab();"/>
</menupopup> 
Buttons

Buttons that users can click on are defined using the button element. To show one with an icon as shown in Figure 8, define an image's URI as the value for the image attribute.

Note: To see this sample in operation, use any image as the one to display and place it in the same folder as the XUL document.

Using the icon attribute instead of the image attribute allows you to display buttons with icons that are standard for the platform.

Possible values for the icon attribute are given in Table 2.

Figure 8: A button with an image

icon attribute value icon attribute value
accept close
cancel print
help add
open remove
save refresh
find go-forward
clear go-back
yes properties
no select-font
apply select-color

Table 2: Values for the icon attribute

Toolbar buttons

The toolbarbutton element is the element used to define toolbar buttons. This is typically placed inside a toolbar element, which defines a toolbar, but it can be used in other locations.

These mostly act the same as button elements, but as shown in Listing 13, you can change the behavior of toolbarbutton elements using the type attribute. This is illustrated in the output in Figure 9.

<toolbar>
  <toolbarbutton label="Checkbox Type" type="checkbox" image="firefox.png"/>
  <toolbarbutton label="Menu Type" type="menu" popup="button-popup" image="firefox.png"/>
  <toolbarbutton label="Menu Button Type" type="menu-button" popup="button-popup" image="firefox.png"/>
  <menupopup id="button-popup">
    <menuitem label="Item 1"/>
    <menuitem label="Item 2"/>
    <menuitem label="Item 3"/>
  </menupopup>
</toolbar>

Listing 13: Various toolbar buttons

Figure 9: Output of Listing 13

- Checkbox-type toolbar buttons

Specifying type="checkbox" results in a button that stays depressed when clicked, and pops up when clicked again. The button in its depressed state has checked="true" set. This is the kind of button used for the History and Bookmark buttons that can be used to customize the toolbar.

- Menu-type toolbar buttons

Specifying type="menu" and adding a menupop child element (In Firefox 3, you can also use the panel element), or setting the popup attribute to reference a popup menu located elsewhere by its id, will display a popup menu when the button is pressed. Here, the button-click itself is not taken as the input; instead, a command event is issued only when a popup menu item is selected. This is the type of button used for the List All Tabs button at the right edge of the tab bar.

- Menu-button-type toolbar buttons

Specifying type="menu-button" results in a special button that combines features of both a normal toolbar button and one with type="menu", so that clicking the button itself does issue a command event. This is the type of button used for the Back and Forward buttons.

Input Controls

XUL includes a number of input control elements that are very similar to HTML's form-related elements.

Labels

Use the label element for individual text labels, such as descriptive text. The value of the value attribute is the text that will be displayed. The control attribute takes as its value an id reference to another XUL element with that id; clicking that label or giving it focus can be used to pass the focus to the referenced XUL element.

This can also be used to display longer chunks of text. If you want to insert long strings that automatically wrap to the window’s width, you can use the flex attribute as shown in Listing 14, which will automatically expand it; in this case you don't set the value using the value attribute, instead you should place it in the contents of the element. Figure 10 shows the output from this listing.

<hbox>
  <label flex="1">
  Proceeding with this action will send your personal information to a server. Are you sure you want to proceed?
  </label>
</hbox>

Listing 14: Handling long text in a label

Figure 10: Output from Listing 14

Shortening labels

Between the label element and the label attribute on other XUL elements, there can be a lot of labeled elements in XUL. A common attribute that can be set on all of them is the crop attribute.

By applying the crop attribute to elements with the label attribute set or to the label element, part of the label will be replaced by an ellipsis (…) if it overflows the width of the parent element. The part of the label that gets cropped can be controlled by setting its value to start, center, or end.

You can set the maximum width of the box using the CSS max-width property.

Checkboxes

The checkbox shown in Figure 11 was marked up using the checkbox element. When it is in its checked state, its checked attribute is set to true.

<checkbox label="Checkbox checked" checked="true"/>
<checkbox label="Checkbox unchecked" checked="false"/>

Listing 11: Checkboxes in different state

Figure 11: Output from listing 11

Radio Buttons

Radio buttons in HTML are multiple input elements grouped by assigning them all the same name attribute. In XUL, this is expressed using a combination of two elements: radiogroup and radio. You set exclusive options by inserting multiple radio elements into the content of one radiogroup element; the currently selected radio element will have selected="true" set.

The radio element can be uniquely identified by setting its value attribute. When that radio element is selected, the value of its value attribute is copied to the radiogroup, so that you can check for the selected radio button simply by fetching the radiogroup element’s value attribute.

There may be situations where you want to locate a radio element outside of a radiogroup element. By combining it with hbox and vbox, you can write code similar to that shown in Listing 15, which will produce output as shown in Figure 12.

<radiogroup orient="vertical">
  <hbox>
    <radio label="Top Left"/>
    <radio label="Top Right"/>
  </hbox>
  <hbox>
    <radio label="Bottom Left"/>
    <radio label="Bottom Right"/>
  </hbox>
</radiogroup>

Listing 15: A complex layout of radio buttons

Figure 12: Output from Listing 15

Text boxes

Use the textbox element to accept text input. In HTML, there are two separate elements—input for one line of text, and textarea for multiple lines. But both these functions are handled by textbox as shown in Listing 16, which produces output as shown in Figure 13.

The textbox element uses the value attribute to set its default content; content entered by the user also can be captured using the value attribute. To set a maximum length in characters, declare the maxlength attribute with a positive integer value. Setting type="password" will turn the textbox into a special password-entry field, in which characters are hidden as they're typed.

Every character entered into a textbox generates an input event. If you use the oninput event handler, you can implement commands that reflect user input in real time.

<vbox align="start">
  <textbox/>
  <textbox multiline="true" rows="5" cols="15"/>
</vbox>

Listing 16: textbox examples

Figure 13: Output from Listing 16

Autocomplete

Adding type="autocomplete" to a textbox element enables an automatic completion function for it.

Note that to actually use this function, you must also specify a search target for the autocomplete text using the autocompletesearch attribute. If you want to use the Location Bar history, set the value for this to history; if you want it to be the Search Bar history, use search-history; and if you want it to be another form’s input history, use form-history; this attribute can take one value or multiple space-delimited values.

Also, in order to actually read in a history for autocomplete, you will need XPConnect privileges, which we will cover in Chapter 4.

Use the menulist element to create a control for making selections from a drop-down list. As shown in Listing 17, this is implemented in combination with the menupopup element; it produces output as shown in Figure 14.

A click reveals the drop-down list; when an item is selected, the values of that item’s label and value attributes are copied to the label and value attributes of the menulist element itself. By default, the first menu item is selected, but any menu can be preselected by adding selected="true" to it.

<menulist>
  <menupopup>
    <menuitem label="Item 1" value="1"/>
    <menuitem label="Item 2" value="2"/>
    <menuitem label="Item 3" value="3"/>
  </menupopup>
</menulist>

Listing 17: A menulist example

Figure 14: Output from Listing 17

By adding editable="true" to the menulist element, you can accept arbitrary text input in addition to a list selection, as is often used on font selection menus in word processors.

Special elements

Embedding images

In addition to writing direct JavaScript code in event handlers, XUL also allows you to embed scripts using a script element, just like in HTML; this can be used to read in an external script or to place the code in the script element's content.

If you are embedding a script in a page, you should bracket your code inside a CDATA section as shown in Listing 18; this will avoid errors caused by mistakenly reading "&" and other characters as the beginnings of entity references.

<script type="application/javascript"><![CDATA[
  var nodes = gBrowser.mTabContainer.childNodes;
  for (var i = 0; i < nodes.length; i++)
  alert(nodes[i].label);
]]></script>

Listing 18: Embedding a script in XUL

Note: Although embedding JavaScript is permitted, it's generally encouraged that you instead place your JavaScript code in an external file.

Browser and tabbrowser

XUL allows you to use inline frames. You can set the src attribute of an iframe element to the URI of another XUL document or web page, and it will be displayed there. But iframes are rarely used in XUL—in practice, the browser element is used more often.

The browser element is essentially a more powerful version of an inline frame, equipped with all the basic functions of a web browser. Unlike an iframe, a browser element has the ability to navigate backward and forward through pages and includes the ability to prevent scripts in embedded frames from accessing outside the frame. For building applications linked to external web pages, this is a more secure and more convenient approach.

The tabbrowser element is even more capable than the browser element because it includes the basic tab-handling features from Firefox. To use this requires XPConnect privileges, the same as autocomplete does; this will be covered in Chapter 4.

Opening a page

If you specify a URI value for the src attribute in the browser element, that URI will be opened by default. If the src attribute's value changes, you can dynamically open a different web page or XUL document.

Functions like Back, Forward, Reload can all be called using the goBack(), goForward(), and reload() methods. Using the stop() method stops loading the page currently in the process of being opened.

Access restrictions

With frames defined using HTML's frame and iframe elements, it is possible for a child frame to access its parent and ancestor frames by getting the parent and top properties of window objects. For an application that can open any web page, this could give a script on a web page access to an XUL document's frame, creating a dangerous opening for personal information to leak out.

In the interests of security, you can enforce access restrictions by declaring type="content" on a browser element. When you do this, any web page that gets opened will see itself as the topmost frame. If you are developing an application with an interface that can open web pages, I recommend that you always use this declaration to limit access to the parent frame.

In certain circumstances, you may want to declare type="content-primary". A browser element with this declaration is treated as a special browser among other XUL documents, such that the window object displaying its web page can be accessed using the window.content property. In Firefox itself, the content region of the active tab is declared as a browser element with type="content-primary".

Keyboard shortcuts

To implement keyboard shortcuts (pressing a key while holding down modifier keys like Control or Shift.) in a DHTML-based interface, you need a script that will intercept keyboard inputs.

In XUL, you can define keyboard shortcuts simply by using the key element, as shown in Listing 19.

<key id="key-save" key="s" modifiers="accel,shift" oncommand="save();"/>
<key id="key-scroll-up" keycode="VK_PAGE_UP" oncommand="advanceFocus(-1);"/>

Listing 19: Defining keyboard shortcuts

To make letters, numbers, symbols, or the space bar act as triggers, declare that character as the key attribute’s value. You can use either uppercase and lowercase letters and they will be handled the same.

For triggers on other special keys, use the keycode attribute with the appropriate keycode name value. In general you can use the keycode name for any key; the most commonly used ones are shown in Table 3.

Key Keycode name
Return VK_RETURN
Enter VK_ENTER
Backspace VK_BACK_SPACE
Delete VK_DELETE
Escape VK_ESCAPE
VK_UP
VK_DOWN
VK_LEFT
VK_RIGHT
Table 3: Typical keycode names
Using modifier keys

Use the modifiers attribute with one or more of the following comma-delimited values: "control", "alt", "shift", and "meta" in order to limit the use of keyboard shortcuts to only operate when those modifier keys are pressed together with another key. The standard modifier key on Windows and Linux is Control; on Mac OS it is Command. An easy way to automatically use the modifier key appropriate for the current platform is to set the modifier key to "accel", which maps to Control on Windows and Linux, Command on Mac OS.

Note: The "meta" key is the Command key on Mac OS X (the one with the cloverleaf design on it).

The key element itself is never displayed on screen. In order to display defined keyboard shortcuts as shown in Figure 15, reference the key element's id from the menu or menuitem element’s key attribute.

FIXME: Figure 15: Displaying keyboard shortcuts in menu items:Menu 1/Save/Scroll

XUL includes a number of elements used strictly to organize the layout of screen widgets.

spacer

Use the spacer element if you want to open up the space between buttons. You can use the flex attribute or style attribute to make whitespace stretch or set it to a certain size.

grid

In the same way that the table element is used to lay out content in HTML, you can use the grid element in XUL. As shown in Listing 20, you first declare the number of columns, and then define the content of each row. The output of this sample is shown in Figure 16. You can also code it so that the rows come first, declaring the number of rows and then defining content by column.

The rowspan and colspan attributes available in HTML tables are not available in XUL grids.

<grid>
  <columns>
    <column/>
    <column flex="1"/>
  </columns>
  <rows>
    <row align="center">
      <label value="User ID"/>
      <textbox/>
    </row>
    <row align="center">
      <label value="Name"/>
      <textbox/>
    </row>
  </rows>
</grid>

Listing 20: Layout using the grid

Figure 16: Output from Listing 20

stack

Use the stack element to overlap multiple widgets. The stack element differs from a normal box in that all the elements in it are layered over each other, working from the bottom up. Listing 21 shows an example of a thermometer-style progress bar, with its output shown in Figure 17.

<stack>
  <progressmeter mode="normal" value="50"/>
  <hbox align="center">
    <label value="In progress…"/>
  </hbox>
</stack>

Listing 21: Overlapping with stack

Figure 17: Progress Bar

tab

Use the tab element to divide multiple pages, as used in the Properties dialog; use the tabbox element to group related elements. This is demonstrated in Listing 22, with its output shown in Figure 18.

<tabbox>
  <tabs>
    <tab label="tab1"/>
    <tab label="tab2" selected="true"/>
  </tabs>
  <tabpanels>
    <tabpanel>
      <checkbox label="check1"/>
    </tabpanel>
    <tabpanel orient="vertical">
      <checkbox label="check2"/>
      <checkbox label="check3"/>
    </tabpanel>
  </tabpanels>
</tabbox>

Listing 22: Tabs in use

Figure 18: Output from listing 22

Other XUL functions

Overlays

One of XUL’s distinctive features is overlays. These give you the ability to combine multiple XUL documents and process them as a single XUL document. In Firefox, this is used to modularize functions and implement extensions.

By inserting an xul-overlay processing instruction between the XML declaration and the opening tag of the root element, the XUL document specified by the xul-overlay will be read in at the same time as the current XUL document. The XUL document that actually gets displayed will be a combination of the original XUL document and the one specified in xul-overlay.

Try typing up Listing 23 and saving it as base.xul. Take a look at the overlay example used by this XUL document.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/"?>
<?xul-overlay href="overlayDocument.xul"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <hbox id="box1">
    <label id="label1" value="Text label"/>
  </hbox>
  <hbox id="box2">
    <label id="label2" value="Text label"/>
  </hbox>
</window>

Listing 23: base.xul

Appending elements

When defining an XUL document to open as an overlay, use an overlay element as the root element. Save the text of Listing 24 as overlayDocument.xul in the same directory as base.xul.

Here, we’ll open base.xul in the Firefox window. overlayDocument.xul gets read in at the same time as base.xul, resulting in what we see in Figure 19, where overlayDocument.xul gets appended to the end of base.xul.

<?xml version="1.0" encoding="UTF-8"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <button label="Button 1"/>
  <button label="Button 2"/>
  <button label="Button 3"/>
</overlay>

Listing 24: overlayDocument.xul

Figure 19: Output from listings 23 and 24

Merging and inserting elements

By inserting id attributes into the elements being appended in the XUL overlay document, elements in the base document with the same id values will have their attributes and values merged with the overlay document.

Try overwriting the contents of overlayDocument.xul with Listing 25 and re-saving.

<?xml version="1.0" encoding="UTF-8"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <hbox id="box1" align="center" style="border: 1px solid; margin: 1em;">
    <button label="Button 1"/>
    <button label="Button 2" insertbefore="label1"/>
  </hbox>
  <button label="Button 3"/>
</overlay>

Listing 25: Substitute code for overlayDocument.xul

Let's open base.xul in a Firefox window again. As shown in Figure 20, the style attribute from the element with the id "box1" in the overlay document has been merged with the markup for the button with that id in the base document, and inserted into the box.

Figure 20: Output from listing 23 and 25

Furthermore, following the declaration insertbefore="label1", the "button2" button now has been inserted before the element with the id "label1". Conversely, if we wanted to insert it directly after, we could have used the attribute insertafter. We can also use the position attribute as a way to locate an element numerically at any position; for example, using position="4" would insert it as the fourth element.

External entities

Being a form of XML, XUL can also use entity references based on DTDs (document type definitions) as shown in Listing 26. Note that external DTD files are limited to what is given in the chrome URL, which will be discussed in Chapter 5. Please be aware that ordinary DTD files on the web or in a local directory will not be opened.

Note: You may use a relative path to access a chrome URL from an XUL document.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/"?>
<!DOCTYPE window SYSTEM "chrome://testapp/locale/testapp.dtd">
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <button label="&button.1.label;"/>
  <button label="&button.2.label;"/>
</window>

Listing 26: Reading in external entities

<!ENTITY button.1.label "Firefox">
<!ENTITY button.2.label "Thunderbird">

Listing 27: testapp.dtd

Substituting CSS for attribute declarations

The Gecko rendering engine includes a number of CSS properties with -moz- prepended to them; these are custom properties for XUL that have been implemented prior to being a W3C recommendation. The ones shown in Table A below correspond to the common attributes in XUL. In fact, those XUL attributes operate through these CSS properties.

The values declared for these CSS properties are the same integers or keywords used by the XUL attributes. While we don't recommend that you use these properties in ordinary XUL development, they can be useful for customizing the Firefox UI without scripting by using userChrome.css. For example, if you save the contents of Listing 28 as userChrome.css and save that in the chrome directory under the user profile directory, the tab bar will appear at the bottom of the content area.

XUL attribute CSS property Example
orient -moz-box-orient

-moz-box-orient: vertical;

align

-moz-box-align

-moz-box-align: start;

pack

-moz-box-pack

-moz-box-pack: stretch;

flex -moz-box-flex -moz-box-flex: 1;
ordinal -moz-box-ordinal-group -moz-box-ordinal-group: 2

Table 4: CSS properties corresponding to XUL attributes

tabbrowser .tabbrowser-strip {
  -moz-box-ordinal-group: 2;
}
tabbrowser tabpanels {
  -moz-box-ordinal-group: 1;
}

Listing 28: A user stylesheet that locates the tab bar at the bottom

Icons corresponding to filetypes

Firefox allows you to use a special URI scheme, moz-icon, that produces filetype icons that are standard for whatever platform it is running on. For example, to display a 16x16 icon for a file with a .pdf dot-extension (a PDF file), you would write moz-icon://.PDF?size=16.

You can reference icons using content type as a key rather than dot-extension; for example, you could display a plain-text icon using moz-icon://goat?size=16&contentType=text/plain.

You can also display specific icons using the markup moz-icon:file URL?size=size.

The button icons mentioned in Table 2 also can be called using moz-icon URIs, using the markup moz-icon://stock/gtk-button name?size=button. Note, however, that this does not work in Linux environments.

Spin buttons

You can add a type="number" declaration to a textbox element, making it a textbox specifically for entering numeric values. This will cause the spin buttons seen in Figure 21 to appear. Additional attributes that accompany this are min and max to set minimum and maximum values, and increment, which sets the amount by which one click on the spin buttons will change the displayed value.

Figure 21: A textbox for numeric input

Sliders

The scale element is used similarly to the volume slider in Windows, allowing the user to vary a value by moving a slider up/down or left/right (see Listing 28 and Figure 22).

Note: Although this control is also known as a slider, the slider element already exists in XUL as a part of a scrollbar, so this element has been named "scale".
<hbox>
  <scale orient="horizontal" min="-100" max="100" value="0"/>
  <scale orient="vertical" min="0" max="100" value="50"/>
</hbox>

Listing 29: The scale element

Figure 22: Output from Listing 29