Create your own browser extensions, Part 2: Extend your reach into Firefox

Write a basic browser extension for Firefox

Every browser has its fans, detractors, advantages, and disadvantages. One thing they all have in common is that people increasingly spend more time using them. This series examines how to build the same basic extension for Chrome, Firefox, and Safari. You'll learn what it is like to extend each browser, how hard or easy it is to perform some common tasks, and how to distribute your extension. In this article, you'll build an extension for Firefox.

02 Apr 2013 - Added links to Part 3 in About this series and Resources.

05 Apr 2013 - Added links to Part 4 in About this series and Resources.

Duane O'Brien, Software developer, Freelance

Photograph of Duane O'BrienDuane O'Brien is a tired computer scientist. He has written a number of articles on developing web applications and various PHP frameworks. To learn more about Duane, check out his blog or read his tweets.



05 April 2013 (First published 26 March 2013)

Also available in Chinese Russian Japanese Vietnamese

About this series

In this four-part series, build the Gawkblocker extension for three browsers: Chrome, Firefox, and Safari.

  • In Part 1, take your Google Chrome extension from inception to the app store.
  • In this part, build an add-on (or extension) for Mozilla Firefox.
  • In Part 3 , tailor the extension to the Safari browser.
  • In Part 4, tweak your code for a browser-agnostic extension.

This article, the second in a four-part series on building browser extensions, shows you how to build an add-on (or extension) for Mozilla Firefox. In "Create your own browser extensions, Part 1: Extend your reach into Chrome," you built the Gawkblocker extension for Chrome. In this article, you port the Gawkblocker Chrome extension to Firefox. (See Download to get the complete source code.)

Gawkblocker redux

Recall that Gawkblocker enables you (and its other users) to block certain domains that you prefer not to visit, such as time-consuming blogs. Gawkblocker has several components:

  • A pop-up window that shows you what you're blocking
  • A visible browser icon that's the point of entry for the extension
  • An options page for configuring the domains that you want to block and where you want to go instead

In Chrome, your Gawkblocker extension attaches a listener to each tab or window and matches URLs to a block list, redirecting blocked URLs to a local page. Now you will learn how the Gawkblocker extension changes in Firefox.

Gawkblocker reaches into the browser in specific ways to do things that you'll do with all the extensions you build in this series. As in Part 1, you want answers to these questions:

  • How hard is it to get a presence somewhere in the browser UI?
  • What is involved in persisting data between browser sessions?
  • How do the different parts of the extension talk to one another?
  • How deep can you go into the user's data?

You can answer these questions after you complete the process of building Gawkblocker for Firefox.


Before you start

Add-ons are extensions

Firefox extensions are called add-ons. Chrome, Safari, and Internet Explorer all use the term extensions. This series uses both terms interchangeably to refer to Firefox add-ons.

For this article, download and install Firefox version 12 or higher (see Resources). (The examples that are shown here are based on version 12.) You also want a tool that edits HTML, CSS, and JavaScript. Some experience using Firefox and some Firefox add-ons is helpful. If you lack such experience, browse the extensions available at the Mozilla Add-ons page (see Resources). Try a few of the extensions for context for this article.

Your reference document is the Mozilla Add-on SDK developer guide (see Resources). You'll do most of your work in the Add-on Builder — a web-based tool for building Firefox extensions. The Add-on Builder is part of Jetpack, a Firefox project. Jetpack's purpose is to make it easy to start writing extensions with only HTML, CSS, and JavaScript (just as you did for the Chrome extension). Other ways to build extensions for Firefox are also available.

See Download to get the complete source code.


Anatomy of a Firefox extension

Alternative approaches

In addition to the process shown in this article, you have a couple other ways to build an extension for Firefox. You can download the SDK files that the Add-on Builder uses directly from the Developer Hub (see Resources). Download the SDK files so you can use the IDE of your choice as you build your add-on.

You can also build a traditional (or classic or XUL) extension. Building extensions in this way has some drawbacks: Installing the extension requires a restart, and the process of writing the extension is more complicated. The payoff is that you can modify the browser in ways not possible with the Add-on Builder or SDK. For example, with XUL you can get placement of your extension icon in places other than the Add-on bar. You can read more about XUL extensions on the Mozilla Development Network (see Resources).

Firefox extensions that are built with the Add-on Builder use CommonJS conventions to pull in required libraries. Any combination of HTML, CSS, and JavaScript files can be in the extension, but everything begins with the main.js file.

The main.js file is the heart of your Firefox extension. It tells Firefox which modules to pull in, and is where you perform any initialization tasks for your extension. Looking back at the Chrome extension in Part 1, the main.js is analogous to the background.html page. It runs behind the scenes, no one interacts with it directly, and it runs once during startup.

You also can have a number of pages that you display in panels within your Firefox extension. You'll use one such page as a combined pop-up and options page in Figure 1:

Figure 1. The pop-up/options page
Screen capture of the Gawkblocker pop-up/options page

You can also use content scripts in Firefox, basically the same way you can in Chrome. Content scripts are JavaScript files that you can inject into web pages for interacting with them. In Firefox, content scripts run effectively in the page context, but access to and from the DOM is proxied to prevent some security problems. Content scripts can communicate with the rest of your extension using port.

For Gawkblocker, you'll use:

  • A main.js file
  • A JavaScript file that contains some core functionality (mostly portable from the Chrome extension)
  • A combination pop-up/options page
  • An icon or two

The implementation is slightly slimmed down from what you did in Chrome, but the user experience is effectively the same. You can download a working Gawkblocker extension from my Add-on Builder profile (see Resources) to see the extension in action as I describe its various parts here.


Using the Add-on Builder

To use the Add-on Builder, in Figure 2 and available at https://builder.addons.mozilla.org/, you must complete a free registration process. Then, you can log in to the Add-on Builder and create your own add-ons.

Figure 2. The Add-on Builder
Screen capture of the Mozilla Add-on Builder

Fully walking through the Add-on Builder interface is outside the scope of this article, but two things in the file structure are noteworthy. The Lib directory is where Firefox looks when you pull in libraries with require. You'll put the Gawkblocker core JavaScript class in that directory. The Data directory is where you'll put images, HTML, CSS, and any other assets that your extension might serve up.

When you build an add-on and test it, you are prompted to install the Add-on Builder helper. This helper handles uninstalling and installing your add-on during development.


Updating the Gawkblocker core class

In Part 1, you wrote what seemed like a reasonably portable core class file for Gawkblocker. Now that you want to use it in the Firefox extension, you can find out how portable that class file really is.

As it turns out, you must make a couple of important changes:

  • Use the Firefox simple-storage extension API instead of localStorage
  • Add the GB object to exports

In the class file from Part 1, you defined a Storage Manager object (named SM) — a wrapper for localStorage — to handle persisting data between sessions. That code doesn't work inside a Firefox extension. Instead, Firefox provides an API called simple-storage that can persist the data for you. You can easily update the Storage Manager object from Part 1, as in Listing 1:

Listing 1. Updating the SM object
var SM = (function () {
    var SS = require("simple-storage");
    var my = {};
 
    my.get = function (key) {
        return SS.storage[key];
    }
    ...

    return my;

}());

The GB object doesn't require a change, but you must add it to exports, following CommonJS conventions. The final line of Listing 2 handles that task:

Listing 2. Adding the GB object to exports
var GB = (function (SM) {
   var my = {};

   my.blockTheseSites = {
       "gawker.com"        : "Gawker Media",
       "io9.com"           : "SciFi Blog",
       "gizmodo.com"       : "Gadget Blog",
       ...
   }

   ...

}(SM));

exports.GB = GB;

As these changes are minimal, you can easily roll them into the GB object so it works in either Firefox or Chrome. (I'll leave that for you to take on as your own project if you want.)

Rename the file to GB.js and upload it to the Lib directory, so you can see how to use the object in main.js.


The main.js file in Gawkblocker

In Chrome, you checked URLs with a background page to see whether to block them. In Firefox, you do this check inside the main.js file. Before main.js can do anything, you must pull in the modules and APIs that you plan to use with a series of require statements, as in Listing 3:

Listing 3. require statements
var data = require("self").data,
    tabs = require("tabs"),
    GB = require("GB").GB,
    popupPanel = require("panel").Panel({
        height: 500,
        contentURL: data.url("popup.html")
    });

In sequence, the statements in Listing 3 tell main.js to give you:

  • An object that you use to access the Data directory
  • An object for handling tabs
  • The GB object that you exported from the main class
  • A popupPanel object to hold the pop-up window. The code also creates the pop-up window.

Additionally, since the pop-up window that you create also serves as the options page, you must set up some listeners. In Chrome, you reached into the background page and told it what you wanted it to do. In Firefox, you send messages to main.js for the same purpose. For example, Listing 4 shows a listener that sets the default landing page for sites that users choose to block:

Listing 4. Listener that sets the default landing page for sites that users choose to block
popupPanel.port.on("watchthis", function () {
    GB.setWatchThisInstead(http://www.youtube.com/watch?v=N-uyWAe0NhQ);
    console.log("watchthis");
});

You'll pass messages to that port a little later, when I discuss the pop-up page.

The main.js file is also where Gawkblocker accesses the tabs to see which, if any, URLs that the user wants to block. Listing 5 shows the code that listens to tabs for updates:

Listing 5. Listening to tabs for updates
tabs.on("ready", function checkForBlock(tab) {
    for (site in GB.getBlockedSites()) {

        if (tab.url.match(site)) {
            tab.url = GB.getWatchThisInstead();
        }
    }
});

The functions and performance are similar to what you did in Chrome. Using the APIs also is similar (call a method, pass a callback) to what you did in Chrome.

Finally, to add that little badge on the lower-right corner of the browser, create a Widget, as in Listing 6:

Listing 6. Creating a Widget
require("widget").Widget({
    id: "GBBrowserAction",
    label: "Gawkblocker",
    contentURL: data.url("images/GB-19.png"),
    panel: popupPanel
});

Once you have everything in place for the main.js file, you can dig into the changes for the pop-up page.


The pop-up page

In the Chrome extension, the pop-up page was just a list of domains that you blocked. Now is a good time to iterate on that design. For the Firefox extension, migrate the functionality from the options page into the pop-up page. And add a click handler to the title shown in the pop-up page that swaps the options div and the domain list. The options allow user sites access to the block list and supply the redirect address for blocked sites. Figure 3 shows the options in the Gawkblocker pop-up page:

Figure 3. Options in the Gawkblocker pop-up page
Screen capture of the Add-on Builder shows the options in the Gawkblocker pop-up page

When you set up a port in Listing 4, remember to listen for watchthis in main.js. In Listing 7, you send that message from the pop-up page using addon.port.emit:

Listing 7. Sending the message from the pop-up page using addon.port.emit
$("#watchthis").click(function () {
    addon.port.emit("watchthis");
    $("#status").text("YOU'RE GOOD MATE.  ");
});

You get the list of blocked sites from main.js by also listening for the list using port. In main.js, send the list when the pop-up page tells you that it's ready, as in Listing 8:

Listing 8. Sending the list when the pop-up object tells you that it's ready
popupPanel.port.on("pop", function () {
    popupPanel.port.emit("blocklist", GB.getBlockedSites());
    ...
});

And in the pop-up object, you listen for it and modify the page as in Listing 9:

Listing 9. Listening and modifying the page
addon.port.on("blocklist", function (blocklist) {
    $("#blockedlist").children().remove();
    $.each(blocklist, function (index, value) {
        $("#blockedlist").append("<div class='siterow' title='"+value+"'>
<div class='sitename'>"+index+"</div><span class='sitedesc'> : 
"+value+"</span></div>");
        showBlockList(blocklist);
    });
});

The pop-up page asks main.js for a list of blocked sites. It then iterates through that list and appends the blocked-site details to a display div in the pop-up window.


The redirect landing page

In Chrome, you sent redirects to a landing page that was part of the extension. Firefox does not do the same thing, so send them straight to the source (the YouTube URL that you embedded on the landing page: "Hey You! Don't Watch That! Watch This!").

You set the initial condition for the redirect in main.js, as in Listing 10:

Listing 10. Setting the initial condition
if (!GB.getWatchThisInstead()) {
   GB.setWatchThisInstead("http://www.youtube.com/watch?v=N-uyWAe0NhQ");
}

Figure 4 shows the redirect landing page:

Figure 4. The redirect landing page
Screen capture in Firefox of the redirect landing page on YouTube

Testing from the Add-on Builder

When you use the Add-on Builder, Firefox makes it easy to test your extension as you go. You get an error console, a test button, and an Add-on Builder helper that automatically reloads your extension every time you save. Figure 5 shows the Add-on Builder helper in action:

Figure 5. Add-on Builder utilities
Screen capture of the Add-on Builder helper displaying an error message

Distributing your extension

When your extension is ready for prime time, you have a couple options for distributing it. If you mark the Add-on as public in your profile, you can send a link to your potential users, and they can install it from there. You can also download-distribute a packaged extension, or upload your extension to addons.mozilla.org.

Distributing a packaged extension

To distribute a packaged extension, download it from the Add-on Builder. Click the Download icon to get an XPI file that anyone can install. You can distribute it however you want (email, hosting, installers, and other methods). But you're responsible for handling updates and hosting.

Uploading to addons.mozilla.org

The process to get an extension into addons.mozilla.org is less technically involved than distributing the extension on your own, but you go through a review process and some other hurdles. To work through this process, click the upload to AMO link next to the extension in your profile, in Figure 6:

Figure 6. Uploading to AMO
Screen capture of the Add-on dashboard, with the Upload to AMO link

Follow the upload instructions from there.


Finding your answers

Now that your Firefox add-on is complete, it's time to see how the answers to your questions turned out, compared to your answers for Chrome:

How hard is it to get a presence somewhere in the browser UI?
Not much harder than in Chrome when you want the presence in the Add-on Bar at the bottom of the browser. You got that by creating a Widget in the main.js file.
What is involved in persisting data between browser sessions?
Use the Firefox-specific simple-storage API. If you want one Storage Manager class to work in both Chrome and Firefox, implement some feature detection.
How do the different parts of the extension talk to one another?
Create this kind of communication with port and set up listeners and emitters.
How deep can you go into the user's data?
Without explicit permission from the user, you can still access at least every URL the user visits. That's pretty deep.

Conclusion

The extension is only the start of what you can do inside a Firefox add-on. You are far from testing the limits of what the Add-on Builder can give you. Even more is available if you decide to go deeper. Between the Add-on Builder, the Add-on SDK, and the more complicated XUL extensions, you have many ways to extend your reach into Firefox.

Stay tuned for Part 3 in this series, in which you port Gawkblocker to the Safari browser.


Download

DescriptionNameSize
Article source codefirefox-sourcecode.zip38KB

Resources

Learn

Get products and technologies

Discuss

  • Get involved in the developerWorks community: Connect with other developerWorks users as you explore the developer-driven blogs, forums, groups, and wikis.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


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. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

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.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

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

 


All information submitted is secure.

Dig deeper into Open source on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Web development
ArticleID=862242
ArticleTitle=Create your own browser extensions, Part 2: Extend your reach into Firefox
publish-date=04052013