Operator Action Architecture

One of the things I've been trying to finalize for the next release of Operator is the action architecture. Here's what things look like now.

ufJSActions.actions.corkd_search_tags = {
  description: "Find wines on Cork'd",
  icon: "http://corkd.com/favicon.ico",
  scope: {
    semantic: {
      "tag" : "tag"
    url: "http://corkd.com"
   * Perform an action
   * @param semanticObject JavaScript representation of the semantic object
   * @param semanticObjectType Semantic object type as a string
   * @param domNode DOM Node associated with the semantic object
   * @return If you return a value, we attempt to open it as a url
  doAction: function(semanticObject, semanticObjectType, domNode) {
    if (semanticObjectType == "tag") {
      if (semanticObject.tag) {
        return("http://corkd.com/tags/" + encodeURIComponent(semanticObject.tag));

First let's talk about scope. Semantic scope is about indicating which microformats (and in the future other semantic data) this actions works with. On the left is the type of semantic data, on the right is the required value. So for instance, if you had an action that worked on hCalendar and required a dtstart, it would be "hCalendar" : "dtstart". You can use url to specify that an action should only be displayed when you are on a certain web page.

Next, let's talk about the action itself. When you return a value from an action, we assume it is a URL and open it. If you want to do your own thing (like export a vCard), you can do arbitrary JavaScript and just return. I'm also working on a "doActionAll" which will allow on action to act on all microformats of a given type on a web page.

What do people think of this overall design? Is it simple enough to encourage anyone to write actions? Am I missing anything obvious?

Firefox and the Windows Registry

So it ended up that it wasn't that easy to switch our installer to use "Product Version" to determine what version of Firefox was installed, so I was asked to provide information on how to use the Windows Registry to determine the installed version of Firefox. Since that information is useful to everyone, I thought I would post it here.

The following registry path is where Mozilla Firefox info is contained:


(Note that Firefox 1.5 and below used to put information in HKEY_CURRENT_USER as well. Firefox 2 does not.)

Within this registry path, there is a key, CurrentVersion. This key indicates the installed version of Firefox as well as the locale. Don't confuse this with HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\CurrentVersion which is something else entirely.

If you need to locate the installation, there is a key under Mozilla Firefox with the same name as CurrentVersion. That key contains another key called Main that has a value for the install path (Install Directory).

Incidentally, in the screenshot, you might notice that other folder, Mozilla Firefox, which has subfolders called bin and extensions. That's there for posterity. A long time ago, we told people that to find the plugins directory, concatenate the product and version together and then look for the plugins key as a child of that key. If I remember correctly, that's how it was done even in the Netscape Navigator 2/3 days. You can read more detail about those keys on MDC.

Microformat Objects in JavaScript

Unfortunately for everyone out there, when I discover new things in JavaScript, it means I rewrite some parts of Operator, and I am in the middle of that right now. The thing that I discovered is the ability to have custom getters for objects in JavaScript.

One of the things I have wrestled with in the past is that my current model for accessing a microformat in JavaScript is clunky. You have two choices - you can either create the microformat in its entirety:

var hcard = createMicroformat(node, "hCard");

and access the properties like this:


or you can access an individual property like this:

var fn = getMicroformatProperty(node, "hCard", "fn");

The problem with the first approach is that you do a lot of work if you only want part of the microformat, and the problem with the second approach is that there is no caching of the data.

Enter custom getters:

var hcard = new hCard(node);

and hcard.fn is not queried until you access it the first time and it is cached.

What I do now is that when the hCard is instantiated, I set the getter for each property to be a function that queries the property and then sets it (getMicroformatPropertyGenerator):

object.__defineGetter__(property, ufJSParser.getMicroformatPropertyGenerator(node, microformat, property, object));

getMicroformatPropertyGenerator looks like this:

getMicroformatPropertyGenerator: function(node, name, property, microformat)
  return function() {
    var result = ufJSParser.getMicroformatProperty(node, name, property);
    if (result) {
      microformat.__defineGetter__(property, function() {return result});
      return result;

The result allows for much cleaner code in an action, because the actions are now going to receive the microformat data object directly so they don't have to worry about DOM nodes and things like that. All an action has to do is access the individual properties. This will also allow for actions that aren't so dependent on microformats.

I'll still keep the old way around if people want to use the microformat parser in web pages. If anyone knows of a cross browser way to do what I am doing, or even a better way to do it, please let me know.

Firefox Internal Versioning

Updated with an additional column in the table (Size on disk). See comment after the table.

So we're running into a problem with our internal deployment of Firefox that our software installer can't tell which is newer, Firefox or Firefox At the same time, I was asked by someone to create a list of files that could be used as "signature files" for Firefox. (For this purpose, a signature file is a file in a distribution with a unique file size that can be used to identify a particular version of the distribution.)

As a result of this effort, I came up with this lovely table.

  FIREFOX.EXE Values are taken from the
Version page of firefox.exe properties
Firefox Version Size Size on disk File version File Version Product Version
1.0 6621794 6623232 1 1.7.5: 2004110711
1.0.1 6626916 6627328 1.0.1 1.7.6: 2005022518
1.0.2 6627428 6631424 1.0.2 1.7.6: 2005031717
1.0.3 6631012 6631424 1.0.3 1.7.7: 2005041417
1.0.4 6631012 6631424 1.0.4 1.7.8: 2005051112
1.0.5 6636644 6639616 1.0.5 1.7.9: 2005071118
1.0.6 6636644 6639616 1.0.6 1.7.10: 2005071605
1.0.7 6637156 6639616 1.0.7 1.7.12: 2005091517
1.0.8 6639716 6643712 1.0.8 1.7.13: 2006041017
1.5 7162979 7163904 1.8.20051.11116 1.8: 2005111116 1.5 7166053 7168000 1.8.20060.11112 2006011112 7171685 7172096 1.8.20060.30804 2006030804 7172197 7176192 1.8.20060.42618 2006042618 7177325 7180288 1.8.20060.50817 2006050817 7183469 7184384 1.8.20060.6376 2006071912 7183469 7184384 1.8.20060.7278 2006072814 7190637 7192576 1.8.20060.25382 2006090918 7191149 7192576 1.8.20061.2516 2006102516 7200365 7200768 1.8.20061.20612 2006120612 7206509 7208960 1.8.20070.21601 2007021601 7206509 7208960 1.8.20070.31202 2007031202 7209069 7213056 1.8.20070.50813 2007050813
2.0 7604331 7606272 1.8.20061.1023 1.8.1: 2006101023 2.0 7620696 7622656 1.8.20061.20418 2006120418 7633008 7634944 1.8.20070.21917 2007021917 7633008 7634944 1.8.20070.30919 2007030919 7637104 7639040 1.8.20070.51502 2007051502 7644008 7647232 1.8.20070.5781 2007071317 7644520 7647232 1.8.20070.6982 2007072518

NOTE: I've updated this table to include a new field, Size on disk. This field is obtained from the General Properties page for FIREFOX.EXE. I got my original file sizes by looking at the file system from a command line and they correspond to Size, NOT Size on disk. I have no idea why, but I wanted to post both values here for posterity.

So first lets talk about the "signature file" problem. Unfortunately I quickly discovered that there are a few versions between which the file sizes for firefox.exe are EXACTLY the same. In particular:

  • 1.0.3 and 1.0.4
  • 1.0.5 and 1.0.6
  • and
  • and
  • and

I investigated further and found that just looking at disk footprint, there is no way to tell those versions apart. I'm curious to know if other products besides our Tivoli products use file size to determine signature. If this is the case, it might be worth while to have a small signature file in Firefox that simply grows by a byte with each release. This would give us a file for which we could track versioning using file size.

The second problem is much more serious for us and revealed that despite the fixing of bug 286825, there is still a major problem with Firefox file versioning. In particular, take a look at the File version (not Version) of Firefox and Firefox You'll see that Firefox is 1.8.20070.31202 and Firefox is 1.8.20070.30919. So if an application uses File version to determine which is the latest version, it will claim that Firefox is newer than Firefox This is what is causing our software install problem.

Hopefully we can workaround the problem on our end, but at the same time, we need to get the file versioning in Firefox done properly. Otherwise, this is just yet another hurdle for the enterprise deployment of Firefox.

Operator 0.7 is available

Operator 0.7 is on addons.mozilla.org. I have a few things I wanted to share about this release. New features will be at the end of this post.

I'm not sure people realize there are other ways to interact with Operator besides the toolbar. Not only can you have Operator on the status bar or as a toolbar button, but you can right click on a microformat on a web page to access Operator functionality (Sometimes you can guess where they are, but if you want to see exactly where the microformats are, you can turn on Highlight Microformats in Options). If you want to access Operator from the status bar, go to Options and check the box that says Display icon in status bar. If you want to acesss Operator as a toolbar button, select View->Toolbars->Customize... There is an Operator toolbar button that can be dragged into the Firefox user inteface. Once you have done either of these, you can go to View->Toolbars and turn off the Operator Toolbar.

This release introduces a basic user interface for installing user scripts. Note that ability to add user scripts has always been there, I've just never had a good UI. User scripts can be used to add new microformats and new actions to Operator. As a part of the introduction of this UI, I'm moving some of what I call the non tier 1 microformats out of the core Operator and made them available as user scripts. My decision as to which ones to remove was mainly based on which microformats fit best into the Operator model; that is to say it is easy to provide actions that relate to the microformat. It is very important for you to realize that user scripts have FULL JavaScript privileges. Do NOT install Operator user scripts unless you trust the people that are providing them. I am going to make all my scripts available at http://www.kaply.com/weblog/operator-user-scripts/. If you want to write your own actions or microformats, feel free to look at my scripts, but please be aware that this API is very much in flux. I don't a good versioning mechanism right now, so at some point your scripts would cease to work. Note that I continue to support the original Operator action model, but not the original Operator microformats model. The microformats model is actually pretty close to final.

When I first created Operator, my stated goal was take the microformats that are on a web page and provide functionality up and above what was available in the web browser. Unfortunately, when I first started creating actions for the microformats, some of the actions I created were just a case of moving browser interaction into the Operator menus. (Go to web page being the best example). With the advent of a user interface for user scripts, I've taken the opportunity to move some of the function that shouldn't have been there in the first place out of the core. Note that no functionality has been removed; it is all available as user scripts. If you have a very strong opinion on this, one way or the other, please let me know.

All that being said, let me outline the new features in Operator.

  • User interface for managing user scripts
  • Operator updated when DOM nodes are added or removed
  • Export All available for contacts and events
  • 30 Boxes support has been greatly improved
  • Interaction problems with McAfee SiteAdvisor are fixed
  • Major performance enhancements
  • Native getElementsByClassName is used on Firefox 3
  • Operator wasn't working correctly with frames and iframes
  • New tag action for searching for videos on YouTube

In addition to those changes to the core, the following user scripts are now available:

  • Send to Bluetooth (Mac only)
  • hAtom
  • Simple GRDDL RDF export
  • XFN
  • species

Deploying Firefox 2 within the Enterprise: Part 6

I want to cover few more topics to wrap this things up.

The first thing you might be thinking is "man that seems like a lot of work. Isn't there something else I can do?" As I pointed out in the first post, Benjamin Smedberg's Firefox Release Repackager is great if you are a Mac user. If you are not using a Mac, or you want to add more than two extensions, or if you need to add files to the removed-files list we talked about, you can't use the release repackager. Here's how to manually modify a Firefox install.

First, download a Firefox setup executable. Unpack that file into a directory using 7z.

7z x "Firefox Setup"

If you want to add files to the removed files list, do it now. Edit the file removed-files.log and add your files and directories following the syntax in the file. If you want to add extensions, you can follow the steps we outlined earlier, unzipping them in a temporary directory in nonlocalized/extensions and then renaming the temporary directory to the extension ID based on the information in install.rdf

If you are not interested in having a self extracting executable, this is actually enough. If you were to run setup.exe at this point, it would do the install, removing the files and directories you specified, and including the extensions you unzipped. If you want to create a self extracting executable, you would need to manually repackage the EXE. Unfortunately, I don't have those steps in front of me right now. I'll update this post if I find them.

Another problem that might arise is that you don't mind updates coming from Mozilla, but you'd like to determine when they get pushed to your users. For instance, you might want to do some internal testing. You can actually set preferences that prevent specific updates from being offered to your users. For instance, the preference app.update.never. tells Firefox to never offer the upgrade. So you can use the CCK to set these preferences, and then update the CCK when you have verified a particular version.

What if Firefox can't upgrade your users because it is running at the wrong permissions level? If this is the case, you'll obviously need to create your own upgrade solution and turn off the Mozilla upgrades. To turn off the Mozilla upgrade checking, set the preference app.update.enabled to false.

Forgot one - if you want to do a silent/unattended install, invoke the installer (setup.exe or the full installer executable) with -ms.

Well, that's about all I have. I hope you've enjoyed this series of posts. If you have any questions about deploying Firefox in the enterprise, please let me know.

In addition, I'll take this moment to shamelessly plug IBM. If you are an enterprise and need help customizing and/or deploying Firefox, IBM can help. Contact me at mkaply@us.ibm.com

Deploying Firefox 2 within the Enterprise: Part 5

IMPORTANT! Some of the information in this post is not correct. I will be updating it soon.

Updated: See section on serving the update.xml file

OK, now that we have MAR files, how do we get them to our users?

Setting Up a Firefox Update Server

A Firefox update server is actually not that complex of a setup. Basically a Firefox update server takes a URL that describes a particular Firefox version, and if there is an update available, it returns an XML file that identifies the location of the update. So let's take a look at this URL.

If you are to go into about:config and search for app.update.url, you would see the following:


When Firefox looks for an upgrade (English on Windows), the URL looks like this:


Most of this URL is self explanatory. The only part we need to explain is BUILD_ID. The build ID of a Firefox is a unique identifer that conveys the exact date down to the hour when a build was created. For the build above, that data is March 9, 2007 at 7:00 PM. You can see the build ID for any Firefox build by type about: in the URL bar. Note that selecting Help->About does not display the complete build ID. If you build a custom version of Firefox, you can see the build ID by looking in the file mozilla/obj/config/nsBuildID.h. If you see a build ID of 0000000000 in this file, it means that you forgot to set MOZILLA_OFFICIAL and BUILD_OFFICIAL before building.

If we look at the URL that is served, it ends in update.xml. So what do these update.xml files look like? Here's the update.xml to upgrade from Firefox to Firefox (https://aus2.mozilla.org/update/1/Firefox/

<?xml version="1.0"?>
  <update type="minor"
    <patch type="complete"
    <patch type="partial"

Rather then go into a lot of detail here about this file, I'd rather point you to wiki.mozilla.org which has a great explanation of this file. (Incidentally, the wiki you can lots of other great information about the Software Update process) One thing you might be wondering is how to generate the hashValue. Microsoft has a tool available called FCIV that can be used to generate the hashes.

OK, we know the URL and we have the update.xml file, so how do we serve it to the browser?

Update: as Benjamin Smedberg pointed out, I am doing a lot of work for nothing. mod_rewrite can be used to accomplish this task. Create a file called "noupdate.xml" that contains <updates></updates>Then put this in a .htaccess file:

RewriteEngine on

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*) noupdate.xml

What this says is if you find the file, serve it, if you don't, serve the file that represents the empty update.

At first, it looks like we can just setup directories that correspond to the URL, but this won't work since we need to send an XML file that looks like:


If there are no updates available. So I'm going to use a simple PHP file to function as our update server. We'll need to do some changes to our web server configuration as well. Note I am using Apache.

First go into httpd.conf and make sure that mod_rewrite is available. Then go into your main web directory and add or edit the file .htaccess with this:

RewriteEngine on
RewriteRule ^/?update/(.*) /update.php/$1 [L]

What this rule says is whenever you see a URL that begins with "update" send it to update.php. The actual update.php file looks like this:

header("Content-type: text/xml");


$updatefile = "$noidea/$product/$version/$build_id/$build_target/$locale/$channel/$filename";
//echo "$updatefile";

if (file_exists($updatefile)) {
  $handle = fopen($updatefile, "r");
  $contents = fread($handle, filesize($updatefile));
  echo "$contents";
} else {
  echo '';
  echo '';

This PHP simply looks for the presence of an update.xml directory structure specified by the URL and if it is available, it returns it, otherwise it returns <updates></updates>

So how do we publish updates then? We simply create an update.xml file and place it in the appropriate subdirectory.

The last step is updating the browser to point to our new update URL. Your first thought might have been to simple update app.update.url in about:config, but that will not work. When app.update.url is queried by Firefox, it always looks for the "default preference" which means that even if a user changes the preference, it won't be used. For testing purposes, you can add a new preference called "app.update.url.override" that will work, but when you go to deploy your updates, you should set app.update.url in an extension like the CCK. That will cause it to become a "default preference."

One more installment to go. I'll try to address some general deployment issues, and if you have questions (any questions), PLEASE let me know.

Operator and DOM Mutation Events

This is working so well, I have to tell the world.

I just added support for DOM Mutation events to Operator. So now when DOM nodes are added and removed from the document, Operator will update appropriately.

But that's not the coolest part...

I added support for the DOMAttrModified event. So combined with Firebug, you can actually interactively modify your HTML to add/remove/modify a microformat and Operator will update live based on the changes you make. It's REALLY sweet.

New version coming soon....

Deploying Firefox 2 within the Enterprise: Part 4

My original topic was Setting up your own update server to deploy Firefox patches. I think what I am going to do is split things up into two sections - Creating Firefox Update files and setting up a Firefox Update Server. So...

Creating Firefox Update Files (MARs)

Firefox and were released yesterday (March 21, 2007), and at some point in the next few days, if you use Firefox, you'll receive a message telling you that an update is available. Alternatively, you can go to Help->Check for Updates... and Firefox will connect to an update server to check to see if there is an update available. If you deploy Firefox in a large corporation, you might want to either prevent your users from getting Firefox updates until you have certified them, or you might want to push your own updates to Firefox that include additional function. Today we're going to talk a little about how the update process works and how you can create your own update files. In the next installment we'll talk more about how the update process works and how to push updates to your users.

In the distant past, whenever there was an update to Firefox, users had to download a new installer and basically reinstall Firefox. To solve this problem, a new update system for Firefox was created. This system uses a file format called MAR which is short for Mozilla Archive. The MAR file contains the binary diffs for files that have changed in the update, or if the bzipped diffs are larger than the file they are patching, it contains the entire file. Whenever a new version of Firefox is released, two MAR files are created as can be seen with Firefox The partial MAR is used to upgrade users directly from Firefox to Firefox The complete MAR is used if the partial upgrade could not be applied correctly or if you are on a version of Firefox that is more than one version behind.

To provide an update server within your enterprise, you'll have to create your own MAR files based on your custom Firefox builds. Today we're going to talk about creating those files.

Before we get started, we need to make a few fixes to our build tree. Basically the problem is that the update packaging scripts weren't working with the MozillaBuild tools in Firefox First we need to patch the update files. Download the patch from bug 373121 and place it in the mozilla/tools/update-packaging directory. Then type:

patch < bugmsysupdate-packaging-1.8.patch

Once this change has been made, we need to make a small change to our build tools. Go to the mozilla-build/msys/bin directory and copy the file sh.exe to bash.exe. (Future versions of MozillaBuild will have bash.exe.) Alternatively, you can just grab the latest version of MozillaBuild (1.1).

OK, now we're ready to get started. First we need to build the diff utility we need. Go to the mozilla/obj/other-licenses/bsdiff directory and type make. Next we're going to build the full MAR file. This is the easy part. Go to the mozilla/obj/tools/update-packaging directory and type make. Once this process completes, if you look in the mozilla/obj/dist/update directory, you'll see the file firefox- This is the complete MAR file we talked about earlier.

Creating the partial MAR file requires a little more work. Since a partial MAR is a diff between two versions of Firefox, we'll need to have the two versions of Firefox we want to diff against in separate directories so that we can create the partial MAR. If you want to create a partial MAR against your own custom Firefox, you'll need to create a new build tree and build Firefox using the instructions in the first post. (Change FIREFOX_2_0_0_2_RELEASE to FIREFOX_2_0_0_3_RELEASE) We're going to take a shortcut and use the files that were created as part of the official Firefox release.

The partial MARs are created by diffing the contents of two complete MAR files. So what we'll need to do is create two directories and unpack the complete MAR files into those directories. Let's make two directories. For convenience, we'll put them under the mozilla/tools/update-packaging directory. One we'll call ff2002 and the other we'll call ff2003. Copy the complete MAR that you created above into ff2002 and download the complete MAR for Firefox and put it into ff2003.

In order to unpack the MAR files, there are tools in the mozilla/tools/update-packaging directory. There is a PERL script called unwrap_full_update.pl and a shell script called unwrap_full_update.sh. Either one can be used. The tools assume that mar.exe is in the path or pointed to by an environment variable. Let's go ahead and put it in the path by copying mar.exe from mozilla/obj/dist/host/bin to mozilla-build/moztools-180compat/bin. (Ugly I know, but convenient). Now we need to go to the ff2002 directory where we put the MAR file and unpack it. The unwrap scripts work in the current directory, so running them will look something like this:

perl ../unwrap_full_update.pl firefox-


sh ../unwrap_full_update.sh firefox-

Once you have unpacked the MAR file, delete the original MAR file or move it out of the way so that we don't diff it. Then do the same process for the MAR file.

Creating the partial MAR is actually done using make, so once we've unpacked the MAR files, we'll need to go back to the mozilla/obj/tools/update-packaging directory. There are a few environment variables we need to set in order to tell the partial MAR packaging what to do.

These names aren't totally accurate. They are actually the version numbers of the two builds. They will be used to name the output MAR file. For our builds, they are and
These actually point to the locations of the files we just unpackaged.


SRC_BUILD=../../../tools/update-packaging/ff2002; export SRC_BUILD;
DST_BUILD=../../../tools/update-packaging/ff2003; export DST_BUILD;

Then type make partial-patch.

And that's it! In the mozilla/obj/dist/update directory, you'll see the file firefox- Which is the partial MAR between Firefox and Firefox

Obviously in this post I chose to place files in certain locations that made it easier to describe the process. With the information I have presented, you might find a way to simplify this process. If you have better ideas on how to do this stuff, please let me know. I'm going to look at making changes to the build process to try to simplify this.

Next time we'll talk about how to get those files to our users.

Deploying Firefox 2 within the Enterprise: Part 3

Creating a Firefox installer

Continuing where we left off...

Now that we have built Firefox, our next step is to build the installer. Before we do that, however, we need to place our custom extensions in our build so that they get packaged with Firefox.

There are three ways that extensions can be installed in Firefox: user extensions, application extensions, and registry-install extensions. When a user installs an extension from a web page, it is installed in their profile directory and is called a user extension. If the user switches profiles, that extension is no longer available to them. Alternatively, extensions can be installed in the extensions directory where the firefox executable is located. If extensions are installed here, they are called application extensions. These extensions essentially become a part of the application install, so they are available to any profile that is created. (Note that when an application extension is upgraded, the upgrade is done as a user extension, so if you switch to another profile, the extension needs to be upgraded again.)

We want to package our extensions as application extensions, so we need to place them in the extensions directory under the executable so that the installer packages them as a part of Firefox. In our build, this directory is located at mozilla/obj/dist/bin/extensions, so let's go there and install our extensions.

If you look in the extensions directory, you'll see one or more directories. Each of these directories represents an extension. The names of the extension directories are determined by the ID of the extension. These IDs can either be UUIDs like {972ce4c6-7e08-4474-a285-3208198ce6fd} or they can be an email format like name@extensions.domainname.tld. When we first place the extensions we want to install into this directory, we won't know the ID of the extension, so we'll have to create a temporary directory in which to unzip the extension. Once we've unzipped the extension, we'll be able to find out the ID of the extension and then we can rename the directory appropriately.

To install an extension, create a temporary directory under the extensions directory and unzip the XPI that corresponds to the extension into the temporary directory. Once you have unzipped the XPI file, you should see a file called install.rdf in the temporary directory. If you edit this file, you will see a line that looks like this:


or this


The value in the middle is the extension ID. We will need to rename the temporary directory to match this ID. Note that when you rename the directory, you might need to enclose the ID in quotation marks.

You should follow the above process for all extensions you want to preinstall, including the CCK you created in Step 2.

There's one other small change we might want to make before we build the installer. This step is needed if there are files we want to remove as part of our install. For instance, when we deployed Firefox originally inside of IBM, we used a version of the CCK that wasn't as self contained as the current version. We can use this method to cleanup any files that might be left behind. The file that we want to change is removed-files.in in the mozilla/browser/installer directory. The format of this file is very simple. Just add any files you want to remove during installation to the list. Make sure you use forward slashes. Also note that the top level directory for anything referenced in this file is the directory where the firefox executable is located.

The build system maintains a list of all files that should be a part of the Firefox package. Because our files aren't in that list, we're going to have to make a small change to the build system before we build the installer. First go to the directory mozilla/toolkit/mozapps/installer. Then download the patch file. Save it in the directory mozilla/toolkit/mozapps/installer. Now go to a command line and type patch < packager.mk.patch. What this change does is cause the build process to copy all the files we just placed in the extensions directory before it builds the installer.

Now we are ready to actually build our installer. Go to the mozilla/obj/browser/installer directory and type:

make installer

After this process completes, your installer executable will be in:


If you need to do an unattended install you can invoke the executable with the parameter /S. You can get more information on customizing the installer from the Mozilla Wiki.

Next time we'll talk about creating an update server and producing update files for Firefox.