Mozilla Labs has announced a new project called Jetpack to provide an easy way for users and developers to enhance the Firefox web browser. Jetpack is a lightweight extension system that makes it possible to build new Firefox features with HTML, JavaScript, and other conventional web technologies and, although it's still at a relatively early stage of development, the project already delivers some extremely useful capabilities. In this tutorial, we will look at what Jetpack has to offer, then show you how to use it to augment Firefox's functionality.

The Firefox user interface is largely built with XUL, an XML-based user interface design language. Much like regular web content, XUL user interface elements can be styled with CSS and manipulated with JavaScript through the browser Document Object Model (DOM). These characteristics are what enable Firefox to offer its powerful extension system.

Regular Firefox extensions primarily consist of XUL overlays and JavaScript code that modifies the browser's behavior. Many extensions use XPCOM, which provides a JavaScript interface to the underlying native code components of the browser. XPCOM provides deep access to some of the browser's most sophisticated functionality, but it can be arcane and difficult to use.

The Jetpack project is a higher-level extension system that abstracts away the complexities of XUL and the idiosyncrasies of XPCOM. It allows web developers to use their knowledge of HTML and JavaScript, and it also offers a simpler deployment model that spares developers from the challenges of extension packaging.

Jetpack comes with its own set of standard JavaScript libraries, which include simple functions that provide easy access to native browser features. This library is currently very limited and provides only a few features, but it will be extended as the project matures. Jetpack also comes bundled with a version of the popular JQuery JavaScript library to simplify HTML manipulation. The developers hope to eventually add support for importing additional third-party JavaScript libraries.

Jetpack is intended to complement Firefox's existing extension system and is not a full replacement. It is well-suited for minor additions and feature improvements, but its simplicity limits the extent to which it can reshape the browser. For more complex enhancements, the conventional extension system will still be needed. It's helpful to think about Jetpack as being closely analogous to the Greasemonkey user scripting system but with an emphasis on modifying the browser rather than page content.

Use Jetpack to integrate Digg in Firefox

The Jetpack prototype is distributed as a Firefox extension, but it could eventually become a standard browser feature in the future. To get started with Jetpack, install the extension by clicking the link on the project's web site. After installation (and a browser restart), you can access Jetpack's features by typing about:jetpack into your browser's URL bar. Next, click the Develop link to get to Jetpack's built in development interface.

The Develop page houses a simple textbox in which you can input JavaScript code. To run the code in Jetpack, simply click the "Try out this code" link that appears below the textbox. This provides a quick and easy way to prototype new Jetback-based extensions. The code will run directly in your current Firefox instance and will be active in other tabs and windows. The content will also persist through a page refresh, so you can leave and come back to it without worrying about losing it.

One of the main user interface enhancement features supported in the Jetpack prototype is the ability to add new items to the Firefox statusbar. In this article, I will show you how to add a Digg notifier to the statusbar. It will use Digg's web API to retrieve the Digg count for each page and display the number alongside a little Digg icon. The number will be shown in red if the page's Digg status is "popular," and in green if it is "upcoming."

This can be achieved by using the jetpack.statusbar.append method. It will create an iframe in the statusbar that can be populated with any HTML content. The method takes several parameters, including the HTML content, the desired width of the iframe, and an onReady function handler that will be executed when the statusbar item is created:

jetpack.statusBar.append({ html: ' Some arbitrary HTML ', width: 55, onReady: function(doc) { // Code to execute when the statusbar item is created } });

The onReady handler is where you will put the code that dictates the behavior of the statusbar item. The parameter that is passed to that function, which we call doc in the example above, is the HTML document of the statusbar item iframe.

In our Digg display, we want to update the Digg count every time a new page is loaded and every time that the user switches to a different tab. To implement a custom behavior that will take place when a page loads, we will bind a callback to the jetpack.tabs.onReady event in the onReady handler. In the following example, a popup notification will be displayed every time that a page finishes loading:

jetpack.statusBar.append({ html: ' This is a test! ', width: 200, onReady: function(doc) { jetpack.tabs.onReady(function() { jetpack.notifications.show("The page finished loading!"); }); } });

The next step is to implement the function that will obtain the Digg data and update the text in the statusbar. We will use JQuery for those tasks because it provides useful functions for handling JSON and manipulating HTML content.

function updateDiggs(doc) { url = jetpack.tabs.focused.contentWindow.location.href; $.getJSON("http://services.digg.com/stories", {"type": "json", "appkey": "http://arstechnica.com", "link": url}, function(data) { if (data.stories) $(doc).find("#count").text(data.stories[0].diggs).css( "color", data.stories[0].status == "popular" ? "red" : "green") else $(doc).find("#count").text("0").css("color", "black"); }); }

In the updateDiggs function, we use the Jetpack API to get the URL of the currently active page. This URL is then passed as the link parameter in the Digg web API call which is executed with JQuery's getJSON method. In the getJSON callback, we check to see if the returned JSON data includes the stories attribute, which indicates whether the page has been submitted to Digg. If it has, then the statusbar text is modified to display the number of Diggs and the text color is changed based on the item's status. If the current page has not been submitted to Digg, then the statusbar item text will be changed to show 0.

The #count HTML element that is referenced in the function would be defined in the HTML string that gets passed into jetpack.statusBar.append . The following code is the complete example:

jetpack.statusBar.append({ html: ' Digg ', width: 55, onReady: function(doc) { var url = jetpack.tabs.focused.contentWindow.location.href; function updateDiggs() { if (url != jetpack.tabs.focused.contentWindow.location.href) { url = jetpack.tabs.focused.contentWindow.location.href; console.log(url); $.getJSON("http://services.digg.com/stories", {"type": "json", "appkey": "http://arstechnica.com", "link": url}, function(data) { console.log(data); if (data.stories) $(doc).find("#count").text(data.stories[0].diggs).css( "color", data.stories[0].status == "popular" ? "red" : "green") else $(doc).find("#count").text("0").css("color", "black"); }); } } jetpack.tabs.onReady(updateDiggs); setInterval(updateDiggs, 5000); } });

In the complete example, we bind the updateDiggs call to the onReady function and also to a setInterval function, which will perform the action repeatedly at the specified time interval. Jetpack unfortunately doesn't allow us to bind to a tab change event, so we use the interval as a workaround. The updateDiggs function checks to see if the URL is the same as it was during the last update and will only retrieve Digg data if it detects that the URL has changed. This prevents it from doing the full update at every interval.

Testing and deploying a Jetpack feature

You can run your code in the about:jetpack environment for testing purposes. If your code contains bugs, you can see them in Firefox's JavaScript error console, which is available from the Tools menu. You can also use Firebug to debug Jetpack code.

To make the Jetpack feature available to end users, you can take advantage of the Jetpack installation system. Save your script in a file on a web server and then make an HTML page that references it in a link element in the header:

JetPack Installation Test page

When a Jetpack user visits the page, he will see a bar along the top that invites him to install the script. Clicking on the Install button will cause Jetpack to display a warning message that asks the user to confirm installation. Unlike conventional Firefox extensions, Jetpack scripts are installed immediately and do not require users to restart their browsers. Users can manage and remove Jetpack scripts by clicking the "Installed Features" link in the about:jetpack environment.

Conclusion

There are many other Jetpack features that were not addressed in this article. It is possible, for example, to modify page content with a Jetpack script. In the Jetpack API, the jetpack.tabs.focused.contentDocument attribute will give you the page DOM, which you can easily manipulate with JQuery.

The developers behind Jetpack are working on integrating support for Jetpack script development into Bespin, Mozilla's innovative browser-based development environment. The prospect of interactively extending the browser through a browser-based editing environment is very compelling—it's like a modern web adaptation of the underlying principles behind the programmable Emacs editor. It's a major contribution to the ongoing transformation of Firefox from a browser into a platform.

The Jetpack prototype is still incomplete, but it shows promise and could be extremely powerful when the developers extend it to support a broader number of user interface enhancements, such as menu and toolbar customization.