Packaging simply means organizing your files into a Mozilla application structure. Packaging your application is required to make it installable and to make it something that Mozilla recognizes as one of its own. Whether your Mozilla-based becomes a part of an existing Mozilla application, like Mouse Gestures, or will exist as a standalone application, like JabberZilla, you will need to package it.
When you are done with this packaging section, package your Mozilla-based applications in the same way that we packaged the xFly example in Chapter 2. This chapter describes the manifests and other necessary files. Then the Installation section shows how you can put your package in a XPI file and create installation script(s) so it can be distributed and installed.
All new packages must have manifests describing their contents, skin information, and locale information. These manifests are formatted in RDF, which makes them easy to combine with the RDF data that makes up the chrome registry and makes it easy to fit the package into the Mozilla software. There is some flexibility about where in the package the manifest must appear, but the registration process must find and read it regardless of where it is.
The installation script points out the manifest locations so the package can be registered properly. Note that manifests appear in JARs, but they do not appear in XPIs, since the latter is a temporary file that gets deleted once the files it contains, including JARs, are installed (see the Section 6.3 section later in this chapter for more information about XPI install files).
Example 6-2 shows a manifest for a new theme to be installed in Mozilla. It is simple because it describes only one type of package, the "fly skin," and the existing component it interacts with, the communicator -- the default Mozilla browser (the syntax and structure is the same for all manifests, however). The manifest says, in effect, this is what I have here (the metadata about the theme -- its name, a description, etc.), and this is what it affects (the list of chrome:packages to which the theme should be applied).
Example 6-2. Simple theme package manifest
<?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#"> <!-- List all the skins being supplied by this theme --> <RDF:Seq about="urn:mozilla:skin:root"> <RDF:li resource="urn:mozilla:skin:flyskin/1.0" /> </RDF:Seq> <!-- Fly Skin Information --> <RDF:Description about="urn:mozilla:skin:flyskin/1.0" chrome:displayName="Fly Skin" chrome:author="frillies" chrome:description="shimmering, purple/black, hairy"> <chrome:packages> <RDF:Seq about="urn:mozilla:skin:classic/1.0:packages"> <RDF:li resource="urn:mozilla:skin:classic/1.0:communicator"/> </RDF:Seq> </chrome:packages> </RDF:Description> </RDF:RDF>
When you look at a package manifest that describes a new locale, as shown in Example 6-3 (which is for a German language pack in Mozilla), you see a similar structure. Again, the manifest describes the new package first and then lists the existing components to which this new package applies.
Example 6-3. Locale package manifest
<?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#"> <!-- list all the skins being supplied by this package --> <RDF:Seq about="urn:mozilla:locale:root"> <RDF:li resource="urn:mozilla:locale:en-DE"/> </RDF:Seq> <!-- locale information --> <RDF:Description about="urn:mozilla:locale:en-DE" chrome:displayName="English (German)" chrome:author="mozilla.org" chrome:name="en-DE" chrome:previewURL="http://www.mozilla.org/locales/en-DE.gif"> <chrome:packages> <RDF:Seq about="urn:mozilla:locale:en-DE:packages"> <RDF:li resource="urn:mozilla:locale:en-DE:communicator"/> <RDF:li resource="urn:mozilla:locale:en-DE:editor"/> <RDF:li resource="urn:mozilla:locale:en-DE:global"/> <RDF:li resource="urn:mozilla:locale:en-DE:messenger"/> <RDF:li resource="urn:mozilla:locale:en-DE:navigator"/> </RDF:Seq> </chrome:packages> </RDF:Description> </RDF:RDF>
Note that in Example 6-3's package manifest, all major components are affected by this new locale package. When the package is installed and the manifest is read, the chrome registry is made aware of a German language pack that it can use to display German in the interface of each Mozilla component.
The package manifests for content and new applications -- which may include new content, skin, and locale information -- have an identical syntax and a very similar structure, as you will see in the following sections. The manifest for a full Mozilla-based application like xFly describes the content, the skin, and the locale in a single file that sits at the top of that package.
When you create new applications on top of Mozilla, you will often create new content, new skins, and your own localizable language elements, such as DTDs. For applications, the manifest must describe these parts of your application if Mozilla is to find and register them properly.
Example 6-4, the package manifest from the XMLTerm Mozilla extension, describes the contents, skin, and locale in a single file, which is most common for Mozilla-based applications.
Example 6-4. manifest.rdf describing the XMLTerm extension
<?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#"> <!-- list all the packages being supplied by this jar --> <RDF:Seq about="urn:mozilla:package:root"> <RDF:li resource="urn:mozilla:package:xmlterm"/> </RDF:Seq> <RDF:Seq about="urn:mozilla:skin:root"> <RDF:li resource="urn:mozilla:skin:modern/1.0" /> </RDF:Seq> <RDF:Seq about="urn:mozilla:locale:root"> <RDF:li resource="urn:mozilla:locale:en-US"/> </RDF:Seq> <!-- xmlterm package information --> <RDF:Description about="urn:mozilla:package:xmlterm" chrome:displayName="XMLterm" chrome:author="xmlterm.org" chrome:name="xmlterm"> </RDF:Description> <!-- xmlterm overlay information --> <RDF:Seq about="urn:mozilla:overlays"> <RDF:li resource="chrome://communicator/content/tasksOverlay.xul"/> </RDF:Seq> <RDF:Seq about="chrome://communicator/content/tasksOverlay.xul"> <RDF:li>chrome://xmlterm/content/xmltermOverlay.xul</RDF:li> </RDF:Seq> <!-- locale information --> <RDF:Description about="urn:mozilla:locale:en-US" chrome:displayName="English(US)" chrome:author="mozilla.org" chrome:name="en-US" chrome:previewURL="http://www.mozilla.org/locales/en-US.gif"> <chrome:packages> <RDF:Seq about="urn:mozilla:locale:en-US:packages"> <RDF:li resource="urn:mozilla:locale:en-US:xmlterm"/> </RDF:Seq> </chrome:packages> </RDF:Description> <!-- xmlterm skin information --> <RDF:Description about="urn:mozilla:skin:modern/1.0" chrome:displayName="Modern" chrome:author="mozilla.org" chrome:name="modern/1.0"> <chrome:packages> <RDF:Seq about="urn:mozilla:skin:modern/1.0:packages"> <RDF:li resource="urn:mozilla:skin:modern/1.0:xmlterm"/> </RDF:Seq> </chrome:packages> </RDF:Description> </RDF:RDF>
The structure in Example 6-4 is exactly the same as that in more focused manifests (Example 6-2 and Example 6-3), but all of the skin, content, and locale structures sit together in a single manifest.rdf file. This manifest follows the Mozilla convention of introducing the package contents at the top and then expanding upon the basic listing of each separate sections, providing the necessary metadata about the items in the middle, and then listing the components that are affected by the items at the end. However, the flexibility of the RDF format means you could just as easily order this information differently -- which is why RDF is sometimes described as creating a "soup" of statements about resources.
Note that the overlay section in the middle of the example is part of the content description. It tells the chrome registry that the contents of the file xmltermOverlay.xul should be overlaid into the tasksOverlay.xul file in Mozilla, in which much of the Tools menu is defined. The package manifest for the xFly sample application that we discuss here, also a single file, is very similar to the manifest in Example 6-4.
Typically, registration occurs during installation, which is why the Section 6.3 section of this chapter goes into more detail about the specific methods and objects available for package registration. The registration process deals with packages and package manifests, however, so the following two sections describe the two types of package registration that are possible in Mozilla. The first provides an overview of how to register a package on installation, as is typically done, and the second describes how to use a special file to register your work with Mozilla as you develop it so that you can view your work as it progresses.
Generally, the registration process is a transaction that takes place between your installation scripts, the chrome registry, and the manifests that describe the package. Usually, registration happens upon installation. You can approach this transaction in many ways, but the general relationship is shown in Figure 6-2.
In this relationship, the install script is responsible for managing the transfer of files to a specified location on the local disk and alerting the chrome registry to the new files and their manifests. The chrome registry then finds and reads those manifests. If the information there is formatted correctly, then that information is added to the sum of package information that the chrome registry manages -- a single, overarching datasource of all packages in Mozilla, including skins, locales, overlays, and other software. In this way, the package is added to Mozilla. The major players in this interaction between the packages, the package descriptions, and the chrome registry are shown in the following list.
Manifests in the archives themselves
XPInstall, the technology that performs the downloading and resource installation
The chrome registry, the database of packages, and user information that is read and written to when new software is installed
The file installed-chrome.txt is a convenience for developers who want to create and test new packages without having to install them with installation scripts and manifests. Some earlier xFly examples in this book already used this method. The installed-chrome.txt file is a list of entries that point to package manifests. Each line provides the chrome registry with a pointer to a manifest in which new software is described: new skin information, new packages, and new locales.
In the following snippet from the installed-chrome.txt file in the Mozilla chrome directory, five entries point to contents.rdf type manifests that describe the modern skin resources particular to the application's major components. The first line in this list, for example, tells the chrome registry to find a contents.rdf file in the subdirectory skin/modern/communicator contained in the modern.jar file, which describes the resources present there to skin the communicator component. When the chrome registry reads this line, it uses those resources to skin the communicator component, shown here:
skin,install,url,jar:resource:/chrome/modern.jar!/skin/modern/communicator/ skin,install,url,jar:resource:/chrome/modern.jar!/skin/modern/editor/ skin,install,url,jar:resource:/chrome/modern.jar!/skin/modern/global/ skin,install,url,jar:resource:/chrome/modern.jar!/skin/modern/messenger/ skin,install,url,jar:resource:/chrome/modern.jar!/skin/modern/navigator/
Instead of installing your package with installation scripts, you can add the appropriate entries to this file, as seen in the following examples. Adding these entries only registers local content on your machine. When you use the installed-chrome.txt file, you neither install a new package nor make that package installable by others. Editing the installed-chrome.txt file directly is a shortcut for making Mozilla aware of packages so that you can check your progress as you develop. You probably need to create an installer for packages you want to distribute and install on other systems.
To register a local copy of the xFly application with the Mozilla chrome directory, you would add the following three entries, where the xFly directory and the appropriate subdirectories sit directly under the chrome directory:
content,install,url,resource:/chrome/xfly/content/ skin,install,url,resource:/chrome/xfly/skin/ locale,install,url,resource:/chrome/xfly/locale/en-US/
The first line tells the chrome registry that the content is found in the directory chrome/xfly/content. The next line points to the skin resources at chrome/xfly/skin, and so on. Note that creating a single entry for the xFly skin and locating its resources underneath the xFly application directory (as opposed to a subdirectory in the modern and/or classic JARs) means that the xFly skin will not change when the user changes skins.
If we had the same structure archived in a JAR file called xfly.jar rather than in a directory, the installed-chrome.txt entries would look like this:
content,install,url,jar:resource:/chrome/xfly.jar!/content/ skin,install,url,jar:resource:/chrome/xfly.jar!/skin/ locale,install,url,jar:resource:/chrome/xfly.jar!/locale/en-US/
This code tells the chrome registry to look in the content, skin, and locale/en-US directories in the JAR file to locate the manifests.
This skin entry seems to indicate that only one set of skin information is available for the xFly sample, and that it always applies. If the xFly skin inherits, as many skins do, from one or another of the preinstalled theme (e.g., Modern), it may look very bad or even break when that theme is not selected. See the section Section 184.108.40.206 in Chapter 4 for a discussion of skin inheritance and tips on how to make sure your skin is structured to best take advantage of it.
When you make these additions to the installed-chrome.txt file and restart Mozilla, the chrome registry looks for manifests in the directories you specify and registers the packages described there. The installed-chrome.txt entries in this section do not necessarily need to be included on your final XPI resource, but you will see them in some XPIs bundled in their own installed-chrome.txt file separate form the main one. See the section Section 6.2.4 for more information about this process.
The xFly sample package is a relatively straightforward arrangement of content, skin, and locale. You have already seen how to set up most preliminaries you need to make it a package in Chapter 2, but this section will discuss the process in detail.
To start working immediately with tools like XUL and CSS, you can move some things around and hack one of the chrome registry files, as you have already seen. This section reviews those important preliminary application development steps in more detail.
Because it has its own interface and is not worried (for now) about being made available in languages other than English, the only one for which it has a language pack, the xFly package is self-contained:
chrome xfly content skin locale
All parts of the package can be held in a single JAR file or a single chrome subdirectory. Contrast this with a large component-like communicator that, when you include its content, skin, localized information, and the Mozilla services it uses via XPConnect, is spread out into all of the main JAR files that make up the distribution:
chrome/modern.jar!/skins/modern/communicator/ chrome/comm.jar!/content/communicator/ chrome/en-US.jar!/locale/en-US/communicator/
When you develop your application, it's typical to work within a regular directory. As you finish the application and make it available for distribution and installation, however, you may want to use one of the installation archive formats to package your work, such as a JAR file. See the section Section 6.3.1 later in this chapter for more details.
When your application is large and distributed in this way, the RDF-based manifests "compose" all of the metadata and treat all files as a single component.
The entries you made to the installed-chrome.txt in Chapter 2 tell the chrome registry that a new package must be registered:
content,install,url,resource:/chrome/xfly/content/ skin,install,url,resource:/chrome/xfly/skin/ locale,install,url,resource:/chrome/xfly/locale/en-US/
These entries tell the chrome registry that, in addition to all of the packages that make up the main Mozilla browser (e.g., the communicator skin in the Modern theme, the en-US locale, and the content that makes up the navigator), the xFly content, skin, and locale should be registered as components of a new package. When the chrome registry reads these entries, it tries to find the manifests for these parts of the xFly package. When it finds them, it registers the package, overlays any files it finds in that package (see the next section), and makes it accessible via the special chrome:// URLs given in the manifests.
It is also possible to add an item to the Window menu that includes links to Navigator (the default browser), the Mail and Newsgroup client, the Chatzilla IRC client, Composer, and the Address Book (which only show up if they were added as part of the Mozilla installation process or if they were added separately afterwards).
Here we describe how you can use special constructions in your package's manifest to tell the chrome registry about files from your package that should be overlaid into the main browser. Overlaying these files lets you add interface items, such as this link to xFly, in an existing application. Example 6-5 shows the simple overlay file that puts xFly in the Tools menu.
The top-level element is an <overlay/> rather than a <window/>. Direct children of the overlay are associated with certain elements in the main browser (in this case, the <menupopup/> of the Tools menu) and their contents are interpolated into the list of children there. This file overlays an extra <menuitem/> into the Tools menu. In this case, the menuitem has an oncommand event handler that calls toOpenWindowByType -- a function for opening new chrome defined in the tasksOverlay.js that much of the chrome in the Mozilla UI imports.
Example 6-5. The xFly overlay
<?xml version="1.0"?> <overlay id="xflyTasksOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <menupopup id="taskPopup"> <menuitem label="xFly" oncommand="toOpenWindowByType(`xfly:main', `chrome://xfly/content/');" /> </menupopup> </overlay>
But how does the overlay know where to overlay itself? The IDs of the overlay children and the original XUL are matched to find the target within the files, but the manifest that accompanies the overlay in your package tells Mozilla which overlays are associated with which XUL files. The part of the manifest that deals with an overlay looks like the code in Example 6-6. This code is put in the contents.rdf file in the content directory of the xFly package.
Example 6-6. Overlay information in the manifest
<!-- overlay information --> <RDF:Seq about="urn:mozilla:overlays"> <RDF:li resource="chrome://communicator/content/tasksOverlay.xul" /> </RDF:Seq> <RDF:Seq about="chrome://communicator/content/tasksOverlay.xul"> <RDF:li>chrome://xfly/content/xflyOverlay.xul</RDF:li> </RDF:Seq>
The first RDF element in the manifest is a list of affected target files and the second is a list of files that should be overlayed into those targets. Do not be confused by the names of the files in this case: the tasksOverlay.xul file is a target -- though it is an overlay, as described in the following section -- and xflyOverlay.xul is the overlay with the xFly menu item.
In the previous section, we described how to use the XUL overlay technology to put information from your application into the Mozilla browser. When developers use overlays in this way, they usually add a menuitem or a new UI to the browser that provides access to the application.
But overlays can also add interface elements and other data from Mozilla into the application itself. In fact, each component in Mozilla imports a lot of its own user interface from such XUL overlay mainstays as globalOverlay.xul, tasksOverlay.xul (the file into which the xFly menuitem is overlaid), and navigatorOverlay.xul. As you can see when you look at the main browser file, navigator.xul, shown in Example 6-7, most user interface is actually brought in from these reusable overlays. A relatively small percentage of all that appears in the browser is defined within that particular navigator.xul file.
Example 6-7. Overlays in navigator.xul
<?xul-overlay href="chrome://navigator/content/navigatorOverlay.xul"?> <?xul-overlay href="chrome://navigator/content/navExtraOverlay.xul"?> <?xul-overlay href="chrome://navigator/content/linkToolbarOverlay.xul"?> <?xul-overlay href="chrome://communicator/content/sidebar/sidebarOverlay.xul"?> <?xul-overlay href="chrome://communicator/content/securityOverlay.xul"?> <?xul-overlay href="chrome://communicator/content/communicatorOverlay.xul"?> <?xul-overlay href="chrome://communicator/content/bookmarks/bookmarksOverlay.xul"?>
Of these overlays, those with the most value for application developers are the communicatorOverlay.xul, which defines many of browser menus; the tasksOverlay.xul, which adds the Tools menu and brings in all of its application menuitems as well as a lot of important browser functions like toOpenWindowByType and toNavigator (for returning to the main browser window); and the globalOverlay (which is overlaid into navigatorOverlay.xul, so it gets loaded there), which defines even more general and low-level features like pop ups and functions for quitting the application or setting tooltip text.
Once files are divided into subdirectories and the manifests for each subdirectory, your application is technically a package -- although until you compress it and create an install script, it's a package only for your computer and the environment in which it was created. See the section Section 6.4 later in this chapter to see how you can use the file format and installation information to make the xFly something you can put on a web server and have users install with a single click on a web page.
The chrome registry was briefly mentioned several times in this book. It plays an important (but sometimes invisible) role in the way Mozilla applications, including the Mozilla browser itself, deal with user information, new components, skins, and other resources.
At the beginning of the book, you had to create RDF files that would describe your application to Mozilla. Special entries also needed to be made to the installed-chrome.txt file in the chrome application directory. These entries are just two of the most common ways to address the chrome registry, which is what Mozilla uses to persist configurable aspects of the browser and its other applications.
The chrome registry is not a single file and it's not stored in a single place. Rather, it is a distributed collection of data and interfaces for data manipulation. The data itself generally lives in RDF files, many of which are in the chrome application directory. The chrome registry APIs -- principally nsIChromeRegistry -- are used by installation scripts when they register new software, by the skin-switching UI, and by the language selection facility. The chrome registry is the means through which packages are registered.
An install script is a required part of a software package like the xFly. The two main functions of an installation script are the physical download and installation of files in the package and registration of that software with the chrome registry. Install scripts use functions from the XPInstall API to install the files and functions from the chrome registry interface to handle the latter, as seen in this snippet:
registerChrome(PACKAGE | DELAYED_CHROME, getFolder("Chrome", "help"), "content/");
The registration process is typically something that happens between the install initialization and the actual execution of the install:
initInstall( ) // initialize the overall installation // add items to installed using: // addFolder, addDirectory, getFolder, and others registerChrome(TYPE, dir, subdir) performInstall( );
Scripts and the installation process, including the registration of installed packages, are detailed in the next section.