Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Browser extensions using XUL, Part 2: Assemble a cross-platform Firefox extension

Uche Ogbuji (uche@ogbuji.net), Partner, Zepheira, LLC
Uche photo
Uche Ogbuji is Partner at Zepheira, LLC, a solutions firm specializing in the next generation of Web technologies. Mr. Ogbuji is lead developer of 4Suite, an open source platform for XML, RDF and knowledge-management applications and lead developer of the Versa RDF query language. He is a Computer Engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. You can find more about Mr. Ogbuji at his Weblog Copia.

Summary:  XUL is a surprisingly easy way to build cross-platform browser extensions or even stand-alone applications. Discover how to build powerful, flexible Mozilla browser extensions that go beyond the capabilities of other tools like embedded scripting languages or CGI—because they're built right into the user's browser.

View more content in this series

Date:  16 Oct 2007
Level:  Intermediate
Also available in:   Chinese  Japanese

Activity:  12163 views
Comments:  

Mozilla's XML User Interface Language (XUL) is a versatile language for developing stand-alone applications or browser extensions. In the first part of this series I showed the basic building blocks for a XUL extension, and in this article I show how to assemble these building blocks into a cross-platform Firefox extension. The code in the first article was limited because of the higher security barrier for XUL files in the browser, and such restrictions are much fewer for extensions, so I will also be providing some of the missing functionality for the code.

Basic extension structure

The first step is to build an extension with the XUL and support files as developed in the previous article. If you've installed other Firefox add-ons, you might notice that they come in a package with a .xpi filename extension, which refers to XUL's Cross-Platform Install (XPI) package format (pronounced "zippy"). XPI defines bundles, which are .zip files with a specific layout and manifest files. This is similar to Java's JAR format, which also combines .zip content with manifest files. Listing 1 is the .zip file directory for an extension I prepared using the XUL, JavaScript, and CSS from the previous article.


Listing 1. .zip file directory for a Firefox extension XPI bundle
                
  Archive:  /Users/uche/stats.xpi
   Length   Method    Size  Ratio   Date   Time   CRC-32    Name
  --------  ------  ------- -----   ----   ----   ------    ----
         0  Stored        0   0%  10-08-07 23:10  00000000  chrome/
         0  Stored        0   0%  10-08-07 23:10  00000000  chrome/content/
       271  Defl:N      184  32%  10-09-07 00:13  44bb6d65  chrome/content/overlay.js
       464  Defl:N      284  39%  10-09-07 00:13  1d873c24  chrome/content/overlay.xul
       909  Defl:N      366  60%  10-09-07 00:13  67869f05  chrome/content/stats.js
      1581  Defl:N      544  66%  10-09-07 00:13  49def584  chrome/content/stats.xul
         0  Stored        0   0%  10-08-07 23:10  00000000  chrome/skin/
       230  Defl:N      147  36%  10-09-07 00:13  a732aecb  chrome/skin/stats.css
       181  Defl:N      105  42%  10-09-07 00:13  f7317c0a  chrome.manifest
         0  Stored        0   0%  10-08-07 23:10  00000000  defaults/
         0  Stored        0   0%  10-08-07 23:10  00000000  defaults/preferences/
       948  Defl:N      440  54%  10-09-07 00:13  50ee7971  install.rdf
         0  Stored        0   0%  10-08-07 23:10  00000000  locale/
         0  Stored        0   0%  10-08-07 23:10  00000000  locale/en-US/
        43  Stored       43   0%  10-09-07 00:13  d9c38400  locale/en-US/overlay.dtd
  --------          -------  ---                            -------
      4627             2113  54%                            15 files

  

Pay close attention to the file layout, because problems in this area are the biggest pain when developing extensions. You will notice some new files that were not covered in the last article, and I'll cover these in the next section.

XUL overlays

In the previous article the new XUL window was stand-alone, but now that it is bundled as an extension it has to be invoked from Firefox somehow. This generally involves tweaking the browser user interface to give the user a means of invoking the extension. XUL makes it easy to extend user interfaces in this way using what are called overlays. Listing 2 (overlay.xul) is an overlay to add a menu item for launching the stats viewer window.


Listing 2 (overlay.xul). XUL overlay to add a menu item
                
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://stats/skin/overlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://stats/locale/overlay.dtd">
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="stats-overlay">
  <script src="overlay.js"/>

  <menupopup id="menu_ToolsPopup">
    <menuitem id="stats" label="&statsmenuitemname;"
        oncommand="Stats.onMenuItemCommand(event);"/>
  </menupopup>
</overlay>

    

I'll discuss chrome URLs further in a later section. Notice the document type declaration, which is used to load a DTD, which contains any strings that should be localized (basically any natural language strings presented to the user). The next section covers a bit more about this neat approach to localization (l10n) in Mozilla chrome applications. Within the overlay XUL proper, I specify a menu addition. The outer element specifies the existing target menu item in Firefox, by ID— menu_ToolsPopup is the "Tools" menu. By default the new item will be appended as the last menu item. The new menu item, stats, uses a localized string for the label the user sees, provided as an entity in the DTD I mentioned. The oncommand attribute is key, because it establishes what happens when the user selects the menu item. The JavaScript function specified is provided in the separate script file overlay.js, which is Listing 3.


Listing 3 (overlay.js). Script providing the action for the added menu item
                
var Stats = {
  onLoad: function() {
    //You can place set-up code for the extension here
  },

  onMenuItemCommand: function() {
    window.open("chrome://stats/content/stats.xul", "", "chrome, resizable=yes");
  }
};

window.addEventListener("load", function(e) { Stats.onLoad(e); }, false);


    

Most of this script is defining a single structure, Stats, to wrap all the functions. You would also want to place any global variables in this structure. This reduces the footprint of the extension by only using up one name in the global namespace (the name of the outer structure itself), and this is standard behavior of all well-behaved extensions, to avoid clashing with object names used by other extensions or even content scripts. You've already seen how to access the wrapped objects in Listing 2, in the JavaScript snippet Stats.onMenuItemCommand(event);, which accesses the second structure item in Listing 3. I included a Stats.onLoad function for completeness, though it's not really used in this extension. You would use this to run any extension initialization code. The last line of the script sets up the Stats.onLoad function to run as soon as the chrome has completed loading. The Stats.onMenuItemCommand function simply launches a window with the XUL example applet from the last article. The second attribute is a window ID, which I don't use. The third is a list of flags, chrome, indicating that the new window has its own chrome, and should not be opened within the browser's chrome (so for example the new window doesn't inherit Firefox's menu, toolbars, status bar, tabs, and so on). The resizable=yes enables controls for the user to resize the window.


Resources for chrome

You can already see the files proliferating for this extension, and an important aspect of XPI is finding files within the bundle. This is where the manifest file comes in, chrome.manifest, provided in Listing 4.


Listing 4. The chrome manifest file for the Stats extension
                
content stats chrome/content/
overlay chrome://browser/content/browser.xul chrome://stats/content/overlay.xul

locale stats en-US locale/en-US/

skin stats classic/1.0 chrome/skin/

    

The first line determines where the main content lives, such as XUL files and scripts. It uses the stats alias to refer to the "chrome/content/" folder within the bundle, so for example, if you use the chrome URL chrome://stats/content/stats.xul it resolves this by mapping the stats alias and looking in the "chrome/content/" folder for the "stats.xul" file. The second line establishes an overlay on the main browser chrome using overlay.xul (Listing 2). The third line supports l10n by establishing the location of DTDs that contain natural language strings. The example only has a US English version of these strings, but you could translate them to French and place them within a fr-FR folder, to German and use a de-DE folder, and so on. The browser will automatically use the one that corresponds to the language preference set by the user. The final line establishes a location for CSS files that are to be combined with the standard browser chrome.

Installer setup

Another key information file for XPI is install.rdf, Listing 5, which contains instructions for installing the extension.


Listing 5 (install.rdf). Installation parameters for the Stats extension
                
<?xml version="1.0"?>

<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">

    <Description about="urn:mozilla:install-manifest">

        <!-- Required Items -->
        <em:id>xulapp@example.com</em:id>
        <em:name>Stats viewer</em:name>
        <em:version>1.0</em:version>

        <em:targetApplication>
            <Description>
                <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
                <em:minVersion>1.5</em:minVersion>
                <em:maxVersion>2.0.0.*</em:maxVersion>
            </Description>
        </em:targetApplication>

        <!-- Optional Items -->
        <em:creator>Uche Ogbuji</em:creator>
        <em:description>
            Opens a window in which you can view simple stats about Web pages
        </em:description>
        <em:homepageURL>http://www.ibm.com/developerworks/web/</em:homepageURL>

    </Description>
</RDF>

    

Most of this is self explanatory and I won't go into too much detail except to mention that the em:targetApplication element I use specifies any Firefox version greater than 1.5 and less than 2.0.1. The long em:id string is the application UUID of Firefox itself.

You should be able to just drop in the sample files from the last article to the locations indicated in Listing 1 with the only exception that you should adjust the CSS link in "stats.xul", which used to assume "stats.css" was in the same directory. Instead you would use the chrome link chrome://stats/skin/stats.css.

Completing the extension functionality

Once again I had to dummy out some of the functionality in stats.js XUL file because of security restrictions. Now that I have it packaged as an extension, I can put back in the missing pieces. Listing 6 is an updated stats.js that truly does execute the counting operations expected in the extension.


Listing 6. Complete JavaScript for the Stats viewer window
                
//Invoked in response to a click on the "Go!" button
function change_url(event)
{
    //Variables for convenient access to specific elements in the XUL
    var urlbox = document.getElementById("url");
    var contentview = document.getElementById("contentview");
    var wordcountbox = document.getElementById("wordcount");
    var charcountbox = document.getElementById("charcount");
    var elemcountbox = document.getElementById("elemcount");

    alert(urlbox.value);
    contentview.contentDocument.location.href = urlbox.value;

    //Use Firefox XPath to get the raw text of the document
    var doctext = contentview.contentDocument.evaluate(
        "string(.)", document, null, XPathResult.STRING_TYPE, null).stringValue;

    var wordcount = doctext.split(" ").length()
    alert(wordcount);
    wordcountbox.value = String(wordcount);
    charcountbox.value = String(doctext.length());

    //Use Firefox XPath to count elements in the document
    var elemcount = contentview.contentDocument.evaluate(
        "count(//*)", document, null, XPathResult.NUMBER_TYPE, null).numberValue;

    elemcountbox.value = String(elemcount);
}

    

This time I use two XPath expressions on the Web page loaded by the user into the contentview. From the first expression string(.), I can get the character count (from the string length) and the word count (by splitting the string by spaces and counting the results). From the second expression //* I get the number of elements. I then use these values to update the appropriate text boxes.


Wrap up

Creating Firefox extensions can be tricky, but luckily the community has produced a lot of extensions that help in various ways. The sidebar lists a few of these. In this pair of articles you've learned that with some care you can develop Firefox extensions, and that you can use this knowledge for easier development of some things that seemed obvious territory for a full application. There is an impressive amount of power in XUL, JavaScript, and the Mozilla platform overall. And you can target more than just the classic Web browser. It's usually not very hard to adapt Firefox extensions for Mozilla (and generally XUL-based) applications such as the Songbird music management application and the Flock social Web browser.

"Extension developers' extensions"

Firebug is a top-notch debugger for Firefox, and practically a must for any extension developer.

I mentioned Ted Mielczarek's Extension Developer's Extension in the last article, but it has more than just the Live XUL Editor. You might find the JavaScript debugging tools useful, and there are also tools to simplify packaging tasks for extensions.

Firefox's error console is very useful, but it's getting even better in upcoming browser releases and you can take early advantage of these improvements by installing the Console² extension.

.


Resources

Learn

Discuss

About the author

Uche photo

Uche Ogbuji is Partner at Zepheira, LLC, a solutions firm specializing in the next generation of Web technologies. Mr. Ogbuji is lead developer of 4Suite, an open source platform for XML, RDF and knowledge-management applications and lead developer of the Versa RDF query language. He is a Computer Engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. You can find more about Mr. Ogbuji at his Weblog Copia.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=262515
ArticleTitle=Browser extensions using XUL, Part 2: Assemble a cross-platform Firefox extension
publish-date=10162007
author1-email=uche@ogbuji.net
author1-email-cc=