Home > Writing Themes > Writing Themes Part Four: Theme Technologies

Writing Themes Part Four: Theme Technologies

This section talks about the Mozilla technologies that you'll want to know as a theme developer. If you read through Part One as someone new to Firefox development, you may have questions about how it all works and how the pieces fit together, technically. We try to answer those questions here.

There's a fair amount of relevant material and quite a few concepts involved. I've tried to provide an overview from the theme writer's perspective, leaving out the parts you can skip, with links to official specifications for needed detail. The fact is that there's a lot to know as a theme author. If this is new to you, there's a lot of ground to cover, and there's not much you can do but shoulder your pack and start walking.

It is possible to write themes without knowing all this. After all, you're writing themes for fun, right? You already have a job, or university, or whatever it is you spend your time on during the day. It's completely possible to play with themes and make your way through it with just trial and error, copying from examples in the default theme, poking here and tweaking there to see what does what. Lots of people do it that way. If that's how you want to go at it, then this section is far more than you want to know.

If, however, you've bumped up against something that's not working in your theme and you want to know why, or you find technology interesting for its own sake, or you'd rather proceed from theory to practice instead of implementing-by-wandering-around, or you have the kind of mind that won't rest in the presence of an unanswered question, then this section is for you.

Everything discussed here is something I needed to know at one time or another during the course of theme development.

Overview of Technologies Relevant to Firefox Themes

Here's the complete list.

  • XML and markup-based languages in general - the general method of using tags, elements, properties, and attributes to add structure and meaning to text information
  • XUL - The Mozilla-specific XML-based User Interface Language used to define the Firefox user interface
  • XBL - The Mozilla-specific Extensible Bindings Language, used to attach content and behaviors to parts of the Firefox user interface
  • CSS - Cascading Style Sheets, a global standard cough standard defined by the W3C specifying the appearance and presentation of content, independent of the content itself
  • Mozilla-specific CSS extensions - CSS statements that are meaningful only to the Mozilla rendering engine, and not to other browsers
  • XHTML/HTML - The language of web content
  • The XUL Box Model - the way in which Firefox visually arranges all the elements that are part of an XUL-based user interface definition
  • RDF - Resource Description Framework, a way of defining lists of things and their properties, using a variation of XML
  • DOM - The Document Object Model, used by Firefox to internally represent an XML-based document, including both web pages and the Firefox user interface itself
  • Chrome - A general term for the Firefox approach to representing its user interface, namely, as XUL documents styled by CSS
  • Chrome URLs and the Chrome Registry - Used by Firefox to internally represent all the component files that make up the user interface
  • Manifest files - A Mozilla-specific file format used to identify the files that define the Firefox user interface

As a theme developer, you won't need to know any of the following Firefox-related technologies at all: Javascript (the default theme contains no Javascript files, but you may encounter some if you venture into the source code for the Firefox UI), C/C++, XPCOM, AJAX, SVG, RSS, XForms, XSLT, and SOAP. Likewise, you won't need to know how to compile Firefox from source.

The Mozilla Developer Center provides an authoritative reference for all Firefox-related technologies (including many that you won't need to know about as a theme developer).

We'll begin our discussion with chrome, the concept that ties it all together.

The Chrome Concept in Mozilla Software

The term "chrome" is used to refer to the way that Firefox defines its user interface as a set of documents that are read and rendered (drawn on-screen) by Firefox in realtime. These documents are simply text files containing statements in an HTML-like language called XUL, short for "XML User Interface Language". (I call it HTML-like because more people are familiar with using HTML markup to create web pages than with XML in general, but it might be more properly said that both XUL and HTML are XML-based languages.) All of the Firefox UI is specified using XUL documents. XUL is well-documented here.

The key to the whole chrome concept is in understanding that all of the Firefox user interface is rendered in realtime in exactly the same way as web pages are rendered in the window (and using exactly the same rendering engine, the part of Firefox called Gecko). The toolbar of the Firefox browser is like a little web page in its own right, except that it is specified in terms of XUL rather than HTML, and that the objects in the toolbar are interactive widgets that interact with the Firefox internals, rather than being composed of just the static images and text of a regular web page. The same is true for every window in the Firefox UI: like a little web page, its contents are specified in a source document (a .xul file) and rendered using the same engine that renders the regular web pages in the browser window.

The term "chrome" of course originates with the gleaming shiny decoration on the outside of a car, which gives the car its visual personality and wraps around the functional and powerful engine at its heart. Similarly, Firefox chrome wraps around the functional and powerful rendering engine and related functionality at the heart of the browser.

You'll see the term "chrome" used in different ways at different times:

  • as just described, to refer to the way that Firefox defines its user interface as a set of documents that are read and rendered in realtime
  • to refer to the Firefox user interface in general
  • to refer to the replaceable skins or themes which are the subject of this web site
  • to refer to the seven specific files (all having the extension ".jar") which contain the definition of the Firefox user interface, and their associated manifest files which tell Firefox how to load the contents of the jar files
  • to refer to the Firefox ability to "mount" or load jar files into an internal Firefox directory structure, accessed by a special kind of URL called a chrome URL

Figure: Image showing the contents of the Firefox chrome directory. The chrome directory is a subdirectory under the directory where you installed Firefox. In this image, jar files are incorrectly identified as executable. The system on which this screenshot was captured has identified these files as component files for the Java language, when they are actually jar files installed with Firefox. The installation directory for Firefox shown here is the result of a custom installation and not the default location; none the less, the relationship of the chrome directory to the Firefox installation directory (being a subdirectory of it) is still the same as with a default installation.

The chrome concept is a big gulp because you have to take in so many related concepts at once for it all to make sense. Packages. Manifest files. Jars. Jars other than themes. Manifest instruction types. XUL. CSS. XBL. Bindings. Mozilla-specific CSS extensions. Overlays. The chrome registry. Locations of the installation directory. Location of a profile folder. Chrome folder. There truly is a lot going on here, but once you get past the vocabulary you'll see that it begins to make sense.

This PDF contains an introduction to the overall architecture of Firefox and Mozilla-based software.

Significance of the Chrome Concept

Why is the chrome concept significant enough to warrant all the special terminology and attention? For several reasons. By decoupling the definition of user interface from the underlying Gecko rendering engine:

  • it becomes possible for the underlying engine to be shared among multiple applications, as is done with Firefox, Thunderbird, Seamonkey, and all the other members of the Mozilla software family, by using application-specific user interface definitions with the shared underlying engine
  • it becomes easier to partition the work of maintaining the software among multiple people by separating user interface and engine into separate modules interacting through well-defined interfaces
  • it becomes possible to have themes as replaceable skins
  • it becomes possible to have extensions, which has been so much a part of Firefox's success
  • the user interface (as well as themes and extensions) becomes easier to maintain, because all that is required is a text editor rather than a compiler
  • it becomes possible for more people to create themes and extensions, because, while not everyone has ready access to a working compiler, almost everyone has access to a text editor
The Chrome Registry

The Firefox user interface is represented (as of Firefox 1.5) as seven jar files. One of these files is classic.jar, the Firefox default theme. The other six contain the XUL and related files which define the content of the user interface (content, as opposed to visual style, as defined by the theme).

Firefox builds a map of the contents of these seven jar files in an internal directory structure that only Firefox can see. This internal directory is called the chrome registry. The chrome registry is simply a table that allows Firefox to, given some file that is part of the Firefox UI, look up the location of that file. The items to locate are specified as chrome URLs, and the chrome registry tells Firefox how to locate the item in a jar file or on hard drive.

Manifest files (and sometimes contents.rdf) define how files from the chrome jar files are mapped to the directory structure of the chrome registry. Firefox gets the information to put into the chrome registry by reading manifest files. Also relevant is the concept of packages. You can read more about manifest files and packages below.

For example, after Firefox reads this from chrome.manifest:

001:   skin communicator classic/1.0 jar:classic.jar!/skin/classic/communicator/ 
002:   skin global classic/1.0 jar:classic.jar!/skin/classic/global/ 
003:   skin mozapps classic/1.0 jar:classic.jar!/skin/classic/mozapps/ 
004:   skin help classic/1.0 jar:classic.jar!/skin/classic/help/ 
005:   skin browser classic/1.0 jar:classic.jar!/skin/classic/browser/ 

then the chrome registry is a table containing a set of entries that look like this:

package/part      --> location
----------------- --- ---------------------------------------
global/skin       --> jar:classic.jar!/skin/classic/global/
communicator/skin --> jar:classic.jar!/skin/classic/communicator/
mozapps/skin      --> jar:classic.jar!/skin/classic/mozapps/
help/skin         --> jar:classic.jar!/skin/classic/help/
browser/skin      --> jar:classic.jar!/skin/classic/browser/

Of course Firefox loads a lot more manifest files than just that one, and the registry contains information derived from manifest instruction types other than just "skin", and from sources like extensions and themes in addition to the default Firefox chrome files, so the resulting table would be much more complex than shown in this example.

There's no way to directly view the contents of the chrome registry. As a theme developer, especially as you are learning to write your first theme, you may find yourself wishing you could see how the chrome URLs in CSS files you examine are resolved into files in the default theme. There is, unfortunately, no way to do this other than by calculating the contents of the chrome registry by hand.

magneto>    hi, anyone know whether Firefox loads the contents of browser.jar all at once, or just as it needs it?
Ratty>      magneto: Just a WAG, but I think it all goes into the XUL fastcache on first load.

See this comprehensive example showing how chrome jar files, manifest files, the chrome registry, chrome URLs, and Firefox source code work together.

Manifest Files

Manifest files define how files from chrome jar files are mapped into the directory structure of the chrome registry. Manifest files come in a specific format which Firefox knows how to read. Other software uses files also called manifest files, but here we are talking about the format specific to Firefox and the other software members of the Mozilla family of applications.

The following listing shows a typical manifest file.

001:   skin    browser         Test_Theme jar:chrome/chrome.jar!/browser/ 
002:   skin    global          Test_Theme jar:chrome/chrome.jar!/global/ 
003:   skin    mozapps         Test_Theme jar:chrome/chrome.jar!/mozapps/ 
004:   skin    communicator    Test_Theme jar:chrome/chrome.jar!/communicator/ 
005:   skin    help            Test_Theme jar:chrome/chrome.jar!/help/ 

Here are the meanings of the fields in the first line.

  • The first field states the "manifest instruction type" of this line, which is "skin", meaning that this line is telling how to find part of a theme.
  • The second field, e.g. "browser", is the name of the package to which this line applies.
  • The third field, e.g. "Test_Theme", is the internal_name of the theme (or extension) which this package is part of. This internal_name must match the internal_name used in the corresponding install.rdf.
  • In the fourth field, the "jar:" says that the path to the package refers to a directory within a jar.
  • In the fourth field, the "chrome/" says that the jar is located is the chrome subdirectory of the location where the extension is installed.
  • In the fourth field, the "chrome.jar" is of course the name of the jar containing the related files.
  • In the fourth field, the /browser/ is a directory within the jar containing the files.

Each line in a manifest file represents some kind of instruction. See this description of manifest instruction types.

contents.rdf can also sometimes perform the same function as a manifest file.

We need to make a clear distinction between the terms "manifest file" and "chrome.manifest". Manifest files are used in general to define the user interface of Firefox and of related Mozilla-based software, in conjunction with the jar files which also help define the UI. chrome.manifest is the name of one of the seven manifest files which define the default Firefox user interface. chrome.manifest is also the name of a file which is sometimes included in an add-on theme to tell Firefox how to load the theme. chrome.manifest is also the name of the file which Firefox automatically creates for its own use when a theme is installed. The use of chrome.manifest in add-on themes was previously discussed here.

See this comprehensive example showing how chrome jar files, manifest files, the chrome registry, chrome URLs, and Firefox source code work together.

The Package Concept

Firefox uses a concept called "packages" to help structure the contents of the files that make up the Firefox user interface. The term "package" refers to two things:

  1. a virtual directory (or "virtual folder") created within Firefox when Firefox first starts up
  2. a set of files (like CSS files and image file) and directories which may be accessed relative to the virtual directory for a package

The package concept allows items like CSS files and images which define the Firefox user interface to be referenced by a chrome URL, relative to a package name. This is important because it allows the actual contents of the virtual directory to be swapped out (such as when the end user selects an alternate theme, or when Firefox developers construct Firefox out of different building blocks) without having to change the chrome URL that refers to the contents.

Packages were previously described here. You should read that topic as a companion to this section. Beyond what was already stated there, there are several points worth making about packages in general.

  • Packages are defined by manifest files.
  • Packages have names. The name of each package is established when the package is defined in a manifest file.
  • Packages have parts. A package can have a "content" part, a "locale" part, and a "skin" part. Each package has to have at least one part.
  • Each line in a manifest file can define one part of the package, but doesn't necessarily, since manifest files do other things in addition to defining packages. See topic "Manifest Instruction Types" for more.
  • The first use of a package name in a manifest file is enough to create the package. Any subsequent use of the same package name adds to the existing package. As Firefox reads each line in a manifest file, it defines the corresponding package and part. If the line refers to a package that has already been defined, then Firefox adds the new part to the existing package definition.
  • Packages are not files. Instead, packages are "associations" or "mappings" between, on the one hand, a package name and a package part, and, on the other, a location where Firefox can find the files that are part of that package and part. The location would be a directory within a jar file or on your hard drive. Firefox keeps track of these associations in its memory after it reads them from a manifest file; these associations are stored in a data structure called the chrome registry.
  • Package names and parts are used in chrome URLs. If you look at a chrome URL, the first part is the prefix "chrome://", the second part is always a package name, and the third part is always a package part ("content", "locale", or "skin").
  • When Firefox sees a chrome URL, it looks at the package name and package part within the URL, looks up the location associated with it, then knows to go to that location to get the file named in the chrome URL.
  • Packages are divided into parts so that each part can come from a different location, such as a different jar. (This is how your theme works: it tells Firefox to get the "skin" part of a package from the jar file for your theme, instead of from the jar file containing the Firefox default theme.)
  • The Firefox user interface is composed of packages defined by someone who is not you. By the time you, as a theme developer, come along, Firefox developers have already decided which packages make up the Firefox user interface, and what their names are. For Firefox 1.5, the package names are "browser", "communicator", "global", "help", and "mozapps" (and some others, which for reasons not apparent are never addressed in themes). These packages are defined in the seven default manifest files, and they are mapped to locations in the seven default jar files.
  • By the time your theme is loaded, Firefox has already loaded the files that make up the contents of those packages.
  • Thunderbird uses some of these packages as well as some additional packages such as "messenger" and "editor".
  • The five default package names obviously correspond to the package names used in your theme's chrome.manifest or contents.rdf. Your theme works by telling Firefox to get the "skin" part of these packages from your theme instead of from the default theme.
  • Your theme can define new packages, beyond the five default package names, but it can only define the "skin" part. It cannot define the "content" or "locale" parts. This is a restriction imposed by Firefox security policies.
  • Your theme has to provide CSS files and directories relative to certain specific and mandatory chrome URLs, which chrome URLs themselves contain the package names that are defined as part of the Firefox installation. As a theme developer, you don't care about packages as such; you do care about the chrome URLs that the rest of Firefox will use to look for specific mandatory items in the current active theme, including yours. The mandatory directories and files are discussed here. To state this another way, Firefox uses chrome URLs to locate certain files for use in its user interface, and the chrome URLs contain certain specific package names. If you want Firefox to locate those items in your theme rather than in the default theme, then the contents.rdf/chrome.manifest for your theme needs to define a skin part of those packages to point to files and directories within the jar file for your theme.
  • You can define new packages in your theme, merely by using a package name which is not used elsewhere within the files that make up the Firefox user interface.
  • If your theme uses a chrome.manifest, then that file is how your theme adds to or creates new packages. If your theme uses contents.rdf instead, then installation of your theme results in creation of the equivalent chrome.manifest, which file then adds to or creates new packages.
  • If you define a new package in your theme (with contents.rdf or chrome.manifest) you enable the use of a chrome URL that begins with that package's name.

Packages are created by the use of the package name in a manifest file loaded by Firefox. The use of the package name in a manifest file is not just a reference to a package; it is the actual point when the definition and creation of the package occurs. The contents of the package are then defined by whatever the line in the manifest file points to. Packages are used when Firefox internal software refers to an item with a chrome URL. The chrome URL refers to an item that is part of a package, the contents of the package having been mapped to directories in jar files or on your hard drive, and the item is located relative to those directories.

Packages are a key part of the general Firefox concept of chrome, which is that, in general, an application such as Firefox defines its user interface from a set of building blocks which can be mixed, matched and reused to create different applications. For example, Thunderbird and Firefox both are built in part from a common set of packages and in part from different packages. Packages allows Firefox developers (not theme developers, so much) to think of Firefox as composed of a set of packages that are loaded when the program starts up. Another part of the concept is that packages can be replaced as needed (replaced at the time when Firefox is assembled for publication at distribution, and replaced by end users at the time when Firefox is installed) without forcing changes in the other parts of Firefox that use the package. Also, extensions can replace or modify packages in the default Firefox package set.

The realization of the package concept is seen in the seven jar files which make up the default Firefox user interface, and in the seven manifest files which accompany them. Packages are not files, and these files are not packages. Instead, packages are created as constructs, or data structures, within Firefox when it reads the seven jar files and their accompanying manifests.

You can define new packages in your theme, merely by using a package name which is not used elsewhere, which several theme developers do as a convenient way of grouping the files in their theme. As long as the name of the package which you choose is different from any package names used anywhere else in the Firefox user interface, then how you name it and how you use it is up to you. You could in fact achieve the same effect by grouping files into subdirectories (say, by creating a new subdirectory in the global directory within your theme) rather than creating a new package, but you can choose whichever method you prefer.

See this comprehensive example showing how chrome jar files, manifest files, the chrome registry, chrome URLs, packages and Firefox source code work together.

Chrome URLs

Recall that Firefox loads the contents of chrome jar files (including themes) into an internal directory structure that only Firefox can see. This internal directory is the chrome registry, and a chrome URL is the way that Firefox refers to files located in this directory.

Chrome URLs are used throughout the source code that defines the Firefox UI, as well as in your theme, whenever there's a need to refer to some element of the user interface. The element to which a chrome URL refers is almost always an individual file. Here are several example chrome URLs, gathered from various locations in the files that make up the Firefox UI.

Example Chrome URLInterpretation
chrome://browser/content/browser.xulRefers to the XUL file that defines the main browser window
chrome://browser/skin/browser.cssRefers to a CSS file used by the main browser window
chrome://global/skin/Omits the target file name, relying on the chrome URL behavior of filling in a default filename when none is provided. This example is the same as saying "chrome://global/skin/global.css", referring in this case to a CSS file used by almost every window and dialog
chrome://mozapps/skin/update/updates.cssRefers to a CSS file used by the Software Updates wizard
chrome://branding/locale/brand.dtdRefers to a properties file containing a locale-specific string table
chrome://mozapps/content/preferences/preferences.xml#fileFieldRefers to an XBL binding adding content and behavior to the Options window
chrome://mozapps/content/preferences/ocsp.jsRefers to a Javascript file used by the Options window

A chrome URL has the following form.

chrome://browser/skin/browser.css

- 1 -----                           Part 1: the chrome protocol prefix string
         - 2 ---                    Part 2: the package name
                - 3 -               Part 3: the part
                     - 4 --------   Part 4: the remainder of the chrome url

If you were to read this chrome URL aloud, you would say "the file browser.css in the skin part of the browser package." Let's look at each of the four parts of this example chrome URL:

  • Part 1 is the chrome protocol prefix string. In the same way that "http://" identifies a URL as a web address, "chrome://" identifies a URL as a chrome URL. All chrome URLs begin with the "chrome://" prefix string, identifying them as chrome URLs rather than as say "http://" or "ftp://" addresses.
  • Part 2 is the package name. Recall that manifest files define packages, including both the package name and where to find the files that make up the package. The package name used in a chrome URL refers to a package name defined in a manifest file.
  • Part 3 is called, for lack of a better name, the "part", as in "the skin part of the package". Part may be one of "skin", "content", and "locale". This is similar to but different from manifest instruction type. It's similar in that the part to which a chrome URL refers was created by the corresponding manifest instruction (skin part created by skin instruction, content by content, locale by locale) but different in that there are manifest instruction types (comment, overlay, style and override) that can't be used as part 3 of a chrome URL.
  • Part 4 specifies the individual file that the chrome URL refers to. The whole purpose of a chrome URL is to point to some individual file, and this is where that file is given. Part 4 may include a path as well as a file name, in the case when the file is located in a subdirectory. This is shown in the very next example.

Here's an example chrome URL in which the target file is located in a subdirectory relative to the "home" directory of a package.

chrome://browser/skin/preferences/preferences.css

- 1 -----                                           Part 1: the chrome protocol prefix string
         - 2 ---                                    Part 2: the package name
                - 3 -                               Part 3: the part
                     - 4 ------------------------   Part 4: the remainder of the chrome URL

In this example, the chrome URL specifies a file, "preferences.css", which is in the subdirectory "preferences", which is under whatever directory was specified as the location of the home directory for the "skin" part of the "browser" package in the chrome.manifest file line that defined the skin part of the browser package. Does that seem like a lot to keep track of? It certainly is; there's a lot going on between the time when you start with a chrome URL and the time when Firefox decides which file the URL actually refers to. The whole subject of identifying this target file is called "chrome URL resolution", which is discussed below.

You can type chrome URLs into the browser's address field to tell Firefox to load an item from within the chrome rather than from some place on the web. What Firefox does with it depends on the file type of what you specify. CSS and Javascript files are displayed as text. Images are displayed as images. XUL files are actually executed, resulting in some Firefox window being displayed in a tab as if it were a web page, rather than in a separate window. Accessing XUL files like this is a little risky, in that not every XUL file is programmed to run as a tab; some present dialog boxes that close themselves when done, which results in closing the entire browser window; some will resize Firefox; and some will actually lock up the application. Clearly, you'll want to use discretion before choosing to type the chrome URL of an XUL file in the browser's address field.

We said previously that a chrome URL almost always refers to an invididual file. The exception is when referring to an individual binding within an XBL file, as in the following chrome URL.

chrome://mozapps/content/preferences/preferences.xml#fileField

See topic "XBL and the Firefox UI" for more.

You can read Mozilla Development Center documentation of chrome, chrome URLs, manifest files and the chrome registry here. Also, see this comprehensive example showing how chrome jar files, manifest files, the chrome registry, chrome URLs, and Firefox source code work together.

Leaving Out the Filename in Chrome URLs

One property of chrome URLs is that, if the filename is omitted from a chrome URL, then a default filename is provided by Firefox as it resolves the chrome URL. For example, if Firefox encounters this chrome URL:

chrome://global/skin/

which you will actually see a lot if you are reading through Firefox source code, then Firefox supplies the filename "global.css", and reads the chrome URL as if it were had written as

chrome://global/skin/global.css

in the first place.

The general rule is that the default filename (the name Firefox assumes you meant, when you omit it from the end of a chrome URL) is calculated to be composed of a basename which is the same as the package name plus an extension which is driven by part 3 of the chrome URL. If the part is "skin", then the extension is ".css". If the part is "content", then the extension is ".xul". If the part is "locale", then the default extension is ".dtd".

Given Chrome URLPackage NamePartDefault Basename, Same as Package NameDefault Extension, Derived from PartResulting Chrome URL
chrome://global/skin/globalskinglobal.csschrome://global/skin/global.css
chrome://browser/content/browsercontentbrowser.xulchrome://browser/content/browser.xul
chrome://myextension/locale/myextensionlocalemyextension.dtdchrome://myextension/locale/myextension.dtd

One side effect of this behavior is that, while you can enter a chrome URL for an individual file in the browser's Location Bar (just like you would a web address) to view the file, you can't do this for directories, because the lack of a filename in a chrome URL for a directory causes Firefox to add the default filename.

See this comprehensive example showing how chrome jar files, manifest files, the chrome registry, chrome URLs, packages and Firefox source code work together.

Resolving Chrome URLs

When Firefox reads a chrome URL, it has to decide which file (or, in the case of XBL, which file and which id) the URL is referring to. This process is called "resolution", and we say that Firefox "resolves a chrome URL into the relevant filename". For example, if your theme uses the chrome URL "chrome://browser/skin/browser.css", then Firefox resolves that URL into the file "browser.css" located in your theme.

How does Firefox decide which file the chrome URL refers to? The answer is, it depends. It depends on what's in the chrome registry, and that depends on which extensions are installed and which theme is currently active.

When Firefox resolves a chrome URL, it follows a step-by-step procedure to arrive at the result. This procedure assumes that the contents of the chrome registry have already been established as a part of starting up Firefox.

  1. Verify that the chrome URL is in proper format.
  2. Split the chrome URL into its four parts (prefix, package name, part, and filename with optional path
  3. If the filename is omitted, then use package name and part to generate a filename
  4. Use the package name and part, as a pair, to look up the corresponding path in the chome registry.
  5. If the pair is not found, then the chrome URL cannot be resolved, and an error has occurred.
  6. If the pair is found, then use the corresponding path from the chrome registry as the starting point to look for the filename. Let us call the corresponding path the "search location". The search location will be either a directory within a jar (specified in a manifest file with the jar: prefix) or a directory on a hard drive (specified in a manifest file using the file:// prefix).
  7. Look for the filename within the search directory. If the search location is in a jar, then look for the filename in the jar. If the search location is a directory on a hard drive, then look for the filename in the directory on the hard drive. If the filename begins with a directory name or names (as in the case of a chrome URL like "chrome://browser/skin/preferences/preferences.css", which has a filename that begins with the directory "preferences/"), then look for the filename in that subdirectory under the search location.
  8. If the filename is not found, then the chrome URL cannot be resolved and an error has occurred.
  9. If the filename is found, then the chrome URL has been successfully resolved.

You can see a complete, start-to-finish example of chrome URL resolution here.

You have seen that the resolution of a chrome URL into a specific file depends on the contents of the chrome registry. How can you, as a theme developer, see the contents of the chrome registry? You can't, or at least you can't directly. It might seem like a natural idea for someone to write an extension that displays the contents of the chrome registry, but this hasn't happened yet, which means that there's no way for you to get this information. Instead, you have to learn to calculate it for yourself based on the contents of the manifest files for the seven jar files in the default Firefox user interface, based on the contents.rdf and/or chrome.manifest for the currently-selected theme, and based on the manifest files for whatever extensions are currently installed. You would be right if you're thinking this isn't great; this truly is a rough spot in theme development that should be smoothed out.

A Complete Example of Resolving a Chrome URL

Here we provide several examples of how chrome URLs, manifest files, packages, and the chrome registry work together in Firefox (and in other members of the Mozilla software family) to allow Firefox to decide which actual file a chrome URL is referring to.

When Firefox starts up, it begins by reading the seven manifest files for the seven jar files which define the default Firefox user interface. This includes the file named chrome.manifest, which tells Firefox how to load the jar file containing the Firefox default theme. Each manifest file defines packages. Package definitions are added as entries in the chrome registry. After Firefox has finished reading the manifests for the default user interface, it then proceeds to read the manifest files for all installed extensions, and for the current selected theme, if the end user has selected some theme other than the default. As Firefox processes each manifest file, it adds to or replaces package definitions in the chrome registry. The examples that follow show exactly how this works.

Let's start with an example used previously when talking about the chrome registry. Suppose Firefox reads this chrome.manifest (which is the actual manifest file installed with Firefox to describe the contents of the default theme):

skin communicator   classic/1.0 jar:classic.jar!/skin/classic/communicator/
skin global         classic/1.0 jar:classic.jar!/skin/classic/global/
skin mozapps        classic/1.0 jar:classic.jar!/skin/classic/mozapps/
skin help           classic/1.0 jar:classic.jar!/skin/classic/help/
skin browser        classic/1.0 jar:classic.jar!/skin/classic/browser/

After Firefox reads this manifest file, then the chrome registry is a table containing a set of entries (and other entries as well, not shown) that look like this:

package/part      --> location
----------------- --- ---------------------------------------
communicator/skin --> jar:classic.jar!/skin/classic/communicator/
global/skin       --> jar:classic.jar!/skin/classic/global/
mozapps/skin      --> jar:classic.jar!/skin/classic/mozapps/
help/skin         --> jar:classic.jar!/skin/classic/help/
browser/skin      --> jar:classic.jar!/skin/classic/browser/

Suppose the user opens the Help menu and clicks on "About Mozilla Firefox". When Firefox goes to draw the "About Firefox" dialog box, it reads the XUL source for that dialog. Here are the first ten lines of that XUL file.

001:   <?xml version="1.0"?> <!-- -*- Mode: HTML -*- --> 
002:   
003:   <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> 
004:   <?xml-stylesheet href="chrome://browser/content/aboutDialog.css" type="text/css"?> 
005:   
006:   <!DOCTYPE window [
007:   <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
008:   %brandDTD;
009:   <!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
010:   %aboutDialogDTD;

You can see that, as Firefox reads through this file, it encounters the chrome URL chrome://global/skin/. In resolving this chrome URL -- that is, in deciding which file the chrome URL refers to -- Firefox performs the following sequence of steps.

  1. Decide which package it refers to ("global").
  2. Decide which part of the package it refers to ("skin").
  3. Look up package and part in the chrome registry and get the resulting location ("jar:classic.jar!/skin/classic/global/").
  4. Look at the remainder of the chrome URL (after removing package and part) to see which file is named. In this case, there is no remainder, and the rules for chrome URLs that omit the file name apply, which results in "global.css" being used for the file name.
  5. Combine the location from the chrome registry with the file name from the remainder of the chrome URL to get the full path to the final file. In this case, the result is "jar:classic.jar!/skin/classic/global/global.css".

Suppose the user went back to the main browser window and entered the following chrome URL into the browser's address box:

chrome://browser/skin/preferences/preferences.css

Assuming that the same chrome registry is in effect, what would Firefox make of this chrome URL? Running through the same sequence of steps as before:

  1. Decide which package it refers to ("browser").
  2. Decide which part of the package it refers to ("skin").
  3. Look up package and part in the chrome registry and get the resulting location ("jar:classic.jar!/skin/classic/browser/").
  4. Look at the remainder of the chrome URL (after removing package and part) to see which file is named ("preferences/preferences.css").
  5. Combine the location from the chrome registry with the file name from the remainder of the chrome URL to get the full path to the final file. In this case, the result is "jar:classic.jar!/skin/classic/browser/preferences/preferences.css".

Now suppose that, instead of using the Firefox default theme, the end user has chosen to use an add-on theme. Suppose further that the add-on theme uses a contents.rdf, and that the contents.rdf file contains the following package-defining lines (showing only the relevant lines).

020:       <chrome:packages> 
021:         <RDF:Seq about="urn:mozilla:skin:Test_Theme:packages"> 
022:           <RDF:li resource="urn:mozilla:skin:Test_Theme:browser"/> 
023:           <RDF:li resource="urn:mozilla:skin:Test_Theme:communicator"/> 
024:           <RDF:li resource="urn:mozilla:skin:Test_Theme:help"/> 
025:           <RDF:li resource="urn:mozilla:skin:Test_Theme:global"/> 
026:           <RDF:li resource="urn:mozilla:skin:Test_Theme:mozapps"/> 
027:         </RDF:Seq> 
028:       </chrome:packages> 
029:     </RDF:Description> 
030:    
031:     <!-- Version Information.  State that we work only with major version 1 of this package. --> 
032:     <RDF:Description about="urn:mozilla:skin:Test_Theme:browser"       chrome:skinVersion="1.5"/> 
033:     <RDF:Description about="urn:mozilla:skin:Test_Theme:communicator"  chrome:skinVersion="1.5"/> 
034:     <RDF:Description about="urn:mozilla:skin:Test_Theme:help"          chrome:skinVersion="1.5"/> 
035:     <RDF:Description about="urn:mozilla:skin:Test_Theme:global"        chrome:skinVersion="1.5"/> 
036:     <RDF:Description about="urn:mozilla:skin:Test_Theme:mozapps"       chrome:skinVersion="1.5"/> 
037:   </RDF:RDF> 
038:    

How would Firefox handle this case? This question actually has three parts: first, how is contents.rdf converted into a manifest file when the theme is installed; second, what chrome registry results from this new manifest file; and, third, how would a chrome URL be resolved using that chrome registry?

When installing a theme based on contents.rdf, Firefox converts the package-defining lines in contents.rdf into an equivalent manifest file. For the lines just listed, this would result in this equivalent manifest file.

skin    browser         Test_Theme  jar:chrome/test_theme.jar!/browser/
skin    communicator    Test_Theme  jar:chrome/test_theme.jar!/communicator/
skin    help            Test_Theme  jar:chrome/test_theme.jar!/help/
skin    global          Test_Theme  jar:chrome/test_theme.jar!/global/
skin    mozapps         Test_Theme  jar:chrome/test_theme.jar!/mozapps/

We're assuming that contents.rdf uses the internal name "Test_Theme" and that the theme was installed from a jar file called "test_theme.jar", which name is then placed by Firefox in the created manifest file, as just shown.

After loading chrome.manifest for the default theme, the chrome registry would look (in part) like this.

package/part      --> location
----------------- --- ---------------------------------------
communicator/skin --> jar:classic.jar!/skin/classic/communicator/
global/skin       --> jar:classic.jar!/skin/classic/global/
mozapps/skin      --> jar:classic.jar!/skin/classic/mozapps/
help/skin         --> jar:classic.jar!/skin/classic/help/
browser/skin      --> jar:classic.jar!/skin/classic/browser/

After loading the manifest file created for the add-on theme, then the chrome registry would look (in part) like this.

package/part      --> location
----------------- --- ---------------------------------------
communicator/skin --> jar:chrome/test_theme.jar!/communicator/
global/skin       --> jar:chrome/test_theme.jar!/global/
mozapps/skin      --> jar:chrome/test_theme.jar!/mozapps/
help/skin         --> jar:chrome/test_theme.jar!/help/
browser/skin      --> jar:chrome/test_theme.jar!/browser/

You can see that entries based on the add-on theme's manifest have replaced the same-named entries based on the default theme.

Now that the chrome registry has been created, Firefox can resolve a chrome URL like chrome://browser/skin/preferences/preferences.css using the same sequence of steps as shown above.

  1. Decide which package it refers to ("browser").
  2. Decide which part of the package it refers to ("skin").
  3. Look up package and part in the chrome registry and get the resulting location ("jar:chrome/test_theme.jar!/browser/").
  4. Look at the remainder of the chrome URL (after removing package and part) to see which file is named ("preferences/preferences.css").
  5. Combine the location from the chrome registry with the file name from the remainder of the chrome URL to get the full path to the final file. In this case, the result is "jar:chrome/test_theme.jar!/browser/preferences/preferences.css".

This tells us the final file name to use is "preferences.css", located within the directory "preferences" within the directory "browser" within the jar file named "test_theme.jar" which is in the directory "chrome" within whichever directory the theme was installed to on the end user's hard drive. And you wonder why this stuff gives you headaches.

Now suppose that you edited the manifest file for the installed add-on theme to point to a development area on your hard drive rather than into a jar file. How would Firefox handle this case? Let's say that you edited the manifest file for the add-on theme to look like this.

skin    browser         Test_Theme file:///C:/themes/test_theme/unpacked/browser/
skin    communicator    Test_Theme file:///C:/themes/test_theme/unpacked/communicator/
skin    help            Test_Theme file:///C:/themes/test_theme/unpacked/help/
skin    global          Test_Theme file:///C:/themes/test_theme/unpacked/global/
skin    mozapps         Test_Theme file:///C:/themes/test_theme/unpacked/mozapps/

After loading chrome.manifest for the default theme, the chrome registry would look (in part) like this (the same as always).

package/part      --> location
----------------- --- ---------------------------------------
communicator/skin --> jar:classic.jar!/skin/classic/communicator/
global/skin       --> jar:classic.jar!/skin/classic/global/
mozapps/skin      --> jar:classic.jar!/skin/classic/mozapps/
help/skin         --> jar:classic.jar!/skin/classic/help/
browser/skin      --> jar:classic.jar!/skin/classic/browser/

After loading the edited manifest file for the add-on theme, then the chrome registry would look (in part) like this.

package/part      --> location
----------------- --- ---------------------------------------
communicator/skin --> file:///C:/themes/test_theme/unpacked/communicator/
global/skin       --> file:///C:/themes/test_theme/unpacked/global/
mozapps/skin      --> file:///C:/themes/test_theme/unpacked/mozapps/
help/skin         --> file:///C:/themes/test_theme/unpacked/help/
browser/skin      --> file:///C:/themes/test_theme/unpacked/browser/

You can see that entries based on the add-on theme's manifest have replaced the same-named entries based on the default theme, and that they now point to directories on your hard drive rather than in a jar file.

Now that the chrome registry has been created, Firefox can resolve a chrome URL like chrome://browser/skin/preferences/preferences.css using the same sequence of steps as shown above.

  1. Decide which package it refers to ("browser").
  2. Decide which part of the package it refers to ("skin").
  3. Look up package and part in the chrome registry and get the resulting location ("file:///C:/themes/test_theme/unpacked/browser/").
  4. Look at the remainder of the chrome URL (after removing package and part) to see which file is named ("preferences/preferences.css").
  5. Combine the location from the chrome registry with the file name from the remainder of the chrome URL to get the full path to the final file. In this case, the result is "file:///C:/themes/test_theme/unpacked/browser/preferences/preferences.css".

This tells us the final file name to use is "preferences.css", located within the directory "preferences" within the directory "browser" within the directory "unpacked" within the directory "test_theme" within the directory "themes" which is on your system's C: drive.

Firefox Protocols Other than Chrome

You're no doubt familiar with seeing the string "http://" to indicate the web browser protocol with URLs. We've also previously described chrome URLs. There are several other protocols supported by Firefox, some of which are specific to Mozilla software. You may encounter these as you read through the Firefox default theme and the rest of the source code in the jars which define the Firefox user interface.

Protocol String Description
file:// Specifies a location on your hard drive. For example, with Firefox 1.5, the icon for the currently selected search engine in the Search Bar (like the Google icon) is defined as a file URL pointing to a .GIF file in the searchplugins subdirectory under the Firefox installation directory, such as src="file:///C:/Program Files/Mozilla Firefox/searchplugins/google.gif". You can also use this format in the Firefox Location Bar to load web pages from your hard drive rather than from the web.
moz-icon:// Refers to the system-defined icon associated with a particular file type. For example, moz-icon://.avi?size=16 if entered into the Location Bar will display the icon associated with AVI files. See also this use of moz-icon:// in an XBL binding for the Options window of Firefox 1.5.
resource:// Refers to files installed with Firefox but separate from any of the seven jar files. The files referred to are located in the res/ subdirectory of the Firefox installation directory. See for example this use of a resource:// URI in the source XUL for the "Page Info" dialog.
wyciwyg:// Refers to an item located in the Firefox cache. The WYCIWYG protocol is described in slightly more detail on Wikipedia here. See also this reference to wyciwyg:// in source code for the main browser window.
data:// Refers to ASCII-encoded binary data. You will see this, for example, in the source XML for search engine plugins with Firefox 2.0, which use a data:// URI to define the icon which represents each search plugin (as shown here).
engine:// Refers to an XML file defining a search plugin. (Firefox 1.5 only; as of Firefox 2.0, a different method is used to represent search plugins). Each of the menuitems in the dropdown list of search engines in the Firefox Search Bar has an id of the form engine://D%3A%5CAPP%5CFIREFOX%5Csearchplugins%5Cgoogle.src

Formally, http:, chrome:, file:, moz-icon:, resource:, wyciwyg:, data: and engine: (along with others you may be familiar with such as mailto:, ftp: and news:) are called URI schemes.

The Seven Jar Files in the Default Firefox UI

All of the Firefox chrome is represented as seven files in the default installation. These files are loaded by Firefox when it launches to determine the contents of its user interface (the buttons, the menus, the dialog boxes, and so on) as well as its visual style (the default theme). These seven files are:

These files contain the XUL, CSS, XML, Javascript, imagery, and other files that define the Firefox UI.

Theme developers can, for the most part, ignore all of these except classic.jar, which contains the Firefox default theme. While you could, theoretically, modify the contents of these files (on your own hard drive, for your personal installation only, and not in your theme), that's not something you'd really want to do.

These files are located in a subdirectory under whatever directory you installed Firefox in. On Windows XP systems, by default, Firefox is installed in C:\Program Files\Mozilla Firefox\. See this article for locations of the installation directory on other systems. If you open and explore the Firefox installation directory, you will find the seven jar files in a subdirectory called chrome.

For each of these jar files, there is a corresponding manifest file located in the same directory. You can view the contents of these seven manifest files here.

XUL and the Firefox UI

Each Firefox window and dialog is defined in an XUL source document. For the most part, Firefox comes with one XUL source file for each window or dialog (although some files are reused for multiple windows) distributed with Firefox in the seven jar files that define the Firefox user interface. Each of these documents is read and loaded by Firefox, which then creates the corresponding DOM tree to represent the document inside Firefox.

The following is a sample of (slightly edited) actual XUL code from Firefox. This particular sample defines the "About Firefox" dialog, which you will see if you select "About Firefox" from the Firefox "Help" menu. This file is one of the smaller XUL files, but serves as a good illustration of the concept of defining user interface with XUL.

Here is the dialog that results:


Figure: Image showing the "About Firefox" dialog

and here is the source code that defines it.

001:   <?xml version="1.0"?> <!-- -*- Mode: HTML -*- --> 
002:   
003:   <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> 
004:   <?xml-stylesheet href="chrome://browser/content/aboutDialog.css" type="text/css"?> 
005:   
006:   <!DOCTYPE window [
007:   <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
008:   %brandDTD;
009:   <!ENTITY % aboutDialogDTD SYSTEM "chrome://browser/locale/aboutDialog.dtd" >
010:   %aboutDialogDTD;
011:   ]>
012:   
013:   <dialog xmlns:html="http://www.w3.org/1999/xhtml"
014:           xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
015:           id="aboutDialog"
016:           windowtype="Browser:About"
017:           onload="init(event);" onunload="uninit(event);"
018:           title="&aboutDialog.title;"
019:           buttons="accept,extra2"
020:           creditslabel="&copyright;"
021:           aboutlabel="&aboutLink;">
022:       
023:     <script type="application/x-javascript" src="chrome://browser/content/aboutDialog.js"/>
024:   
025:     <deck id="modes" flex="1">
026:       <vbox flex="1" id="clientBox">
027:   
028:   <label id="version" value="&aboutVersion; 1.5.0.1"/>
029:   
030:         <description id="copyright">&copyrightText;</description>
031:         <vbox id="detailsBox" align="center" flex="1">
032:           <spacer flex="1"/>
033:           <textbox id="userAgent" multiline="true" readonly="true" cols="60"/>
034:         </vbox>
035:       </vbox>
036:       <vbox flex="1" id="creditsBox">
037:         <iframe id="creditsIframe" src="chrome://browser/content/credits.xhtml" flex="1"/>
038:       </vbox>    
039:     </deck>
040:     <separator class="groove" id="groove"/>
041:   
042:   </dialog>

Some comments:

  • Lines 3 and 4 say which CSS files will be used to define the appearance of this dialog (namely, chrome://global/skin/global.css and chrome://browser/content/aboutDialog.css). These lines are examples of the use of chrome URLs, which we discuss here.
  • Line 3 illustrates the use of an omitted filename in a chrome URL, in which case Firefox knows to fill in a default filename.
  • Lines 6 through 11 define two string tables to load and substitute for string placeholders (such as &copyrightText;) in the XUL source. This supports localization and internationalization of the user interface.
  • Lines 13 through 21 define the top-level DOM node of the DOM tree that is constructed within Firefox to represent the document contained within this file. This top-level node is an element of type "dialog", having attribute names and values specified within the enclosing brackets, such as id="aboutDialog" and windowtype="Browser:About".
  • Line 23 tells Firefox to load a Javascript source file. The executable code defined in the Javascript file will be attached to the DOM tree for this document.
  • The rest of the document places elements of type deck, vbox, label, description, spacer, textbox, iframe, and separator within the enclosing dialog element.

This topic has provided a very brief exposure to XUL. For more information, see the XULPlanet XUL Tutorial, which provides an excellent introduction, as well as the XULPlanet XUL Element Reference, where you can read about the component elements used in this example. You might also be interested in looking at the XUL source for the main browser window, the Bookmarks Sidebar, and the Options window. Also, this site lists XUL source file names and links in the documentation for each window and dialog for Firefox 1.5, Firefox 2.0, and Thunderbird 1.5.

XUL and Themes

You won't be writing any XUL in your theme (with one possible exception), but you are likely to be reading it, or at least skimming it, so you can understand the underlying framework of DOM nodes over which you're draping your CSS- and image-based skin.


Figure: Screenshot of user right-clicking on a theme in the Theme Manager's list of themes

The possible exception is that install.rdf allows you to specify an arbitrary URL as the source for what to display when the user right clicks on your theme in the Themes Manager (Firefox 1.5) or Add-ons Manager (Firefox 2.0) list of themes, and selects "About Theme". This URL could be an http:// URL, or it could be a chrome URL, in which case it can point to an XUL file supplied by you within your theme. In my theme, I use this to have a custom "About Theme" dialog that's a little more fancy than the standard dialog. This is the line in install.rdf that defines it:

    <em:aboutURL>chrome://mozapps/skin/about/about.xul</em:aboutURL>

The downside is that when your theme is not the one currently active, the user can still right click on your theme in the theme list to bring up the "About Theme" dialog, in which case Firefox will try to resolve the aboutURL to a location that isn't there, leading to plague, pestilence, and all-around mayhem. Or maybe just a strange-looking dialog box with no content.

DOM and DOM Elements

When you look at the Firefox user interface, you see all the elements that make up the interface: the menus, buttons, scrollbars, text boxes, toolbars, and so on. Inside Firefox, each of these elements is represented as what's called a DOM node. This term, DOM node, is simply the name for Firefox's internal representation of each element of the user interface when stored in Firefox's memory.

As a theme developer, you need to know about DOM nodes because the CSS rules in your theme try to match against, and then define the appearance of, the many DOM nodes in the Firefox UI. This is the very essence of how themes work: the Firefox user interface is composed of DOM nodes, and your theme tells Firefox what they should look like. For example, if your theme contained a rule like this:

toolbar#toolbar-menubar {
  background-color: red;
}

then Firefox would know to search through the DOM nodes in its user interface and try to find one that's a toolbar having the element ID "toolbar-menubar". Any nodes that it finds would be drawn with a background-color of red.

Where do DOM nodes come from? They are created when Firefox reads the XUL files that define its user interface. XUL files contains tags that define DOM elements. As Firefox reads through an XUL file, each time the file specifies an element, then Firefox creates the corresponding DOM node in its memory. This is where the term DOM comes from: DOM stands for "Document Object Model", and it refers to the way that a document such as an XUL file (or the HTML file for a web page) is converted by a program like Firefox into a set of objects, or nodes, in its internal memory. You can read more about the Document Object Model here on XUL Planet, which also offers a reference describing all of the possible DOM elements.

A related term is DOM tree, which refers to the way that Firefox organizes all of the DOM nodes that are defined in an XUL file, once it has read the document into memory. All of the DOM nodes defined in the document are arranged in a data structure called a tree.

XUL allows DOM nodes to contain other DOM nodes; when this is the case, the contained nodes are placed in the DOM tree as child nodes of the node that contains them. If you use DOM Inspector to examine the internal structure of a Firefox window, the left pane shows the DOM tree for the Firefox window. Each item in the left pane is one DOM node, and selecting it causes DOM Inspector to display the corresponding information in the right pane. When a node is shown indented, that means it is a child node of the node beneath which it is indented.

The terms "element", "node", "XUL element", "DOM element" and "DOM node" can be used more or less interchangeably. Technically speaking, a source document such as an XUL file contains elements, and these are represented by a program like Firefox as DOM nodes in a DOM tree when the source document is read; in practice, you'll see the terms used interchangably in most of the documentation you may read, including on this site.

Putting it all together, here's how the whole process works.

  1. Firefox reads a source document from disk. An example source document would be browser.xul, containing the definition of the user interface for the main browser window. Firefox reads this document as part of starting up.
  2. The document contains tags that define XUL elements.
  3. When Firefox starts reading a document, it creates a new DOM tree to hold the contents of the document.
  4. Each XUL element is added to the DOM tree, in the form of a DOM node.
  5. Each DOM node is represented as a variable in the computer's working memory, where Firefox can quickly read and write to it (because it's faster to access memory than to access the hard drive).
  6. Once Firefox has finished reading the source document, the entire DOM tree for the document will have been stored as a set of DOM nodes this way.
  7. Once the document is read, Firefox applies all the CSS rules in the current active theme to the DOM nodes in the tree. This results in properties such as size and color being assigned to the DOM nodes.
  8. Firefox then uses the DOM tree to draw, or render, the user interface on-screen.
XBL and the Firefox UI

XBL stands for "Extensible Bindings Language". Along with XUL, CSS, and Javascript, XBL is one of the many technologies used to define and render the Firefox user interface. XBL allows developers to drape behaviors and content onto XUL elements the same way that CSS allows developers to drape visual styles onto XUL elements. To put it another way: similar to the way that CSS allows you to style the appearance of an element, XBL allows you to style the behavior and structure of an element. In this context, "behaviors" are scripts written in Javascript, and "content" is a set of additional elements added to the original element.

As a theme developer, there are just three things you need to know about XBL.

  1. You will not need to either write or modify any XBL for your theme. In fact, if you're new to theme development, then you can consider XBL to be an "advanced topic" that you come back to later, after you've been working with themes for a while.
  2. Certain XBL is required to be part of your theme for Firefox to work right. All of the XBL that is part of the default Firefox theme is required to be in your theme as well. If you based your theme on the default Firefox theme, and you started by modifying a copy of the default Firefox theme, then you're covered.
  3. Almost all of the available documentation about XBL does not apply to themes. It says you can do things with XBL that you can only do if you're writing an extension or a browser. Most of the documentation written about XBL is written for developers of extensions and of Firefox itself.

The rest of this topic gives you a brief introduction to XBL.

Like XUL, XBL is an XML-based language, using tags, elements, attributes and properties. XBL code is stored in .XML files. You might expect XBL code to be stored in files having a ".xbl" extension, but that's not actually the case; all XBL files have a ".xml" extension.

You'll see XBL in the Firefox default theme, such as this code from browser/browser.css:

#feed-button {
  -moz-binding: url("chrome://global/content/bindings/button.xml#menu");
  -moz-appearance: none; 
  min-width: 0px; 
  margin-right: 1px !important;
}

This code creates what's called a "binding", which is an association between some XBL code and certain DOM nodes to which the XBL code is applied or "bound". The binding is defined using the "-moz-binding" property, which is a Mozilla-specific CSS extension. In this example, the XBL code is located within the file "chrome://global/content/bindings/button.xml", and that code is bound to all DOM elements having the element ID "feed-button", defined by the CSS selector. XBL works in conjunction with CSS to select the DOM nodes to which the binding is applied, and bindings are always specified within CSS code: the CSS property "-moz-binding" specifies which XBL code to apply, and the CSS selector specifies the elements to which the XBL binding applies.

XBL is widely used throughout the Firefox user interface. XBL bindings are present both in the default theme and in the other six jar files that define the Firefox UI. The Firefox developers use XBL as a sort of macro to convert simple, single DOM elements into more complicated DOM structures, allowing them to define that structure once (in XBL) without having to restate that structure every place it is used. For example, much of the source XUL for Firefox will define an XUL element of type button, which XBL bindings convert into a button containing several other DOM nodes.

DOM nodes created as a result of an XBL binding are called "anonymous content". These are DOM nodes that were not explicitly defined in an XUL source document, but were instead created as a result of an XBL binding. If you are using DOM Inspector, you'll see anonymous content displayed in red.

The recommendation for developers new to themes, as far as XBL is concerned, is simple:

  • make sure your theme contains all of the bindings that are part of the Firefox default theme (if you based your theme on a copy of the default theme, then this is easy, and simply means you should leave intact any bindings copied from the default theme)
  • don't try to add any new bindings of your own, unless you're willing to step far off the beaten path

Further details of XBL are beyond the scope of this document. See this XBL Tutorial at XULPlanet for more information. Keep in mind that most of what you will read there is not possible in themes. See topic "Hidden Traps of Theme Development" for more information about XBL and themes.

CSS and the Firefox UI

CSS stands for "Cascading Stylesheets". CSS is used in Firefox themes to define the appearance of the Firefox user interface.

While several technologies work together to define the Firefox user interface, CSS, more than any other, is what you will work with as a theme developer. There really is no substitute for understanding CSS in detail if you are going to work with themes. Here we provide a brief overview of CSS and describe some of the finer points as relates to theme development. For more information, see the official CSS2 standard and this well-regarded tutorial.

Some highlights:

  • CSS code is stored in files having a .css extension
  • CSS allows you to define the appearance of the DOM nodes that make up the Firefox user interface
  • Many people are already familiar with CSS from using it as a part of web site development
  • In the seven jar files which define Firefox default user interface, CSS files are used both in the default theme and in the underlying content
  • Add-on themes contain many CSS files as well
  • There are multiple CSS standards. Firefox supports CSS2, and also supports additional Mozilla-specific CSS extensions.

The next several topics cover different aspects of CSS in theme development for Firefox.

Basic CSS Vocabulary

First, a little vocabulary. This is a CSS rule.

button {
  color: red;
  background-color: white;
}

This rule consists of a selector (the word "button") and two assignments of values to properties. The selector acts as a pattern which matches against individual DOM nodes to see whether it applies to them or not. This rule will match all DOM nodes having an element type of button, and, for each matching DOM node, if it is not overridden by a rule having higher precedence, will assign the value red to the property color, and also assign the value white to the property background-color. In effect, this rule says "all buttons should have red text and a white background."

There are lots of different ways to write selectors. You can read about selectors here.

In official CSS parlance, the whole thing is called a rule set or rule. The rule consists of a selector followed by a declaration block. The declaration block is everything between the two curly braces. Within the declaration block is a set of declarations (this example has two), with each declaration consisting of a property, followed by a colon, followed by a value, followed by a semicolon. You can read the official standard for CSS2 here.

To recap the official CSS vocabulary: rule set, rule, selector, declaration block, declaration, property, and value.

Competition Among CSS Rules

Of course, your theme will consist of not just one but many rules. What happens when multiple rules match against an individual DOM node?

Like little wrigglers competing for the chance to fertilize the egg, CSS rules compete to be the one that applies to each node in the DOM tree. CSS rules are ambitious and territorial by nature: each rule wants to beat out all the others and control the style for every single node in the DOM tree. In the end, for a given property of a given DOM node, only one rule will win. It is the selector on each rule that determines whether it will match at all, and the specificity of competing selectors determines which of the matching rules will win. (We're introducing the new term "specificity". We'll come back to it below.)

The competition is not to set all the properties of a DOM node; the competition is, if you will, more intense than that, with the fight being over the right to set the value for each individual property of each DOM node, on its own. Put another way, for any given DOM node, the competition to set values is judged and won or lost for each property on its own. You may (and usually will) have the case that one property is set by one rule, while another property for the same DOM element is set by a different rule.

Another way to state it -- the fact that the competition is over the right to set the value for individual properties, and not for the right to set values for the DOM node as a whole -- is this. When you write a rule like this, with multiple property/value assignments:

button {
  color: red;
  background-color: white;
}

the Firefox rendering engine sees it as if you had written this:

button {
  color: red;
}
button {
  background-color: white;
}

with each property/value pair on its own, which then competes against other individual property/value pairs.

The competition between contending rules is managed and resolved by a part of Firefox called the rendering engine (specifically, the Gecko rendering engine); this is the part of Firefox that knows about all the rules and all the elements in each DOM tree, and (among other important responsibilities) decides which rules apply to which nodes.

Topic "CSS and Specificity" describes how the winner of the competition is determined.

CSS and Specificity

We've established that there's a competition among rules. How is the competition judged? Given two rules, both matching a DOM node and both trying to assign a value to the same property, how does Firefox decide which one wins? Here we come back to the concept of specificity. Specificity is a measure of the extent to which a rule matches an individual DOM node. The question is not just whether a rule matches, but how well and how specifically it matches.

Before we get too abstract, let's show an example. Given the following DOM node:

<button id="startbutton" class="commandbar-button">

and the following pair of rules:

button {
  background-color: white;
}

button#startbutton {
  background-color: blue;
}

we see that both match the DOM node and both compete to set the background-color property. Specificity is represented as a numeric score assigned to each selector. In this case, Firefox computes a numeric specificity score of 1 for the first rule and 101 for the second rule. Since in CSS the higher specificity wins, the second rule will apply (101 being greater than 1), and the property of background-color for the DOM node will be set to blue, and not white.

How exactly is the specificity score calculated? The official CSS2 specification details specificity calculation here.

To state it more generally: recall that DOM nodes have attributes, possibly including an element ID, an element class, and other arbitrary attributes defined in the source code for the DOM tree containing the node (or established at runtime by code such as Javascript which directly manipulates the DOM tree). Firefox takes a look at the selector for the rule, takes a look at the DOM node which the selector matches, and assigns a numeric score based on how closely the selector matches the node and its attributes. This score is called the specificity. Neither rules nor DOM nodes have specificity scores on their own; specificity is a result of the match between an individual rule and an individual DOM node. Having calculated specificity scores for all the rules matching a DOM node, then, in general, the rule with the higher specificity wins. (Specificity is actually only part of the picture, but it is often what decides the competition. We'll come back to the more general case when we talk about rule precedence below.)

Another case to consider is when some of the property/value pairs in a rule run into competition from another rules, while other property/value pairs in the same rule don't have any competition. How is this resolved? Consider the following DOM node, as was used in the previous example:

<button id="startbutton" class="commandbar-button">

and this pair of rules, this time each having multiple property/value pairs:

button {
  background-color: white;
  color: green;
}

button#startbutton {
  background-color: blue;
  font-family: arial;
}

As already mentioned, Firefox interprets rules with multiple property/value pairs as if they were multiple rules, each having a single property/value pair. In this case, it's as if you had written this rule set instead:

button {
  background-color: white;
}

button {
  color: green;
}

button#startbutton {
  background-color: blue;
}

button#startbutton {
  font-family: arial;
}

Now we can easily see that there's competition to set the background-color, and no competition for either the font-family or the color. As before, button#startbutton scores a 101, while plain old button scores a 1, so button#startbutton wins the competition to set background-color, and the button ends up with these values for its properties:

color: green;
background-color: blue;
font-family: arial;

While we're on the subject: the resulting values assigned to the properties of a DOM node are called its "specified values", as described in official CSS2 documentation here.

Note: the way the CSS standard states it is a little indirect, but for the purpose of a specificity score, each use of a class, pseudo-class, or attribute name in a selector counts the same amount (each one adds 10 to the score).

CSS and Rule Precedence in General

We've already discussed how specificity influences the outcome of the competition between two CSS rules, both selecting the same DOM node and both trying to assign a value to the same property. It turns out that specificity is only one consideration in judging the winner of the competition. More generally, and more accurately, CSS rule precedence is based on these four considerations:

  • weight - refers to having !important or not
  • origin - refers to which class of stylesheet the rule was defined in: user-defined, author-defined, or user agent default
  • specificity - refers to a calculated specificity value based on the terms in the selector of the rule
  • order - refers simply to which came first and which came later in the relevant stylesheet

Regarding origin, there are three classes of stylesheets:

  • User-defined: specified by the user using some mechanism specified by the User-Agent e.g. telling it a stylesheet to use. With Firefox, userChrome.css is a user-defined stylesheet.
  • Author-defined: specified as part of the document/content, e.g. those included by XUL or HTML. With Firefox, the CSS files in the active theme and in the rest of the user interface count as author-defined stylesheets.
  • User agent default: defined as part of the user agent itself, specifies sensible stylesheet rules to use as a default in the event of lack of applicable author-defined or user-defined rules. "User agent" is another term for web browser, i.e. Firefox.

The calculation of precedence also takes into account the case of elements containing explicit style attributes. These result in creation of an implied rule, having specificity 100 (that's high), ordered "after all other rules" (that's high too). The implications of explicit style attributes for theme developers are discussed here.

Rather than reiterate the details of how precedence is calculated, we simply refer you to the Cascading Order section of the official CSS2 standard.

Pseudo-Elements and Pseudo-Classes

You'll need to understand these concepts almost as soon as you start trying to understand the CSS that controls the main browser toolbar. In brief:

  • pseudo-elements allow you to select portions of the document tree (content) as if they were individual elements when they are not (example: :first-letter)
  • pseudo-classes allow you to select elements of the document tree (content) based on characteristics "other than name, attribute or content" (example: :link :hover)
  • in CSS2 the pseudo-elements are: :first-line :first-letter :before :after
  • in CSS2 the pseudo-classes are: :first-child :link :visited :hover :active :focus :lang (Mozilla software has more)

You can read about these in the official CSS2 documentation for Pseudo-Elements and Pseudo-Classes here.

Combining Multiple Pseudo-Elements and Pseudo-Classes in a Single Selector
CSS allows you to write selectors that use multiple pseudo-classes and/or pseudo-elements. While you frequently see selectors using just one, like these:
a:visited {
    color: purple;
}

a:hover {
    text-decoration: underline;
    color: #006699;
}
you also see selectors like
a:hover:active {
    text-decoration: underline;
    color: red;
}
which uses multiple pseudo-classes (:hover and :active). This selector says that the <a> must have both hover and active states at the same time for the selector to match. The mouse must still be clicked (the button can't have been released yet) and currently hovering over the element for a selector like this to match. In general, a selector using two pseudo-classes or pseudo-elements A and B requires that both A and B be true for it to match.

The difference between these two rules

button:active { ... }
button:hover:active { ... }
is that the first one, using only :active, will match as long as the mouse button is still clicked and has not been released, no matter where the user may move it on the screen, while the second selector only matches while the mouse is still clicked and currently over the element that was clicked.

The Firefox default theme uses rules of this kind for the checkbox element. The file checkbox.css (details: Firefox 1.5 Firefox 2.0) in the default theme contains this rule

checkbox:hover:active > .checkbox-check {
  background-color: -moz-Dialog;
}
which shows how the background color of a checkbox is set to show the user that they've clicked it and it will change state if they release the mouse button while still hovering over the element, but not if they release when the mouse is moved off of it.

Themes and Explicit Style="" Attributes

Some of the source code for Firefox windows contains an explicit "style" attribute. An explicit style attribute (officially called an "inline style definition" in the CSS spec) defines properties and values, much as if they were contained in a CSS rule that matches the given element, but defined as a DOM element's attribute in a document source file instead. Here's an example from the source code for the Import Wizard, contained in file migration.xul:

<vbox id="dataSources" style="overflow: auto; -moz-appearance: listbox" align="left" flex="1"
    xhtml2:role="wairole:groupbox"/>

In this example, the explicit style attribute is equivalent (with one important difference, discussed in a moment) to the following CSS rule:

#dataSources {
    overflow: auto;
    -moz-appearance: listbox;
}

The difference has to do with the precedence of CSS rules, in the case when two or more rules are competing to style the same property of the same element. When that is the case, an explicit style attribute, defined as part of the element as shown in the example above, then the property/value pairs enter the competition with specificity 100. That's a really high specificity, and it will win against many other rules. The idea is that if a developer has gone to the trouble of explicitly setting a style on an element, then they really mean it, and it should take precedence over almost all other rules. This section of the official CSS2 standard describes how this works:

In HTML, values of an element's "style" attribute are style sheet rules. These rules have no selectors, but for the purpose of step 3 of the cascade algorithm, they are considered to have an ID selector (specificity: a=1, b=0, c=0). For the purpose of step 4, they are considered to be after all other rules.

(The quote refers to HTML, but it applies equally to XUL.) The implication is that if you're going to write a rule in your theme that takes precedence over an explicit style attribute, you need to either:

  1. write your rule to use a selector having specificity greater than 100, or
  2. use !important in your rule.

In the example shown above, you could write a rule like this:

vbox#dataSources {
    overflow: auto;
    -moz-appearance: listbox;
}

which has specificity 101 (100 for an element ID plus 1 for an element type equals 101), which would take precedence over the explicit style attribute.

It's not always possible to write a rule having greater specificity than 100, because that requires using an element ID in the selector, and not every element is defined to have an ID. When that's the case, you have no recourse but to use !important in your rule (with the negative trade-offs which that entails). For example, if the vbox in the code shown above did not have an element id, then this rule

wizardpage > vbox {
  -moz-appearance: none !important;
  /* need the !important, else explicit style= in migration.xul takes precedence */
}

would still take precedence, because rules having !important take precedence over rules that don't.

Using !important with CSS Rules

CSS allows you to add the modifier "!important" to a property/value pair to increase its precedence. As a theme developer, you can do this in your theme, but there are drawbacks:

  1. it makes it impossible for end users to add rules to their userChrome.css that override your rule
  2. adding !important to a rule won't make it start working if the problem is a competing rule that also has !important on it

Reason one is true because, according to the CSS2 standard cascade, rules in author (that's you, the theme developer) stylesheets having !important on them take precedence over rules in user stylesheets (with Firefox, userChrome.css is a user stylesheet) even when user rules contain !important as well.

The use of !important in author-defined rules, i.e., in your theme, is equivalent to saying "cannot be overridden by user". The bottom line is that, as a theme developer, you should really try to avoid using !important in your CSS for these reasons.

Still, there are times when the only solution is to use !important. My practice is to document every occurrence of !important with a description of the reason, such as:

wizardpage > vbox#dataSources {
  -moz-appearance: none !important;
  /* need the !important, else explicit style= in migration.xul takes precedence */
}

Competition among rules containing !important is resolved, as with other rules, by taking order and specificity into account. As with other rules, specificity wins, and the last-occurring rule out of a set of competing rules having identical specificity will take precedence. For example, in the following set of rules:

hbox.searchbar-box .searchbar-textbox {
  background-color: red !important;
}

searchbar#searchbar hbox.searchbar-box .searchbar-textbox {
  background-color: green !important;
}

hbox.searchbar-box .searchbar-textbox {
  background-color: blue !important;
}

the "green" rule takes precedence because it has higher specificity. For another example, in this rule set:

hbox.searchbar-box .searchbar-textbox {
  background-color: red !important;
}

hbox.searchbar-box .searchbar-textbox {
  background-color: blue !important;
}

the "blue" rule takes precedence because it is the last-occurring out of a set of rules having equal specificity.

Of course, rules containing !important take precedence over rules without !important regardless of specificity, as shown in this rule set

toolbaritem#search-container searchbar#searchbar hbox.searchbar-box .searchbar-textbox {
  background-color: #ffff66;
}

.searchbar-textbox {
  background-color: red !important;
}

in which the "red" rule takes precedence despite a lower specificity.

CSS Selectors and Embedded Documents

Sometimes a DOM tree is composed of DOM elements from two separate documents, with one embedded within the other. Remember that each Firefox window and dialog is defined by some XUL source document; these documents can contain certain DOM elements which themselves load a separate source document. This happens when one of the DOM elements browser, iframe, html:iframe, html:frame, and editor is used. The result is two separate DOM trees, one embedded in but distinct from the other.

magneto>    what are all the DOM elements that will result in creating an embedded DOM tree (like <browser> and <iframe>)?
            are those the only two?
Mossop>     magneto: Also html:iframe and html:frame
magneto>    kk
doron>      tabbrowser too, since it creates browsers
magneto>    thx doron
aw1>        magneto: editor

When you're writing rules for this case (and this is the whole point of this topic) you need to be careful in choosing selectors, because you cannot write a selector that successfully matches elements from two separate DOM trees. CSS is defined in general such that a CSS selector will only match elements contained within a single DOM tree. This behavior is a good thing, because it keeps your theme's rules from applying to the contents of web pages displayed by Firefox. (Each web page is represented as its own DOM tree, anchored at a <browser> element within the DOM tree for the Firefox main browser window.) For you as a theme developer, this means you must make sure that any elements which your selector is supposed to match are contained within a single DOM tree.

Here's a case where this matters. In the main browser window, the Bookmarks sidebar is implemented as a <browser> element embedded within the overall main browser window. The following partial DOM tree, copied from DOM Inspector, shows how the Bookmarks sidebar is represented (from Firefox 2.0):

window#main-window
    stack#browser-stack
        hbox#browser
            vbox#sidebar-box .chromeclass-extrachrome
                sidebarheader 
                browser#sidebar
                    #document