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 FileOnce 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.rdfThe 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:).
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 ReleaseSo, 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 UpdateAt 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 publishingThere 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] .
<user profile dir>/extensions/<UUID of extension>/
8.6.4. Distribute Software UpdatesThis 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>
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 |