Building Web Extensions Because You Can

Development

Reading Time: 6 minutes

Browser extensions aren’t a new thing. Firefox had them since before it was named Firefox. Each browser after that created yet another unique way of extending the capabilities of that particular browser. Recently, however, the browsers have been talking more often, and consequently things are getting better.

If you’re a web developer, things have gotten far more cohesive in recent years. Consequently, extending the web now looks a lot like writing for the web (and not like writing ActiveX controls or Objective-C libraries).

These days you can write the same JS, CSS, and HTML, describe it in some JSON, and install it in everything from Microsoft Edge to Vivaldi.

What’s a Web Extension Anyway?

The current thing we call a web extension was started in Google Chrome as a simpler variation on the older (and more proprietary) means of extending a web browser.

A web extension is built of pieces you’ll recognize: JS, CSS, and HTML. These pieces are then tied together in a manifest.json file that also handles describing things like permissions, storage, and other browser or webpage-specific connections.

Mozilla’s What are WebExtensions is a great place to start for a deep dive. For now, let’s take a look through a basic manifest.json file and a handful of things you might use today.

Building blocks

Web extensions are typically packaged in a zip-based archive (despite some variations in file extensions) and contain a manifest.json file. Using the manifest.json as a starting point, the browser then loads the necessary bits into its UI, starts up background scripts, and possibly adds scripts directly to certain web pages.

Behold the Anatomy of a WebExtension:

webextension-anatomy

Here’s the breakdown:

  • Background pages implement long-running logic and can be built from an HTML page or one or more JS files. The HTML page approach can be handy for preloading templates or giving your extension an offscreen DOM work space.
  • Content scripts are directly injected into certain webpages and allow your web extension to interact with their initial content. However, this environment is quite different from injecting a <script> element within a page. However, you can do that too…with some caveats. If you’re wanting to directly change the experience of someone else’s website or application (making it easier or more advanced for yourself or others), these are essential.
  • A browser action (one per web extension) can be added to the browser’s toolbar and can be used to trigger all kinds of events or experiences. One such experience is opening a related popup which is its own HTML (+ JS + CSS) page loaded in a browser-defined popup container. The scripts in this popup can communicate via message passing with background scripts and in turn pass some amount of information to and from content scripts.
  • Page actions are similar to browser actions, but describe a button to be added to the address bar. However, unlike browser action buttons, the page action button has to be toggled from the background script and are off by default. So if you just add the info to the manifest.json file, you’ll be disappointed until you run chrome.pageAction.show() to turn the thing on. Consequently, these are a bit trickier to use, but they have the advantage of being experimentally page-specific (living nearer the URL in the address bar). Also, it should be noted that browser actions and page actions are mutually exclusive.
  • An options page can be provided with your extension and defines a UI for users to view and change your addon’s settings. The default action of most browsers is to link to that page in the addons or extensions settings of the browser (which, you may have guessed, most users rarely visit). You can, however, open it directly using the chrome.runtime.openOptionsPage() method from a browser or page action and optionally open it in a new tab (versus within the settings UX). This page can be very handy for providing a larger visual space for interacting with your web extension.
  • Web-accessible resources allow you to reference assets you’ve packaged with your web extension from within your content scripts or webpages you’re altering. For instance, if you wanted your web extensions logo displayed in each page you’ve altered, you can make the image file web accessible and then inject an <img /> tag that references the internal resource using a special URL (via chrome.extension.getURL()).

Below is a sample manifest.json file that shows all the keys and shapes described in the list above (and a few others). The Chrome developer docs have a complete description of the Manifest File Format that outlines the other available bits.

{
  // Required
  "manifest_version": 2,
  "name": "My Extension",
  "version": "1.0.0",

  // Recommended
  "default_locale": "en",
  "description": "A plain text description",
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },

  // Pick one (or none)
  "browser_action": {
    "default_icon": { // optional
      "16": "images/icon16.png", // optional
      "24": "images/icon24.png", // optional
      "32": "images/icon32.png"  // optional
    },
    "default_title": "Google Mail", // optional; shown in tooltip
    "default_popup": "popup.html" // optional
  },
  "page_action": {...same as browser_action...},

  // Optional - depends on what you're building
   "background": {
     "scripts: ["bg.js"]
     // or
     "page": "bg.html"
  },

  "content_scripts": [
    {
      "matches": ["http://www.example.com/*"],
      "css": ["myextension.css"],
      "js": ["jquery.js", "myscript.js"]
    }
  ],

  "options_page": "options.html",

  "permissions": ["tabs"],

  "web_accessible_resources": [
    "images/*.png"
  ]
}

Comments aren’t allowed in JSON, btw, but I figured having them in this case might be useful.

Sign up for a free Codeship Account

Building a Basic Web Extension

Web extensions are just static files that your browser loads into its own space (where changes are prevented) and executes them from there using its own policies. For example, Mozilla provides a Node.js-based script called web-ext that’s very helpful for testing in Firefox and necessary for packaging the extension to distribute to the addons.mozilla.org site.

However, I’ve found that testing and developing in Chrome is simpler in general. There’s a Developer Mode to toggle in the Extensions section of the Chrome settings. After that, you simply load your manifest.json file using the Load unpacked extension… button. From that point on, click Reload for the extension you’re developing (or press F5 or Ctrl+R on the extensions page) when you’ve made your change. Rinse. Repeat.

The web-ext script does allow for hot reloading, so if you’d rather not click Reload in Chrome, you might enjoy that better.

Start from an example

Another fabulous resource from your friends working on Firefox is the webextensions-examples repo. There are a couple examples that only apply to migrating old Firefox addons, but the vast majority of these will work in all the browsers mentioned and are great places to dive in.

These examples are also openly licensed bits of code that you can (and should) start with for your first foray into web extensions. Pick the one that sounds the most similar to what you want to do and iterate from there.

Gatekeepers

Extending the web used to be a thing you could do from within the web. Once upon a time, distributing your own Firefox extension could be done from your own website.

Obviously, that came with some risks to the users who found them. There was some work to do “signed extensions” for a while, but most recently those efforts have been abandoned (or kept to the “enterprise editions”) and one must go through the gatekeepers to publish the extension.

  • For Chrome (and Vivaldi), there’s the Chrome Web Store.
  • For Firefox, there’s the Addons site.
  • For Edge, you get to go through the Windows Store (sorry..can’t link to anything ’cause it’s in a Windows 10 app…).
  • For Opera, there’s Opera Addons.

So, yeah. Despite all of these browsers now shipping identical web extensions, they all have their own editorial processes (Firefox and Opera), fees (Chrome and Edge), and requirements for the “marketing” bits.

If there’s any part of the web extension world that I’d like to see fixed, this is it. Don’t let it deter you, though. As we’ve already seen, you can benefit from using web extensions you’ve written for yourself and installed off your hard drive.

However, if you want to get your web extension in front of your audience, you’ll have to ask these folks.

Should You Build a Web Extension?

Should we be building web extensions? Maybe, but ask these questions first: Does your audience want them? Are their reasons to go through the gatekeepers to get it in front of them? Are the browser’s limitations in the way of your innovations?

Regardless of your answers to these questions, digging into web extensions will add a new tool to your belt and provide you with a better understanding of what’s possible.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles.
Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.



We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.

  • Conor Gilsenan

    Useful write up! Thanks for sharing