Work with the Tabs API - Mozilla

Work with the Tabs API
======================

Tabs let a user open several web pages in their browser window and then switch between those web pages. With the Tabs API, you can work with and manipulate these tabs to create utilities that provide users with new ways to work with tabs or to deliver the features of your extension.

In this how-to article we'll look at:

* Permissions needed to use the Tabs API.
* Discovering more about tabs and their properties using `tabs.query`.
* Creating, duplicating, moving, updating, reloading, and removing tabs.
* Manipulating a tab's zoom level.
* Manipulating a tab's CSS.

We then conclude by looking at some other, miscellaneous features offered by the API.

**Note:** There are some Tab API features covered elsewhere. These are the methods you can use to manipulate tab content with scripts (`tabs.connect`, `tabs.sendMessage`, and `tabs.executeScript`). If you want more information on these methods, see the Concepts article Content scripts and the how-to guide Modify a web page.

Permissions and the Tabs API
----------------------------

For the majority of the Tabs API functions you don't need any permissions; however, there are some exceptions:

* `"tabs"` permission is needed to access the `Tab.url`, `Tab.title`, and `Tab.favIconUrl` properties of the Tab object. In Firefox, you also need `"tabs"` to perform a query by URL.
* Host permission is needed for `tabs.executeScript()` or `tabs.insertCSS()`.

The following is how you might request `"tabs"` permission in your extension's manifest.json file:

```json
"permissions": [
  "<all\_urls>",
  "tabs"
],
```

This request gives you use of all Tabs API feature on all website your user visits. There is also an alternative approach for requesting permissions to use `tabs.executeScript()` or `tabs.insertCSS()` where you don't need host permission, in the form of `"activeTab"`. This permission provides the same rights as `"tabs"` with `<all_urls>`, but with two restrictions:

* the user must interact with the extension through its browser or page action, context menu, or shortcut key.
* it only grants permission within the active tab.

The benefit of this approach is the user won't get a permissions warning saying your extension can "Access your data for all websites". This is because `<all_urls>` permission gives an extension the ability to execute scripts in any tab, any time it likes, whereas `"activeTab"` is limited to allowing the extension to perform a user requested action in the current tab.

Discovering more about tabs and their properties
------------------------------------------------

There will be occasions when you want to get a list of all the tabs in all the browser windows. Other times you might want to find a subset of tabs that match some specific criteria, such as those opened from a specific tab or displaying pages from a particular domain. And once you have your list of tabs, you'll probably want to know more about their properties.

This is where `tabs.query()` comes in. Used alone to get all tabs or taking the `queryInfo` object—to specify query criteria such as whether the tab is active, in the current window, or one or more of 17 criteria—`tabs.query()` returns an array of `tabs.Tab` objects containing information about the tabs.

Where you want information about the current tab only, you can get a `tabs.Tab` object for that tab using `tabs.getCurrent()`. If you have a tab's ID, you can get its `tabs.Tab` object using `tabs.get()`.

### How to example

To see how `tabs.query()` and `tabs.Tab` are used, let's walk through how the tabs-tabs-tabs example adds the list of "switch to tabs" to its toolbar button popup.

![The tabs toolbar menu showing the switch to tap area](/en-US/docs/Mozilla/Add-ons/WebExtensions/Working_with_the_Tabs_API/switch_to_tab.png)

manifest.json

Here is the `manifest.json`:

```json
{
  "browser\_action": {
    "default\_title": "Tabs, tabs, tabs",
    "default\_popup": "tabs.html"
  },
  "description": "A list of methods you can perform on a tab.",
  "homepage\_url": "https://github.com/mdn/webextensions-examples/tree/main/tabs-tabs-tabs",
  "manifest\_version": 2,
  "name": "Tabs, tabs, tabs",
  "permissions": ["tabs"],
  "version": "1.0"
}
```

**Note:**

* **`tabs.html` is defined as the `default_popup` in `browser_action`.** It is displayed whenever the user clicks the extension's toolbar icon.
* **Permissions includes tabs.** This is needed to support the tab list feature, as the extension reads the title of the tabs for display in the popup.

tabs.html

`tabs.html` defines the content of the extension's popup:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="tabs.css" />
  </head>

  <body>
    <div class="panel">
      <div class="panel-section panel-section-header">
        <div class="text-section-header">Tabs-tabs-tabs</div>
      </div>

      <a href="#" id="tabs-move-beginning">
        Move active tab to the beginning of the window
      </a>
      <br />

      <!-- Define the other menu items -->

      <div class="switch-tabs">
        <p>Switch to tab</p>
        <div id="tabs-list"></div>
      </div>
    </div>

    <script src="tabs.js"></script>
  </body>
</html>
```

This does the following:

1. The menu items are declared.
2. An empty `div` with the ID `tabs-list` is declared to contain the list of tabs.
3. `tabs.js` is called.

tabs.js

In `tabs.js`, we'll see how the list of tabs is built and added to the popup.

#### Creating the popup

First, an event handler is added to execute `listTabs()` when `tabs.html` is loaded:

```js
document.addEventListener("DOMContentLoaded", listTabs);
```

The first thing that `listTabs()` does is to call `getCurrentWindowTabs()`. This is where `tabs.query()` is used to get a `tabs.Tab` object for the tabs in the current window:

```js
function getCurrentWindowTabs() {
  return browser.tabs.query({ currentWindow: true });
}
```

Now, `listTabs()` is ready to create the content for the popup.

To start with:

1. Grab the `<div id="tabs-list">` element.
2. Create a document fragment (into which the list will be built).
3. Set counters.
4. Clear the content of the `<div id="tabs-list">` element.

```js
function listTabs() {
 getCurrentWindowTabs().then((tabs) => {
    const tabsList = document.getElementById('tabs-list');
    const currentTabs = document.createDocumentFragment();
    const limit = 5;
    let counter = 0;

    tabsList.textContent = '';
```

Next, we'll create the links for each tab:

1. Loops through the first 5 items from the `tabs.Tab` object.
2. For each item, add a hyperlink to the document fragment.
    * The link's label—that is, its text—is set using the tab's `title` (or the `id`, if it has no `title`).
    * The link's address is set using the tab's `id`.

```js
for (const tab of tabs) {
  if (!tab.active && counter <= limit) {
    const tabLink = document.createElement("a");

    tabLink.textContent = tab.title || tab.id;

    tabLink.setAttribute("href", tab.id);
    tabLink.classList.add("switch-tabs");
    currentTabs.appendChild(tabLink);
  }

  counter += 1;
}
```

Finally, the document fragment is written to the `<div id="tabs-list">` element:

```js
    tabsList.appendChild(currentTabs);
  });
}
```

#### Working with the active tab

Another related example feature is the "Alert active tab" info option that dumps all the `tabs.Tab` object properties for the active tab into an alert:

```js
else if (e.target.id === "tabs-alertinfo") {
  callOnActiveTab((tab) => {
    let props = "";
    for (const item in tab) {
      props += `${ item } = ${ tab[item] } \n`;
    }
    alert(props);
  });
}
```

Where `callOnActiveTab()` finds the active tab object by looping through the `tabs.Tab` objects looking for the item with active set:

```js
document.addEventListener("click", (e) => {
  function callOnActiveTab(callback) {
    getCurrentWindowTabs().then((tabs) => {
      for (const tab of tabs) {
        if (tab.active) {
          callback(tab, tabs);
        }
      }
    });
  }
}
```

Creating, duplicating, moving, updating, reloading, and removing tabs
---------------------------------------------------------------------

Having gathered information about the tabs you'll most likely want to do something with them—either to offer users features for manipulating and managing tabs or to implement functionality in your extension.

The following functions are available:

* create a new tab (`tabs.create()`).
* duplicate a tab (`tabs.duplicate()`).
* remove a tab (`tabs.remove()`).
* move a tab (`tabs.move()`).
* update the tab's URL—effectively browse to a new page—(`tabs.update()`).
* reload the tab's page (`tabs.reload()`).

**Note:** These functions all require the ID (or IDs) of the tab they are manipulating:

* `tabs.duplicate()`
* `tabs.remove()`
* `tabs.move()`

Whereas the following functions will act on the active tab (if no tab `id` is provided):

* `tabs.update()`
* `tabs.reload()`

### How to example

The tabs-tabs-tabs example exercises all of these features except for updating a tab's URL The way in which these APIs are used is similar, so we'll look at one of the more involved implementations, that of the "Move active tab to the beginning of the window list" option.

But first, here is a demonstration of the feature in action:

manifest.json

None of the functions require a permission to operate, so there are no features in the manifest.json file that need to be highlighted.

tabs.html

`tabs.html` defines the "menu" displayed in the popup, which includes the "Move active tab to the beginning of the window list" option, with a series of `<a>` tags grouped by a visual separator. Each menu item is given an `id`, which is used in `tabs.js` to determine which menu item is being requested.

```html
<a href="#" id="tabs-move-beginning">
  Move active tab to the beginning of the window
</a>
<br />
<a href="#" id="tabs-move-end">Move active tab to the end of the window</a>
<br />

<div class="panel-section-separator"></div>

<a href="#" id="tabs-duplicate">Duplicate active tab</a><br />
<a href="#" id="tabs-reload">Reload active tab</a><br />
<a href="#" id="tabs-alertinfo">Alert active tab info</a><br />
```

tabs.js

To implement the "menu" defined in `tabs.html`, `tabs.js` includes a listener for clicks in `tabs.html`:

```js
document.addEventListener("click", (e) => {
  function callOnActiveTab(callback) {
    getCurrentWindowTabs().then((tabs) => {
      for (const tab of tabs) {
        if (tab.active) {
          callback(tab, tabs);
        }
      }
    });
  }
});
```

A series of `if` statements then look to match the `id` of the item clicked.

This code snippet is for the "Move active tab to the beginning of the window list" option:

```js
if (e.target.id === "tabs-move-beginning") {
  callOnActiveTab((tab, tabs) => {
    let index = 0;
    if (!tab.pinned) {
      index = firstUnpinnedTab(tabs);
    }
    console.log(`moving ${tab.id} to ${index}`);
    browser.tabs.move([tab.id], { index });
  });
}
```

It's worth noting the use of `console.log()`. This enables you to output information to the debugger console, which can be useful when resolving issues found during development.

![Example of the console.log output, from the move tabs feature, in the debugging console](/en-US/docs/Mozilla/Add-ons/WebExtensions/Working_with_the_Tabs_API/console.png)

The move code first calls `callOnActiveTab()` which in turn calls `getCurrentWindowTabs()` to get a `tabs.Tab` object containing the active window's tabs. It then loops through the object to find and return the active tab object:

```js
function callOnActiveTab(callback) {
  getCurrentWindowTabs().then((tabs) => {
    for (const tab of tabs) {
      if (tab.active) {
        callback(tab, tabs);
      }
    }
  });
}
```

#### Pinned tabs

A feature of tabs is that the user can *pin* tabs in a window. Pinned tabs are placed at the start of the tab list and cannot be moved. This means that the earliest position a tab can move to is the first position after any pinned tabs. So, `firstUnpinnedTab()` is called to find the position of the first unpinned tab by looping through the `tabs` object:

```js
function firstUnpinnedTab(tabs) {
  for (const tab of tabs) {
    if (!tab.pinned) {
      return tab.index;
    }
  }
}
```

We now have everything needed to move the tab: the active tab object from which we can get the tab `id` and the position the tab is to be moved to. So, we can implement the move:

```js
browser.tabs.move([tab.id], { index });
```

The remaining functions to duplicate, reload, create, and remove tabs are implemented similarly.

Manipulating a tab's zoom level
-------------------------------

The next set of functions enable you to get (`tabs.getZoom`) and set (`tabs.setZoom`) the zoom level within a tab. You can also retrieve the zoom settings (`tabs.getZoomSettings`) but, at the time of writing, the ability to set the settings (`tabs.setZoomSettings`) wasn't available in Firefox.

The level of zoom can be between 30% and 500% (represented as decimals `0.3` to `5`).

In Firefox the default zoom settings are:

* **default zoom level:** 100%.
* **zoom mode:** automatic (so the browser manages how zoom levels are set).
* **scope of zoom changes:** `"per-origin"`, meaning that when you visit a site again, it takes the zoom level set in your last visit.
### How to example

The tabs-tabs-tabs example includes three demonstrations of the zoom feature: zoom in, zoom out, and reset zoom. Here is the feature in action:

Let's take a look at how the zoom in is implemented.

manifest.json

None of the zoom functions require permissions, so there are no features in the manifest.json file that need to be highlighted.

tabs.html

We have already discussed how the `tabs.html` defines the options for this extension, nothing new or unique is done to provide the zoom options.

tabs.js

`tabs.js` starts by defining several constants used in the zoom code:

```js
const ZOOM\_INCREMENT = 0.2;
const MAX\_ZOOM = 5;
const MIN\_ZOOM = 0.3;
const DEFAULT\_ZOOM = 1;
```

It then uses the same listener we discussed earlier so it can act on clicks in `tabs.html`.

For the zoom in feature, this runs:

```js
  else if (e.target.id === "tabs-add-zoom") {
    callOnActiveTab((tab) => {
      browser.tabs.getZoom(tab.id).then((zoomFactor) => {
        //the maximum zoomFactor is 5, it can't go higher
        if (zoomFactor >= MAX\_ZOOM) {
          alert("Tab zoom factor is already at max!");
        } else {
          let newZoomFactor = zoomFactor + ZOOM\_INCREMENT;
          //if the newZoomFactor is set to higher than the max accepted
          //it won't change, and will never alert that it's at maximum
          newZoomFactor = newZoomFactor > MAX\_ZOOM ? MAX\_ZOOM : newZoomFactor;
          browser.tabs.setZoom(tab.id, newZoomFactor);
        }
      });
    });
  }
```

This code uses `callOnActiveTab()` to get the details of the active tab, then `tabs.getZoom` gets the tab's current zoom factor. The current zoom is compared to the defined maximum (`MAX_ZOOM`) and an alert issued if the tab is already at the maximum zoom. Otherwise, the zoom level is incremented but limited to the maximum zoom, then the zoom is set with `tabs.getZoom`.

Manipulating a tab's CSS
------------------------

Another significant capability offered by the Tabs API is the ability to manipulate the CSS within a tab—add new CSS to a tab (`tabs.insertCSS()`) or remove CSS from a tab (`tabs.removeCSS()`).

This can be useful, for example, if you want to highlight certain page elements or change the default layout of the page.

### How to example

The apply-css example uses these features to add a red border to the web page in the active tab. Here is the feature in action:

Let's walk through how it's set up.

manifest.json

The `manifest.json` requests permissions required to use the CSS features. You need either:

* `"tabs"` permission and host permission; or,
* `"activeTab"` permission.

The latter is the most useful, as it allows an extension to use `tabs.insertCSS()` and `tabs.removeCSS()` in the active tab when run from the extension's browser or page action, context menu, or a shortcut.

```json
{
  "description": "Adds a page action to toggle applying CSS to pages.",

  "manifest\_version": 2,
  "name": "apply-css",
  "version": "1.0",
  "homepage\_url": "https://github.com/mdn/webextensions-examples/tree/main/apply-css",

  "background": {
    "scripts": ["background.js"]
  },

  "page\_action": {
    "default\_icon": "icons/off.svg"
  },

  "permissions": ["activeTab", "tabs"]
}
```

You will note that `"tabs"` permission is requested in addition to `"activeTab"`. This additional permission is needed to enable the extension's script to access the tab's URL, the importance of which we'll see in a moment.

The other main features in the manifest.json file are the definition of:

* **a background script**, which starts running as soon as the extension is loaded.
* **a "page action"**, which defines an icon to be added to the browser's address bar.

background.js

On startup, `background.js` sets some constants to define the CSS to be applied, titles for the "page action", and a list of protocols the extension will work in:

```js
const CSS = "body { border: 20px solid red; }";
const TITLE\_APPLY = "Apply CSS";
const TITLE\_REMOVE = "Remove CSS";
const APPLICABLE\_PROTOCOLS = ["http:", "https:"];
```

When first loaded, the extension uses `tabs.query()` to get a list of all the tabs in the current browser window. It then loops through the tabs calling `initializePageAction()`.

```js
browser.tabs.query({}).then((tabs) => {
  for (const tab of tabs) {
    initializePageAction(tab);
  }
});
```

`initializePageAction` uses `protocolIsApplicable()` to determine whether the active tab's URL is one the CSS can be applied to:

```js
function protocolIsApplicable(url) {
  const anchor = document.createElement("a");
  anchor.href = url;
  return APPLICABLE\_PROTOCOLS.includes(anchor.protocol);
}
```

Then, if the example can act on the tab, `initializePageAction()` sets the tab's `pageAction` (navigation bar) icon and title to use the "off" versions before making the `pageAction` visible:

```js
function initializePageAction(tab) {
  if (protocolIsApplicable(tab.url)) {
    browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
    browser.pageAction.setTitle({ tabId: tab.id, title: TITLE\_APPLY });
    browser.pageAction.show(tab.id);
  }
}
```

Next, a listener on `pageAction.onClicked` waits for the `pageAction` icon to be clicked, and calls `toggleCSS` when it is.

```js
browser.pageAction.onClicked.addListener(toggleCSS);
```

`toggleCSS()` gets the title of the `pageAction` and then takes the action described:

* **For "Apply CSS":**
    + toggles the `pageAction` icon and title to the "remove" versions.
    + applies the CSS using `tabs.insertCSS()`.
* **For "Remove CSS":**
    + toggles the `pageAction` icon and title to the "apply" versions.
    + removes the CSS using `tabs.removeCSS()`.

```js
function toggleCSS(tab) {
  function gotTitle(title) {
    if (title === TITLE\_APPLY) {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/on.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE\_REMOVE });
      browser.tabs.insertCSS({ code: CSS });
    } else {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE\_APPLY });
      browser.tabs.removeCSS({ code: CSS });
    }
  }

  browser.pageAction.getTitle({ tabId: tab.id }).then(gotTitle);
}
```

Finally, to ensure that the `pageAction` is valid after each update to the tab, a listener on `tabs.onUpdated` calls `initializePageAction()` each time the tab is updated to check that the tab is still using a protocol to which the CSS can be applied.

```js
browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
  initializePageAction(tab);
});
```

Some other interesting abilities
--------------------------------

There are a couple of other Tabs API features that don't fit into one of the earlier sections:

* Capture the visible tab content with `tabs.captureVisibleTab`.
* Detect the primary language of the content in a tab using `tabs.detectLanguage`. This could be used, for example, to match the language in your extension's UI with that of the page it's running in.
Learn more
----------

If you want to learn more about the Tabs API, check out:

* Tabs API reference
* Example extensions (many of which use the Tabs API)


What next? - Mozilla

What next?
==========

You'll now be ready to start turning your idea for a browser extension into reality. Before you start that journey, it's worth being aware of a few things that will help to make it a smooth one.

You can find more about many of the things discussed on this page on Extension Workshop, a website dedicated to helping you write, test, publish, and distribute extensions for Firefox.

Your development environment
----------------------------

You don't need any special development or build environment tools to create browser extensions: It's entirely possible to create great browser extensions with no more than a text editor. However, you may have been developing for the web and have a set of tools and an environment you want to reuse. If you do, you need to be aware of a couple of things.

If you use minification or obfuscation tools to deliver your final code, you'll need to provide your source code to the AMO review process. Also, the tools you use—those for minification, obfuscation, and build processes—will need to be open source (or offer unlimited free use) and be available to run on the reviewer's computer (Windows, Mac, or Linux). Unfortunately, our reviewers can't work with commercial or web-based tools.

Learn more about development tools on Extension Workshop

Third-party libraries
---------------------

Third-party libraries are a great way to add complex features or functionality to your browser extensions quickly. When you submit an extension to the AMO review process, the process will also consider any third-party libraries used. To streamline the review, make sure you always download third-party libraries from their official website or repository, and if the library is minified provide a link to the source code. Please note that third-party libraries cannot be modified in any way.

Learn more about submitting source code on Extension Workshop

The Firefox Add-on Distribution Agreement
-----------------------------------------

Browser extensions need to be signed to install into the release or beta versions of Firefox. Signing takes place in addons.mozilla.org (AMO) and is subject to the terms and conditions of the Firefox Add-on Distribution Agreement. The goal of the agreement is to ensure Firefox users get access to well supported, quality add-ons that enhance the Firefox experience.

Read the agreement on Extension Workshop

Learn more about signing on Extension Workshop

The review process
------------------

When a browser extension is submitted for signing, it's subject to automated review. It may also be subject to a manual review, when the automated review determines that a manual review is needed. Your browser extension won't be signed until it's passed the automated review and may have its signing revoked if it fails to pass the manual review. The review process follows a strict set of guidelines, so it's easy to check and avoid any likely review problems.

Check out the review policy and guidelines on Extension Workshop

AMO featured browser extensions
-------------------------------

If you choose to list your browser extension on AMO, your extension could be featured on the AMO website, in the Firefox browser's add-on manager, or elsewhere on a Mozilla website. We've compiled a list of guidelines about how extensions are selected for featuring, by following these guidelines you give your extension the best chance of being featured.

Learn more about getting your add-ons featured on Extension Workshop

Continue your learning experience
---------------------------------

Now you know what lies ahead, it's time to dive into more details about browser extension development. In the sections that follow, you'll discover:

* More about the fundamental concepts behind browser extensions, starting with details on how to use the JavaScript APIs.
* A guide to the user interface components available to your browser extensions.
* A collection of how-to guides on achieving key tasks in your extensions or making use of the JavaScript APIs.
* A full reference guide to the JavaScript APIs.
* A full reference guide to the Manifest keys.

You'll also want to head on over to Extension Workshop where you'll find everything you need to know about creating extensions for Firefox, including:

* an overview of the Firefox extension features
* details of the tools and processes for developing and testing
* how to publish your extension on addons.mozilla.org or distribute it yourself
* how to manage your published extension
* an enterprise guide for developing and using extensions
* how to develop themes for Firefox
* details about the Firefox developer communities


Browser compatibility for manifest.json - Mozilla

Browser compatibility for manifest.json
=======================================

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Browser support for JavaScript APIs


Intercept HTTP requests - Mozilla

Intercept HTTP requests
=======================

 To intercept HTTP requests, use the `webRequest` API.
 This API enables you to add listeners for various stages of making an HTTP request.
 In the listeners, you can:

* Get access to request headers and bodies and response headers.
* Cancel and redirect requests.
* Modify request and response headers.

This article looks at three different uses for the `webRequest` module:

* Logging request URLs as they are made.
* Redirecting requests.
* Modifying request headers.
Logging request URLs
--------------------

 To see how you can use `webRequest` to log requests, create a new directory called "requests".
 In that directory, create a file called "manifest.json" and add:

```json
{
  "description": "Demonstrating webRequests",
  "manifest\_version": 2,
  "name": "webRequest-demo",
  "version": "1.0",

  "permissions": ["webRequest", "<all\_urls>"],

  "background": {
    "scripts": ["background.js"]
  }
}
```

Next, create a file called "background.js" and add:

```js
function logURL(requestDetails) {
  console.log(`Loading: ${requestDetails.url}`);
}

browser.webRequest.onBeforeRequest.addListener(logURL, {
  urls: ["<all\_urls>"],
});
```

 You use `onBeforeRequest` to call the `logURL()` function just before starting the request. The `logURL()` function grabs the URL of the request from the event object and logs it to the browser console.
 The `{urls: ["<all_urls>"]}` pattern means you intercept HTTP requests to all URLs.

To test it:

* Install the extension
* Open the Browser Console (use `Ctrl + Shift + J`)
* Enable *Show Content Messages* in the menu:

 ![Browser console menu: Show Content Messages](/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/browser_console_show_content_messages.png)
* Open some web pages.

 In the Browser Console, you should see the URLs for any resources the browser requests.
 For example, this screenshot shows the URLs from loading a Wikipedia page:

![Browser console menu: URLs from extension](/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/browser_console_url_from_extension.png)

Redirecting requests
--------------------

Now use `webRequest` to redirect HTTP requests. First, replace "manifest.json" with this:

```json
{
  "description": "Demonstrating webRequests",
  "manifest\_version": 2,
  "name": "webRequest-demo",
  "version": "1.0",

  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "https://developer.mozilla.org/"
  ],

  "background": {
    "scripts": ["background.js"]
  }
}
```

The changes here:

* Add the `webRequestBlocking` `permission`.
 This extra permission is needed when an extension wants to modify a request.
* Replace the `<all_urls>` permission with individual host permissions, as this is good practice to minimize the number of requested permissions.

Next, replace "background.js" with this:

```js
let pattern = "https://developer.mozilla.org/\*";
const targetUrl =
  "https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your\_second\_WebExtension/frog.jpg";

function redirect(requestDetails) {
  console.log(`Redirecting: ${requestDetails.url}`);
  if (requestDetails.url === targetUrl) {
    return;
  }
  return {
    redirectUrl: targetUrl,
  };
}

browser.webRequest.onBeforeRequest.addListener(
  redirect,
  { urls: [pattern], types: ["image"] },
  ["blocking"],
);
```

 Again, you use the `onBeforeRequest` event listener to run a function just before each request is made.
 This function replaces the `redirectUrl` with the target URL specified in the function. In this case, the frog image from the your second extension tutorial.

 This time you are not intercepting every request: the `{urls:[pattern], types:["image"]}` option specifies that you only intercept requests (1) to URLs residing under "https://developer.mozilla.org/" and (2) for image resources.
 See `webRequest.RequestFilter` for more on this.

 Also, note that you're passing an option called `"blocking"`: you must pass this whenever you want to modify the request.
 It makes the listener function block the network request, so the browser waits for the listener to return before continuing.
 See the `webRequest.onBeforeRequest` documentation for more on `"blocking"`.

To test it out, open a page on MDN that contains images (for example, the page listing extension user interface components), reload the extension, and then reload the MDN page. You see something like this:

![Images on a page replaced with a frog image](/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/beastify_by_redirect.png)

Modifying request headers
-------------------------

 Finally, use `webRequest` to modify request headers.
 In this example, you change the "User-Agent" header so the browser identifies itself as Opera 12.16, but only when visiting pages under "https://useragentstring.com/".

Update the "manifest.json" to include `https://useragentstring.com/` like this:

```json
{
  "description": "Demonstrating webRequests",
  "manifest\_version": 2,
  "name": "webRequest-demo",
  "version": "1.0",

  "permissions": [
    "webRequest",
    "webRequestBlocking",
    "https://useragentstring.com/"
  ],

  "background": {
    "scripts": ["background.js"]
  }
}
```

Replace "background.js" with code like this:

```js
let targetPage = "https://useragentstring.com/\*";

let ua =
  "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16";

function rewriteUserAgentHeader(e) {
  e.requestHeaders.forEach((header) => {
    if (header.name.toLowerCase() === "user-agent") {
      header.value = ua;
    }
  });
  return { requestHeaders: e.requestHeaders };
}

browser.webRequest.onBeforeSendHeaders.addListener(
  rewriteUserAgentHeader,
  { urls: [targetPage] },
  ["blocking", "requestHeaders"],
);
```

You use the `onBeforeSendHeaders` event listener to run a function just before the request headers are sent.

 The listener function is called only for requests to URLs matching the `targetPage` pattern.
 Also, note that you again pass `"blocking"` as an option. You also pass `"requestHeaders"`, meaning the listener is passed an array containing the request headers you expect to send.
 See `webRequest.onBeforeSendHeaders` for more information on these options.

 The listener function looks for the "User-Agent" header in the array of request headers, replaces its value with the value of the `ua` variable, and returns the modified array.
 This modified array is sent to the server.

 To test it out, open useragentstring.com and check that it identifies the browser as Firefox.
 Then reload the extension, reload useragentstring.com, and see that Firefox is now identified as Opera.

![useragentstring.com showing details of the modified user agent string](/en-US/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests/modified_request_header.png)

Learn more
----------

To learn about all the things you can do with the `webRequest` API, see its reference documentation.


Share objects with page scripts - Mozilla

Share objects with page scripts
===============================

**Note:** The techniques described in this section are only available in Firefox, and only from Firefox 49 onwards.

**Warning:** As an extension developer you should consider that scripts running in arbitrary web pages are hostile code whose aim is to steal the user's personal information, damage their computer, or attack them in some other way.

The isolation between content scripts and scripts loaded by web pages is intended to make it more difficult for hostile web pages to do this.

Since the techniques described in this section break down that isolation, they are inherently dangerous and should be used with great care.

As the content scripts guide notes, content scripts don't see changes made to the DOM by scripts loaded by web pages. This means that, for example, if a web page loads a library like jQuery, content scripts won't be able to use it, and have to load their own copy. Conversely, scripts loaded by web pages can't see changes made by content scripts.

However, Firefox provides some APIs that enable content scripts to:

* access JavaScript objects created by page scripts
* expose their own JavaScript objects to page scripts.
Xray vision in Firefox
----------------------

In Firefox, part of the isolation between content scripts and page scripts is implemented using a feature called "Xray vision". When a script in a more-privileged scope accesses an object that's defined in a less-privileged scope it sees only the "native version" of the object. Any expando properties are invisible, and if any properties of the object have been redefined, it sees the original implementation, not the redefined version.

The purpose of this feature is to make it harder for the less-privileged script to confuse the more-privileged script by redefining the native properties of objects.

So, for example, when a content script accesses the page's window, it won't see any properties the page script added to the window, and if the page script has redefined any existing properties of the window, the content script will see the original version.

Accessing page script objects from content scripts
--------------------------------------------------

In Firefox, DOM objects in content scripts get an extra property `wrappedJSObject`. This is an "unwrapped" version of the object, which includes any changes made to that object by any page scripts.

Let's take a simple example. Suppose a web page loads a script:

```html
<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="UTF-8" />
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>
```

The script adds an expando property to the global `window`:

```js
// main.js

let foo = "I'm defined in a page script!";
```

Xray vision means that if a content script tries to access `foo`, it will be undefined:

```js
// content-script.js

console.log(window.foo); // undefined
```

In Firefox, content scripts can use `window.wrappedJSObject` to see the expando property:

```js
// content-script.js

console.log(window.wrappedJSObject.foo); // "I'm defined in a page script!"
```

Note that once you do this, you can no longer rely on any of this object's properties or functions being, or doing, what you expect. Any of them, even setters and getters, could have been redefined by untrusted code.

Also note that unwrapping is transitive: when you use `wrappedJSObject`, any properties of the unwrapped object are themselves unwrapped (and therefore unreliable). So it's good practice, once you've got the object you need, to rewrap it, which you can do like this:

```js
XPCNativeWrapper(window.wrappedJSObject.foo);
```

See the document on Xray vision for much more detail on this.

Sharing content script objects with page scripts
------------------------------------------------

Firefox also provides APIs enabling content scripts to make objects available to page scripts. There are several approaches here:

* `exportFunction()`: export a function to page scripts.
* `cloneInto()`: export an object to page scripts.
* constructors from the page context
### exportFunction

Given a function defined in the content script, `exportFunction()` exports it to the page script's scope, so the page script can call it.

For example, let's consider an extension which has a background script like this:

```js
/\*
Execute content script in the active tab.
\*/
function loadContentScript() {
  browser.tabs.executeScript({
    file: "/content\_scripts/export.js",
  });
}

/\*
Add loadContentScript() as a listener to clicks
on the browser action.
\*/
browser.browserAction.onClicked.addListener(loadContentScript);

/\*
Show a notification when we get messages from
the content script.
\*/
browser.runtime.onMessage.addListener((message) => {
  browser.notifications.create({
    type: "basic",
    title: "Message from the page",
    message: message.content,
  });
});
```

This does two things:

* execute a content script in the current tab, when the user clicks a browser action
* listen for messages from the content script, and display a notification when the message arrives.

The content script looks like this:

```js
/\*
Define a function in the content script's scope, then export it
into the page script's scope.
\*/
function notify(message) {
  browser.runtime.sendMessage({ content: `Function call: ${message}` });
}

exportFunction(notify, window, { defineAs: "notify" });
```

This defines a function `notify()`, which just sends its argument to the background script. It then exports the function to the page script's scope. Now the page script can call this function:

```js
window.notify("Message from the page script!");
```

### cloneInto

Given an object defined in the content script, this creates a clone of the object in the page script's scope, thereby making the clone accessible to page scripts. By default, this uses the structured clone algorithm to clone the object, meaning that functions in the object are not included in the clone. To include functions, pass the `cloneFunctions` option.

For example, here's a content script that defines an object that contains a function, then clones it into the page script's scope:

```js
/\*
Create an object that contains functions in
the content script's scope, then clone it
into the page script's scope.

Because the object contains functions,
the cloneInto call must include
the `cloneFunctions` option.
\*/
let messenger = {
  notify(message) {
    browser.runtime.sendMessage({
      content: `Object method call: ${message}`,
    });
  },
};

window.wrappedJSObject.messenger = cloneInto(messenger, window, {
  cloneFunctions: true,
});
```

Now page scripts see a new property on the window, `messenger`, which has a function `notify()`:

```js
window.messenger.notify("Message from the page script!");
```

### Constructors from the page context

On the xrayed window object pristine constructors for some built-in JavaScript objects such as `Object`, `Function` or `Proxy` and various DOM classes are available. `XMLHttpRequest` does not behave in this way, see the XHR and fetch section for details. They will create instances belonging to the page global's object hierarchy and then return an xray wrapper.

Since objects created this way already belong to the page and not the content script passing them back to the page will not require additional cloning or exporting.

```js
/\* JavaScript built-ins \*/

const objA = new Object();
const objB = new window.Object();

console.log(
  objA instanceof Object, // true
  objB instanceof Object, // false
  objA instanceof window.Object, // false
  objB instanceof window.Object, // true
  "wrappedJSObject" in objB, // true; xrayed
);

objA.foo = "foo";
objB.foo = "foo"; // xray wrappers for plain JavaScript objects pass through property assignments
objB.wrappedJSObject.bar = "bar"; // unwrapping before assignment does not rely on this special behavior

window.wrappedJSObject.objA = objA;
window.wrappedJSObject.objB = objB; // automatically unwraps when passed to page context

window.eval(`
 console.log(objA instanceof Object); // false
 console.log(objB instanceof Object); // true

 try {
 console.log(objA.foo);
 } catch (error) {
 console.log(error); // Error: permission denied
 }

 try {
 objA.baz = "baz";
 } catch (error) {
 console.log(error); // Error: permission denied
 }

 console.log(objB.foo, objB.bar); // "foo", "bar"
 objB.baz = "baz";
`);

/\* other APIs \*/

const ev = new Event("click");

console.log(
  ev instanceof Event, // true
  ev instanceof window.Event, // true; Event constructor is actually inherited from the xrayed window
  "wrappedJSObject" in ev, // true; is an xrayed object
);

ev.propA = "propA"; // xray wrappers for native objects do not pass through assignments
ev.propB = "wrapper"; // define property on xray wrapper
ev.wrappedJSObject.propB = "unwrapped"; // define same property on page object
Reflect.defineProperty(
  // privileged reflection can operate on less privileged objects
  ev.wrappedJSObject,
  "propC",
  {
    get: exportFunction(() => {
      // getters must be exported like regular functions
      return "propC";
    }, window),
  },
);

window.eval(`
 document.addEventListener("click", (e) => {
 console.log(e instanceof Event, e.propA, e.propB, e.propC);
 });
`);

document.dispatchEvent(ev); // true, undefined, "unwrapped", "propC"
```

### Promise cloning

A Promise cannot be cloned directly using `cloneInto`, as Promise is not supported by the structured clone algorithm. However, the desired result can be achieved using `window.Promise` instead of `Promise`, and then cloning the resolution value like this:

```js
const promise = new window.Promise((resolve) => {
  // if just a primitive, then cloneInto is not needed:
  // resolve("string is a primitive");

  // if not a primitive, such as an object, then the value must be cloned
  const result = { exampleKey: "exampleValue" };
  resolve(cloneInto(result, window));
});
// now the promise can be passed to the web page
```


manifest.json - Mozilla

manifest.json
=============

**Note:** This article describes manifest.json for web extensions. If you are looking for information about the manifest.json in PWAs, check out the Web App Manifest article.

The `manifest.json` file is the only file that every extension using WebExtension APIs must contain.

Using `manifest.json`, you specify basic metadata about your extension such as the name and version, and can also specify aspects of your extension's functionality (such as background scripts, content scripts, and browser actions).

It is a JSON-formatted file, with one exception: it is allowed to contain "`//`"-style comments.

List of manifest.json keys
--------------------------

These are the `manifest.json` keys; these keys are available in Manifest V2 and above unless otherwise noted:

* action (Manifest V3 and above)
* author
* background
* browser\_action (Manifest V2 only)
* browser\_specific\_settings
* chrome\_settings\_overrides
* chrome\_url\_overrides
* commands
* content\_scripts
* content\_security\_policy
* declarative\_net\_request
* default\_locale
* description
* developer
* devtools\_page
* dictionaries
* externally\_connectable
* homepage\_url
* host\_permissions (Manifest V3 and above)
* icons
* incognito
* manifest\_version
* name
* offline\_enabled
* omnibox
* optional\_permissions
* options\_page
* options\_ui
* page\_action (Manifest V2 only in Chrome)
* permissions
* protocol\_handlers
* short\_name
* sidebar\_action
* storage
* theme
* theme\_experiment
* user\_scripts (Manifest V2 only)
* version
* version\_name
* web\_accessible\_resources
### Notes about manifest.json keys

* `"manifest_version"`, `"version"`, and `"name"` are the only mandatory keys.
* `"default_locale"` must be present if the "`_locales`" directory is present, and must be absent otherwise.
* `"browser_specific_settings"` is not supported in Google Chrome.
### Accessing manifest.json keys at runtime

You can access your extension's manifest from the extension's JavaScript using the `runtime.getManifest()` function:

```js
browser.runtime.getManifest().version;
```

Example
-------

The block below shows the basic syntax for some common manifest keys.

**Note:** This is not intended to be used as a copy-paste-ready example. Selecting the keys you'll need depends on the extension you are developing.

For complete example extensions, see Example extensions.

```json
{
  "browser\_specific\_settings": {
    "gecko": {
      "id": "addon@example.com",
      "strict\_min\_version": "42.0"
    }
  },

  "background": {
    "scripts": ["jquery.js", "my-background.js"]
  },

  "browser\_action": {
    "default\_icon": {
      "19": "button/geo-19.png",
      "38": "button/geo-38.png"
    },
    "default\_title": "Whereami?",
    "default\_popup": "popup/geo.html"
  },

  "commands": {
    "toggle-feature": {
      "suggested\_key": {
        "default": "Ctrl+Shift+Y",
        "linux": "Ctrl+Shift+U"
      },
      "description": "Send a 'toggle-feature' event"
    }
  },

  "content\_security\_policy": "script-src 'self' https://example.com; object-src 'self'",

  "content\_scripts": [
    {
      "exclude\_matches": ["\*://developer.mozilla.org/\*"],
      "matches": ["\*://\*.mozilla.org/\*"],
      "js": ["borderify.js"]
    }
  ],

  "default\_locale": "en",

  "description": "…",

  "icons": {
    "48": "icon.png",
    "96": "icon@2x.png"
  },

  "manifest\_version": 2,

  "name": "…",

  "page\_action": {
    "default\_icon": {
      "19": "button/geo-19.png",
      "38": "button/geo-38.png"
    },
    "default\_title": "Whereami?",
    "default\_popup": "popup/geo.html"
  },

  "permissions": ["webNavigation"],

  "version": "0.1",

  "user\_scripts": {
    "api\_script": "apiscript.js"
  },

  "web\_accessible\_resources": ["images/my-image.png"]
}
```

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

For a full overview of all manifest keys and their sub-keys, see the full manifest.json browser compatibility table.

See also
--------

`permissions` JavaScript API


Firefox workflow overview - Mozilla

Firefox workflow overview
=========================

|  |  |  |  |  |
| --- | --- | --- | --- | --- |
| Firefox workflow prepare step graphic | Firefox workflow code step graphic | Firefox workflow publish step graphic | Firefox workflow enhance step graphic | Firefox workflow retire step graphic |
| * Choose a Firefox version for web extension development
* Choose your IDE or code editor
* Install web-ext
* Get familiar with the
 add-on policies and developer agreement
 | * Code your extension
* Run your extension withweb-ext run
 or
 about: debugging)
* Test persistent and restart features
* Debug with theAddon Debugging Window
 | * Package your extension withweb-ext build
* Create an
 addons.mozilla.org account
* Submit your extension
* Submit your source code
 (if required)
* Create an appealing listing
 | * Responded to Mozilla extension review
* Promote your extension
* Nominate your extension to be featured
* Update and improve your extension
 | * Retire your extension
 |

\* Or distribute your extension for sideloading, desktop apps, or in an enterprise.

**Have an extension you want to bring to Firefox?** We provide advice, guidelines, and tools to help making make porting straightforward. To get started, visit Porting a Google Chrome extension.


Content Security Policy - Mozilla

Content Security Policy
=======================

Extensions developed with WebExtension APIs have a Content Security Policy (CSP) applied to them by default. This restricts the sources from which they can load code such as <script> and disallows potentially unsafe practices such as using `eval()`. This article briefly explains what a CSP is, what the default policy is and what it means for an extension, and how an extension can change the default CSP.

Content Security Policy (CSP) is a mechanism to help prevent websites from inadvertently executing malicious content. A website specifies a CSP using an HTTP header sent from the server. The CSP is mostly concerned with specifying legitimate sources of various types of content, such as scripts or embedded plugins. For example, a website can use it to specify that the browser should only execute JavaScript served from the website itself, and not from any other sources. A CSP can also instruct the browser to disallow potentially unsafe practices, such as the use of `eval()`.

Like websites, extensions can load content from different sources. For example, a browser action's popup is specified as an HTML document, and it can include JavaScript and CSS from different sources, just like a normal web page:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <!--Some HTML content here-->
    <!--
 Include a third-party script.
 See also https://developer.mozilla.org/en-US/docs/Web/Security/Subresource\_Integrity.
 -->
    <script
 src="https://code.jquery.com/jquery-2.2.4.js"
 integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI="
 crossorigin="anonymous"></script>

    <!-- Include my popup's own script-->
    <script src="popup.js"></script>
  </body>
</html>
```

Compared to a website, extensions have access to additional privileged APIs, so if they are compromised by malicious code, the risks are greater. For this reason:

* a fairly strict content security policy is applied to extensions by default. See default content security policy.
* the extension's author can change the default policy using the `content_security_policy` manifest.json key, but there are restrictions on the policies that are allowed. See `content_security_policy`.
Default content security policy
-------------------------------

The default content security policy for extensions using Manifest V2 is:
```

"script-src 'self'; object-src 'self';"
```

While for extensions using Manifest V3, the default content security policy is:
```

"script-src 'self'; upgrade-insecure-requests;"
```

These policies are applied to any extension that has not explicitly set its own content security policy using the `content_security_policy` manifest.json key. It has the following consequences:

* You may only load <script> and <object> resources that are local to the extension.
* The extension is not allowed to evaluate strings as JavaScript.
* Inline JavaScript is not executed.
* WebAssembly cannot be used by default.
* Insecure network requests are upgraded in Manifest V3.
### Location of script and object resources

Under the default CSP, you can only load code that is local to the extension. The CSP limits `script-src` to secure sources only, which covers <script> resources, ES6 modules and web workers. In browsers that support obsolete plugins, the `object-src` directive is also restricted. For more information on object-src in extensions, see the WECG issue Remove object-src from the CSP (at least in MV3)).

For example, consider a line like this in an extension's document:

```html
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
```

This doesn't load the requested resource: it fails silently, and any object that you expect to be present from the resource is not found. There are two main solutions to this:

* download the resource, package it in your extension, and refer to this version of the resource.
* allow the remote origin you need using the `content_security_policy` key or, in Manifest V3, the `content_scripts` property.

**Note:** If your modified CSP allows remote script injection, your extension will get rejected from addons.mozilla.org (AMO) during the review. For more information, see details about security best practices.

### eval() and friends

Under the default CSP, extensions cannot evaluate strings as JavaScript. This means that the following are not permitted:

```js
eval("console.log('some output');");
```

```js
setTimeout("alert('Hello World!');", 500);
```

```js
const f = new Function("console.log('foo');");
```

### Inline JavaScript

Under the default CSP, inline JavaScript is not executed. This disallows both JavaScript placed directly in `<script>` tags and inline event handlers, meaning that the following are not permitted:

```html
<script>
 console.log("foo");
</script>
```

```html
<div onclick="console.log('click')">Click me!</div>
```

If you are currently using code like `<body onload="main()">` to run your script when the page has loaded, listen for DOMContentLoaded or load instead.

### WebAssembly

Extensions wishing to use WebAssembly require `'wasm-unsafe-eval'` to be specified in the `script-src` directive.

From Firefox 102 and Chrome 103, `'wasm-unsafe-eval'` can be included in the `content_security_policy` manifest.json key to enable the use of WebAssembly in extensions.

Manifest V2 extensions in Firefox can use WebAssembly without `'wasm-unsafe-eval'` in their CSP for backward compatibility. However, this behavior isn't guaranteed, see Firefox bug 1770909. Extensions using WebAssembly are therefore encouraged to declare `'wasm-unsafe-eval'` in their CSP.

For Chrome, extensions cannot use WebAssembly in version 101 or earlier. In 102, extensions can use WebAssembly (the same behavior as Firefox 101 and earlier). From version 103, extensions can use WebAssembly if they include `'wasm-unsafe-eval'` in the `content_security_policy` in the manifest key.

### Upgrade insecure network requests in Manifest V3

Extensions should use `https:` and `wss:` when communicating with external servers. To encourage this as the standard behavior, the default Manifest V3 CSP includes the `upgrade-insecure-requests` directive. This directive automatically upgrades network requests to `http:` to use `https:`.

While requests are automatically upgraded, it is still recommended to use `https:`-URLs in the extension's source code where possible. In particular, entries in the `host_permissions` section of manifest.json should start with `https://` or `*://` instead of only `http://`.

Manifest V3 Extensions that need to make `http:` or `ws:` requests can opt out of this behavior by overriding the default CSP using the `content_security_policy` manifest.json key with a policy that excludes the `upgrade-insecure-requests` directive. However, to comply with the security requirements of the Add-on Policies, all user data must be transmitted securely.

CSP for content scripts
-----------------------

 In Manifest V2, content scripts have no CSP.
 As of Manifest V3, content scripts share the default CSP as extensions. It is currently not possible to specify a separate CSP for content scripts (source).

 The extent to which the CSP controls loads from content scripts varies by browser.
 In Firefox, JavaScript features such as eval are restricted by the extension CSP. Generally, most DOM-based APIs are subjected to the CSP of the web page.
 In Chrome, many DOM APIs are covered by the extension CSP instead of the web page's CSP (crbug 896041).


Add a button to the toolbar - Mozilla

Add a button to the toolbar
===========================

Toolbar buttons are one of the main UI components available to extensions. Toolbar buttons live in the main browser toolbar and contain an icon. When the user clicks the icon, one of two things can happen:

* If you have specified a popup for the icon, the popup is shown. Popups are transient dialogs specified using HTML, CSS, and JavaScript.
* If you have not specified a popup, a click event is generated, which you can listen for in your code and perform some other kind of action in response to.

With WebExtension APIs, these kinds of buttons are called "browser actions", and are set up like so:

* The manifest.json key `browser_action` is used to define the button.
* The JavaScript API `browserAction` is used to listen for clicks and change the button or perform actions via your code.
A simple button
---------------

In this section we'll create an extension that adds a button to the toolbar. When the user clicks the button, we'll open https://developer.mozilla.org in a new tab.

First, create a new directory, "button", and create a file called "manifest.json" inside it with the following contents:

```json
{
  "description": "Demonstrating toolbar buttons",
  "manifest\_version": 2,
  "name": "button-demo",
  "version": "1.0",

  "background": {
    "scripts": ["background.js"]
  },

  "browser\_action": {
    "default\_icon": {
      "16": "icons/page-16.png",
      "32": "icons/page-32.png"
    }
  }
}
```

This specifies that we'll have a background script named "background.js", and a browser action (button) whose icons will live in the "icons" directory.

Next, create the "icons" directory inside the "buttons" directory, and save the two icons shown below inside it:

**"page-16.png":**

!["16 pixel icon of a lined sheet of paper"](/en-US/docs/Mozilla/Add-ons/WebExtensions/Add_a_button_to_the_toolbar/page-16.png)

**"page-32.png":**

!["32 pixel icon of a lined sheet of paper"](/en-US/docs/Mozilla/Add-ons/WebExtensions/Add_a_button_to_the_toolbar/page-32.png)

**Note:** These icons are from the bitsies! iconset created by Recep Kütük.

We have two icons so we can use the bigger one in high-density displays. The browser will take care of selecting the best icon for the current display.

Next, create "background.js" in the extension's root directory, and give it the following contents:

```js
function openPage() {
  browser.tabs.create({
    url: "https://developer.mozilla.org",
  });
}

browser.browserAction.onClicked.addListener(openPage);
```

This listens for the browser action's click event; when the event fires, the `openPage()` function is run, which opens the specified page using the `tabs` API.

At this point the complete extension should look like this:
```

button/
    icons/
        page-16.png
        page-32.png
    background.js
    manifest.json
```

Now install the extension and click the button:

![The toolbar button added by the extension](/en-US/docs/Mozilla/Add-ons/WebExtensions/Add_a_button_to_the_toolbar/toolbar_button.png)

Adding a popup
--------------

Let's try adding a popup to the button. Replace manifest.json with this:

```json
{
  "description": "Demonstrating toolbar buttons",
  "manifest\_version": 2,
  "name": "button-demo",
  "version": "1.0",

  "browser\_action": {
    "default\_popup": "popup/choose\_page.html",
    "default\_icon": {
      "16": "icons/page-16.png",
      "32": "icons/page-32.png"
    }
  }
}
```

We've made two changes from the original:

* removed the reference to "background.js", because now we're going to handle the extension's logic in the popup's script (you are allowed background.js as well as a popup, it's just that we don't need it in this case).
* added `"default_popup": "popup/choose_page.html"`, which is telling the browser that this browser action is now going to display a popup when clicked, the document for which can be found at "popup/choose\_page.html".

So now we need to create that popup. Create a directory called "popup" then create a file called "choose\_page.html" inside it. Give it the following contents:

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="choose\_page.css" />
  </head>
  <body>
    <div class="page-choice">developer.mozilla.org</div>
    <div class="page-choice">support.mozilla.org</div>
    <div class="page-choice">addons.mozilla.org</div>
    <script src="choose\_page.js"></script>
  </body>
</html>
```

You can see that this is a normal HTML page containing three `<div>` elements, each with the name of a Mozilla site inside. It also includes a CSS file and a JavaScript file, which we'll add next.

Create a file called "choose\_page.css" inside the "popup" directory, and give it these contents:

```css
html,
body {
  width: 300px;
}

.page-choice {
  width: 100%;
  padding: 4px;
  font-size: 1.5em;
  text-align: center;
  cursor: pointer;
}

.page-choice:hover {
  background-color: #cff2f2;
}
```

This is just a bit of styling for our popup.

Next, create a "choose\_page.js" file inside the "popup" directory, and give it these contents:

```js
document.addEventListener("click", (event) => {
  if (!event.target.classList.contains("page-choice")) {
    return;
  }

  const chosenPage = `https://${event.target.textContent}`;
  browser.tabs.create({
    url: chosenPage,
  });
});
```

In our JavaScript, we listen for clicks on the popup choices. We first check to see if the click landed on one of the page-choices; if not, we don't do anything else. If the click did land on a page-choice, we construct a URL from it, and open a new tab containing the corresponding page. Note that we can use WebExtension APIs in popup scripts, just as we can in background scripts.

The extension's final structure should look like this:
```

button/
    icons/
        page-16.png
        page-32.png
    popup/
        choose_page.css
        choose_page.html
        choose_page.js
    manifest.json
```

Now reload the extension, click the button again, and try clicking on the choices in the popup:

![The toolbar button added by the extension with a popup](/en-US/docs/Mozilla/Add-ons/WebExtensions/Add_a_button_to_the_toolbar/toolbar_button_with_popup.png)

Page actions
------------

Page actions are just like browser actions, except that they are for actions which are relevant only for particular pages, rather than the browser as a whole.

While browser actions are always shown, page actions are only shown in tabs where they are relevant. Page action buttons are displayed in the URL bar, rather than the browser toolbar.

Learn more
----------

* `browser_action` manifest key
* `browserAction` API
* Browser action examples:
    + beastify
    + Bookmark it!
    + favourite-colour
    + open-my-page-button
* `page_action` manifest key
* `pageAction` API
* Page action examples:
    + chill-out


Modify a web page - Mozilla

Modify a web page
=================

One of the most common use cases for an extension is to modify a web page. For example, an extension might want to change the style applied to a page, hide particular DOM nodes, or inject extra DOM nodes into the page.

There are two ways to do this with WebExtensions APIs:

* **Declaratively**: Define a pattern that matches a set of URLs, and load a set of scripts into pages whose URL matches that pattern.
* **Programmatically**: Using a JavaScript API, load a script into the page hosted by a particular tab.

Either way, these scripts are called *content scripts*, and are different from the other scripts that make up an extension:

* They only get access to a small subset of the WebExtension APIs.
* They get direct access to the web page in which they are loaded.
* They communicate with the rest of the extension using a messaging API.

In this article we'll look at both methods of loading a script.

Modifying pages that match a URL pattern
----------------------------------------

First of all, create a new directory called "modify-page". In that directory, create a file called "manifest.json", with the following contents:

```json
{
  "manifest\_version": 2,
  "name": "modify-page",
  "version": "1.0",

  "content\_scripts": [
    {
      "matches": ["https://developer.mozilla.org/\*"],
      "js": ["page-eater.js"]
    }
  ]
}
```

The `content_scripts` key is how you load scripts into pages that match URL patterns. In this case, `content_scripts` instructs the browser to load a script called "page-eater.js" into all pages under https://developer.mozilla.org/.

**Note:** Since the `"js"` property of `content_scripts` is an array, you can use it to inject more than one script into matching pages. If you do this the pages share the same scope, just like multiple scripts loaded by a page, and they are loaded in the order that they are listed in the array.

**Note:** The `content_scripts` key also has a `"css"` property that you can use to inject CSS stylesheets.

Next, create a file called "page-eater.js" inside the "modify-page" directory, and give it the following contents:

```js
document.body.textContent = "";

let header = document.createElement("h1");
header.textContent = "This page has been eaten";
document.body.appendChild(header);
```

Now install the extension, and visit https://developer.mozilla.org/. The page should look like this:

![developer.mozilla.org page "eaten" by the script](/en-US/docs/Mozilla/Add-ons/WebExtensions/Modify_a_web_page/eaten_page.png)

Modifying pages programmatically
--------------------------------

What if you still want to eat pages, but only when the user asks you to? Let's update this example so we inject the content script when the user clicks a context menu item.

First, update "manifest.json" so it has the following contents:

```json
{
  "manifest\_version": 2,
  "name": "modify-page",
  "version": "1.0",

  "permissions": ["activeTab", "contextMenus"],

  "background": {
    "scripts": ["background.js"]
  }
}
```

Here, we've removed the `content_scripts` key, and added two new keys:

* `permissions`: To inject scripts into pages we need permissions for the page we're modifying. The `activeTab` permission is a way to get this temporarily for the currently active tab. We also need the `contextMenus` permission to be able to add context menu items.
* `background`: We're using this to load a persistent "background script" called `background.js`, in which we'll set up the context menu and inject the content script.

Let's create this file. Create a new file called `background.js` in the `modify-page` directory, and give it the following contents:

```js
browser.contextMenus.create({
  id: "eat-page",
  title: "Eat this page",
});

browser.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "eat-page") {
    browser.tabs.executeScript({
      file: "page-eater.js",
    });
  }
});
```

In this script we're creating a context menu item, giving it a specific id and title (the text to be displayed in the context menu). Then we set up an event listener so that when the user clicks a context menu item, we check to see if it is our `eat-page` item. If it is, we inject "page-eater.js" into the current tab using the `tabs.executeScript()` API. This API optionally takes a tab ID as an argument: we've omitted the tab ID, which means that the script is injected into the currently active tab.

At this point the extension should look like this:
```

modify-page/
    background.js
    manifest.json
    page-eater.js
```

Now reload the extension, open a page (any page, this time) activate the context menu, and select "Eat this page":

![Option to eat a page on the context menu](/en-US/docs/Mozilla/Add-ons/WebExtensions/Modify_a_web_page/eat_from_menu.png)

Messaging
---------

Content scripts and background scripts can't directly access each other's state. However, they can communicate by sending messages. One end sets up a message listener, and the other end can then send it a message. The following table summarizes the APIs involved on each side:

|  | In content script | In background script |
| --- | --- | --- |
| Send a message | `browser.runtime.sendMessage()` | `browser.tabs.sendMessage()` |
| Receive a message | `browser.runtime.onMessage` | `browser.runtime.onMessage` |

**Note:** In addition to this method of communication, which sends one-off messages, you can also use a connection-based approach to exchange messages. For advice on choosing between the options, see Choosing between one-off messages and connection-based messaging.

Let's update our example to show how to send a message from the background script.

First, edit `background.js` so that it has these contents:

```js
browser.contextMenus.create({
  id: "eat-page",
  title: "Eat this page",
});

function messageTab(tabs) {
  browser.tabs.sendMessage(tabs[0].id, {
    replacement: "Message from the extension!",
  });
}

function onExecuted(result) {
  let querying = browser.tabs.query({
    active: true,
    currentWindow: true,
  });
  querying.then(messageTab);
}

browser.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "eat-page") {
    let executing = browser.tabs.executeScript({
      file: "page-eater.js",
    });
    executing.then(onExecuted);
  }
});
```

Now, after injecting `page-eater.js`, we use `tabs.query()` to get the currently active tab, and then use `tabs.sendMessage()` to send a message to the content scripts loaded into that tab. The message has the payload `{replacement: "Message from the extension!"}`.

Next, update `page-eater.js` like this:

```js
function eatPageReceiver(request, sender, sendResponse) {
  document.body.textContent = "";
  let header = document.createElement("h1");
  header.textContent = request.replacement;
  document.body.appendChild(header);
}
browser.runtime.onMessage.addListener(eatPageReceiver);
```

Now, instead of just eating the page right away, the content script listens for a message using `runtime.onMessage`. When a message arrives, the content script runs essentially the same code as before, except that the replacement text is taken from `request.replacement`.

Since `tabs.executeScript()` is an asynchronous function, and to ensure we send message only after listener has been added in `page-eater.js`, we use `onExecuted()` which will be called after `page-eater.js` executed.

**Note:** Press `Ctrl`+`Shift`+`J` (or `Cmd`+`Shift`+`J` on macOS) OR `web-ext run --bc` to open Browser Console to view `console.log` in background script.

Alternatively, use Add-on Debugger which allows you set breakpoint. There is currently no way to start Add-on Debugger directly from web-ext.

If we want send messages back from the content script to the background page, we would use `runtime.sendMessage()` instead of `tabs.sendMessage()`, e.g.:

```js
browser.runtime.sendMessage({
  title: "from page-eater.js",
});
```

**Note:** These examples all inject JavaScript; you can also inject CSS programmatically using the `tabs.insertCSS()` function.

Learn more
----------

* Content scripts guide
* `content_scripts` manifest key
* `permissions` manifest key
* `tabs.executeScript()`
* `tabs.insertCSS()`
* `tabs.sendMessage()`
* `runtime.sendMessage()`
* `runtime.onMessage`
* Examples using `content_scripts`:
    + borderify
    + emoji-substitution
    + notify-link-clicks-i18n
    + page-to-extension-messaging
* Examples using `tabs.executeScript()`:
    + beastify
    + context-menu-copy-link-with-types


What are extensions? - Mozilla

What are extensions?
====================

**Note:** If you are already familiar with the basic concepts of browser extensions, skip this section to see how extension files are put together. Then, use the reference documentation to start building your extension. Visit Firefox Extension Workshop to learn more about the workflow for testing, publishing, and extensions for Firefox.

An extension adds features and functions to a browser. It's created using familiar web-based technologies — HTML, CSS, and JavaScript. It can take advantage of the same web APIs as JavaScript on a web page, but an extension also has access to its own set of JavaScript APIs. This means that you can do a lot more in an extension than you can with code in a web page. Here are just a few examples of the things you can do:

**Enhance or complement a website**: Use an add-on to deliver additional in-browser features or information from your website. Allow users to collect details from pages they visit to enhance the service you offer.

![Amazon add-on example providing price comparison features](/en-US/docs/Mozilla/Add-ons/WebExtensions/What_are_WebExtensions/amazon_add_on.png)

Examples: Amazon Assistant for Firefox, OneNote Web Clipper, and Grammarly for Firefox.

**Let users show their personality**: Browser extensions can manipulate the content of web pages; for example, letting users add their favorite logo or picture as a background to every page they visit. Extensions may also enable users to update the look of the Firefox UI, the same way standalone theme add-ons do.

![My Web New Tab add-on showing a Batman theme](/en-US/docs/Mozilla/Add-ons/WebExtensions/What_are_WebExtensions/myweb_new_tab_add_on.png)

Examples: MyWeb New Tab, Tabliss, and VivaldiFox.

**Add or remove content from web pages**: You might want to help users block intrusive ads from web pages, provide access to a travel guide whenever a country or city is mentioned in a web page, or reformat page content to offer a consistent reading experience. With the ability to access and update both a page's HTML and CSS, extensions can help users see the web the way they want to.

![uBlock origin add-on example with blocked tracker statistics](/en-US/docs/Mozilla/Add-ons/WebExtensions/What_are_WebExtensions/ublock_origin_add_on.png)

Examples: uBlock Origin, Reader, and Toolbox for Google Play Store™.

**Add tools and new browsing features**: Add new features to a taskboard, or generate QR code images from URLs, hyperlinks, or page text. With flexible UI options and the power of the WebExtensions APIs you can easily add new features to a browser. And, you can enhance almost any website's features or functionality, it doesn't have to be your website.

![QR code generator add-on example](/en-US/docs/Mozilla/Add-ons/WebExtensions/What_are_WebExtensions/qr_code_image_generator_add_on.png)

Examples: Swimlanes for Trello and Tomato Clock.

**Games**: Offer traditional computer games with off-line play features, or explore new game possibilities; for example, by incorporating gameplay into everyday browsing.

Examples: Solitaire Card Game, and 2048 Prime.

**Add development tools**: You may provide web development tools as your business or have developed a useful technique or approach to web development that you want to share. Either way, you can enhance the built-in Firefox developer tools by adding a new tab to the developer toolbar.

![Axe accessibility testing addon example](/en-US/docs/Mozilla/Add-ons/WebExtensions/What_are_WebExtensions/axe_developer_tools_add_on.png)

Examples: Web Developer, Web React Developer Tools, and aXe Developer Tools.

Extensions for Firefox are built using the WebExtensions APIs, a cross-browser system for developing extensions. To a large extent, the API is compatible with the extension API supported by Google Chrome and Opera. Extensions written for these browsers will in most cases run in Firefox or Microsoft Edge with just a few changes.

If you have ideas or questions, you can reach us on the Add-ons Discourse or in the Add-ons room on Matrix.

What's next?
------------

* Walk through the development of a simple extension in Your first extension.
* Learn about the structure of an extension in Anatomy of an extension.
* Try out some example extensions in Example extensions.


Developing WebExtensions for Thunderbird - Mozilla

Developing WebExtensions for Thunderbird
========================================

You'll approach the coding of an extension for Thunderbird in the same way as you would for a Firefox extension; using a text editor or tool of your choice to write the code.

API differences
---------------

**Note:** See ReadTheDocs for Thunderbird specific WebExtension API documentation.

Being both Gecko based, Thunderbird supports many of the APIs Firefox supports, with some differences, see browser compatibility for manifest.json and browser support for JavaScript APIs for details.

See also
--------

* Introduction to Thunderbird Add-On development
* Thunderbird specific WebExtension API documentation
* Browser support for JavaScript APIs
* Browser compatibility for manifest.json


Your first extension - Mozilla

Your first extension
====================

**Note:** If you're familiar with the basic concepts of browser extensions, skip this section to see how extension files are put together. Then, use the reference documentation to start building your extension. Visit Firefox Extension Workshop to learn more about the workflow for testing, publishing, and extensions for Firefox.

This article walks through creating an extension for Firefox, from start to finish. The extension adds a red border to any pages loaded from "`mozilla.org`" or any of its subdomains.

The source code for this example is on GitHub: https://github.com/mdn/webextensions-examples/tree/main/borderify.

Writing the extension
---------------------

In a suitable location, such as in the `Documents` directory, create a new directory called `borderify` and navigate to it. You can do this using your computer's file explorer or command line terminal. Understanding how to use the command line terminal is a handy skill, as it helps with your more advanced extension development. Using the terminal, you create the directory like this:

```bash
mkdir borderify
cd borderify
```

### manifest.json

Using a suitable text editor, create a new file called "manifest.json" directly under the "borderify" directory. Give it the following contents:

```json
{
  "manifest\_version": 2,
  "name": "Borderify",
  "version": "1.0",

  "description": "Adds a red border to all webpages matching mozilla.org.",

  "icons": {
    "48": "icons/border-48.png"
  },

  "content\_scripts": [
    {
      "matches": ["\*://\*.mozilla.org/\*"],
      "js": ["borderify.js"]
    }
  ]
}
```

* The first three keys: `manifest_version`, `name`, and `version`, are mandatory and contain basic metadata for the extension.
* `description` is optional, but recommended: it's displayed in the Add-ons Manager.
* `icons` is optional, but recommended: it allows you to specify an icon for the extension, that will be shown in the Add-ons Manager.

The most interesting key here is `content_scripts`, which tells Firefox to load a script into Web pages whose URL matches a specific pattern. In this case, we're asking Firefox to load a script called "borderify.js" into all HTTP or HTTPS pages served from "mozilla.org" or any of its subdomains.

* Learn more about content scripts.
* Learn more about match patterns.

**Warning:** In some situations you need to specify an ID for your extension. If you do need to specify an add-on ID, include the `browser_specific_settings` key in `manifest.json` and set its `gecko.id` property:

```json
"browser\_specific\_settings": {
  "gecko": {
    "id": "borderify@example.com"
  }
}
```

### icons/border-48.png

The extension should have an icon. This will be shown next to the extension's listing in the Add-ons Manager. Our manifest.json promised that we would have an icon at "icons/border-48.png".

Create the "icons" directory directly under the "borderify" directory. Save an icon there named "border-48.png". You could use the one from our example, which is taken from the Google Material Design iconset, and is used under the terms of the Creative Commons Attribution-ShareAlike license.

If you choose to supply your own icon, It should be 48x48 pixels. You could also supply a 96x96 pixel icon, for high-resolution displays, and if you do this it will be specified as the `96` property of the `icons` object in manifest.json:

```json
"icons": {
  "48": "icons/border-48.png",
  "96": "icons/border-96.png"
}
```

Alternatively, you could supply an SVG file here, and it will be scaled correctly. (Though: if you're using SVG and your icon includes text, you may want to use your SVG editor's "convert to path" tool to flatten the text, so that it scales with a consistent size/position.)

* Learn more about specifying icons.
### borderify.js

Finally, create a file called "borderify.js" directly under the "borderify" directory. Give it this content:

```js
document.body.style.border = "5px solid red";
```

This script will be loaded into the pages that match the pattern given in the `content_scripts` manifest.json key. The script has direct access to the document, just like scripts loaded by the page itself.

* Learn more about content scripts.
Trying it out
-------------

First, double check that you have the right files in the right places:
```

borderify/
    icons/
        border-48.png
    borderify.js
    manifest.json
```

### Installing

In Firefox: Open the about:debugging page, click the This Firefox option, click the Load Temporary Add-on button, then select any file in your extension's directory.

The extension now installs, and remains installed until you restart Firefox.

Alternatively, you can run the extension from the command line using the web-ext tool.

### Testing

**Note:** By default extensions don't work in private browsing. If you want to test this extension in private browsing open "`about:addons`", click on the extension, and select the Allow radio button for Run in Private Windows.

Now visit a page under "`https://www.mozilla.org/en-US/`", and you should see the red border round the page.

![Border displayed on mozilla.org](/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension/border_on_mozilla_org.png)

**Note:** Don't try it on "`addons.mozilla.org`", though! Content scripts are currently blocked on that domain.

Try experimenting a bit. Edit the content script to change the color of the border, or do something else to the page content. Save the content script, then reload the extension's files by clicking the Reload button in "`about:debugging`". You can see the changes right away.

* Learn more about loading extensions
Packaging and publishing
------------------------

For other people to use your extension, you need to package it and submit it to Mozilla for signing. To learn more about that, see "Publishing your extension".

What's next?
------------

Now you've had an introduction to the process of developing a WebExtension for Firefox:

* write a more complex extension
* read more about the anatomy of an extension
* explore the extension examples
* find out what you need to develop, test, and publish your extension
* take your learning further.


Chrome incompatibilities - Mozilla

Chrome incompatibilities
========================

The WebExtension APIs aim to provide compatibility across all the main browsers, so extensions should run on any browser with minimal changes.

However, there are significant differences between Chrome (and Chromium-based browsers), Firefox, and Safari. In particular:

* Support for WebExtension APIs differs across browsers. See Browser support for JavaScript APIs for details.
* Support for `manifest.json` keys differs across browsers. See the "Browser compatibility" section on the `manifest.json` page for more details.
* Extension API namespace:
    + **In Firefox and Safari:** Extension APIs are accessed under the `browser` namespace. The `chrome` namespace is also supported for compatibility with Chrome.
    + **In Chrome:** Extension APIs are accessed under the `chrome` namespace. (cf. Chrome bug 798169)
* Asynchronous APIs:
    + **In Firefox and Safari:** Asynchronous APIs are implemented using promises.
    + **In Chrome:** In Manifest V2, asynchronous APIs are implemented using callbacks. In Manifest V3, support is provided for promises on most appropriate methods. (cf. Chrome bug 328932) Callbacks are supported in Manifest V3 for backward compatibility.

The rest of this page details these and other incompatibilities.

JavaScript APIs
---------------

### chrome.\* and browser.\* namespace

* **In Firefox and Safari:** The APIs are accessed using the `browser` namespace.

```js
browser.browserAction.setIcon({ path: "path/to/icon.png" });
```

* **In Chrome:** The APIs are accessed using the `chrome` namespace.

```js
chrome.browserAction.setIcon({ path: "path/to/icon.png" });
```

### Callbacks and promises

* **In Firefox and Safari (all versions), and Chrome (starting from Manifest Version 3):** Asynchronous APIs use promises to return values.

```js
function logCookie(c) {
  console.log(c);
}

function logError(e) {
  console.error(e);
}

let setCookie = browser.cookies.set({
  url: "https://developer.mozilla.org/",
});
setCookie.then(logCookie, logError);
```

* **In Chrome:** In Manifest V2, asynchronous APIs use callbacks to return values and `runtime.lastError` to communicate errors. In Manifest V3, callbacks are supported for backward compatibility, along with support for promises on most appropriate methods.

```js
function logCookie(c) {
  if (chrome.runtime.lastError) {
    console.error(chrome.runtime.lastError);
  } else {
    console.log(c);
  }
}

chrome.cookies.set({ url: "https://developer.mozilla.org/" }, logCookie);
```

### Firefox supports both the chrome and browser namespaces

As a porting aid, the Firefox implementation of WebExtensions supports `chrome` using callbacks and `browser` using promises. This means that many Chrome extensions work in Firefox without changes.

**Note:** The `browser` namespace is supported by Firefox and Safari. Chrome does not offer the `browser` namespace, until Chrome bug 798169 is resolved.

If you choose to write your extension to use `browser` and promises, Firefox provides a polyfill that should enable it to run in Chrome: https://github.com/mozilla/webextension-polyfill.

### Partially supported APIs

The Browser support for JavaScript APIs page includes compatibility tables for all APIs that have any support in Firefox. Where there are caveats regarding support for an API method, property, type, or event, this is indicated in these tables with an asterisk "\*". Selecting the asterisk expands the table to display a note explaining the caveat.

The tables are generated from compatibility data stored as JSON files in GitHub.

The rest of this section describes the main compatibility issues you may need to consider when building a cross-browser extension. Also, remember to check the browser compatibility tables, as they may contain additional compatibility information.

#### Notifications API

For `notifications.create()`, with `type "basic"`:

* **In Firefox**: `iconUrl` is optional.
* **In Chrome**: `iconUrl` is required.

When the user clicks on a notification:

* **In Firefox**: The notification is cleared immediately.
* **In Chrome**: This is not the case.

If you call `notifications.create()` more than once in rapid succession:

* **In Firefox**: The notifications may not display. Waiting to make subsequent calls within the `notifications.create()` callback function is not a sufficient delay to prevent this.

#### Proxy API

Firefox and Chrome include a Proxy API. However, the design of these two APIs is incompatible.

* **In Firefox**: Proxies are set using the proxy.settings property or proxy.onRequest to provide ProxyInfo dynamically.
 See proxy for more information on the API.
* **In Chrome**: Proxy settings are defined in a `proxy.ProxyConfig` object. Depending on Chrome's proxy settings, the settings may contain `proxy.ProxyRules` or a `proxy.PacScript`. Proxies are set using the proxy.settings property.
 See chrome.proxy for more information on the API.

#### Tabs API

When using `tabs.executeScript()` or `tabs.insertCSS()`:

* **In Firefox**: Relative URLs passed are resolved relative to the current page URL.
* **In Chrome**: Relative URLs are resolved relative to the extension's base URL.

To work cross-browser, you can specify the path as an absolute URL, starting at the extension's root, like this:
```

/path/to/script.js
```

When calling `tabs.remove()`:

* **In Firefox**: The `tabs.remove()` promise is fulfilled after the `beforeunload` event.
* **In Chrome**: The callback does not wait for `beforeunload`.

#### WebRequest API

* **In Firefox:**
    + Requests can be redirected only if their original URL uses the `http:` or `https:` scheme.
    + The `activeTab` permission does not allow for intercepting network requests in the current tab. (See bug 1617479)
    + Events are not fired for system requests (for example, extension upgrades or search bar suggestions).
        - **From Firefox 57 onwards:** Firefox makes an exception for extensions that need to intercept `webRequest.onAuthRequired` for proxy authorization. See the documentation for `webRequest.onAuthRequired`.
    + If an extension wants to redirect a public (e.g., HTTPS) URL to an extension page, the extension's `manifest.json` file must contain a `web_accessible_resources` key with the URL of the extension page.

    **Note:** *Any* website may link or redirect to that URL, and extensions should treat any input (POST data, for example) as if it came from an untrusted source, as a normal web page should.
    + Some of the `browser.webRequest.*` APIs allow for returning Promises that resolves `webRequest.BlockingResponse` asynchronously.
* **In Chrome:** Only `webRequest.onAuthRequired` supports asynchronous `webRequest.BlockingResponse` by supplying `'asyncBlocking'`, through a callback instead of a Promise.

#### Windows API

* **In Firefox:** `onFocusChanged` of the `windows` API triggers multiple times for a focus change.
### Unsupported APIs

#### DeclarativeContent API

* **In Firefox:** Chrome's declarativeContent API is not implemented. In addition, Firefox will not support the `declarativeContent.RequestContentScript` API (which is rarely used and is unavailable in stable releases of Chrome).
### Miscellaneous incompatibilities

#### URLs in CSS

* **In Firefox:** URLs in injected CSS files are resolved relative to *the CSS file*.
* **In Chrome:** URLs in injected CSS files are resolved relative to *the page they are injected into*.

#### Support for dialogs in background pages

* **In Firefox:** `alert()`, `confirm()`, and `prompt()` are not supported in background pages.

#### web\_accessible\_resources

* **In Firefox:** Resources are assigned a random UUID that changes for every instance of Firefox: `moz-extension://«random-UUID»/«path»`. This randomness can prevent you from doing things, such as adding your extension's URL to another domain's CSP policy.
* **In Chrome:** When a resource is listed in `web_accessible_resources`, it is accessible as `chrome-extension://«your-extension-id»/«path»`. The extension ID is fixed for an extension.

#### Manifest "key" property

* **In Firefox:** As Firefox uses random UUIDs for `web_accessible_resources`, this property is unsupported. Firefox extensions can fix their extension ID through the `browser_specific_settings.gecko.id` manifest key (see browser\_specific\_settings.gecko).
* **In Chrome:** When working with an unpacked extension, the manifest may include a `"key"` property to pin the extension ID across different machines. This is mainly useful when working with `web_accessible_resources`.

#### Content script HTTP(S) requests

* **In Firefox:** When a content script makes an HTTP(S) request, you *must* provide absolute URLs.
* **In Chrome:** When a content script makes a request (for example, using `fetch()`) to a relative URL (like `/api`), it is sent to `https://example.com/api`.

#### Content script environment

* **In Firefox:** The global scope of the content script environment is not strictly equal to `window` (Firefox bug 1208775). More specifically, the global scope (`globalThis`) is composed of standard JavaScript features as usual, plus `window` as the prototype of the global scope. Most DOM APIs are inherited from the page through `window`, through Xray vision to shield the content script from modifications by the web page. A content script may encounter JavaScript objects from its global scope or Xray-wrapped versions from the web page.
* **In Chrome:** The global scope is `window`, and the available DOM APIs are generally independent of the web page (other than sharing the underlying DOM). Content scripts cannot directly access JavaScript objects from the web page.

#### Executing code in a web page from content script

* **In Firefox:** `eval` runs code in the context of the content script and `window.eval` runs code in the context of the page. See Using `eval` in content scripts.
* **In Chrome:** `eval` and `window.eval` always runs code in the context of the content script, not in the context of the page.

#### Sharing variables between content scripts

* **In Firefox:** You cannot share variables between content scripts by assigning them to `this.{variableName}` in one script and then attempting to access them using `window.{variableName}` in another. This is a limitation created by the sandbox environment in Firefox. This limitation may be removed; see Firefox bug 1208775.

#### Content script lifecycle during navigation

* **In Firefox:** Content scripts remain injected in a web page after the user has navigated away. However, window object properties are destroyed. For example, if a content script sets `window.prop1 = "prop"` and the user then navigates away and returns to the page `window.prop1` is undefined. This issue is tracked in Firefox bug 1525400.
 To mimic the behavior of Chrome, listen for the pageshow and pagehide events. Then simulate the injection or destruction of the content script.
* **In Chrome:** Content scripts are destroyed when the user navigates away from a web page. If the user clicks the back button to return to the page through history, the content script is injected into the web page.

#### "per-tab" zoom behavior

* **In Firefox:** The zoom level persists across page loads and navigation within the tab.
* **In Chrome:** Zoom changes are reset on navigation; navigating a tab always loads pages with their per-origin zoom factors.

See `tabs.ZoomSettingsScope`.

manifest.json keys
------------------

The main `manifest.json` page includes a table describing browser support for `manifest.json` keys. Where there are caveats around support for a given key, this is indicated in the table with an asterisk "\*". Selecting the asterisk expands the table to display a note explaining the caveat.

The tables are generated from compatibility data stored as JSON files in GitHub.

Native messaging
----------------

### Connection-based messaging arguments

**On Linux and Mac:** Chrome passes one argument to the native app, which is the origin of the extension that started it, in the form of `chrome-extension://«extensionID/»` (trailing slash required). This enables the app to identify the extension.

**On Windows:** Chrome passes two arguments:

1. The origin of the extension
2. A handle to the Chrome native window that started the app
### allowed\_extensions

* **In Firefox:** The manifest key is called `allowed_extensions`.
* **In Chrome:** The manifest key is called `allowed_origins`.
### App manifest location

* **In Chrome:** The app manifest is expected in a different place. See Native messaging host location in the Chrome docs.
### App persistence

* **In Firefox:** When a native messaging connection is closed, Firefox kills the subprocesses if they do not break away. On Windows, the browser puts the native application's process into a Job object and kills the job. Suppose the native application launches other processes and wants them to remain open after the native application is killed. In that case, the native application must use `CreateProcess`, instead of `ShellExecute`, to launch the additional process with the `CREATE_BREAKAWAY_FROM_JOB` flag.
Data cloning algorithm
----------------------

Some extension APIs allow an extension to send data from one part of the extension to another, such as `runtime.sendMessage()`, `tabs.sendMessage()`, `runtime.onMessage`, the `postMessage()` method of `runtime.port`, and `tabs.executeScript()`.

* **In Firefox:** The Structured clone algorithm is used.
* **In Chrome:** The JSON serialization algorithm is used. It may switch to structured cloning in the future (issue 248548).

The Structured clone algorithm supports more types than the JSON serialization algorithm. A notable exception are (DOM) objects with a `toJSON` method. DOM objects are not cloneable nor JSON-serializable by default, but with a `toJSON()` method, these can be JSON-serialized (but still not cloned with the structured cloning algorithm). Examples of JSON-serializable objects that are not structured cloneable include instances of `URL` and `PerformanceEntry`.

Extensions that rely on the `toJSON()` method of the JSON serialization algorithm can use `JSON.stringify()` followed by `JSON.parse()` to ensure that a message can be exchanged because a parsed JSON value is always structurally cloneable.