Team LiB
Previous Section Next Section

Hack 86. Create a Chrome Package

Packages are the fundamental concept underlying most chrome content. Here's how to make one.

Pieces of software installed on top of Firefox are called extensions, themes, locales, or add-ons. The name extension and those other names are all product bundle names. Extension product bundles, for example, are handled by the Extension Manager. Inside these product bundles is the lower-level nuts-and-bolts concept of a package. This hack covers the different package representations and the extra steps that are needed for packages to be formally recognized by Firefox in the chrome registry.

An extension's packages include the extension's user-interface (UI) content, plus some additional information. This information makes up the extra bits and pieces needed for the extension to work in Firefox. Users see an extension as something that carries out a particular task for them. This encompasses the UI and the code to implement any functionality. A package includes these files, but it also serves as the name of the extension as it is used in file paths and in RDF files. Packages underlie all themes, locales, and extensions for Firefox.

Here are the steps needed to bring a package into existence:

  1. Create your files locally on disk.

  2. Choose to leave them in a flat file structure or put them in a JAR.

  3. Create the contents.rdf files necessary for Firefox chrome registration.

  4. Enable special chrome:// URLs for your package.

After these steps, the package is ready for further bundling into an XPI file [Hack #88] and final distribution.

8.4.1. Create a Local Folder Hierarchy

Before you begin to write your extension, the first thing you should decide on is the folder structure on disk for development. It should closely mirror the structure of the JAR file or packaged folders that you will create. Let's have a look at an example.

Examples in this chapter are drawn from a real extension out in the wild called chromEdit. This is a simple yet very useful program for customizing the appearance and preferences of Firefox. As its name suggests, it allows you to edit files in Mozilla chrome, but not the ones talked about in this chapter. You can change userContent.css and userChrome.css for a personalized look and use user.js to change preferences. [Hack #23] covers these files in more detail. You can download chromEdit from http://cdn.mozdev.org/chromedit, and it is also available from http://update.mozilla.org.


On disk, a typical structure looks like this:

chromedit
  content
    chromeedit.xul
  locale
    en-US
      chromedit.properties
    fr-FR
      chromedit.properties
  skin
    classic
      chromedit.css
    groovy
      chromedit.css

Each directory requires different kinds of files. As a general rule, content contains XUL and JavaScript files, locale contains DTD and string bundles, and skin contains CSS files and images.

8.4.2. Create a Package Representation

The local folder hierarchy must be converted into a formal package. You have a choice between a flat file structure or a JAR file. A package must use one or the other format. Firefox's standard install provides packages in JAR format.

8.4.2.1 Flat file structure

If you choose this option, it means that files arranged on disk stay as is and are not compressed or reorganized. This is suitable for small packages. There is no set rule regarding how many files you need to have to prefer this approach. It is entirely up to you. However, a good rule of thumb is that if there are more than four or five files, they should be compressed in a JAR for space-saving purposes. The choice of the flat structure means that the files stay in the same folders as they are on local disk. That makes life easier if you are debugging a package. Here's that standard directory layout for the chromedit package:

chromedit
  content
  locale
    en-US
    fr-FR
    ...
  skin
    classic
    groovy
    ...

There's just one top-level directory, named after the package. This listing is the same as the previous listing.

8.4.2.2 JAR representation

At the most abstract level, the JAR representation of a package is a simple JAR filename, the same name as the extension's package name. In the case of this sample JAR file, the filename is chromedit.jar. The internal hierarchy in the JAR file looks like this:

content
  chromedit
locale
  en-US
    chromedit
  fr-FR
    chromedit
  ...
skin
  classic
    chromedit
  groovy
    chromedit
  ...

In this case, there are three top-level directories: content/, locale/, and skin/. The files are compressed in ZIP format, leaving a smaller footprint on disk and making for a smaller download. Contained within the JAR file are the subdirectories, with their hierarchy intact. In this case, though, the package name dangles underneath the other details. A JAR file can contain more than one package.

8.4.2.3 Default URL names for package files

At this point, the files, whether flat or in a JAR file, are each accessible via a resource:// URL if they are placed in the Firefox install area. However, once registered, the package gains full security privileges and file access is achieved via chrome:// URLs, as we're about to see.

8.4.3. Register a Package with the Chrome Registry

Having the right folders and files gets you only halfway to where you want to go. Once the package is bundled and installed [Hack #88], it will just sit in its destination directory anonymously unless it provides some information needed by Firefox to recognize it internally. This process is known as chrome registration and is enabled by RDF files known as manifests.

8.4.3.1 Make a contents.rdf manifest file

The nature of the term manifest is not something be dwelled upon; what is more important is the form that manifest files take and how they work. The form they take in chrome registration is one or more contents.rdf files placed in the folder hierarchy. They tell Firefox that, if it finds a contents.rdf file in a particular folder, this folder will be one of the core content, skin, or locale folders that matches up with special chrome:// URLs to access files internally.

Here is a standard boilerplate contents.rdf file to be placed in the content/ folder of an extension or package; all contents.rdf files look almost identical to this one:

<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
  <RDF:Seq about="urn:mozilla:package:root">
    <RDF:li resource="urn:mozilla:package:chromedit" />
  </RDF:Seq>
  <!-- Extension information -->
  <RDF:Description about="urn:mozilla:package:chromedit"
                   chrome:description="A Simple User Profile File Editor"
                   chrome:name="chromedit">
  </RDF:Description>
</RDF:RDF>

The bolded information is the only part that varies between packages (in the simple case). This RDF file has standard XML syntax and uses two namespaces: RDF and chrome. The most important items are the type of the manifest and the description. Lines 4 through 6 state the name of the package, which is also the folder name of the package. This information is added to the package root, which holds the full list of packages. That parent resource is called urn:mozilla:package:root.

Lines 7 through 11 tell us a little bit about the extensionhere, the name and a description. These attributes are largely redundant, due to the frontend metadata contained in another file, install.rdf [Hack #88] . However, for completeness, Table 8-1 provides a complete list of attributes.

Table 8-1. Chrome package attributes

Attribute

Description

Author

Name of person or organization that created the extension

AuthorURL

Web site of the extension

Description

A one-liner about the extension

DisplayName

Full pretty name of the extension

Extension

Boolean value to enable/disable the package as an extension, a legacy of the days before the Extension Manager

Name

The internal name of the package


When Firefox starts up for the first time after you install the extension, it will read this information in and make the necessary chrome registry changes. It knows where to find these files because it is told where to look in install.rdf [Hack #88] .

8.4.3.2 Use the new chrome: URLs

Recall that a chrome: URL is a special internal path that can be used to access files registered in the chrome. A typical chrome: URL looks like this:

chrome://chromedit/content/filename.js

This particular URL can be used in a XUL file, for example, to pull in the JavaScript file. The extension name registered for the chrome URL is chromedit and is extracted from the package contents.rdf. In the sample contents.rdf manifest, it is included in what is known as the root-sequence section:

<RDF:Seq about="urn:mozilla:package:root">
  <RDF:li resource="urn:mozilla:package:chromedit" />
</RDF:Seq>

It is possible to distribute and register more than one extension. All you have to do is add another resource list item in the root sequence.


The URL chrome://chromedit/content/ resolves to the directory where the contents.rdf file was found. So, typically, this is where you would place all your application XUL and JavaScript files. Subdirectories are fine to use, but they have to be included in the URL. So, if you have a folder called overlays under the folder where the contents.rdf lives, the URL would look like this:

chrome://chromedit/content/overlays/filename.xul

For completeness, here are the contents.rdf files for skin/ and locale/ of the extension, necessary to register chrome URLs for these portions:

chrome://chromedit/skin/
chrome://chromedit/locale/

When filenames are omitted from the URL, such as in the previous examples, the URL resolves to the package name and the default extension for each component. So, chrome://chromedit/content/ looks for chromedit.xul in that folder. The default extension for skin/ is .css, and .dtd is the default for locale/.

This example shows how the skin part of a chrome: URL is registered:

<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
  <RDF:Seq about="urn:mozilla:skin:root">
    <RDF:li resource="urn:mozilla:skin:classic/1.0" />
  </RDF:Seq>
  <RDF:Description about="urn:mozilla:skin:classic/1.0">
    <chrome:packages>
      <RDF:Seq about="urn:mozilla:skin:classic/1.0:packages">
        <RDF:li resource="urn:mozilla:skin:classic/1.0:chromedit"/>
      </RDF:Seq>
    </chrome:packages>
  </RDF:Description>
</RDF:RDF>

This example shows how the locale part of a chrome: URL is registered:

<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:chrome="http://www.mozilla.org/rdf/chrome#">
  <RDF:Seq about="urn:mozilla:locale:root">
    <RDF:li resource="urn:mozilla:locale:en-US"/>
  </RDF:Seq>
  <RDF:Description about="urn:mozilla:locale:en-US"
                   chrome:displayName="English(US)"
                   chrome:name="en-US">
    <chrome:packages>
      <RDF:Seq about="urn:mozilla:locale:en-US:packages">
        <RDF:li resource="urn:mozilla:locale:en-US:chromedit"/>
      </RDF:Seq>
    </chrome:packages>
  </RDF:Description>
</RDF:RDF>

Brian King

    Team LiB
    Previous Section Next Section