How to build a firefox extension


before we start, we can introduce a development tool firstly

web-ext which is a command line tool designed to speed up various parts of the extension development process, making development faster and easier.
you can install it to global environment.

npm install --global web-ext

Now, we create a folder name “firebox-extension-demo”, and create a manifest.json file. we can build a firefox extension start from a manifest.json, the file is the enterance of a extension. The manifest.json should include the name, version,manifest_version at least.

    "name": "996.ICU",
    "version": "1.0",
    "manifest_version": "1.0",
    "description": "firefox version of 996.ICU",
    "icons": {
        "48": "/icons/icon_48.png",
        "96": "/icons/icon_96.png"
    "content_scripts": [{
      "matches": [],
      "js": ["content.js"]
    "background": {
      "scripts": ["background.js"],
      "page": ["background-page.htmml"]

“icons” will add a icon a firefox’s extension page, “content_scripts” ‘s value will be loaded to the “matches” URLs.

Here is an image to descripe how a extension be structed;
firefox extension

what’s content script?

A content script is a part of your extension that runs in the context of a particular web page. Content scripts can access and modify the page’s DOM, just like normal page scripts can. Content scripts can only access a small subset of the WebExtension APIs. And the background script can access the full WebExtension APIs.

then we go to the root folder of firefox extension, use command

web-ext run

This starts Firefox and loads the extension temporarily in the browser, and will auto reload when your source files are edited.

and we build a content.js file

Those are subset of WebExtensions API which content script can access to: extenstion.getURl,extension.inIncognitoContext, runtime APIs, includes connect(), getManifest(), getUrl(), onConnect(), onMessage() and sendMessage(), all storage APIs.

what’s background page?

Sometimes, extensions often need to maintain long-term state or perform long-term operations independently of the lifetime of any particular web page or browser window. That is what background scripts are for.
You can descript a background script or you can descript a background page then load the background script. This can support the ES6 module.
Background page can access to window glogal varible, full DOM API and full WebExtensions API.

what’s extensiton page(standlong)?

The Firefox support three kinds of page to build a extension. they are slides, popup window and options page. And the support a special page to build your app.
you can use window.create() or tab.create() to create a standlong page.
This page can include html, css and javascript. it can access all full WebExtensions API too. But it has it’s own global.

demo to create a extension page

const createData = {
  type: 'detached_panel', // "normal", "popup", "panel", "detached_panel"
  url: 'standlong.html',
  width: 345,
  heigt: 500,

How content script communicate with background script?

  1. one-off message.

content => background

browser.runtime.sendMessage(extensionId, message, options);
browser.runtime.onMessage.addListener(function(message, sender, sendRespone) {});

background => content

browser.tabs.sendMessage(tabId, message, options);
browser.runtime.onMessage.addListener(function(message, sender, sendRespone) {});
  1. connection-based messaging.
    This is kind of longer-lived connection between the two contexts

content => background

// content
// background
browser.runtime.onConnect.addListener(function(portFromCS) {
    console.log('message from content', message);

How content script communicate with page script?

content script can’t access to page’s script directly, but it can use DOM window.postMessage() API and window.addEventListener API to access page’s script indirectly.
pagg => content

// page
window.postMessage(message, targetOrigin);
// content
window.addEventListener('message', function(event) {
 console.log('message from page script',;

content to page is the same.