Learn XPI Installer Scripting by Example

Parts of this page show the use of the XPInstall API. The majority of this API is now deprecated and as of Gecko 1.9 no longer available. Extension, Theme, and plug-in developers must switch away from install.js based packages to the new packaging scheme with an install.rdf manifest. In particular plugin developers should see how to package a plugin as an extension.

This article uses the installer script from browser.xpi install package as the basis for discussing XPI installations in general. This installer script is relatively short, but it exercises most of the important features of the XPInstall API, and it can easily be used as a template for other more general software installations. In this article, we use the unix install file, but the installers for all the platforms are quite similar, and you can easily take what you learn here and apply it to installations on any platform that mozilla supports.

About browser.xpi

browser.xpi is the XPI archive in which the main components of the Mozilla browser are archived for installation. Mozilla cross-platform installations use the XPI format as a way to organize, compress, and automate software installations and software updates. A XPI is a PKZIP-compressed archive (like ZIP and JAR files) with a special script at the highest level that manages the installation. That installer script , usually named install.js, is the subject of this article.

First, a quick scan of the contents of the XPI file (which you can open using with any unzip utility) reveals the following high-level directory structure:

install.js
bin\
  chrome\
  components
  defaults\
  icons\
  plugins\
  res\

Note that this high-level structure parallels the directory structure of the installed browser very closely:

mozilla directory on linux

As you will see in the installation script, the contents of the archive are installed onto the file system in much the same way that they are stored in the archive itself, though it's possible to rearrange things arbitrarily upon installation--to create new directories, to install files in system folders and other areas.

Overview of the Install Script

XPI install scripts are written in JavaScript using XPInstall Engine syntax defined in the XPInstall API Reference.

Most installation scripts, including the one discussed here, take the following basic form (in pseudo-code and with links to the sections in which these installation steps are documented):

initInstall();
if (verify_space()) {
   err = add_dirs_and_files;
   register_files;

   if (err==SUCCESS) { performInstall() };
   else { cancelInstall() };
}

As you can see in the code listing, the verification process at the top is on lines 1 to 18; the file addition process, here part of the main installation block, is on lines 24 to 41; the registration part of the main installation block is on lines 42-58; and the execution at the end of the main block is on lines 59 to 71. If you choose not to register the installed software or do the verifications at the front end of the installation, then at a minimum, the install scripts mustinitialize, add the files to be installed, and execute.

Note also that when you call methods on the Install--as you do so often in installation scripts (getFolder, initInstall, addFile, and performInstall are all examples of common Install object methods)--the Install object is implicit; like the window object in regular web page scripts, the Install object does not need to be prefixed to the method.

Initializing the Installation

All installations must begin with initInstall(). The initInstall() method on the Install object creates a new installation for the specified software and version. In the browser.xpi installation, this function appears at line 20: var err = initInstall("Netscape Seamonkey", "Browser", "6.0.0.2000110807");

If you call a method on the Install object before initInstall(), you will get an error.

The initInstall method takes the following parameters: the display name of the package, the name of the package as it appears in the client registry, and the version, which can be expressed as a number or as an InstallVersion object. In the example above, "Netscape Seamonkey" is the display name, "Browser" is the registry name, and the version is "6.0.0.2000110807." See initInstall in the XPInstall API Reference for more information on the initialization process.

Verifying the Target

The first thing the installation script does when it's executed is to check that there is adequate disk space for the software to be installed, where the verifyDiskSpace function is called as a condition of the main installation block that starts at line 24).

 // this function verifies disk space in kilobytes
 function verifyDiskSpace(dirPath, spaceRequired)
 {
   var spaceAvailable;
   // Get the available disk space on the given path
   spaceAvailable = fileGetDiskSpaceAvailable(dirPath);

   // Convert the available disk space into kilobytes
   spaceAvailable = parseInt(spaceAvailable / 1024);
   // do the verification
   if(spaceAvailable < spaceRequired)
   {
      logComment("Insufficient disk space: " + dirPath);
      logComment("  required : " + spaceRequired + " K");
      logComment("  available: " + spaceAvailable + " K");
      return(false);
   }
   return(true);
 }

In the verifyDiskSpace block, fileGetDiskSpaceAvailable is called with dirPath as its expected input. This input is defined in line 22, where getFolder() is used to assign a value to the communicatorFolder variable representing the "Program" folder on the local system:

var communicatorFolder = getFolder("Program");
spaceAvailable = fileGetDiskSpaceAvailable(dirPath);

spaceRequired, the other expected input to the verifyDiskSpace function, is given as 17311 kilobytes on line 19. Inside the function, the two sizes are compared and if the available space is larger than the required space, the installation proceeds.

Adding Files and Directories (Full of Files) to the Install

Once you have verified that the target can accomodate the software to be installed and initialized the actual installation, you must add files and directories to the installation in order to have them installed. In the browser.xpi install script, the files are added in lines 26-41:

  err = addDirectory("Program",
                     "6.0.0.2000110807",
                     "bin",              // jar source folder
                     communicatorFolder, // target folder
                     "",                 // target subdir
                     true );             // force flag

  logComment("addDirectory() returned: " + err);

  // create the plugins folder next to mozilla
  var pluginsFolder = getFolder("Plugins");
  if (!fileExists(pluginsFolder))
  {
      var ignoreErr = dirCreate(pluginsFolder);
      logComment("dirCreate() returned: " + ignoreErr);
  }
  else
      logComment("Plugins folder already exists");

In this case, the files are contained within a single directory, so calling the Install object's addDirectory method is sufficient to queue all the files in the archive for installation. The addDirectory, like addFile, handles both the source file location and the target location. In the example above, all of the contents of the "bin" directory in the archive are queued for installation, and the target of that installation (when the installation is actually begun with a call to performInstall at the end), is the communicatorFolder directory defined at line 22 as "Program."

"Program" is one of a short list of keywords that can be used in place of full path names in methods such as addFile. "Program" represents the directory where software itself is installed (e.g., C:\Program Files\ on win32 systems), as opposed to supporting directories like "Components", "Chrome", or "Temporary" (see getFolder in the XPInstall API Reference for a list of keywords).

Registering the Software

Registering software is sometimes a requirement of both the operating system and of the Netscape 6 platform. When you install new chrome, for example, like the browser.xpi install does, you need to alert the chrome registry to these changes, so that skins, user preferences, packaging lists, and localization bundles will all track the new software.

For registering software with the win32 operating system, the XPInstall API provides two special Install objects, WinProfile and WinReg. These two objects provide programmatic access to the Windows user profile and the Windows registry, respectively. The browser.xpi install script does not demonstrate the use of these objects, but see the XPInstall API Reference for more information about registering software with the win32 operating systems and other operating systems.

To register new Netscape 6-based software (e.g., plug-ins, new components, new themes, new packages) with the chrome registry, you must use the registerChrome function of the Install object. If successful, this function returns a "0" and makes entries into theinstalled-chrome.txt file in the chrome subdirectory, which is then used to regenerate the various RDF files that make up the chrome registry.

 var cf = getFolder("Chrome");
 registerChrome(CONTENT | DELAYED_CHROME, getFolder(cf,"toolkit.xpi"),"content/global/");
 registerChrome(CONTENT | DELAYED_CHROME, getFolder(cf,"browser.xpi"),"content/communicator/");
 registerChrome(CONTENT | DELAYED_CHROME, getFolder(cf,"browser.xpi"),"content/editor/");
 registerChrome(CONTENT | DELAYED_CHROME, getFolder(cf,"browser.xpi"),"content/navigator/");
 registerChrome(SKIN | DELAYED_CHROME, getFolder(cf,"modern.jar"),"skin/modern/communicator/");
 registerChrome(SKIN | DELAYED_CHROME, getFolder(cf,"modern.jar"),"skin/modern/editor/");
 ...

In lines 42-58, registerChrome is called as many times as there are different directories that contain content that needs to be registered with the chrome registry. Note that in the first few lines of this process, new content from the XPI is being registered, and in the remainder, it is new skin information. In this most common form of the registerChrome function (it can also be used to support the now-deprecatedmanifest.rdf style of installation archive), the three parameters represent, in order, the chrome SWITCH used to indicate what kind of software is being registered, the target destination of the software (e.g., the "Chrome" folder in the example above), and the path within the XPI (or JAR theme archive) where the contents.rdf file is located.

See registerChrome in the XPInstall API Reference for more information about registering new packages with the chrome registry.

Executing the Installation

Once you have added all the files to the installation, the final step is to actually execute the installation. Note that until this point, the install calls you have been making on the Install object are preliminary only. Recall that an install process takes the following general form:

initInstall();
if (verify_space()) {
   err = add_dirs_and_files;
   register_files;

   if (err==SUCCESS) { performInstall() };
   else { cancelInstall() };
}

In this arrangement, the actual execution of the installation is checked against the errors returned from the addition of files to the installation, which may itself have been conditioned on some verification of version and necessary disk space.

The actual install code used to execute the installation appears in theinstall.js file as follows:

if (err==SUCCESS)
  {
     err = performInstall();
        logComment("performInstall() returned: " + err);
  }

  else
  {
     cancelInstall(err);
	 logComment("cancelInstall() due to error: " + err);
  }
}
else
   cancelInstall(INSUFFICIENT_DISK_SPACE);

performInstall is the function used to execute the install once it has been initialized and loaded, and it is the last step to installing the software. Note that in the example above, the installation is cancelled if the error code from the file addition process returns success (0), and also cancelled outside the main block if the earlier verification process is not successful.

Note also the comments that indicate the success of various steps in the process--including the performInstall and cancelInstall steps--are written to the install log using the logComment, described in the following section.

Installation Logging

Logging is an important feature of the XPInstall API that can help you streamline and debug your installations. In the example in the Executing the Installation section and in many places in the installation script, the logComment API is used to write data to a log file that can then be reviewed when things don't go as planned.

The install log is created in the product directory by default (where the browser executable is). If the installation doesn't have proper permission, the install log is written to the user's profile directory. Respectively, these directories correspond to the "Program" and "Current User" keywords for the getFolder method.

Extending the Example

What does all this mean to you? How can this information be adapted for your own installations? In this final section, we describe the application of the XPInstall technology described here in the creation and deployment of a very simple installation script and installation archive (XPI).

Say you have a simple executable and a README file that goes with it, and you want to make it available for installation from a XPI. After putting these two files in a XPI (which, as described above, is simply a ZIP file with an install.js script at the top and a suffix of '.xpi'), the next step is to add an installation script to the XPI.

Minimally, the installation script must:

  • Call initInstall with the name and version of the executable (the version is not optional, though you may or may not use the version in subsequent installations or updates)
  • Find somewhere to put the installed files. In the example below, getFolder is used with the "Program" keyword to specify the browser's program directory as the target for installation. Since I am using NS6 right now on a Windows machine, that target directory is "C:\Program Files\Netscape\Netscape 6\".
  • Add files to the initialized installation using addFile.
  • Call performInstall to execute the installation.

Here is an example of small but complete installation script.

var xpiSrc = "cd_ripper.exe";
var xpiDoc = "README_cdrip";

initInstall("My CD Ripper", "cdrip", "1.0.1.7");
f = getFolder("Program");
setPackageFolder(f);
addFile(xpiSrc);
addFile(xpiDoc);

if (0 == getLastError())
	performInstall();		
else
	cancelInstall();

The example above shows this minimal installation. This install script does not include any version or disk space checking, very little error checking, only a single executable, no registration, and no commenting. It does, however, take the executable and the README file and install them on the user's system. Note also that for the installation script in a XPI to be automatically triggered from a web page, you must use a "trigger script." The following InstallTrigger function, called from an event handler on a regular web page, will point to the remotely-hosted XPI (called here 'cdrip.xpi') and trigger its installation:

function putIt()
{
  xpi={'CD_Ripper':'cdrip.xpi'};
  InstallTrigger.install(xpi);
}
...

<a href="#" onclick="putIt();">install the CD Ripper Now!</a>

See the InstallTrigger object in the XPInstall API Reference for more information on triggering installations.

Original Document Information