The Mozilla UI

This document is still a "work in progress" document. It may be incomplete, contain all kinds of errors and will change frequently...
Please refresh the page frequently too ;.)

This article is based on Firefox 3.0 under Windows XP.

Building a browser with LEGO

So, let’s imagine we want to construct our own Mozilla based browser.

We order the “Mozilla 3.0 Kit” by LEGO, and after a few days we receive at home a big packet containing two boxes.
One of them is a big black box labeled “Gecko”. The other one is labeled “User Interface”. We'll ignore the big black box for now.
It seems that first we have to build this “User Interface” and then connect it to the big black box…

The “User Interface” box

Let’s have a look inside the “User Interface” box. It contains some big, some medium-sized and some small things. We can call them elements or objects. We will see lots of objects: "windows", "preference windows", "dialogs", "pop ups", "buttons", "toolbars", "horizontal boxes", "vertical boxes", "menus", "menu items"… There are indeed many, many elements.

The Main Window

So - we can begin by building a window to accommodate a big panel (which we will connect to the “Gecko” black box) just to navigate the Internet, looking at home pages and similar stuff… We will call this panel (actually a horizontal box), “browser”.

This window that will accommodate the “browser” box we call “main-window”. So, now we begin inserting all kinds of elements inside this window. We insert a menu bar and populate it with menus and menu items. We insert some toolbars that we can populate with some buttons, some text boxes. All of those elements can also contain other elements like images, drop markers and so on…

We can imagine these objects as layers that we are consecutively overlapping. First a window, then toolbars, then menus, toolbar buttons and so on...

Similarly, we will build other windows, such as a window to customize the browser, a preference window, a window to manage extensions, dialogue windows and many, many other parts we understand should belong to our “User Interface”.

Describing our “User Interface”

In this process we’ve noticed some curious things. Those objects have some properties, attributes, methods, etc… Some windows can be resized, some objects can be dragged. Buttons react to mouse clicks. Pop-ups appear when we mouse over some elements. Some of them "communicate” with us (as users), like pop-ups, dialogs, labels, etc. Most of them have an appearance.

It would be fantastic if we could share our “User Interface”, our windows, our objects, how we have organized them, describing their exactly positions, properties, attributes, words with which they speak to us and so on, just using plain text.

This is possible with XUL. Using XUL (and a couple of other tools like CSS, RDF, XBL, JavaScript) we are able to describe all of our windows (and much more). We need just a text editor to write XUL files. The same goes for the other tools. Very convenient.

In this way we can have the whole "User Interface" as a set of descriptions. And so detailed that we don't need the objects themselves.

But let’s think about how we can organize these files.

It makes a lot of sense to describe things like properties, methods, attributes, etc. separately from words which those elements “talk” (they can change according to the language used by our users) and the appearance those elements have (it can also be changed by themes and other user tools).

So we can imagine we could reorganize our “User Interface” box, inserting in it a package for each window, each one containing three other small packages describing:

Add-ons

Add-ons work in this way too. They too add to Firefox a package describing how it works.
If you look carefully inside add-ons, you will see the same structure.
A theme is an add-on that contains just a skin provider.
A language package will contain only the locale provider.
Extensions may contain all of these providers plus some other files or maybe just one content part.

Note

Let's go back to our LEGO box. Imagine you'd like to have a clock on the toolbar
You can maybe build one. You can take some other objects (as pointers, or LCD display) to compose it.
You can for example decide if your clock will read the time from user system or from some atom clock in the net.
You can think about if it will interact with the user and so on...

You are extending the UI adding a new functionality.
You could make the same as we did for our windows, describing your new object "myClock".
You will have also three sets of descriptions:content - all attributes, properties, types (analog, digital), methods, getters, setters, etc.locale - If your clock will display for example, the actual date, it would be very nice to display it in the user's languageskin - how your clock will appear, if the display type is analog or digital, etc.

So, you've just built an extension for the browser. You can now package it and offer it at AMO...

Firefox native packages

A clean installation from Firefox brings some native packages. We can investigate how many and which packages they are, not looking for the jar files inside the chrome directory, but looking at the chrome manifests (see on the link the XULRunner application part) inside it. We will look only for those lines that start with content, locale and skin and only for the first and second terms in those lines.

The Chrome Structure

There's no requirement that says each package needs to have all three kinds of chrome providers. Not every package contains all three kinds. So, we can recognize some packages with full chrome providers like:

fig.02

and other with partial providers:

fig.03

This is the chrome structure, and is very important for us. All linkage to documents and images we make, are using the chrome protocol:

This chrome structure has nothing to do with the files we have on the hard disk. To get our files into this chrome structure, we have first to tell Mozilla where those files exist on the hard disk, i.e., make the association between the chrome protocol and the file protocol. This is the job from chrome.manifest, the Chrome Registry.

File Organization

I have on my workbench an installation of Firefox that has all the chrome files uncompressed. If you look at the file organization after uncompressing the jar files, you will got something very similar to fig.04 :

fig.04

fig.08

To get this installation to work -- so that Firefox loads from these files instead of the packaged-up files it installed for itself -- I have to modify the chrome manifests to point to the new locations from my files. Let’s take, e.g., the pippki.manifest:

content pippki jar:pippki.jar!/content/pippki/ xpcnativewrappers=yes

This manifest registers only one content provider for the pippki package.

We can see in this manifest a new protocol, the "jar:" protocol. We can use this protocol to point files inside a compressed jar file.

But now, after I uncompressed the jar files, I have to point to where my uncompressed files are. We do this relative to where the chrome.manifest resides (actually the pippki.manifest):

fig.10

Doing this, we are simply telling the chrome registry that the contents from chrome://pippki/content are inside our file://content/pippki/. It sounds confusing, doesn’t it?? But it's actually very simple:

fig.11

IMPORTANT

Looking at the above structure we can notice something curious.

We use to organize our files in the way showed above just for convenience.

Actually it doesn't matter which names you give for your folders. You can have any other file structure.
Once you register (point) these files to the corresponding chrome provider, everything will work fine:

fig.12

which gets the result:

fig.13

Now your file://myroot/mydirectory/doc01 will be available at chrome://pippki/content/doc01

TIP - Uncompressing Firefox's Chrome

You can also have an uncompressed installation of Firefox.

But make sure you do this installation in a separate folder. Don't use it as your default browser.

The best is to pick a zipped file from the nightly and unzip it into a folder of your choice.

Go to the chrome directory and extract the jar files into the same folder that contains those jars.
To the question if you want to override contents, answer yes.

Now you have just to edit the .manifest files with any good text editor and remove this part of code:

Writing a Default Theme

Let’s suppose we want to write a default theme for Firefox. This default theme will be shipped together with Firefox.

Theme Structure

Our job now is to write a skin provider for all skinnable packages inside Firefox. Let’s take the native packages from table01, which have a skin part:
global, mozapps, help, browser, communicator and reporter.

We know that from the perspective of Firefox's chrome, the packages will have this chrome structure (fig.07, makes easy to understand the "Chrome Protocol"):

fig.07

But as we could see on fig.04, the "Chrome Mapping", a skin package will have this structure:

Skin Registry

To make our folders and files that reside on our hard drive available for Firefox, we need to associate them to corresponding addresses with the chrome protocol. The same as we did for the pippki package, remember?

In the above image we can see one item that is different from the content manifest. The skinname. That's the internal name and every theme must have one. The default theme's internal name is classic/1.0.

Let's write our chrome.manifest:

fig.17

With this manifest we are associating our files to the chrome structure:

fig.18

This means that, for example, a file placed inside myDefaultTheme/very_classic/folder01/ will be available for Firefox at chrome://skin/browser/. All right?

It's easy to see that the structure we've chosen for our files isn't really good. It's highly recommended for our convenience that we name our folders in a more consistent way (but notice that's not necessary at all .. The file naming on disk is completely up to us):

But if you look carefully to table01, you will notice that the reporter package already register a skin provider:

fig.20

This means we don't need to register a skin provider for the reporter package, because a skin provider is already registered for it.
Otherwise, we would substitute the skin provider for the reporter package, needing to provide css rules for it.

After we remove it our workbench will look so:

fig.21

Now we have to rewrite our chrome.manifest to point to the new folders:

fig.22

And while we're at it, let's rename the file chrome.manifest as classic.manifest.

Note

If we look again at tab.01, we will notice that the help package has no content provider.
Searching for links that could point to the help skin package, I've found nothing.

This leads me to think the skin provider for the help package is unnecessary.
(Also the locale provider. Firefox 3.5 will not have them anymore).

Necessary Files

Now let’s look which files we need to have inside our folders.

As we repackaged our LEGO "User Interface", we came to a set of object descriptions instead of the objects themselves.

The most important package was the content provider. The content provider has not only descriptions about how objects work (attributes, methods, properties, etc) but also links to the other providers indicating how they look, and what words (and which language) they "speak".

Inside the content provider we will find several files like .xul files, .js , .xml, .xhtml and .css files. They describe how windows are composed out of objects, functions, behaviors and widgets..

We can notice that the content provider already contains some .css files. They contain bindings statements and some basic rules for XUL elements.
For other appearance statements, they contain links to style sheets or images inside the skin provider.
The skin providermust contain these files.

Two files we need to place on the root from our theme. They will appear on the Extension Manager: icon.png
preview.png

Now, our job is to provide all images and css rules necessary to give appearance to elements defined in browser content packages (with XUL, XML, XHTML). We can do this by creating new folders and links to new files, giving us a structure we find reliable and consistent..

We repeat this process for all the other packages: communicator, global, help(?) and mozapps.

So, we are writing a theme, which will be shipped with Firefox for Windows. It may be installed on supported Windows Operating Systems versions like Windows XP and Windows Vista.

To ensure compatibility and stylistic integration with these different flavors of Windows, we now want to include in our default theme some different files for Windows Vista.

So, our manifest now could look something like this, if we want to provide different image and rules for the browser package on Vista :

fig.23

Our file structure will look in this way:

fig.24

The conditional flag manifest we've wrote above will match in this way:if the Operational System is Windows XPthen register the content from classic/browser as chrome://browser/skin/if the Operational System is Windows Vistathen register the content form classic/anotherfolder as chrome://browser/skin/

And now suppose we want to provide different Vista files also for the global and mozapps packages.
Let's also choose some better organization for our files on the hard drive and our structure from fig.19 will become:

fig.25

And our classic.manifest:

fig.26

This will result into this chrome structure:

fig.27

Note

Actually the default theme's manifest is not created "by hand" as we did, but in Firefox's build process.
It uses JAR Manifests that are automatically processed from the building system.

The work on making the default theme is far more complicated as I described above...

Remember that our Default Theme we've written above has already been installed together with Firefox (or any other Mozilla Application).
It means all necessary skin providers for the native packages (and, as we have seen above, extensions bring also their skin providers), are already registered.

The third party theme will fully substitute skin providers for the skinnable packages and/or will overwrite rules defined from the already registered skin providers.

Install Manifest

Our third party theme has to be installed first.
For it, we have to provide a file with some information about the theme:
the Install Manifest.
This file must be named install.rdf and have to live at the root from our theme.
Let's have a look at this manifest:

type - an integer value indicating the type of Addon. For themes is 4.

targetApplication - an object indicating in which application our Theme will be installed. This object is from type <Description> and contains information about the application.

name - the name from our Theme. A string. Is the name which appears on the UI.

internalName - This string will appear on the Preferences.

Important

Make sure you are on the safe side and don't use for internalName spaces in the string. Also avoid uppercase.
For example, don't use something like My Theme, but mytheme, or my_theme instead.
For the name property you can use whatever you want.

OK. Let's see an example how an Install Manifest could look like. This Manifest will install a Theme "Our Theme" on Firefox:

Important

Everywhere we use the string ourtheme, please change it to the name of your theme!!!!

"Light-weight" Themes

If we want to replace some toolbar buttons, change some backgrounds, some font colors for the main-window (browser.xul), this approach is the right one for us.
It is actually perfect for beginners.
It will simply overwrite rules defined from the Default Theme, without having to register other skin providers for the skinnable packages.

This approach is very similar to the userChrome.css way of customization. But with an important difference:
The userChrome.css rules will always apply. Also when the user changes the theme.
With this approach our rules applies only if our theme is the one we are using.

So, let's see another Manifest Instruction, the style instruction:Thanks to Mook for pointing this out

fig.29

This instruction will register an overlay, a custom CSS, which will be applied to a chrome window, in our case the main-window. The chrome://browser/content/browser.xul.

We have to indicate a file which will overlay the default set of rules applied to browser.xul.
But note that only stylesheets at chrome URIs can be applied in this way.

So, to make this file available at a chrome perspective we have to first register a package containing only a skin provider. Let's call this new package customskin.
The skin instruction could look like:

fig.30

Note

The skinname we have to use is the same we've declared on our install.rdf as internalName.

Very Important

Notice that once we have registered a chrome package, it belongs now to the Chrome, and the file customskin.css is available to the whole application.

Since a style overlay will also exist for the whole application once we've called it at our chrome.manifest, it's necessary to first registry an empty file customskin.css for the default theme.
After this we can define our own skin provider, which will substitute the first one.

In this way we are sure that our customskin.css will not apply when the Default Theme (or another theme) is in use instead of our theme.

This means that if the user has selected our theme as used theme, our customskin.css will take effect. If the default theme or some other theme is the chosen one, the empty file (at chrome/classic/customskin) will take effect.
Another theme using this same approach will apply its rules as soon as the user choose it... So, it's very convenient for all themes following this approach to maintain this name.

Now, let's organize the file structure. We need a folder named classic/customskin with an empty file - customskin.css - inside it, for other themes except our theme.
And we need another folder named
customskin and a file customskin.css inside it, to accomodate our rules. Something like this:

fig.31

At a chrome perspective (fig.07), after registering the customskin package and the respectively skin providers, the chrome will look like:

"Heavy-weight" Themes

So, but maybe we need more control over our theme, because we have really different UI proposition as the Default Theme.
We could considerate to substitute all or some skin providers for the skinnable packages.

Packaging the Theme

Now we have to package our theme for distribute it.

The best way to do this is using the .jar in .jar approach:
We will firstly package our chrome folder and subsequently package it and the remaining files, so that we will have at the end a .jar inside another .jar.

fig.36

Because we have now our files inside a .jar file, we have to modify our chrome.manifest, to point to the files inside the .jar file.
We have already seen the JAR Protocol at fig.09.