Making it into a static overlay

Now that we have a working Mozilla extension that shows tinderbox status, we need to make it distributable to other users. The two ways of doing that are to integrate it into the Mozilla codebase (in which case it is no longer an extension but rather part of the default Mozilla distribution) and to package it into an installer that users can run from within Mozilla to add the extension to their Mozilla installation.

Most extensions are distributed as installer packages, and that's how we'll distribute our extension. Integrating extensions into the Mozilla codebase is beyond the scope of this tutorial, but for more information see mozilla.org's hacking documentation.

Mozilla installer packages are called XPIs (pronounced "zippies"), which stands for cross-platform installer. The packages are just standard ZIP archives of the files to be installed along with a JavaScript script that performs the installation and some RDF files that describe the components being installed for the chrome registry.

Because our extension modifies an existing file in the Mozilla distribution, before we package it up we need to separate the modifications into a different file and add them back in at run time via a dynamic XUL overlay.

A XUL overlay is a XUL file containing elements to be inserted into another XUL file when the other XUL file is rendered into an application interface. Static overlays are added to a XUL file via a reference at the top of the file (much like stylesheets and JavaScript scripts). Dynamic overlays are added to a XUL file via a reference in the chrome registry.

Overlays provide a way to break up a large XUL file into several different files (one that describes the overall structure of an application window and the others to implement specific portions of the window) to improve code readability, maintainability, and extensability. Dynamic overlays also make it possible to modify a XUL file without actually changing the code of the file itself, which is necessary when installing an extension like ours. We'll first make the file into a static overlay, then we'll make it into a dynamic overlay.

To make the file into a static overlay, we need to move all the code we added to navigator.xul into a new file tinderstatusOverlay.xul in the same directory:

<?xml version="1.0"?>

<?xml-stylesheet
      href="chrome://navigator/content/tinderstatus.css"
      type="text/css"?>

<overlay id="tinderstatusOverlay"
      xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<script type="application/javascript"
        src="chrome://navigator/content/tinderstatus.js" />

<statusbar id="status-bar">
  <statusbarpanel class="statusbarpanel-iconic"
          id="tinderbox-status"
          insertbefore="offline-status"
          status="none"/>
</statusbar>

</overlay>

tinderstatusOverlay.xul starts with an XML processing instruction that identifies the file as XML (all XUL files need to include this). Its next line is the stylesheet reference we previously added to navigator.xul. After that is a XUL overlay element. This element is the top-level element in a XUL overlay file and serves to identify the file as an overlay. Within that element is the script reference we previously added to navigator.xul. Then there is a statusbar element containing a statusbarpanel element.

The value of the id attribute of the statusbar element in our overlay matches the value of the id attribute of the statusbar element in navigator.xul. When navigator.xul is rendering into the browser's application interface, this causes any attributes or child elements of the statusbar element in the overlay file to be added to the interface's DOM and thus show up in the interface as if they were defined on the same element in navigator.xul.

Note that we've added a new attribute to the statusbarpanel element: insertbefore. This attribute identifies another statusbarpanel element within statusbar before which our element should appear. Thus it allows us to define the exact position of our element relative to the other statusbarpanel elements within statusbar.

If insertbefore is omitted, the element will be added as the last child of statusbar, usually before the resizer grippy. We could instead have used an insertafter attribute to place the element after another.

To use this overlay instead of our changes to navigator.xul we need to remove our changes, then add a reference to the overlay to the top of navigator.xul:

...

<?xml-stylesheet href="chrome://navigator/skin/" type="text/css"?>
<?xml-stylesheet
      href="chrome://navigator/content/tinderstatus.css"
      type="text/css"?>

<?xul-overlay href="chrome://navigator/content/navigatorOverlay.xul"?>
<?xul-overlay href="chrome://navigator/content/navExtraOverlay.xul"?>
<?xul-overlay href="chrome://navigator/content/linkToolbarOverlay.xul"?>
<?xul-overlay href="chrome://navigator/content/tinderstatusOverlay.xul"?>
<?xul-overlay href="chrome://communicator/content/conten...extOverlay.xul"?>
<?xul-overlay href="chrome://communicator/content/sideba...barOverlay.xul"?>
<?xul-overlay href="chrome://communicator/content/communicatorOverlay.xul"?>
<?xul-overlay href="chrome://communicator/content/bookma...rksOverlay.xul"?>

...

<!-- Navigator -->
<script type="application/javascript"
        src="chrome://navigator/content/browser.js"/>
<script type="application/javascript"
        src="chrome://navigator/content/navigator.js"/>
<script type="application/javascript"
        src="chrome://navigator/content/navigatorDD.js"/>
<script type="application/javascript"
        src="chrome://navigator/content/sessionHistoryUI.js"/>

<script type="application/javascript"
        src="chrome://navigator/content/tinderstatus.js"/>

<!-- hook for stringbundle overlays -->

...

<statusbar id="status-bar" class="chromeclass-status"
          ondragdrop="nsDragAndDrop.drop(event, contentAreaDNDObserver);">
  <statusbarpanel id="component-bar"/>
  <statusbarpanel id="statusbar-display" label="&statusText.label;" flex="1"/>
  <statusbarpanel class="statusbarpanel-progress">
    <progressmeter class="progressmeter-statusbar"
            id="statusbar-icon" mode="normal" value="0"/>
  </statusbarpanel>
  <statusbarpanel class="statusbarpanel-iconic"
            id="tinderbox-status" status="none"/>
  <statusbarpanel class="statusbarpanel-iconic" id="offline-status"/>
  <statusbarpanel class="statusbarpanel-iconic" id="security-button"
                  onclick="BrowserPageInfo(null, 'securityTab')"/>
</statusbar>
...