Team LiB
Previous Section Next Section

Hack 88. Make, Bundle, and Publish an XPI

Create an XPI installer for your extension and publish it for others to discover and use.

If you've stepped through the hacks in this chapter in order, or done any serious XUL experimentation, then you're ready to consider releasing that XML as an extension. You have your content, locale, and skin files in place as a package, written and tested. All that is left is to bundle it up and release it. The best method to bundle it up is to use Mozilla's Cross-Platform Install (XPI) technology. XPI files are compressed files that can be put together using standard zip tools. Firefox can load and install XPI files.

This hack explores the structure of the XPI formatin particular, which files go where in the compressed archive. We'll also dissect the install.rdf install file to ensure that installation is successful. Finally, to ensure your extension is not hidden away, we'll discuss the best ways to distribute it for people to use.

8.6.1. Making a Firefox XPI File

Once created, the XPI file has a rigid structure for Firefox extensions, more so than it does for a Mozilla Application Suite extension. Most extensions just install chrome (i.e., a JAR file or directory with XUL, JavaScript, CSS, and locale files). However, there are optional folders for distributing other files, including XPCOM components [Hack #82], command-line handlers, preference files, and icons. Here's an XPI directory breakdown:

chromedit.xpi
  chrome
    chromedit.jar
  components
  defaults
    ce-main.xpm
    ce-main.ico
    userChrome-example.css
    userContent-example.css
  install.rdf

Each of the folderschrome/, components/, and defaults/is optional. The install.rdf file, however, is compulsory. chromEdit does not have any XPCOM components. To view this example on your computer, save the chromedit.xpi file locally (by right-clicking the download link) from http://cdn.mozdev.org/chromedit/ and unzip it.

To compress the JAR and XPI files, you can use any standard ZIP program, such as WinZip on Windows. This is something you will be doing frequently during the development cycle, especially if you are using a JAR file. Instead of constantly dealing with GUI ZIP programs or manually working with ZIP files, your best bet is to automate the process.

Here is a simple shell script for compressing the JAR file and the XPI file:

#!/bin/sh
echo "building chromedit ...";            # cleanup any old stuff
rm chromedit.xpi;
rm chrome/chromedit.jar;
echo "JAR file ...";                      # make and move the JAR
zip -r0 chromedit.jar content locale skin
mv chromedit.jar chrome;
echo "XPI file ...";                      # make the XPI
zip -r9 chromedit.xpi install.rdf defaults chrome
echo "Extension build finished.";

This script uses the standard Info-ZIP zip program, which can also be found on Windows as part of the Cygwin package. The ZIP flags used here are -r to recurse into subfolders, -0 to store only (no compression), and -9 for maximum compression.

8.6.2. Understanding install.rdf

The core install file for a Firefox extension is called install.rdf. This file lives at the root of the XPI file. During install, if Firefox detects this file, it reads in the metadata to add the extension. Here's a sample install file for the chromEdit extension:

<?xml version="1.0"?>

<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">

  <Description about="urn:mozilla:install-manifest">
    <em:id>{2cf89d59-8610-4053-b207-85c6a128f65d}</em:id>
    <em:version>0.1.1.1</em:version>

    <!-- Firefox --> 
    <em:targetApplication>
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
        <em:minVersion>0.9</em:minVersion>
        <em:maxVersion>1.0+</em:maxVersion>
      </Description>
    </em:targetApplication>

    <!-- Front End MetaData -->
    <em:name>ChromEdit</em:name>
    <em:description>A Simple User Profile File Editor</em:description>
    <em:creator>Chris Neale</em:creator>
    <em:contributor>Andrew Wooldridge</em:contributor>
    <em:contributor>Brian King</em:contributor>
    <!-- more ... -->

    <em:homepageURL>http://cdn.mozdev.org/chromedit/</em:homepageURL>
    <em:updateURL>http://cdn.mozdev.org/chromedit/update.rdf</em:updateURL>

    <em:iconURL>chrome://chromedit/skin/images/ce.x32.png</em:iconURL>

    <em:file>
      <Description about="urn:mozilla:extension:file:chromedit.jar">
        <em:package>content/</em:package>
        <em:skin>skin/classic/</em:skin>
        <em:locale>locale/en-US/</em:locale>
        <em:locale>locale/de-DE/</em:locale>
        <em:locale>locale/it-IT/</em:locale>
        <em:locale>locale/nl-NL/</em:locale>
        <em:locale>locale/pl-PL/</em:locale>
      </Description>
    </em:file>
  </Description>
</RDF>

Every extension needs its own unique ID in Firefox, and the format this takes is a UUID. A UUID is a registration code that can be generated in a number of ways, including on Windows using guidgen.exe, on Unix using uuidgen, or using IRC by connecting to irc.mozilla.org and typing the following commands:

/join #mozilla
/msg mozbot uuid

This ID is used throughout the deployment of the extension and should stay with it for its lifetime.

Let's break up the sample install.rdf file into its most important parts for further explanation. Entries don't have to be in any particular order, but for clarity, the first entries in the install-manifest description should be for the extension. The em:id tag holds the unique UUID, and em:version holds the version of any given release.

The <em:targetApplication> tag contains information about the program you are targeting, such as Mozilla, Thunderbird, or in this case, Firefox. Like extensions, each program has its own UUID. Firefox's unique ID is {ec8030f7-c20a-464f-9b0e-13a3a9e97384}. You specify the versions that your extension works with in minVersion and maxVersion. It's tempting to pick these version numbers out of the air, especially for the older releases for minVersion, but it is wise to test first!

What follows is some more information about your extension. Table 8-2 shows all possible tags. Each needs to be prefixed with the Extension Manager namespace (em:).

Table 8-2. Extension manifest elements

Tag

Description

Name

UI friendly name

Description

One-liner about the extension

Creator

Individual or company's name that wrote the extension

Contributor

Other authors (can be more than one)

HomepageURL

URL of extension homepage

UpdateURL

URL of update file

AboutURL

URL (usually chrome:) to about dialog for the extension

IconURL

URL to image shown in the Extension Manager

File

Pointers to packages, skins, and locales that this extension registers


Finally, the file states the ordinary packages, skins, and locales that this extension registers. Relative JAR file paths are used to point to the locations of the package, skin and locale contents.rdf files.

8.6.3. The First Big Release

So, now you have reached the point where you have the XPI installer file or files, and all that's left is for people to find out about it and download/install it. This section covers the two ways you can do this: either hosting it on Mozilla's update service or hosting it on your own servers with helper scripts to get you going.

8.6.3.1 Using Mozilla Update

At the time of writing, the process of getting your extension listed is not mature and can be frustrating. This is because all requests for listings and updates must be done through Mozilla's bug tracking system (http://bugzilla.mozilla.org). The admin interface, which will eventually be part of the site and easily accessible, is not up and running yet. The plan is that extension authors, once logged in, will have complete control over their distributions: the uploading of new versions, editing of the description, and moderation of user comments. The download counter is a good measure of how popular your extension is.

8.6.3.2 DIY publishing

There is an alternative to using Mozilla Update, and that is to host the XPI on the server of your choice. The most straightforward approach is to just upload the file and link directly to it from an HTML page. However, there are some helper JavaScript install scripts that you can use to assist installation. Here is a very basic one:

function install(aXpi) {
  if (!InstallTrigger || !InstallTrigger.updateEnabled(  ))
    return false; 
  else
    return InstallTrigger.install(aXpi);
}

It can be called like this in the HTML:

<a href="#" onclick="install('http://myhost.com/extension/extension.xpi')">

The install function checks that software installation is enabled (ToolsOptions...Web Features"Allow web sites to install software") and, if so, proceeds with the installation. Note the use of the global InstallTrigger object. You can find more information about it at http://www.xulplanet.com/references/elemref/ref_InstallTrigger.html. Make sure the web server is set up correctly [Hack #27] .

Once installed, the extension will not become active until Firefox is restarted. The extension on local disk is located here:


<user profile dir>/extensions/<UUID of extension>/

Try Right-clicking on the extension when it appears in Extension Manager to see the operations you can perform on it.

8.6.4. Distribute Software Updates

This section covers extension updates. Firefox has a built-in update mechanism [Hack #13] . Earlier in this hack, in Section 8.6.2, you might have noticed this entry in that file:

<em:updateURL>http://cdn.mozdev.org/chromedit/update.rdf</em:updateURL>

This entry tells Firefox where to look for the update file that will contain version information and links to updates for the extension. The following example shows the file for the jsLib extension (http://jslib.mozdev.org/updateLite.rdf):

<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">

  <RDF:Description about="urn:mozilla:extension:{DF8E5247-8E0A-4de6-
B393-0735A39DFD80}">
    <em:updates>
      <RDF:Seq>
        <RDF:li resource="urn:mozilla:extension:
{DF8E5247-8E0A-4de6-B393-0735A39DFD80}:0.1.214"/>
      </RDF:Seq>
    </em:updates>
    <em:version>0.1.214</em:version>

    <em:updateLink>http://download.mozdev.org/jslib/xpi/jslib_current_lite.xpi</em:updateLink>
  </RDF:Description>
     
  <RDF:Description about="urn:mozilla:extension:{DF8E5247-8E0A-4de6-B393-
0735A39DFD80}
:0.1.214">
    <em:version>0.1.214</em:version>
    <em:targetApplication>
      <Description>
    <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
    <em:minVersion>0.9</em:minVersion>
    <em:maxVersion>1.0</em:maxVersion>

    <em:updateLink>http://download.mozdev.org/jslib/xpi/
jslib_current_lite.xpi</em:updateLink>
      </Description>
    </em:targetApplication>
  </RDF:Description>
</RDF:RDF>

Unlike install.rdf, this update manifest can have any name; it's delivered by a web server. update.rdf is the convention for static versions.


Again, the server must be set up with the right types [Hack #27] . This example shows two necessary description blocks. The first one covers the extension itself, telling Firefox that an update is available for an extension of a given ID. The new version number of that extension is also listed, along with the location of the new extension.

The second description block details the target application that the extension applies to. In this case, it is Firefox; so the Firefox em:id is given, plus the minimum and maximum version of Firefox that this update applies to. When Firefox checks for updates, it compares the version already installed with the version in the update file. If a later version is found, the user is given the option to install the newer version.

Brian King

    Team LiB
    Previous Section Next Section