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:

HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\Mozilla Firefox

(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 2.0.0.3, 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:

alert(hcard.fn);
alert(hcard.adr.region);

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);
alert(hcard.fn);

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 2.0.0.3 that our software installer can't tell which is newer, Firefox 1.5.0.11 or Firefox 2.0.0.3. 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.0.0.0 1 1.7.5: 2004110711
1.0.1 6626916 6627328 1.0.1.0 1.0.1 1.7.6: 2005022518
1.0.2 6627428 6631424 1.0.2.0 1.0.2 1.7.6: 2005031717
1.0.3 6631012 6631424 1.0.3.0 1.0.3 1.7.7: 2005041417
1.0.4 6631012 6631424 1.0.4.0 1.0.4 1.7.8: 2005051112
1.0.5 6636644 6639616 1.0.5.0 1.0.5 1.7.9: 2005071118
1.0.6 6636644 6639616 1.0.6.0 1.0.6 1.7.10: 2005071605
1.0.7 6637156 6639616 1.0.7.0 1.0.7 1.7.12: 2005091517
1.0.8 6639716 6643712 1.0.8.0 1.0.8 1.7.13: 2006041017
           
1.5 7162979 7163904 1.8.20051.11116 1.8: 2005111116 1.5
1.5.0.1 7166053 7168000 1.8.20060.11112 1.8.0.1: 2006011112 1.5.0.1
1.5.0.2 7171685 7172096 1.8.20060.30804 1.8.0.2: 2006030804 1.5.0.2
1.5.0.3 7172197 7176192 1.8.20060.42618 1.8.0.3: 2006042618 1.5.0.3
1.5.0.4 7177325 7180288 1.8.20060.50817 1.8.0.4: 2006050817 1.5.0.4
1.5.0.5 7183469 7184384 1.8.20060.6376 1.8.0.5: 2006071912 1.5.0.5
1.5.0.6 7183469 7184384 1.8.20060.7278 1.8.0.6: 2006072814 1.5.0.6
1.5.0.7 7190637 7192576 1.8.20060.25382 1.8.0.7: 2006090918 1.5.0.7
1.5.0.8 7191149 7192576 1.8.20061.2516 1.8.0.8: 2006102516 1.5.0.8
1.5.0.9 7200365 7200768 1.8.20061.20612 1.8.0.9: 2006120612 1.5.0.9
1.5.0.10 7206509 7208960 1.8.20070.21601 1.8.0.10: 2007021601 1.5.0.10
1.5.0.11 7206509 7208960 1.8.20070.31202 1.8.0.11: 2007031202 1.5.0.11
1.5.0.12 7209069 7213056 1.8.20070.50813 1.8.0.11: 2007050813 1.5.0.12
           
2.0 7604331 7606272 1.8.20061.1023 1.8.1: 2006101023 2.0
2.0.0.1 7620696 7622656 1.8.20061.20418 1.8.1.1: 2006120418 2.0.0.1
2.0.0.2 7633008 7634944 1.8.20070.21917 1.8.1.2: 2007021917 2.0.0.2
2.0.0.3 7633008 7634944 1.8.20070.30919 1.8.1.3: 2007030919 2.0.0.3
2.0.0.4 7637104 7639040 1.8.20070.51502 1.8.1.4: 2007051502 2.0.0.4
2.0.0.5 7644008 7647232 1.8.20070.5781 1.8.1.5: 2007071317 2.0.0.5
2.0.0.6 7644520 7647232 1.8.20070.6982 1.8.1.6: 2007072518 2.0.0.6

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
  • 1.5.0.5 and 1.5.0.6
  • 1.5.0.10 and 1.5.0.11
  • 2.0.0.2 and 2.0.0.3

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 1.5.0.11 and Firefox 2.0.0.3. You'll see that Firefox 1.5.0.11 is 1.8.20070.31202 and Firefox 2.0.0.3 is 1.8.20070.30919. So if an application uses File version to determine which is the latest version, it will claim that Firefox 1.5.0.11 is newer than Firefox 2.0.0.3. 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 2.0.0.3.exe"

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.2.0.0.1 tells Firefox to never offer the 2.0.0.1 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:


https://aus2.mozilla.org/update/1/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/update.xml

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


https://aus2.mozilla.org/update/1/Firefox/2.0.0.3/2007030919/WINNT_x86-msvc/en-US/release/update.xml

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 2.0.0.2 to Firefox 2.0.0.3. (https://aus2.mozilla.org/update/1/Firefox/2.0.0.2/2007021917/WINNT_x86-msvc/en-US/release/update.xml)

<?xml version="1.0"?>
<updates>
  <update type="minor"
          version="2.0.0.3"
          extensionVersion="2.0.0.3"
          buildID="2007030919"
          detailsURL="http://en-US.www.mozilla.com/en-US/firefox/2.0.0.3/releasenotes/">
    <patch type="complete"
           URL="http://download.mozilla.org/?product=firefox-2.0.0.3-complete&amp;os=win&amp;lang=en-US"
           hashFunction="SHA1"
           hashValue="bc64376c13867b5524d71e87f7ad98e727f2d3f4"
           size="7617299"/>
    <patch type="partial"
           URL="http://download.mozilla.org/?product=firefox-2.0.0.3-partial-2.0.0.2&amp;os=win&amp;lang=en-US"
           hashFunction="SHA1"
           hashValue="2555dcad94c9bc1c7327cb3699844f633888ea4a"
           size="270514"/>
  </update>
</updates>

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:

<updates>
</updates>

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:

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

$url_array=explode("/",$_SERVER["REQUEST_URI"]);
$noidea=$url_array[2];
$product=$url_array[3];
$version=$url_array[4];
$build_id=$url_array[5];
$build_target=$url_array[6];
$locale=$url_array[7];
$channel=$url_array[8];
$filename=$url_array[9];

$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";
  fclose($handle);
} 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 2.0.0.3 and 1.5.0.11 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 2.0.0.3. The partial MAR is used to upgrade users directly from Firefox 2.0.0.2 to Firefox 2.0.0.3. 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 2.0.0.2. 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-2.0.0.2.en-US.win32.complete.mar. 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 2.0.0.3, you'll need to create a new build tree and build Firefox 2.0.0.3 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 2.0.0.3 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 2.0.0.3 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-2.0.0.2.en-US.win32.complete.mar

or

sh ../unwrap_full_update.sh firefox-2.0.0.2.en-US.win32.complete.mar

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 2.0.0.3 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.

SRC_BUILD_ID and DST_BUILD_ID
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 2.0.0.2 and 2.0.0.3
SRC_BUILD and DST_BUILD
These actually point to the locations of the files we just unpackaged.

So:

SRC_BUILD_ID=2.0.0.2; export SRC_BUILD_ID;
DST_BUILD_ID=2.0.0.3; export DST_BUILD_ID;
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-2.0.0.2.en-US.win32.partial.2.0.0.2-2.0.0.3.mar. Which is the partial MAR between Firefox 2.0.0.2 and Firefox 2.0.0.3.

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:

name@extensions.domainname.tld

or this

{972ce4c6-7e08-4474-a285-3208198ce6fd}

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:

mozilla/obj/dist/install/sea

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.

Deploying Firefox 2 within the Enterprise: Answering Some Questions

Some questions have already come up about the posts I've written, and rather than answering in comments or in a post at the end, I'd like to address some of them now. Note that I'm paraphrasing most of the questions.

Why would you need to build your own custom version of Firefox? Couldn't you just customize the build that Mozilla produces?

There are a couple answers to this question.

Answer: Custom Fixes

There might be cases where a fix is needed for your enterprise that either will not make it into the source code fast enough, or will not be accepted at all.

Last year we had a problem with a buggy IBM printer driver that caused Firefox to crash printing certain web pages. The most problematic pages seemed to be IBM internal pages. Unfortunately, this particular driver was in use for just about every printer inside IBM. The fix for this problem was unfortunate: it involved putting some exception handling in Firefox to catch the printer driver crash. We weren't sure it was going to make it into the source code fast enough to meet our needs, so we actually prepared to build a custom Firefox. Luckily we were able to get the fix in and we deployed the next Firefox 1.5 update quickly. But this could have certainly turned into a case where we needed to ship a custom version of Firefox.

You might run into similar cases where you might need a feature or fix that is not available in the version of Firefox you have deployed.

Answer: You Might Need to Extend Support Beyond what Mozilla Offers

A lot of people might not realize this, but the Mozilla Corporation policy is that they will only provide security fixes for software for six months after the release of the next major version. So for instance, Firefox 2 was released on October 24, 2006 so support for Firefox 1.5 will be discontinued on April 24, 2007.

For consumers, this might not be an issue, but for enterprises and educational institutions this can be a problem. Let's start with educational institutions.

Check out this excellent comment from UK Web Focus. The problem is that educational institutions tend to deploy new software during the summer break. So Firefox 1.5 will be out of support before the spring semester is over. Things would have been even worse if Firefox 1.5 had been released in August or September.

But educational institutions are only part of the story. Let's talk about the enterprise. I've worked with a number of companies deploying applications so what I'm going to talk about here are real world scenarios.

Scenario 1: Piloting and Testing takes six months or more

For a large enterprise to deploy a new web browser, there is a process that must be followed to ensure that the browser works with their internal infrastructure. Usually this involves the IT folk installing the browser locally and doing some initial testing, piloting the browser with some small group of people, and then eventually deploying the new web browser to the rest of their organization. In addition, if there are major changes to the browser (as there was with Firefox 2), there are other organizations that must be involved like the help desk or web application teams. The help desk will need to do things like rewrite support scripts or other training material. The web application teams will need to test the new browser with their applications. If either of these teams run into problems, this would delay the release of the new web browser to the entire organization.

As we mentioned before, Firefox 2 was released on October 24, 2006. If you are in a large organization like mine, you are aware that very little happens at the end of the year whether due to vacation or lack of funding. So if an enterprise were to start the process of deploying Firefox 2, it would not start until probably mid January. So essentially an enterprise has about three months to complete the process that I have detailed above. In my experience this process can take six months to a year.

Scenario 2: Deployment windows

In some large enterprises, there are "deployment windows." That is to say specific times of the year when new software can be deployed. These deployment windows can be any time during the year, but experience has shown they they are typically in the fall and they are usually once a year or even once every two years.

Right away, you can see a problem. With a once a year deployment window, you will have at least six months where the browser is unsupported.

What can I do?

In theory, you could cherry pick security fixes from Firefox 2 security releases and backport them to Firefox to 1.5. This is clearly not an optimum solution.

Is there a good solution?

The fact is that life cycle for enterprise software needs to be one year at the minimum, two years preferred. If this is something that is important to you, you should at least let Mozilla Corporation know. This also might be a great business opportunity for some enterprising folk...

Why is the CCK a wizard? Why isn't the information provided in your post a part of the CCK?

The Firefox CCK was designed to look and work exactly like the Netscape CCK. If you were to install the Netscape CCK, you would see that a lot of the text is even the same. The CCK is not a tool that is designed to be used every day. Most organizations would probably only use it once or twice a year. For that reason, it was designed to be minimalist. The CCK was a one man project (me) so I would certainly appreciate any help anyone wanted to provide in helping to maintain the CCK. The source code is available here.

Can I set preferences for extensions within the CCK?

I believe that if you set the preferences and then locked them in the CCK, they would not be overridden when the extension is installed. The problem has to do with load order. If you just set them in the CCK, when the extension is loaded, it overrides the preferences that were set by the CCK.

Bonus Question: What if I just want to rename the browser or change the branding? Do I have to build it?

I needed an excuse to plug my other extension, so here it is. If you ever need to rename Firefox for some reason (like for instance, to call it Iceweasel), I have creating an extension that specifically facilitates changing all the branding in the browser including the product name, the images in the about screen, and the icons. That extension is called Rebrand.

Our next installment is tomorrow, and we'll talk about building the Firefox installer with our custom extensions.