Create your own browser extensions, Part 1: Extend your reach into Chrome

Write a basic browser extension for Chrome

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 build an extension for Chrome.

26 Mar 2013 - Added About this series and item about Part 2 in Resources and revised the abstract.

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 21 August 2012)

Also available in Chinese Russian Japanese Vietnamese Portuguese

Before you start

About this series

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

  • In this part, take your Google Chrome extension from inception to the app store.
  • In Part 2, 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.

For this article you need to download and install Google Chrome version 19 or higher (the examples shown here are based on version 19). You'll also want something you can use to edit HTML, CSS, and JavaScript. It will help if you have some experience using Chrome and some Chrome extensions first. Take some time to browse the Chrome Web Store (see Resources). Look at the extensions that are available, and take some for a spin. This will provide some context for this article.

Why build a browser extension?

You might want to build a browser extension for several reasons. A common use of browser extensions is to create an interaction between a browser and another application or service. Evernote, 1Password, and Adobe Shadow all do something like this, as do many other extensions. Or you might want to add some new functionality to the browser that it lacks, by adding developer tools or screen capture utilities. Some developers have written extensions with very focused purposes—sports score trackers, enhancements for specific websites, weather reporting, and more. People do a lot of different things with extensions. What will you do?


What extension are you building?

To demonstrate the process for building an extension in Chrome, you'll write an extension called Gawkblocker. Gawkblocker will allow you to block certain domains that you're trying not to visit for one reason or another. Gawkblocker has several components:

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

Broadly speaking, Gawkblocker will attach a listener to each tab or window, and when the tab URL changes, the extension compares the URL to the list of blocked domains. If the URL matches a blocked domain, the request is redirected to an extension page instead (see Figure 1).

Figure 1. The Gawkblocker extension
Screen capture showing the sites being blocked by Gawkblocker

Gawkblocker reaches into the browser in specific ways to do some specific things that you'll do in other extensions. 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 each other?
  • How deep can you go into the user's data?

The process of building Gawkblocker should answer these questions.


What is your reference document?

Your reference document is the Chrome Extension docs (see Resources). For the most part the documentation for writing Chrome extensions is pretty thorough. A simple one-page tutorial on how to build a "Hello World" extension will give you a super-simple overview of how extensions are put together, but it won't be much use for a deeper dive on the subject.


Anatomy of a Chrome extension

A typical Chrome extension consists of a manifest file and some combination of a background page, UI pages, and content scripts.

Everything begins with a manifest file called manifest.json. This file contains the information Chrome needs to know about how to load your extension—things like title, description, required permissions, and icon references.

A background page is a single page that is launched and run within its own context, no matter how many tabs or windows are open. An extension can have only one background page. They are useful for extensions where you want to have a process running that persists across all pages within Chrome.

UI Pages refer to any page that the extension presents to the user. A UI Page might be a pop-up, an options page, a page that's part of the extension, or a page that overrides a default Chrome page (like the new tab page).

Content scripts are JavaScript files that you inject into web pages to interact with them. Content scripts execute in their own isolated context, but they can access the DOM of the page. Content scripts can also communicate with other pages in your extension using a special message passing API.

For Gawkblocker, you will use a manifest file, a JavaScript file that contains some core functionality (something you will make portable), a background page, an options page, a pop-up, a redirect landing page, and an icon or two. You can download a working Gawkblocker extension from the Chrome Web Store if you want to see the extension in action as the various parts are described.


Gawkblocker's manifest

Listing 1 is a copy of the manifest.json file for Gawkblocker.

Listing 1. The manifest.json file for Gawkblocker.
{
  "name": "GawkBlocker",
  "version": "1.7",
  "description": "Tired of taking the Nerd Bait? Use GawkBlocker!",
  "background_page" : "background.html",
  "options_page": "options.html",
  "icons": {
    "16" : "images/GB-19.png",
    "48" : "images/GB-48.png",
    "128" : "images/GB-128.png"
  },
  "browser_action": {
    "default_icon": "images/GB-19.png",
    "default_title": "GawkBlocker",
    "default_popup": "popup.html"
  },
  "permissions": ["tabs"]
}

Look at some of these entries in detail.

version
Whenever you upload a new version of your extension to the Chrome Web Store, you will need to increment your version number or the upload will fail.
icons
The icons property contains a list of available icons, according to sizes. Chrome looks for specific sizes of icons to use them in different contexts. Listing 1 specifies paths to icon files that are relative to the extension.
browser_action
When you specify a browser_action, you tell Chrome that you want an icon to the right of the URL bar (Chrome calls the URL the Omnibar), and that clicking on the icon will do something. In this case, clicking on the browser action button opens a pop-up.
Only specify a browser_action when your extension applies to any web page. If you want an alternative that only applies to specific pages or types of pages, then you specify a page_action. You can not specify both a page_action and a browser_action—it's one or the other.
permissions
In this case, the only permission you need to request is 'tabs'—this permission will give you some information about individual tabs (all you really want is the URL). More permissions are available, but you should only request the permissions you need. Both Chrome and Android handle permissions in very similar ways—when the user installs your extension, Chrome will explicitly show you the permissions the extension is requesting, in much the same way that you are prompted with a list of permissions when installing an Android application.

Now that you've walked through the manifest, let's get into the files that give functionality to Gawkblocker.


The Gawkblocker core class

Most of what Gawkblocker does will be controlled by a single core JavaScript file. In the file, you define a Storage Manager object (SM) to handle persisting data between sessions (right now this is just a wrapper for localStorage), and a Gawkblocker object (GB) that handles some of the common functionality (managing the blocked sites list and options). See Listing 2.

Listing 2. Defining a Storage Manager object and Gawkblocker object
var SM = (function () {

    var my = {};

    my.get = function (key) {
        return localStorage.getItem(key);
    }

    ...

    return my;

}());

var GB = (function (SM) {
    var my = {};

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

    if (!SM.get("blocklist")) {
        SM.put("blocklist", JSON.stringify(my.blockTheseSites));
    }

    my.getBlockedSites = function () {
        return JSON.parse(SM.get("blocklist"));
    }

    my.setWatchThisInstead = function (value) {
        ...
    }

    my.getWatchThisInstead = function () {
        return SM.get("instead");        
    }

    my.addBlockedSite = function (site) {
        my.blockedSites = JSON.parse(SM.get("blocklist"));
        my.blockedSites[site] = "Custom Add";
        SM.put("blocklist", JSON.stringify(my.blockedSites));
    }

    my.removeBlockedSite = function (site) {
        my.blockedSites = JSON.parse(SM.get("blocklist"));
        delete my.blockedSites[site];
        SM.put("blocklist", JSON.stringify(my.blockedSites));
    }

    return my;
}(SM));

I used the Module Patten here to create a couple objects that should be reasonably portable. Now see what it's like to use them in the background page.


The background page

Gawkblocker will use a background page to listen to URLs and compare them against the blocked sites list. Remember, you can only have one instance of the background page, and it is shared across all open tabs and windows (excluding incognito windows, unless you request that permission or the user explicitly allows it). Because the background page itself has no visible component, there's no display-related HTML in the page itself.

Listing 3 shows the code that listens to tabs for updates.

Listing 3. Listening to tabs for updates
chrome.tabs.onUpdated.addListener(function(tabId, changedInfo, tab) {
    for (site in GB.getBlockedSites()) {
        if (tab.url.match(site)) {
            chrome.tabs.update(tabId, {"url" : GB.getWatchThisInstead()}, 
function () {});
        }
    }
});

There are more performant ways to walk the list of blocked sites, but that's not what you should look at. Instead, look at how you attached to Chrome using chrome.tabs.onUpdated.addListener and passing in a callback.

Every chrome.* API call varies, but generally they follow this pattern of calling a method and passing in a callback. Most of the API calls are asynchronous. This can cause timing issues if you don't expect this behavior, so read what the reference document has to say on this subject.


The pop-up

Gawkblocker doesn't really need a pop-up, but by including one provides an easily accessible place for the user to see what sites are currently being blocked. Listing 4 shows the most interesting part of this page.

Listing 4. Pop-up page
$(document).ready(function(){
    $.each(chrome.extension.getBackgroundPage().GB.getBlockedSites(), 
function (index, value) {
        $("#blockedlist").append("<div class='siterow' title='"+value+"'>
<div class='sitename'>"+index+"</div><span class='sitedesc'> : 
"+value+"</span></div>");
    });
});

The call to chrome.extension.getBackgroundPage().GB.getBlockedSites() is how you get information from the background page into the pop-up. This is one way to handle communication between pages within an extension, though it's not necessary to do this in the pop-up. You might include the gawkblocker.js file and call the GB object directly. But if you have a lot of asynchronous activity, it's useful to have the different components ask questions from the same place.

The pop-up asks the background page 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 (see Figure 2).

Figure 2. Pop-up
Screen capture of Gawkblocker's pop-up showing blocked sites

The options page

You need a place to control the behavior of the extension. If you specify an Options page using the manifest (as you did), the User can easily access the page either from the extensions management page, or right click on the browser action button. Options pages are themselves optional (see Figure 3).

Figure 3. Options page
Screen capture showing the configurable options for Gawkblocker

Gawkblocker uses the Options page to allow the user to specify behavior when visiting a blocked site, to add new sites to the block list, or to remove sites from the block list entirely (see Listing 5).

Listing 5. Options page
$("#blockthistoo").click(function () {
    GB.addBlockedSite($("#dontgothere").val());
    ...
});

You can go through the background page, just like you did in the pop-up (see Listing 6).

Listing 6. Background page
$("#blockthistoo").click(function () {
    chrome.extension.getBackgroundPage().GB.addBlockedSite($("#dontgothere").val());
    ...
});

Depending on your extension, you might prefer one method over the other.


The redirect landing

Finally, when a request is redirected, it needs to go to a local page. This page is the most straight-forward of all. As written, it's simply a page that embeds a YouTube video ("Hey You! Don't Watch That! Watch This!"). It doesn't interact with the rest of the extension in any meaningful way, it's just the end point of the process (see Figure 4).

Figure 4. Redirect landing
Screen capture of a browser page titled 'Wouldn't you rather watch madness??' with a running video

Pulling it all together

Now you have this great extension, but how do you test it out? To start, load the extension files without packaging them. This is called loading an unpacked extension and you control it from the Extension Management page (see Resources) (you can also right-click on any installed extension and select Manage Extensions).

To load unpacked extensions, select the Developer Mode box to enable the Load unpacked extension button. To load your extension, click the Load unpacked extension button and navigate to the folder containing the extension. If your manifest file has an error, your extension might be disabled or simply not load. Chrome will tell you if this is the case.

Once you're certain your extension is ready for prime time, you have a couple different options for distributing your extension. You might send out the raw files, but your users won't be able to install the extension unless they also go to the extensions management page and enable developer mode. Your other options are to distribute a packed extension, or put your extension in the Chrome Web Store.


Distributing a packed extension

On the extension management page, next to the Load unpacked extension button, is a Pack extension button (see Figure 5). Click this button to start the process of packaging your extension for distribution. Chrome will ask you for the directory of the unpacked extension, and optionally for a private key file. I'll come to that private key file in a moment.

Figure 5. The extensions management page
Screen capture of Chromes extensions management page

The first time you pack the extension, Chrome will generate two files: a .crx file and a .pem file. The .crx file is your packed extension, all ready for distribution. The .pem file is the private key mentioned earlier. Chrome tells you to keep that file safe, and they mean it—if you want to package an update to your extension, you will need that private key file. Without it, Chrome will treat the update as a completely new extension.

Once you have your packed extension, you can distribute it any way you please—email it out, put it up on your website, package it in your own installer, drop it on flash drives, whatever fits with your plans. The main downside to distributing it yourself is you also handle the update process.

For your Chrome extension to check for updates, add a line to the manifest file that tells it where to look for updates (see Listing 7).

Listing 7. Checking for updates
{
  "name": "GawkBlocker",
  "version": "1.7",
  "update_url": "http://yourawesomedomain.com/ext/updates.xml",
  ...
}

Then you will need to host an XML file that follows the format specified in the Autoupdating section of the reference document. It's not complicated, but it does put more work on your end. And it also means you don't get any visibility in the Chrome Web Store.


Getting your extension into the Chrome Web Store

The process for getting an extension into the Chrome Web Store is less technically involved than distributing the extension on your own, but there are some things you will need to do.

First, get set up as a developer in the Web store. For this you need a Google account. Once you sign in, go to the Developer Dashboard and register as a developer. This will cost you $5.00, and you'll need to pay using Google Wallet. If your Google account is well established, you might be activated in the Developer Dashboard right away. If your account is new or not well used, there might be a delay while the account is reviewed.

After you register as a developer, go back to the directory with your unpacked extension and zip up all the extension files—just the HTML, CSS, JavaScript and any image files. Don't include the .crx or .pem files. The Chrome Web Store will create a .crx file from your zip, and sign it with a private .pem key file.

Once your account is ready to go and you have a zip file, you're ready to click that big blue Add new item button on the dashboard to upload your extension (see Figure 6).

Figure 6. Chrome Web store
Screen capture of the developer page in Chrome's web store

If anything is wrong with your zip file, the dashboard will spit it out with some error messages. If your zip is well formed and your manifest is correctly formatted, you'll see on a page that asks for meta-data about your extension (screen captures, countries, description, and so on). Enter information appropriate to your extension and you can push it to the store with ease.


Learn how updates work

Once your extension is in the store, make a couple small changes and upload updates to the store to get a feel for what that process is like. A common gotcha is forgetting to update the version number in the manifest file. Every time you upload an update, you can click the Update extensions now button on the Extensions Management page to force an update. Chrome Extensions ping for updates every few hours, so it doesn't take long to get updates out to your users.


Finding the answers

Let's see how the answers to our questions turned out:

How hard is it to get a presence somewhere in the browser UI? Pretty easy as it turns out. You only needed to specify a browser_action in the manifest file and provide an icon.

What is involved in persisting data between browser sessions? In Chrome, you get access to localStorage that persists across application launches. That makes persisting data between browser sessions very simple.

How do the different parts of the extension talk to each other? All the pages can talk to the background page using chrome.extension.getBackgroundPage(), and a message passing API allows the different extension components to talk to each other if needed.

How deep can you go into the user's data? As deep as the user lets you. The permissions each have specific warnings, though it might not be entirely clear to the user exactly what those permissions imply.


Conclusion

Having written a basic extension for Chrome, it won't be long now before you go even deeper. You can access a lot in the chrome.* APIs and you've only begun to scratch the surface. Now that you've learned the basics, start over from the beginning and see how far you can extend your reach into Chrome.


Download

DescriptionNameSize
Article source codeGawkBlocker.zip60KB

Resources

Learn

Get products and technologies

  • Use the Google Chrome release from the Developer Channel to get the latest version of Developer Tools.
  • Evaluate IBM products in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement Service Oriented Architecture efficiently.

Discuss

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, XML
ArticleID=830867
ArticleTitle=Create your own browser extensions, Part 1: Extend your reach into Chrome
publish-date=04052013