Firefox in the Enterprise: Part 1 - Obstacles

I've been thinking a lot about Firefox in the enterprise and I wanted to capture some of my thoughts. This two part article will first cover obstacles to enterprise adoption of Firefox (with some possible solutions) and then cover specific benefits to Firefox in the enterprise.

Before I get any complaints, I am aware that there are large enterprises that are already using Firefox (including IBM). I believe that most of these are supporting Firefox as a secondary browser as opposed to switching to Firefox as the primary browser. Also, I'd love to open up more discussion on this subject, so please comment and correct me if I'm wrong.

Introduction

As Firefox gains marketshare, there comes a point where increasing that marketshare depends on the adoption of Firefox in large enterprises. This article investigates what type of issues arise when supporting Firefox in a large enterprise, and what can be done to solve those issues.

The primary areas I will address are:

  • Release Lifecycle
  • Service and Support
  • Business Value
  • Third Party Applications
  • Intranet Applications
  • Deployment issues

Release Lifecycle

Simply put, the current Firefox policy of supporting prior versions of Firefox for six months after the next release is just too short, especially considering the time of year when new versions of Firefox are typically released. Many companies only deploy new software once or twice a year, and the process to get that software deployed takes many months.

I know of a company that deploys software once a year, in September. In order to be a part of that deployment, you have to start piloting your application in June. If this company wanted to move from Firefox 1.5 to Firefox 2, they would have had to pilot it in June, for deployment in September. This means that they would have had an unsupported browser for over five months.

In addition, Firefox so far has been released in the fourth quarter (Firefox 1 was release in November, Firefox 2 was released in October). If you work in IT, you know that new software does not get deployed in November, December, or even January. So three months of the six month Firefox support cycle was simply a wash.

Either Mozilla Corporation needs to step up and extend the lifecycle of Firefox to one year, or some enterprising company should take this opportunity to provide better release lifecycle for Firefox.

Service and Support

When a piece of software is an integral part of a company's business, it is imperative that they have someone to call when they have problems. "Open a bug in bugzilla" or "we accept patches" are simply not good answers when companies are experiencing problems. Certainly companies can staff developers that can build and deploy custom versions of Firefox, but this is the exception, not the rule. In addition, companies that have their own internal help desk need training on how to support Firefox.

This problem is more difficult to solve. Mozilla's role is not to provide one on one support for customers. This is, however, an excellent opportunity for someone to step in and provide enterprise level phone support for Firefox, as well as to create education modules to train internal help desks.

Business Value

Every organization is in business to provide value to their customers. For a publicly traded company, it's about shareholder value. For a nonprofit, it might be about reducing overhead so that more donations can go to their primary cause. Unfortunately, it is difficult to show that supporting two browsers or switching to a completely different browser adds business value.

While I don't have a good solution to this problem, it is worth noting that, for many companies, the move to support Internet Explorer 7 will be just as much work as supporting Firefox. From that perspective, the best way to increase business value
would be to move towards an internal infrastructure that uses open standards so that dependencies on particular platforms are diminished. In addition, if a company ever wanted to move to a platform where Internet Explorer was not available, using open standards would make it easer to move.

Third Party Applications

When most of us surf the web, we don't tend to find a lot of web pages that simply don't work in Firefox. This problem has mostly been solved. Companies, however, use third party applications that may or may not work in Firefox. While the current version of a lot of these applications may work with Firefox, older versions of these applications are deployed in many companies with no plans to upgrade in the foreseeable future due to cost (see Business Value above).

There are a few ways to solve this problem. First off, third party application providers should provide free or low cost upgrades to companies that have software that doesn't work with Firefox. Secondly, when purchasing third party applications, companies should ensure that the software they are purchasing uses open standards. Finally, application vendors should ensure they are using open standards to create their applications so they don't lock their customers into a particular browser vendor.

Intranet Applications

Third party applications aren't the only place where support for Firefox can be a problem. What tends to happen in companies is that internal applications get written, deployed, and then forgotten about. In addition, because it might take years to write an internal application, by the time it is deployed, it might be designed for an older browser. Fixing these applications can be some of the most difficult problems to solve, due to lack of funding and even lack of interest by the developers.

Your company should have policies in place to ensure that internal applications are written using open standards so that they don't depend on a particular browser. In addition, when applications are created, it is imperative that funding be set aside so that if problems are encountered with a particular application in the future, that there is a way to get the problem fixed.

Deployment issues

One of the main things I've tried to do in my job is help with deployment issues. In addition to working on the CCK, I've worked with some smaller entities to deploy Firefox using things like the Firefox Release Repackager. But these things simply aren't enough. Companies already have existing deployment infrastructures using Tivoli or Microsoft Active Directory. In addition, the types of customizations that a lot of entities want to do are outside the scope of the CCK.

While there are third party solutions that provide a customized Firefox to use Active Directory and Microsoft installers, there needs to be more work in the community to make Active Directory support and Microsoft installers an integral part of Firefox. While Firefox developers do not want to make MSI the primary ship vehicle for Firefox, work could be done to make the MSI installer on par with the Nullsoft Installer. In addition, there needs to be more of an ecosystem around customizing Firefox for certain types of deployment.

Closing Comments

So there you have it. Some quick thoughts on enterprise deployment. In the next installment, I'll talk about the benefits of Firefox in the enterprise, and in particular some things we've found out inside IBM. As always, I'd love to here your thoughts, and if I've missed anything.

Operator 0.8a is Available

I have made an alpha of Operator 0.8 available. Special thanks to Elias Torres for the RDFa support.

My primary goal with Operator 0.8a was to attempt to finalize APIs for both Operator and Firefox 3. What I actually ended up with is very similar to the APIs I created with the very first version of Operator. You can see the microformats API for Firefox 3 here (looking for feedback). I'll be documenting the action and microformats API more completely in the next few days.

New features in this release include:

  • RDFa support (view only - there are no actions yet)
  • Unified actions architecture - actions are no longer specific to a microformat
  • Support for Address microformat to allow some actions (like map lookups) to be more granular.
  • Better support for iframes/frames/nested documents
  • Debug mode uses X2V for hCards and hCalendars
  • Support for non HTML documents
  • Bug fixes galore.

One feature I removed that I know people will complain about is the ability to have custom names for the actions. If I get enough complaints, I'll put this back.

IMPORTANT NOTE: This release uses new preferences so it will wipe out your old prefs. This should be the last time that happens. I also changed the location of user scripts so it won't pick up your old ones (they won't work anyway).

If you want new user scripts, check out: http://www.kaply.com/weblog/operator-user-scripts/

If you have user scripts you want to quickly port to Operator 0.8, please send them to me and I'll help.

Please report bugs here.

Have fun.

Where is the next version of Operator?

You might have heard from Elias Torres that we have RDFa working in Operator. You might also be wondering where that version is.

Basically I've been working very hard to finalize the APIs so that I can get things ready for Firefox 3. You can take a look at them here. The ironic part is that I've actually gone back to the way I wrote Operator the first time in terms of how microformats and actions are added to Operator. I'll be documenting this soon. And if you wrote to any of my other APIs, I'll work with you to move things to the new model.

So what do you have to look forward to in Operator 0.8a?

  • RDFa support
  • Unified action architecture (actions on the toolbar aren't specific to microformats)
  • Support for non HTML documents
  • A final architecture for actions and microformats so people can start coding to it
  • Debug mode displays X2V output
  • Export All available in "Microformats mode"
  • Lots of bug fixes

Stay tuned. Just some fine tuning and we're good to go.

Microformats and Firefox 3 (for Developers)

So let's talk about what Firefox 3 will hopefully offer for developers. The goal is that extension authors will have an API to access microformatted data directly without worrying about parsing. In addition, there will be a model for adding additional microformats, as well as adding semantic actions (more about that in a minute). Here's what the API looks like right now.

Any microformat can be instantiated from a DOM Node:

var hcard = new hCard(DOMNode);

Once the microformat has been instantiated, members can be accessed directly, like hcard.fn. If the property is defined as an array in the microformat specification, it is an array in the newly created object. A pointer to the original DOM node is also stored in this object if you want to do anything to the microformat node on the glass.

Microformat objects can be queried from a document:

var hCards = Microformats.get(document, "hCard");

I need a better name for this one I think. Given a document and a microformat type, this API returns an array of objects that represent each microformat in the document. I'm not sure if this should go through all the child frames and return every microformat in every sub document. I have to think about that.

If you have DOM Nodes, you might need some information about them. The scenario here is what if you have a DOM Node and you don't know if it is a microformat or not? What APIs could help you?

Microformats.isMicroformat(DOMNode)

This function tells you if the specific DOM Node you passed in is a microformat. It returns a boolean.

Microformats.getParentMicroformatNode(DOMNode)

This function gets the first microformat that is a parent of this node. If you pass in a microformat, it still gets the parent. This is important for finding nested microformats.

Microformats.getMicroformatNamesFromNode(DOMNode)

This function returns an array of the microformat types that this node represent.

So here's how you might use those three functions, for instance when a context menu is displayed on a given node:

  if (Microformat.isMicroformatNode(node)) {
    mfNode = node;
  } else {
    mfNode = Microformats.getParentMicroformatNode(node);
  }
  while (mfNode) {
    var mfNames = Microformats.getMicroformatNamesFromNode(mfNode);
    /* enumerate through names and do something */
    mfNode = Microformats.getParentMicroformatNode(mfNode);
  }

I mentioned semantic actions earlier. I'm still working on how I can create an action model that is easily extensible. In an earlier post, I suggested that actions be implemented like this:

semanticActions.actions.youtube_search_tags = {
  description: "Find videos on YouTube",
  icon: "http://youtube.com/favicon.ico",
  scope: {
    semantic: {
      "tag" : "tag"
    }
  },
  doAction: function(semanticObject, semanticObjectType) {
    if (semanticObject.tag) {
      return("http://youtube.com/results?search_query=" + encodeURIComponent(semanticObject.tag));
    }
  }
};

I'm now trying to decide if I should use a function method, something like:

semanticActions.addAction("youtube_search_tags", youtube_object);

There will be much more on that later.

Anyway, I'm primarily looking for feedback from people. Are these APIs sufficient? Did I miss something obvious? Thanks for the input.

Microformats and Firefox 3 (for Users)

I'm now the official owner of microformats support in Firefox 3. Here is the preliminary design document.

At this point, we'll primarily be focusing on making microformats available to extension developers, with very little UI. The primary motivator behind this is that I don't think anyone has come up with a good user interface for microformats. I want to take some time to bring up some different UI paradigms and have some general discussion on these ideas just to see what people think. We're actually trying to have a UI discussion on the labs forums, but we're not getting much input so I thought I would try here.

So far there have been multiple extensions with multiple ways to interact with microformats:

  • Operator (toolbar, status bar button, toolbar button, right click on microformats)
  • WebCards (in page "cards" - notifcation ribbon on the bottom of the screen, right click on microformats)
  • Tails Export (sidebar)
  • Tails (icon on the status bar - window presenting content of the microformats)

Some of the other ideas that have been floated have been using the Firefox notification bar to inform about microformats, in page highlighting of microformats, notifications on the URL bar similar to RSS feeds and changing the mouse pointer. You can see some of these ideas at Alex Faaborg's blog.

The primary concern we have in creating a microformats UI is that it's not necessarily something that is going to be accessed a lot (like a back button for instance, or bookmarks), so how much screen real estate should it take?

With Operator, my original plan was to allow for many different UI paradigms, and I'm continuing that in the next release by allowing individual buttons for specific microformats (a Contacts toolbar button for instance). So now I'm putting the question to you:

What UI paradigm for microformats do you prefer? Is there a paradigm you wish was in Operator (like a sidebar for instance)? What do you think microformats should look like in Firefox 3?

Feel free to come up with any wild idea you want.

In my next post, I'll be talking about what we should do for developers.

Deploying Firefox in the Enterprise: Part 5 (Revisited)

Since my original post on setting up a Firefox update server, I realized that some of my information was not correct. This post is to correct that information.

My mistake stemmed from the fact that the format of the Firefox update URL changed between Firefox 1.5 and Firefox 2.

Here's the URL for Firefox 1.5:


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

Here's the URL for Firefox 2:


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

You'll notice two differences:

  1. The number after update was changed from 1 to 2 (this identifies which URL format to use)
  2. %OS_VERSION% was added to the URL

The important change here is the addition of OS_VERSION. Whereas before, the update URL provided information only about the browser, with OS_VERSION, the URL now contains information that changes based on the machine on which the browser is running. This makes providing updates a little trickier (although not that much).

(Incidentally, the reason that OS_VERSION was added was so that the update server could detect Windows98 and Windows95 and not send them updates that didn't work on those machines.)

Let's present a modified .htaccess file that handles this situation. I'm going to assume that in your enterprise deployment, you don't care about OS_VERSION, so we are just going to ignore it. In our original .htaccess file, we simply checked for the existence of update.xml in a path that corresponded to the URL. Because of the addition of OS_VERSION, that won't work anymore, since we would have to duplicate the update.xml file based on every possible OS_VERSION that could be sent to us. So we need to create a .htaccess file that ignores OS_VERSION. The way we do this, is simply redirect the URL to a new URL that doesn't contain OS_VERSION, and then use our old method of checking for the existence of a file.

RewriteEngine on

RewriteRule ^(.*)/release/(.*)/update.xml /$1/release/update.xml [R]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*) noupdate.xml

What this says is if the URL contains something between release and update.xml (OS_VERSION), remap it to remove the OS_VERSION. Then send it to the server again.

I'll leave updating the PHP script as an exercise for the reader.

I hope this clears up any issues with my original post.

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:

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.