Work with files - Mozilla

Work with files
===============

Your browser extension may need to work with files to deliver its full functionality. This article looks at the five mechanisms you have for handling files:

* Downloading files to the user's selected download folder.
* Opening files using a file picker on a web page.
* Opening files using drag and drop onto a web page.
* Storing files or blobs locally with IndexedDB using the idb-file-storage library.
* Passing files to a native application on the user's computer.

For each of these mechanisms, we introduce their use with references to the relevant API documentation, guides, and any examples that show how to use the API.

Download files using the downloads API
--------------------------------------

This mechanism enables you to get a file from your website (or any location you can define as a URL) to the user's computer. The key method is `downloads.download()`, which in its simplest form accepts a URL and downloads the file from that URL to the user's default downloads folder:

```js
browser.downloads.download({ url: "https://example.org/image.png" });
```

You can let the user download to a location of their choice by specifying the `saveAs` parameter.

**Note:** Using URL.createObjectURL() you can also download files and blobs defined in your JavaScript, which can include local content retrieved from IndexedDB.

The downloads API also provides features to cancel, pause, resume, erase, and remove downloads; search for downloaded files in the download manager; show downloaded files in the computer's file manager; and open a file in an associated application.

To use this API, you need to have the `"downloads"` API permission specified in your `manifest.json` file.

 Example: Latest download
 API reference: downloads API

Open files in an extension using a file picker
----------------------------------------------

If you want to work with a file from the user's computer one option is to let the user select a file using the computer's file browser. Either create a new page or inject code into an existing page to use the `file` type of the HTML `input` element to offer the user a file picker. Once the user has picked a file or files, the script associated with the page can access the content of the file using the DOM File API, in the same way a web application does.

 Example: Imagify
 Guide: Using files from web applications
 API references: HTML input element | DOM File API

**Note:** If you want to access or process all the files in a selected folder, you can do so using `<input type="file" webkitdirectory="true"/>` to select the folder and return all the files it contains.

Open files in an extension using drag and drop
----------------------------------------------

The Web Drag and Drop API offers an alternative to using a file picker. To use this method, establish a 'drop zone' that fits with your UI, then add listeners for the `dragenter`, `dragover`, and `drop` events to the element. In the handler for the drop event, your code can access any file dropped by the user from the object offered by the `dataTransfer` property using `DataTransfer.files`. Your code can then access and manipulate the files using the DOM File API.

 Example: Imagify
 Guides: Using files from web applications | File drag and drop
 API references: DOM File API

Store files data locally using the IndexedDB file storage library
-----------------------------------------------------------------

If your extension needs to save files locally, the idb-file-storage library provides a simple Promise-based wrapper to the IndexedDB API to aid the storage and retrieval of files and blobs.

The key features of the library are:

getFileStorage

Returns an `IDBFileStorage` instance, creating the named storage if it does not exist.

IDBFileStorage

Provides the methods to save and retrieve files, such as:

* list to obtain an optionally filtered list of file in the database.
* put to add a file or blob to the database.
* get to retrieve a file or blob from the database.
* remove to delete a file or blob from the database.

The Store Collected Images example illustrates how to use most of these features.

The Store Collected Images example lets users add images to a collection using an option on the image context menu. Selected images are collected in a popup and can be saved to a named collection. A toolbar button (`browserAction`) opens a navigate collection page, on which the user can view and delete saved images, with a filter option to narrow choices. See the example in action.

The workings of the library can be understood by viewing image-store.js in /utils/:

### Creating the store and saving the images

```js
async function saveCollectedBlobs(collectionName, collectedBlobs) {
  const storedImages = await getFileStorage({ name: "stored-images" });

  for (const item of collectedBlobs) {
    await storedImages.put(`${collectionName}/${item.uuid}`, item.blob);
  }
}
```

`saveCollectedBlobs` is called when the user clicks save in the popup and has provided a name for the image collection.

First, `getFileStorage` creates, if it does not exist already, or retrieves the IndexedDB database `"stored-images"` to the object `storedImages`. `storedImages.put()` then adds each collected image to the database, under the collection name, using the blob's unique id (the file name).

If the image being stored has the same name as one already in the database, it is overwritten. If you want to avoid this, query the database first using `imagesStore.list()` with a filter for the file name; and, if the list returns a file, add a suitable suffix to the name of the new image to store a separate item.

### Retrieving stored images for display

```js
export async function loadStoredImages(filter) {
  const imagesStore = await getFileStorage({ name: "stored-images" });
  let listOptions = filter ? { includes: filter } : undefined;
  const imagesList = await imagesStore.list(listOptions);
  let storedImages = [];
  for (const storedName of imagesList) {
    const blob = await imagesStore.get(storedName);
    storedImages.push({ storedName, blobUrl: URL.createObjectURL(blob) });
  }
  return storedImages;
}
```

`loadStoredImages()` is called when the user clicks view or reload in the navigate collection page. `getFileStorage()` opens the `"stored-images"` database, then `imagesStore.list()` gets a filtered list of the stored images. This list is then used to retrieve images with `imagesStore.get()` and build a list to return to the UI.

Note the use of `URL.createObjectURL(blob)` to create a URL that references the image blob. This URL is then used in the UI (navigate-collection.js) to display the image.

### Delete collected images

```js
async function removeStoredImages(storedImages) {
  const imagesStore = await getFileStorage({ name: "stored-images" });
  for (const storedImage of storedImages) {
    URL.revokeObjectURL(storedImage.blobUrl);
    await imagesStore.remove(storedImage.storedName);
  }
}
```

`removeStoredImages()` is called when the user clicks delete in the navigate collection page. Again, `getFileStorage()` opens the `"stored-images"` database then `imagesStore.remove()` removes each image from the filtered list of images.

Note the use of `URL.revokeObjectURL()` to explicitly revoke the blob URL. This enables the garbage collector to free the memory allocated to the URL. If this is not done, the memory will not get returned until the page on which it was created is closed. If the URL was created in an extension's background page, this is not unloaded until the extension is disabled, uninstalled, or reloaded, so holding this memory unnecessarily could affect browser performance. If the URL is created in an extension's page (new tab, popup, or sidebar) the memory is released when the page is closed, but it is still a good practice to revoke the URL when it is no longer needed.

Once the blob URL has been revoked, any attempt to load it will result in an error. For example, if the blob URL was used as the `SRC` attribute of an `IMG` tag, the image will not load and will not be visible. It is therefore good practice to remove any revoked blob URLs from generated HTML elements when the blob URL is revoked.

 Example: Store Collected Images
 API References: idb-file-storage library

**Note:** You can also use the full Web IndexedDB API to store data from your extension. This can be useful where you need to store data that isn't handled well by the simple key/value pairs offered by the DOM Storage API.

Process files in a local app
----------------------------

Where you have a native app or want to deliver additional native features for file processing, use native messaging to pass a file to a native app for processing.

You have two options:

Connection-based messaging

Here you trigger the process with `runtime.connectNative()`, which returns a `runtime.Port` object. You can then pass a JSON message to the native application using the `postMessage()` function of `Port`. Using the `onMessage.addListener()` function of `Port` you can listen for messages from the native application. The native application is opened if it is not running when `runtime.connectNative()` is called and the application remains running until the extension calls `Port.disconnect()` or the page that connected to it is closed.

Connectionless messaging

Here you use `runtime.sendNativeMessage()` to send a JSON message to a new, temporary instance of the native application. The browser closes the native application after receiving any message back from the native application.

To add the file or blob you want the native application to process use `JSON.stringify()`.

To use this method the extension must request the `"nativeMessaging"` permission or optional permission in its `manifest.json` file. Where optional permission is used, remember to check that permission has being granted and where necessary request permission from the user with the `permissions` API. Reciprocally, the native application must grant permission for the extension by including its ID in the `"allowed_extensions"` field of the app manifest.

 Example: Native Messaging (illustrates simple messaging only)
 Guides: Native messaging
 API references: runtime API


Extend the developer tools - Mozilla

Extend the developer tools
==========================

**Note:** This page describes the devtools APIs in Firefox 55. Although the APIs are based on the Chrome devtools APIs, Firefox does not implement all those features; therefore, not all features are documented here. To see which features are missing, refer to Limitations of the devtools APIs.

You can use WebExtensions APIs to extend the browser's built-in developer tools. To create a devtools extension, include the "devtools\_page" key in your manifest.json file:

```json
"devtools\_page": "devtools/devtools-page.html"
```

The value of this key is a URL pointing to an HTML file bundled with your extension, a special extension page called the devtools page. The URL must be relative to the manifest.json file.

This manifest key implicitly sets the `"devtools"` permission, which triggers an install-time permission warning about devtools. To avoid this warning, mark the feature as optional by listing the `"devtools"` permission in the `optional_permissions` manifest key. Setting the optional permission can be particularly helpful when introducing devtools features in an update, as it prevents the extension from being disabled (in Chrome) or blocked from updating (in Firefox).

The devtools page
-----------------

The devtools page is loaded when the browser devtools are opened, and unloaded when it is closed. Note that because the devtools window is associated with a single tab, it's quite possible for more than one devtools window - hence more than one devtools page - to exist at the same time.

The devtools page doesn't have any visible DOM, but can include JavaScript sources using `<script>` tags. The sources must be bundled with the extension itself. The sources get access to:

* The normal DOM APIs accessible through the global `window` object
* The same WebExtension APIs as in Content Scripts
* The devtools APIs:
    + `devtools.inspectedWindow`
    + `devtools.network`
    + `devtools.panels`

Note that the devtools page does not get access to any other WebExtension APIs, and the background page doesn't get access to the devtools APIs. Instead, the devtools page and the background page must communicate using the `runtime` messaging APIs. Here's an example:

```html
<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>DevTools Extension</title>
  </head>
  <body>
    <script src="devtools.js"></script>
  </body>
</html>
```

The `devtools.js` file will hold the actual code creating your dev tools extensions.

Creating panels
---------------

The devtools window hosts a number of separate tools - the JavaScript Debugger, Network Monitor, and so on. A row of tabs across the top lets the user switch between the different tools. The window hosting each tool's user interface is called a "panel".

Using the `devtools.panels.create()` API, you can create your own panel in the devtools window:

```js
browser.devtools.panels
  .create(
    "My Panel", // title
    "/icons/star.png", // icon
    "/devtools/panel/panel.html", // content
  )
  .then((newPanel) => {
    newPanel.onShown.addListener(initialisePanel);
    newPanel.onHidden.addListener(unInitialisePanel);
  });
```

This takes three mandatory arguments: the panel's title, icon, and content. It returns a `Promise` which resolves to a `devtools.panels.ExtensionPanel` object representing the new panel.

Interacting with the target window
----------------------------------

The developer tools are always attached to a particular browser tab. This is referred to as the "target" for the developer tools, or the "inspected window". You can interact with the inspected window using the `devtools.inspectedWindow` API.

### Running code in the target window

The `devtools.inspectedWindow.eval()` provides one way to run code in the inspected window.

This is somewhat like using `tabs.executeScript()` to inject a content script, but with one important difference:

* unlike content scripts, scripts loaded using `devtools.inspectedWindow.eval()` **do not** get a "clean view of the DOM": that is, they can see changes to the page made by page scripts.

**Note:** A clean view of the DOM is a security feature, intended to help prevent hostile pages from tricking extensions by redefining the behavior of native DOM functions. This means you need to be very careful using eval(), and should use a normal content script if you can.

Scripts loaded using `devtools.inspectedWindow.eval()` also don't see any JavaScript variables defined by content scripts.

### Working with content scripts

A devtools document doesn't have direct access to `tabs.executeScript()`, so if you need to inject a content script, the devtools document must send a message to the background script asking it to inject the script. The `devtools.inspectedWindow.tabId` provides the ID of the target tab: the devtools document can pass this to the background script, and the background script can in turn pass it into `tabs.executeScript()`:

```js
// devtools-panel.js

const scriptToAttach = "document.body.innerHTML = 'Hi from the devtools';";

window.addEventListener("click", () => {
  browser.runtime.sendMessage({
    tabId: browser.devtools.inspectedWindow.tabId,
    script: scriptToAttach,
  });
});
```

```js
// background.js

function handleMessage(request, sender, sendResponse) {
  browser.tabs.executeScript(request.tabId, {
    code: request.script,
  });
}

browser.runtime.onMessage.addListener(handleMessage);
```

If you need to exchange messages between the content scripts running in the target window and a devtools document, it's a good idea to use the `runtime.connect()` and `runtime.onConnect` to set up a connection between the background page and the devtools document. The background page can then maintain a mapping between tab IDs and `runtime.Port` objects, and use this to route messages between the two scopes.

![The background page tab ID is connected to the content script on the content page by a runtime.sendmessage() object. The Port of the background page is connected to the port of the Devtools document by a port.postMessage() object.](/en-US/docs/Mozilla/Add-ons/WebExtensions/Extending_the_developer_tools/devtools-content-scripts.png)

Limitations of the devtools APIs
--------------------------------

These APIs are based on the Chrome devtools APIs, but many features are still missing, compared with Chrome. This section lists the features that are still not implemented, as of Firefox 54. Note that the devtools APIs are under active development and we expect to add support for most of them in future releases.

### devtools.inspectedWindow

The following are not supported:

* `inspectedWindow.getResources()`
* `inspectedWindow.onResourceAdded`
* `inspectedWindow.onResourceContentCommitted`

None of the options to `inspectedWindow.eval()` are supported.

Scripts injected using `inspectedWindow.eval()` can't use all the Console's command-line helper functions, but `$0` and `inspect()` are both supported (starting from Firefox 55).

### devtools.panels

The following are not supported:

* `panels.elements`
* `panels.sources`
* `panels.setOpenResourceHandler()`
* `panels.openResource()`
* `panels.ExtensionPanel.createStatusBarButton()`
* `panels.Button`
* `panels.ElementsPanel`
* `panels.SourcesPanel`
Examples
--------

The webextensions-examples repo on GitHub, contains several examples of extensions that use devtools panels:

* devtools-panels use devtools panels:


Your second extension - Mozilla

Your second extension
=====================

If you've been through the Your first extension article, you've already got an idea of how to write an extension. In this article, you'll write a slightly more complex extension that demonstrates a few more of the APIs.

The extension adds a new button to the Firefox toolbar. When the user clicks the button, we display a popup enabling them to choose an animal. Once they choose an animal, we'll replace the current page's content with a picture of the chosen animal.

To implement this, we will:

* **define a browser action, which is a button attached to the Firefox toolbar**.
 For the button we'll supply:
    + an icon, called "beasts-32.png"
    + a popup to open when the button is pressed. The popup will include HTML, CSS, and JavaScript.
* **define an icon for the extension**, called "beasts-48.png". This will be shown in the Add-ons Manager.
* **write a content script, "beastify.js" that will be injected into web pages**.
 This is the code that will actually modify the pages.
* **package some images of the animals, to replace images in the web page**.
 We'll make the images "web accessible resources" so the web page can refer to them.

You could visualize the overall structure of the extension like this:

![The manifest.json file includes icons, browser actions, including popups, and web accessible resources. The choose beast JavaScript popup resource calls in the beastify script.](/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/untitled-1.png)

It's a simple extension, but shows many of the basic concepts of the WebExtensions API:

* adding a button to the toolbar
* defining a popup panel using HTML, CSS, and JavaScript
* injecting content scripts into web pages
* communicating between content scripts and the rest of the extension
* packaging resources with your extension that can be used by web pages

You can find complete source code for the extension on GitHub.

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

Create a new directory and navigate to it:

```bash
mkdir beastify
cd beastify
```

### manifest.json

Now create a new file called "manifest.json", and give it the following contents:

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

  "description": "Adds a browser action icon to the toolbar. Click the button to choose a beast. The active tab's body content is then replaced with a picture of the chosen beast. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#beastify",
  "homepage\_url": "https://github.com/mdn/webextensions-examples/tree/main/beastify",
  "icons": {
    "48": "icons/beasts-48.png"
  },

  "permissions": ["activeTab"],

  "browser\_action": {
    "default\_icon": "icons/beasts-32.png",
    "default\_title": "Beastify",
    "default\_popup": "popup/choose\_beast.html"
  },

  "web\_accessible\_resources": [
    "beasts/frog.jpg",
    "beasts/turtle.jpg",
    "beasts/snake.jpg"
  ]
}
```

* The first three keys: `manifest_version`, `name`, and `version`, are mandatory and contain basic metadata for the extension.
* `description` and `homepage_url` are optional, but recommended: they provide useful information about the extension.
* `icons` is optional, but recommended: it allows you to specify an icon for the extension, that will be shown in the Add-ons Manager.
* `permissions` lists permissions the extension needs. We're just asking for the `activeTab` permission here.
* `browser_action` specifies the toolbar button. We're supplying three pieces of information here:
    + `default_icon` is mandatory, and points to the icon for the button
    + `default_title` is optional, and will be shown in a tooltip
    + `default_popup` is used if you want a popup to be shown when the user clicks the button. We do, so we've included this key and made it point to an HTML file included with the extension.
* `web_accessible_resources` lists files that we want to make accessible to web pages. Since the extension replaces the content in the page with images we've packaged with the extension, we need to make these images accessible to the page.

Note that all paths given are relative to manifest.json itself.

### The icon

The extension should have an icon. This will be shown next to the extension's listing in the Add-ons Manager (you can open this by visiting the URL "about:addons"). Our manifest.json promised that we would have an icon for the toolbar at "icons/beasts-48.png".

Create the "icons" directory and save an icon there named "beasts-48.png". You could use the one from our example, which is taken from the Aha-Soft's Free Retina iconset, and used under the terms of its 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/beasts-48.png",
  "96": "icons/beasts-96.png"
}
```

### The toolbar button

The toolbar button also needs an icon, and our manifest.json promised that we would have an icon for the toolbar at "icons/beasts-32.png".

Save an icon named "beasts-32.png" in the "icons" directory. You could use the one from our example, which is taken from the IconBeast Lite icon set and used under the terms of its license.

If you don't supply a popup, then a click event is dispatched to your extension when the user clicks the button. If you do supply a popup, the click event is not dispatched, but instead, the popup is opened. We want a popup, so let's create that next.

### The popup

The function of the popup is to enable the user to choose one of three beasts.

Create a new directory called "popup" under the extension root. This is where we'll keep the code for the popup. The popup will consist of three files:

* **`choose_beast.html`** defines the content of the panel
* **`choose_beast.css`** styles the content
* **`choose_beast.js`** handles the user's choice by running a content script in the active tab

```bash
mkdir popup
cd popup
touch choose_beast.html choose_beast.css choose_beast.js
```

#### choose\_beast.html

The HTML file looks like this:

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="choose\_beast.css" />
  </head>

  <body>
    <div id="popup-content">
      <button>Frog</button>
      <button>Turtle</button>
      <button>Snake</button>
      <button type="reset">Reset</button>
    </div>
    <div id="error-content" class="hidden">
      <p>Can't beastify this web page.</p>
      <p>Try a different page.</p>
    </div>
    <script src="choose\_beast.js"></script>
  </body>
</html>
```

We have a `<div>` element with an ID of `"popup-content"` that contains a button for each animal choice and a reset button. We have another `<div>` with an ID of `"error-content"` and a class `"hidden"`. We'll use that in case there's a problem initializing the popup.

Note that we include the CSS and JS files from this file, just like a web page.

#### choose\_beast.css

The CSS fixes the size of the popup, ensures that the three choices fill the space, and gives them some basic styling. It also hides elements with `class="hidden"`: this means that our `<div id="error-content"...` element will be hidden by default.

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

.hidden {
  display: none;
}

button {
  border: none;
  width: 100%;
  margin: 3% auto;
  padding: 4px;
  text-align: center;
  font-size: 1.5em;
  cursor: pointer;
  background-color: #e5f2f2;
}

button:hover {
  background-color: #cff2f2;
}

button[type="reset"] {
  background-color: #fbfbc9;
}

button[type="reset"]:hover {
  background-color: #eaea9d;
}
```

#### choose\_beast.js

Here's the JavaScript for the popup:

```js
/\*\*
 \* CSS to hide everything on the page,
 \* except for elements that have the "beastify-image" class.
 \*/
const hidePage = `body > :not(.beastify-image) {
 display: none;
 }`;

/\*\*
 \* Listen for clicks on the buttons, and send the appropriate message to
 \* the content script in the page.
 \*/
function listenForClicks() {
  document.addEventListener("click", (e) => {
    /\*\*
 \* Given the name of a beast, get the URL to the corresponding image.
 \*/
    function beastNameToURL(beastName) {
      switch (beastName) {
        case "Frog":
          return browser.runtime.getURL("beasts/frog.jpg");
        case "Snake":
          return browser.runtime.getURL("beasts/snake.jpg");
        case "Turtle":
          return browser.runtime.getURL("beasts/turtle.jpg");
      }
    }

    /\*\*
 \* Insert the page-hiding CSS into the active tab,
 \* then get the beast URL and
 \* send a "beastify" message to the content script in the active tab.
 \*/
    function beastify(tabs) {
      browser.tabs.insertCSS({ code: hidePage }).then(() => {
        const url = beastNameToURL(e.target.textContent);
        browser.tabs.sendMessage(tabs[0].id, {
          command: "beastify",
          beastURL: url,
        });
      });
    }

    /\*\*
 \* Remove the page-hiding CSS from the active tab,
 \* send a "reset" message to the content script in the active tab.
 \*/
    function reset(tabs) {
      browser.tabs.removeCSS({ code: hidePage }).then(() => {
        browser.tabs.sendMessage(tabs[0].id, {
          command: "reset",
        });
      });
    }

    /\*\*
 \* Just log the error to the console.
 \*/
    function reportError(error) {
      console.error(`Could not beastify: ${error}`);
    }

    /\*\*
 \* Get the active tab,
 \* then call "beastify()" or "reset()" as appropriate.
 \*/
    if (e.target.tagName !== "BUTTON" || !e.target.closest("#popup-content")) {
      // Ignore when click is not on a button within <div id="popup-content">.
      return;
    }
    if (e.target.type === "reset") {
      browser.tabs
        .query({ active: true, currentWindow: true })
        .then(reset)
        .catch(reportError);
    } else {
      browser.tabs
        .query({ active: true, currentWindow: true })
        .then(beastify)
        .catch(reportError);
    }
  });
}

/\*\*
 \* There was an error executing the script.
 \* Display the popup's error message, and hide the normal UI.
 \*/
function reportExecuteScriptError(error) {
  document.querySelector("#popup-content").classList.add("hidden");
  document.querySelector("#error-content").classList.remove("hidden");
  console.error(`Failed to execute beastify content script: ${error.message}`);
}

/\*\*
 \* When the popup loads, inject a content script into the active tab,
 \* and add a click handler.
 \* If we couldn't inject the script, handle the error.
 \*/
browser.tabs
  .executeScript({ file: "/content\_scripts/beastify.js" })
  .then(listenForClicks)
  .catch(reportExecuteScriptError);
```

The place to start here is line 99. The popup script executes a content script in the active tab as soon as the popup is loaded, using the `browser.tabs.executeScript()` API. If executing the content script is successful, then the content script will stay loaded in the page until the tab is closed or the user navigates to a different page.

A common reason the `browser.tabs.executeScript()` call might fail is that you can't execute content scripts in all pages. For example, you can't execute them in privileged browser pages like about:debugging, and you can't execute them on pages in the addons.mozilla.org domain. If it does fail, `reportExecuteScriptError()` will hide the `<div id="popup-content">` element, show the `<div id="error-content"...` element, and log an error to the console.

If executing the content script is successful, we call `listenForClicks()`. This listens for clicks on the popup.

* If the click was not on a button in the popup, we ignore it and do nothing.
* If the click was on a button with `type="reset"`, then we call `reset()`.
* If the click was on any other button (i.e. the beast buttons), then we call `beastify()`.

The `beastify()` function does three things:

* map the button clicked to a URL pointing to an image of a particular beast
* hide the page's whole content by injecting some CSS, using the `browser.tabs.insertCSS()` API
* send a "beastify" message to the content script using the `browser.tabs.sendMessage()` API, asking it to beastify the page, and passing it the URL to the beast image.

The `reset()` function essentially undoes a beastify:

* remove the CSS we added, using the `browser.tabs.removeCSS()` API
* send a "reset" message to the content script asking it to reset the page.
### The content script

Create a new directory, under the extension root, called "content\_scripts" and create a new file in it called "beastify.js", with the following contents:

```js
(() => {
  /\*\*
 \* Check and set a global guard variable.
 \* If this content script is injected into the same page again,
 \* it will do nothing next time.
 \*/
  if (window.hasRun) {
    return;
  }
  window.hasRun = true;

  /\*\*
 \* Given a URL to a beast image, remove all existing beasts, then
 \* create and style an IMG node pointing to
 \* that image, then insert the node into the document.
 \*/
  function insertBeast(beastURL) {
    removeExistingBeasts();
    const beastImage = document.createElement("img");
    beastImage.setAttribute("src", beastURL);
    beastImage.style.height = "100vh";
    beastImage.className = "beastify-image";
    document.body.appendChild(beastImage);
  }

  /\*\*
 \* Remove every beast from the page.
 \*/
  function removeExistingBeasts() {
    const existingBeasts = document.querySelectorAll(".beastify-image");
    for (const beast of existingBeasts) {
      beast.remove();
    }
  }

  /\*\*
 \* Listen for messages from the background script.
 \* Call "insertBeast()" or "removeExistingBeasts()".
 \*/
  browser.runtime.onMessage.addListener((message) => {
    if (message.command === "beastify") {
      insertBeast(message.beastURL);
    } else if (message.command === "reset") {
      removeExistingBeasts();
    }
  });
})();
```

The first thing the content script does is to check for a global variable `window.hasRun`: if it's set the script returns early, otherwise it sets `window.hasRun` and continues. The reason we do this is that every time the user opens the popup, the popup executes a content script in the active tab, so we could have multiple instances of the script running in a single tab. If this happens, we need to make sure that only the first instance is actually going to do anything.

After that, the place to start is line 40, where the content script listens for messages from the popup, using the `browser.runtime.onMessage` API. We saw above that the popup script can send two different sorts of messages: "beastify" and "reset".

* if the message is "beastify", we expect it to contain a URL pointing to a beast image. We remove any beasts that might have been added by previous "beastify" calls, then construct and append an `<img>` element whose `src` attribute is set to the beast URL.
* if the message is "reset", we just remove any beasts that might have been added.
### The beasts

Finally, we need to include the images of the beasts.

Create a new directory called "beasts", and add the three images in that directory, with the appropriate names. You can get the images from the GitHub repository, or from here:

![A brown frog.](/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/frog.jpg)

![An emerald tree boa with white stripes.](/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/snake.jpg)

![A red-eared slider turtle.](/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/turtle.jpg)

Testing it out
--------------

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

beastify/

    beasts/
        frog.jpg
        snake.jpg
        turtle.jpg

    content_scripts/
        beastify.js

    icons/
        beasts-32.png
        beasts-48.png

    popup/
        choose_beast.css
        choose_beast.html
        choose_beast.js

    manifest.json
```

Now load the extension as a temporary add-on. Open "about:debugging" in Firefox, click "Load Temporary Add-on", and select your **manifest.json** file. You should then see the extension's icon appear in the Firefox toolbar:

![The beastify icon in the Firefox toolbar](/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/beastify_icon.png)

Open a web page, click the icon, select a beast, and see the web page change:

![A page replaced with the image of a turtle](/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_second_WebExtension/beastify_page.png)

Developing from the command line
--------------------------------

You can automate the temporary installation step by using the web-ext tool. Try this:

```bash
cd beastify
web-ext run
```

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

Now that you've created a more advanced WebExtension for Firefox:

* read 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.


Match patterns - Mozilla

Match patterns
==============

Match patterns are a way to specify groups of URLs: a match pattern matches a specific set of URLs. They are used in WebExtensions APIs in a few places, most notably to specify which documents to load content scripts into, and to specify which URLs to add `webRequest` listeners to.

APIs that use match patterns usually accept a list of match patterns, and will perform the appropriate action if the URL matches any of the patterns. See, for example, the `content_scripts` key in manifest.json.

Match pattern structure
-----------------------

**Note:** Some browsers don't support certain schemes.
 Check the Browser compatibility table for details.

All match patterns are specified as strings. Apart from the special `<all_urls>` pattern, match patterns consist of three parts: *scheme*, *host*, and *path*. The scheme and host are separated by `://`.
```

<scheme>://<host><path>
```

### scheme

The *scheme* component may take one of two forms:

| Form | Matches |
| --- | --- |
| `*` |
 Only "http" and "https" and in some browsers also
 "ws" and "wss".
  |
|
 One of `http`, `https`, `ws`,
 `wss`, `ftp`, `data`,
 `file`, or `(chrome-)extension`.
  | Only the given scheme. |

### host

The *host* component may take one of three forms:

| Form | Matches |
| --- | --- |
| `*` | Any host. |
| `*.` followed by part of the hostname. | The given host and any of its subdomains. |
| A complete hostname, without wildcards. | Only the given host. |

*host* must not include a port number.

*host* is optional only if the *scheme* is "file".

Note that the wildcard may only appear at the start.

### path

The *path* component must begin with a `/`.

After that, it may subsequently contain any combination of the `*` wildcard and any of the characters that are allowed in URL paths or query strings. Unlike *host*, the *path* component may contain the `*` wildcard in the middle or at the end, and the `*` wildcard may appear more than once.

The value for the *path* matches against the string which is the URL path plus the URL query string. This includes the `?` between the two, if the query string is present in the URL. For example, if you want to match URLs on any domain where the URL path ends with `foo.bar`, then you need to use an array of Match Patterns like `['*://*/*foo.bar', '*://*/*foo.bar?*']`. The `?*` is needed, rather than just `bar*`, in order to anchor the ending `*` as applying to the URL query string and not some portion of the URL path.

Neither the URL fragment identifier, nor the `#` which precedes it, are considered as part of the *path*.

**Note:** The path pattern string should not include a port number. Adding a port, as in: `http://localhost:1234/*` causes the match pattern to be ignored. However, `http://localhost:1234` will match with `http://localhost/*`.

### <all\_urls>

The special value `<all_urls>` matches all URLs under any of the supported schemes: that is "http", "https", "ws", "wss", "ftp", "data", and "file".

Examples
--------

| Pattern | Example matches | Example non-matches |
| --- | --- | --- |
| `<all_urls>`
Match all URLs. | `http://example.org/`
`https://a.org/some/path/`
`ws://sockets.somewhere.org/`
`wss://ws.example.com/stuff/`
`ftp://files.somewhere.org/` | `resource://a/b/c/`(unsupported scheme)
`ftps://files.somewhere.org/`(unsupported scheme) |
| `*://*/*`
Match all HTTP, HTTPS and WebSocket URLs. | `http://example.org/`
`https://a.org/some/path/`
`ws://sockets.somewhere.org/`
`wss://ws.example.com/stuff/` | `ftp://ftp.example.org/`(unmatched scheme)
`file:///a/`(unmatched scheme) |
| `*://*.mozilla.org/*`

 Match all HTTP, HTTPS and WebSocket URLs that are hosted at
 "mozilla.org" or one of its subdomains.
  | `http://mozilla.org/`
`https://mozilla.org/`
`http://a.mozilla.org/`
`http://a.b.mozilla.org/`
`https://b.mozilla.org/path/`
`ws://ws.mozilla.org/`
`wss://secure.mozilla.org/something` | `ftp://mozilla.org/`(unmatched scheme)
`http://mozilla.com/`(unmatched host)
`http://firefox.org/`(unmatched host) |
| `*://mozilla.org/`

 Match all HTTP, HTTPS and WebSocket URLs that are hosted at exactly
 "mozilla.org/".
  | `http://mozilla.org/`
`https://mozilla.org/`
`ws://mozilla.org/`
`wss://mozilla.org/` | `ftp://mozilla.org/`(unmatched scheme)
`http://a.mozilla.org/`(unmatched host)
`http://mozilla.org/a`(unmatched path) |
| `ftp://mozilla.org/`
Match only "ftp://mozilla.org/". | `ftp://mozilla.org` | `http://mozilla.org/`(unmatched scheme)
`ftp://sub.mozilla.org/`(unmatched host)
`ftp://mozilla.org/path`(unmatched path) |
| `https://*/path`
Match HTTPS URLs on any host, whose path is "path". | `https://mozilla.org/path`
`https://a.mozilla.org/path`
`https://something.com/path` | `http://mozilla.org/path`(unmatched scheme)
`https://mozilla.org/path/`(unmatched path)
`https://mozilla.org/a`(unmatched path)
`https://mozilla.org/`(unmatched path)

`https://mozilla.org/path?foo=1`(unmatched path due
 to URL query string)
  |
| `https://*/path/`

 Match HTTPS URLs on any host, whose path is "path/" and which has no
 URL query string.
  | `https://mozilla.org/path/`
`https://a.mozilla.org/path/`
`https://something.com/path`/ | `http://mozilla.org/path/`(unmatched scheme)
`https://mozilla.org/path`(unmatched path)
`https://mozilla.org/a`(unmatched path)
`https://mozilla.org/`(unmatched path)
`https://mozilla.org/path/?foo=1`(unmatched path due to URL query string) |
| `https://mozilla.org/*`

 Match HTTPS URLs only at "mozilla.org", with any URL path and URL
 query string.
  | `https://mozilla.org/`
`https://mozilla.org/path`
`https://mozilla.org/another`
`https://mozilla.org/path/to/doc`
`https://mozilla.org/path/to/doc?foo=1` | `http://mozilla.org/path`(unmatched scheme)
`https://mozilla.com/path`(unmatched host) |
| `https://mozilla.org/a/b/c/`
Match only this URL, or this URL with any URL fragment. | `https://mozilla.org/a/b/c/`
`https://mozilla.org/a/b/c/#section1` | Anything else. |
| `https://mozilla.org/*/b/*/`

 Match HTTPS URLs hosted on "mozilla.org", whose path contains a
 component "b" somewhere in the middle. Will match URLs with query
 strings, if the string ends in a `/`.
  | `https://mozilla.org/a/b/c/`
`https://mozilla.org/d/b/f/`
`https://mozilla.org/a/b/c/d/`
`https://mozilla.org/a/b/c/d/#section1`
`https://mozilla.org/a/b/c/d/?foo=/`
`https://mozilla.org/a?foo=21314&bar=/b/&extra=c/` | `https://mozilla.org/b/*/`(unmatched path)
`https://mozilla.org/a/b/`(unmatched path)

`https://mozilla.org/a/b/c/d/?foo=bar`(unmatched path
 due to URL query string)
  |
| `file:///blah/*`
Match any FILE URL whose path begins with "blah". | `file:///blah/`
`file:///blah/bleh` | `file:///bleh/`(unmatched path) |

### Invalid match patterns

| Invalid pattern | Reason |
| --- | --- |
| `resource://path/` | Unsupported scheme. |
| `https://mozilla.org` | No path. |
| `https://mozilla.*.org/` | "\*" in host must be at the start. |
| `https://*zilla.org/` | "\*" in host must be the only character or be followed by ".". |
| `http*://mozilla.org/` | "\*" in scheme must be the only character. |
| `https://mozilla.org:80/` | Host must not include a port number. |
| `*://*` | Empty path: this should be "`*://*/*`". |
| `file://*` | Empty path: this should be "`file:///*`". |

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

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


Work with the Bookmarks API - Mozilla

Work with the Bookmarks API
===========================

Bookmarks enable users to collect and organize lists of web pages, so they can easily get back to their favorites. Using the Bookmarks API, your extensions can manipulate bookmarks in much the same way users can.

Permissions
-----------

To make use of the Bookmarks API, you need to ask for the `"bookmarks"` permission in your extension's manifest.json file:

```json
"permissions": [
  "bookmarks"
],
```

Features
--------

The Bookmarks API lets your extension do the things users can do with bookmarks and includes functions for:

* Basic bookmark item manipulation, offering:
    + add (`bookmarks.create`).
    + retrieve (`bookmarks.get`).
    + update (`bookmarks.update`).
    + move (`bookmarks.move`).
    + delete (`bookmarks.remove`).
    + search (`bookmarks.search`).
* Obtaining a list of recently added bookmarks (`bookmarks.getRecent`).
* Bookmark folder tree manipulation to:
    + get tree information (`bookmarks.getTree`, `bookmarks.getChildren`, and `bookmarks.getSubTree`).
    + add branches (`bookmarks.create`).
    + delete nodes (`bookmarks.removeTree`).
    + move nodes (`bookmarks.move`).
* Listening for bookmarks (or bookmark tree folders) events that:
    + add (`bookmarks.onCreated`).
    + change (`bookmarks.onChanged`).
    + move (`bookmarks.onMoved`).
    + reorder (`bookmarks.onChildrenReordered`).
    + remove (`bookmarks.onRemoved`).
* Listening for bookmark imports, which can be used to suspend other bookmark processing while an import is in progress:
    + import started (`bookmarks.onImportBegan`).
    + import finished (`bookmarks.onImportEnded`).
Example walkthrough
-------------------

To gain an understanding of how to work with the Bookmarks API let's take a look at the bookmark-it example. This example adds a toolbar icon (`browserAction`) which, when clicked, adds or removes the current page from bookmarks. If the page is bookmarked (or removed from bookmarks) in some other way, the icon is updated to show the state of the page's bookmarking.

This video shows the extension in action:

### manifest.json

The manifest.json describes the extension:

```json
{
  "manifest\_version": 2,
  "name": "Bookmark it!",
  "version": "1.1",
  "description": "A simple bookmark button",
  "homepage\_url": "https://github.com/mdn/webextensions-examples/tree/main/bookmark-it",
```

Defines the icons that'll be used to represent the extension, in places such as the add-on manager.

```json
  "icons": {
    "48": "icons/bookmark-it.png",
    "96": "icons/bookmark-it@2x.png"
  },
```

Requests permissions. `"bookmarks"` is requested to enable use of the Bookmarks API. `"tabs"` is requested so that the active tab's URL and title can be read and used to create or find the page's bookmark. The need for the Tabs API to access these details means that you're unlikely to use the Bookmark API without the Tabs API.

```json
  "permissions": [
    "bookmarks",
    "tabs"
  ],
```

Sets up the basic toolbar button details. Most of the button's features will be set up in code after the page's bookmark status is known.

```json
  "browser\_action": {
    "default\_icon": "icons/star-empty-38.png",
    "default\_title": "Bookmark it!"
  },
```

Defines the background script that'll add and remove the page's bookmark and set the characteristics of the toolbar button.

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

}
```

### background.js

As with any background script, background.js is run as soon as the extension is started. Initially the script calls `updateActiveTab()` that starts by obtaining the `Tabs` object for the current tab, using `tabs.query`, and passing the object to `updateTab()` with this code:

```js
let gettingActiveTab = browser.tabs.query({
  active: true,
  currentWindow: true,
});
gettingActiveTab.then(updateTab);
```

`updateTab()` first passes the active tab's URL to `isSupportedProtocol()`:

```js
  function updateTab(tabs) {
    if (tabs[0]) {
      currentTab = tabs[0];
      if (isSupportedProtocol(currentTab.url)) {
```

`isSupportedProtocol()` determines if the URL displayed in the active tab is one that can be bookmarked. To extract the protocol from the tab's URL, the extension takes advantage of the HTMLAnchorElement by adding the tab's URL to an `<a>` element and then getting the protocol using the `protocol` property.

```js
function isSupportedProtocol(urlString) {
  let supportedProtocols = ["https:", "http:", "file:"];
  let url = document.createElement("a");
  url.href = urlString;
  return supportedProtocols.includes(url.protocol);
}
```

If the protocol is one supported by bookmarks, the extension determines if the tab's URL is already bookmarked and if it is, calls `updateIcon()`:

```js
      let searching = browser.bookmarks.search({url: currentTab.url});
      searching.then((bookmarks) => {
        currentBookmark = bookmarks[0];
        updateIcon();
```

`updateIcon()` sets the toolbar button's icon and title, depending on whether the URL is bookmarked or not.

```js
function updateIcon() {
  browser.browserAction.setIcon({
    path: currentBookmark
      ? {
          19: "icons/star-filled-19.png",
          38: "icons/star-filled-38.png",
        }
      : {
          19: "icons/star-empty-19.png",
          38: "icons/star-empty-38.png",
        },
    tabId: currentTab.id,
  });
  browser.browserAction.setTitle({
    // Screen readers can see the title
    title: currentBookmark ? "Unbookmark it!" : "Bookmark it!",
    tabId: currentTab.id,
  });
}
```

With the toolbar button initialized, the extension starts listening for a click on the toolbar button, calling `toggleBookmark()` when that happens.

```js
browser.browserAction.onClicked.addListener(toggleBookmark);
```

`toggleBookmark()` uses the result from the search made by `updateTabs()`, which looks for the presence of the URL in a bookmark, to determine whether to remove or add a bookmark for the current URL.

```js
function toggleBookmark() {
  if (currentBookmark) {
    browser.bookmarks.remove(currentBookmark.id);
  } else {
    browser.bookmarks.create({ title: currentTab.title, url: currentTab.url });
  }
}
```

To update the toolbar icon, the extension listens for the creation or removal of bookmarks. This approach has the benefit of capturing both the bookmark update made by the extension and any update made by the user outside the extension.

```js
// listen for bookmarks being created
browser.bookmarks.onCreated.addListener(updateActiveTab);

// listen for bookmarks being removed
browser.bookmarks.onRemoved.addListener(updateActiveTab);
```

Finally, the extension listens for a change to the active tab's URL, or the user switching to another tab or window. These actions could change the viewed URL and therefore the status of the extension's toolbar icon.

```js
// listen to tab URL changes
browser.tabs.onUpdated.addListener(updateActiveTab);

// listen to tab switching
browser.tabs.onActivated.addListener(updateActiveTab);

// listen for window switching
browser.windows.onFocusChanged.addListener(updateActiveTab);
```

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

If you want to learn more, check out the Bookmarks API reference.


User actions - Mozilla

User actions
============

Some WebExtension APIs perform functions that are generally performed as a result of a user action. For example:

* a browser action with a popup will display the popup when the user clicks it, but there's also a `browserAction.openPopup` API enabling an extension to open the popup programmatically.
* if an extension adds a sidebar, it is typically opened by the user via some part of the browser's built-in user interface, such as the View/Sidebar menu. But there's also a `sidebarAction.open` API enabling an extension to open their sidebar programmatically.

To follow the principle of "no surprises", APIs like this can only be called from inside the handler for a user action. User actions include the following:

* Clicking the extension's browser action or page action.
* Selecting a context menu item defined by the extension.
* Activating a keyboard shortcut defined by the extension (this only treated as a user action from Firefox 63 onwards).
* Clicking a button in a page bundled with the extension.

For example:

```js
function handleClick() {
  browser.sidebarAction.open();
}

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

Note that user actions in normal web pages are not treated as user actions for this purpose. For example, if a user clicks a button in a normal web page, and a content script has added a click handler for that button and in that handler sends a message to the extension's background page, then the background page message handler is not considered to be handling a user action.

Also, if a user input handler waits on a promise, then its status as a user input handler is lost. For example:

```js
async function handleClick() {
  let result = await someAsyncFunction();

  // this will fail, because the handler lost its "user action handler" status
  browser.sidebarAction.open();
}

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


User interface - Mozilla

User interface
==============

Extensions that use WebExtension APIs are provided with several user interface options so that their functionality can be made available to the user. A summary of those options is provided below, with a more detailed introduction to each user interface option in this section.

**Note:** For advice on using these UI components to create a great user experience in your extension, please see the User experience best practices article.

| UI option | Description | Example |
| --- | --- | --- |
| Toolbar button
 (browser action)
  |
 A button on the browser toolbar that dispatches an event to the
 extension when clicked. By default, the button is visible in all tabs.
  | Example showing a toolbar button (browser action). |
|
 Toolbar button with a
 popup |
 A popup on a button in the browser toolbar that opens when the button is
 clicked. The popup is defined in an HTML document that handles the user
 interaction.
  | Example of the pop-up on a toolbar button |
| Address bar button
 (page action)
  |
 A button on the browser address bar that dispatches an event to the
 extension when clicked. By default, the button is hidden in all tabs.
  | Example showing an address bar button (page action)  |
|
 Address bar button with a
 popup |
 A popup on a button in the browser address bar that opens when the
 button is clicked. The popup is defined in an HTML document that handles
 the user interaction.
  | Example of a popup on the address bar button |
| Context menu item |
 Menu items, checkboxes, and radio buttons on one or more of the
 browser's context menus. Also, menus can be structured by adding
 separators. When menu items are clicked, an event is dispatched to the
 extension.
  | Example of content menu items added by a WebExtension, from the context-menu-demo example |
| Sidebar |
 An HTML document displayed next to a web page, with the option for
 unique content per page. The sidebar is opened when the extension is
 installed, then obeys the user's sidebar visibility selection. User
 interaction within the sidebar is handled by its HTML document.
  | Example of a sidebar |
| Options page |
 A page that enables you to define preferences for your extension that
 your users can change. The user can access this page from the browser's
 add-ons manager.
  | Example showing the options page content added in the favorite colors example. |
| Extension page |
 Use web pages included in your extension to provide forms, help, or any
 other content required, within windows or tabs.
  | Example of a simple bundled page displayed as a detached panel. |
| Notification |
 Transient notifications displayed to the user through the underlying
 operating system's notifications mechanism. Dispatches an event to the
 extension when the user clicks a notification, or when a notification
 closes (either automatically or at the user's request).
  | Example of an extension triggered system notification |
| Address bar suggestion | Offer custom address bar suggestions when the user enters a keyword. | Example showing the result of the firefox_code_search WebExtension's customization of the address bar suggestions. |
| Developer tools panel |
 A tab with an associated HTML document that displays in the browser's
 developer tools.
  | Example showing the result of the firefox_code_search WebExtension's customization of the address bar suggestions. |

The following how-to guides provide step-by-step guidance to creating some of these user interface options:

* Accessibility guidelines
* Add a button to the toolbar
* Browser styles
* Extending the developer tools
* Implement a settings page


Safely insert external content into a page - Mozilla

Safely insert external content into a page
==========================================

There are times when you might want or need to include content from an external source in your extension. But, there is the risk that the source may have malicious scripts embedded in it—added by either the developer of the source or by a malicious third-party.

Take an RSS reader as an example. You don't know what RSS feeds your extension will open and have no control over the content of those RSS feeds. So, it's possible the user could subscribe to a feed where, for example, a feed item's title includes a script. This could be something as simple as including JavaScript code within `<script></script>` tags. If you were to extract the title, assume it was plain text, and add it to the DOM of a page created by your extension, your user now has an unknown script running in their browser. Therefore, care needs to be taken to avoid evaluating arbitrary text as HTML.

You also need to remember that extensions have privileged contexts, for example in background scripts and content scripts. In the worst case, an embedded script could run in one of these contexts, a situation known as privilege escalation. This situation can leave a user's browser open to remote attack by enabling the website that injected the code to access critical user data, such as passwords, browser history, or browsing behavior.

This article examines how to work safely with remote data and add it to a DOM.

Working with arbitrary strings
------------------------------

When working with strings, there are a couple of recommended options to safely add them to a page: the standard DOM node creation methods or jQuery.

### DOM node creation methods

A lightweight approach to inserting strings into a page is to use the native DOM manipulation methods: `document.createElement`, `Element.setAttribute`, and `Node.textContent`. The safe approach is to create the nodes separately and assign their content using textContent:

```js
let data = JSON.parse(responseText);
let div = document.createElement("div");
div.className = data.className;
div.textContent = `Your favorite color is now ${data.color}`;
addonElement.appendChild(div);
```

This approach is safe because the use of `.textContent` automatically escapes any remote HTML in `data.color`.

However, beware, you can use native methods that aren't safe. Take the following code:

```js
let data = JSON.parse(responseText);
addonElement.innerHTML = `<div class='${data.className}'>Your favorite color is now ${data.color}</div>`;
```

Here, the contents of `data.className` or `data.color` could contain HTML that can close the tag early, insert arbitrary further HTML content, then open another tag.

### jQuery

When using jQuery, functions such as `attr()` and `text()` escape content as it's added to a DOM. So, the "favorite color" example from above, implemented in jQuery, would look like this:

```js
let node = $("</div>");
node.addClass(data.className);
node.text(`Your favorite color is now ${data.color}`);
```

Working with HTML content
-------------------------

When working with externally sourced content that you know is HTML, sanitizing the HTML is essential before it's added to a page. Best practice for sanitizing HTML is to use an HTML sanitization library or a template engine with HTML sanitization features. In this section, we look at some suitable tools and how to use them.

### HTML sanitization

An HTML sanitization library strips anything that could lead to script execution from HTML, so you can safely inject complete sets of HTML nodes from a remote source into your DOM. DOMPurify, which has been reviewed by various security experts, is a suitable library for this task in extensions.

For production use, DOMPurify comes as a minified version: purify.min.js. You can use this script in the way that best suits your extension. For example, you could add it as a content script:

```json
"content\_scripts": [
  {
    "matches" : ["<all\_urls>"],
    "js": ["purify.min.js", "myinjectionscript.js"]
  }
]
```

Then, in myinjectionscript.js you can read the external HTML, sanitize it, and add it to a page's DOM:

```js
let elem = document.createElement("div");
let cleanHTML = DOMPurify.sanitize(externalHTML);
elem.innerHTML = cleanHTML;
```

You can use any method to add the sanitized HTML to your DOM, for example jQuery's `.html()` function. Remember though that the `SAFE_FOR_JQUERY` flag needs to be used in this case:

```js
let elem = $("<div/>");
let cleanHTML = DOMPurify.sanitize(externalHTML, { SAFE\_FOR\_JQUERY: true });
elem.html(cleanHTML);
```

### Template engines

Another common pattern is to create a local HTML template for a page and use remote values to fill in the blanks. While this approach is generally acceptable, care should be taken to avoid use of constructs that would allow the insertion of executable code. This can happen when the templating engine uses constructs that insert raw HTML into the document. If the variable used to insert raw HTML is of a remote source, it is subject to the same security risk mentioned in the introduction.

For example, when using mustache templates you must use the double mustache, `{{variable}}`, which escapes any HTML. Use of the triple mustache, `{{{variable}}}`, must be avoided as this injects a raw HTML string and could add executable code to your template. Handlebars works in a similar way, with variables in double handlebars, `{{variable}}`, being escaped. Whereas, variables in triple handlebars are left raw and must be avoided. Also, if you create a Handlebars helper using `Handlebars.SafeString` use `Handlebars.escapeExpression()` to escape any dynamic parameters passed to the helper. This is a requirement because the resulting variable from `Handlebars.SafeString` is considered safe and it isn't escaped when inserted with double handlebars.

There are similar constructs in other templating systems that need to be approached with the same level of care.

Additional reading
------------------

For more information on this subject, see the following articles:

* XSS (Cross Site Scripting) Prevention Cheat Sheet


Differences between API implementations - Mozilla

Differences between API implementations
=======================================

The browser extensions API is an emerging standard. As a result, while it is supported by most major browsers – including Firefox, Chrome, Edge, and Opera – there are differences between the various implementations. This means some changes may be necessary to implement your extension for multiple browsers.

Among the various browsers supporting the extensions API, Firefox is the most compliant with the emerging standard and, therefore, is your best place to start when developing browser extensions.

The differences between browsers' API implementations fall into four areas: namespace, asynchronous event handling, API coverage, and manifest keys.

Namespace
---------

You reference all extensions API functions using a namespace. For example, `browser.alarms.create({delayInMinutes});` creates an alarm in Firefox that goes off after the time specified in `delayInMinutes`.

There are two API namespaces in use:

* `chrome` used in Chrome, Edge, and Opera.
* `browser` used in Firefox and Safari.
Asynchronous event handling
---------------------------

JavaScript provides several ways to handle asynchronous events. The proposed extensions API standard is to use promises. The promises approach offers significant advantages when dealing with chained asynchronous event calls.

Firefox and Safari implement promises for the extensions API. All other browsers use callbacks. In Manifest V3, Chrome, Edge, and Opera provided for promises on most appropriate methods. (cf. Chrome bug 328932)

If you are unfamiliar with how JavaScript can handle asynchronous events or promises, look at Getting to know asynchronous JavaScript: Callbacks, Promises and Async/Await or the MDN Using promises page.

API coverage
------------

The differences in the extensions API function implementations among the browsers fall into three broad categories:

* Lack of support for an entire function.
* Variations in the support for features within a function. For example, at the time of writing, Firefox doesn't support the `notification` function method `onButtonClicked`, while Firefox is the only browser that supports `onShown`.
* Proprietary functions supporting browser-specific features. For example, at the time of writing, containers is a Firefox-specific feature supported by the `contextualIdentities` function.
Manifest keys
-------------

The differences in the supported `manifest.json` keys among the browsers fall broadly into two categories:

* Extension information attributes. For example, at the time of writing, Firefox and Opera include the `developer` key (in addition to the `author` key) to record details about the extension developer.
* Extension features. For example, at the time of writing, only Firefox supports the `protocol_handlers` key (which registers web-based protocol handlers, applications that know how to handle particular types of links).
More information
----------------

You can find more detailed information about the differences in the supported browser extensions API features in:

* Chrome incompatibilities
* Browser support for JavaScript APIs
* Browser compatibility for manifest.json


Browser support for JavaScript APIs - Mozilla

Browser support for JavaScript APIs
===================================

action
------

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

alarms
------

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

bookmarks
---------

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

browserAction
-------------

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

browserSettings
---------------

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

browsingData
------------

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

captivePortal
-------------

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

clipboard
---------

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

commands
--------

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

contentScripts
--------------

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

contextualIdentities
--------------------

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

cookies
-------

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

declarativeNetRequest
---------------------

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

devtools
--------

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

dns
---

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

dom
---

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

downloads
---------

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

events
------

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

extension
---------

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

extensionTypes
--------------

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

find
----

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

history
-------

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

i18n
----

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

identity
--------

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

idle
----

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

management
----------

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

menus
-----

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

notifications
-------------

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

omnibox
-------

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

pageAction
----------

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

permissions
-----------

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

pkcs11
------

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

privacy
-------

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

proxy
-----

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

runtime
-------

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

scripting
---------

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

search
------

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

sessions
--------

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

sidebarAction
-------------

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

storage
-------

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

tabs
----

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

theme
-----

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

topSites
--------

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

types
-----

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

userScripts
-----------

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

webNavigation
-------------

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

webRequest
----------

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

windows
-------

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

**Note:** Microsoft Edge compatibility data is supplied by Microsoft Corporation and is included here under the Creative Commons Attribution 3.0 United States License.

See also
--------

* Browser compatibility for manifest.json


Native manifests - Mozilla

Native manifests
================

Native manifests are specially formatted JSON files that are provisioned on the user's computer by some means outside the extension installation process. For example, a native manifest might be provisioned by a device administrator or by a native application installer.

There are three different types of native manifest:

|  |  |
| --- | --- |
| Native messaging manifests |
 Enable a feature called
 native messaging, in which an extension can communicate with a native app installed on
 the device.
  |
| Managed storage manifests |
 Define read-only data that an extension can access using the
 `storage.managed` API.
  |
| PKCS #11 manifests |
 Enable an extension to use the `pkcs11` API
 to enumerate PKCS #11 security modules and install them in Firefox.
  |

For all native manifests, you need to arrange things so the browser can find the manifest. The section on manifest location describes these rules.

Native messaging manifests
--------------------------

The native messaging manifest contains a single JSON object with the following properties:

| Name | Type | Description |
| --- | --- | --- |
| `name` | String | Name of the native application.

 This must match the name passed into
 `runtime.connectNative()` or
 `runtime.sendNativeMessage()` by
 the extension.

 On MacOS and Linux, it must also match the native messaging manifest's
 filename (excluding the `.json` extension).

 On Windows, it must match the name of the registry key you create,
 that contains the location of the native messaging manifest.

 The name must match the following regular expression:
 `"^\w+(\.\w+)*$"`. This means that it may only contain
 (lowercase or uppercase) alphanumeric characters, underscores, and
 dots. It may not start or end with a dot, and a dot cannot be followed
 by another dot.
  |
| `description` | String | Description of the native application. |
| `path` | String | Path to the native application.

 On Windows, this may be relative to the manifest itself. On MacOS and
 Linux it must be absolute.
  |
| `type` | String | Describes the method used to connect the extension with the app.

 Currently, only one value can be given here, `"stdio"`,
 which indicates that messages are received by the app using standard
 input (`stdin`) and sent using standard output
 (`stdout`).
  |
| `allowed_extensions` | Array of String |
 An array of
 Add-on ID
 values. Each value represents an extension which is allowed to
 communicate with this native application.

 Note that this means you will probably want to include the
 `browser\_specific\_settings`
 key in your extension's `manifest.json` file, so you can
 set an explicit ID during development.
  |

For example, here's a manifest for the `ping_pong` native application:

```json
{
  "name": "ping\_pong",
  "description": "Example host for native messaging",
  "path": "/path/to/native-messaging/app/ping\_pong.py",
  "type": "stdio",
  "allowed\_extensions": ["ping\_pong@example.org"]
}
```

This allows the extension whose ID is `ping_pong@example.org` to connect, by passing the name `ping_pong` into the relevant `runtime` API function. The application itself is at `/path/to/native-messaging/app/ping_pong.py`.

Managed storage manifests
-------------------------

The managed storage manifest contains a single JSON object with the following properties:

| Name | Type | Description |
| --- | --- | --- |
| `name` | String |
 The ID of the extension that can access this storage, given as the ID
 you've specified in the extension's
 `browser\_specific\_settings`
 key.
  |
| `description` | String | Human-readable description, ignored by Firefox. |
| `type` | String | This must be `"storage"`. |
| `data` | Object |
 A JSON object that may contain any valid JSON values, including
 strings, numbers, booleans, arrays, or objects. This will become the
 data in the `browser.storage.managed` storage area.
  |

For example:

```json
{
  "name": "favourite-color-examples@mozilla.org",
  "description": "ignored",
  "type": "storage",
  "data": {
    "color": "management thinks it should be blue!"
  }
}
```

Given this JSON manifest, the `favourite-color-examples@mozilla.org` extension could access the data using code like this:

```js
let storageItem = browser.storage.managed.get("color");
storageItem.then((res) => {
  console.log(`Managed color is: ${res.color}`);
});
```

PKCS #11 manifests
------------------

The PKCS #11 manifest is a file containing a JSON object with the following properties:

| Name | Type | Description |
| --- | --- | --- |
| `name` | String | Name of the PKCS #11 module.
This must match the name used in the `pkcs11` API.

 On MacOS and Linux, it must also match the manifest's filename
 (excluding the extension).

 On Windows, it must match the name of the registry key you create,
 which contains the location of the manifest.

 The name must match the following regular expression:
 `"^\w+(\.\w+)*$"`. This means that it may only contain
 lowercase alphanumeric characters, underscores and dots. It may not
 start or end with a dot, and a dot cannot be followed by another dot.
  |
| `description` | String | Description of the module.

 This is used to set the friendly name for the module in the browser's
 UI (for example, the "Security Devices" dialog in Firefox).
  |
| `path` | String | Path to the module.

 On Windows, this may be relative to the manifest itself. On MacOS and
 Linux it must be absolute.
  |
| `type` | String | This must be `"pkcs11"`. |
| `allowed_extensions` | Array of String |
 An array of
 Add-on ID
 values. Each value represents an extension which is allowed to
 interact with the module.

**Note:** This means you will probably want to include
 the
 `browser\_specific\_settings`
 key in your extension's `manifest.json` file, so you can
 set an explicit ID during development.

 |

For example:

```json
{
  "name": "my\_module",
  "description": "My test module",
  "type": "pkcs11",
  "path": "/path/to/libpkcs11testmodule.dylib",
  "allowed\_extensions": ["my-extension@mozilla.org"]
}
```

Given this JSON manifest, saved as `my_module.json`, the `my-extension@mozilla.org` extension could install the security module at `/path/to/libpkcs11testmodule.dylib` using code like this:

```js
browser.pkcs11.installModule("my\_module");
```

Manifest location
-----------------

On Linux and macOS, you need to store the manifest in a particular place. On Windows, you need to create a registry key that points to the manifest's location.

The detailed rules are the same for all the manifest types, except that the penultimate component of the path identifies the type of manifest. The examples below show the form for each of the three different types. In all the examples, `<name>` is the value of the `name` property in the manifest.

### Windows

For global visibility, create a registry key with the following name:
```

HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\NativeMessagingHosts\<name>
```

```
HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\ManagedStorage\<name>
```

```
HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\PKCS11Modules\<name>
```

The key should have a single default value, which is the path to the manifest.

**Warning:** As of Firefox 64, the 32-bit registry view Wow6432Node will be checked first for these keys, followed by the "native" registry view. Use whichever is appropriate for your application.

**For Firefox 63 and older:** This key should *not* be created under Wow6432Node, even if the app is 32-bit. Previous versions of the browser will always look for the key under the "native" view of the registry, not the 32-bit emulation. To ensure that the key is created in the "native" view, you can pass the `KEY_WOW64_64KEY` or `KEY_WOW64_32KEY` flags into `RegCreateKeyEx`. See Accessing an Alternate Registry View.

For per-user visibility, create a registry key with the following name:
```

HKEY_CURRENT_USER\SOFTWARE\Mozilla\NativeMessagingHosts\<name>
```

```
HKEY_CURRENT_USER\SOFTWARE\Mozilla\ManagedStorage\<name>
```

```
HKEY_CURRENT_USER\SOFTWARE\Mozilla\PKCS11Modules\<name>
```

The key should have a single default value, which is the path to the manifest.

### macOS

For global visibility, store the manifest in:
```

/Library/Application Support/Mozilla/NativeMessagingHosts/<name>.json
```

```
/Library/Application Support/Mozilla/ManagedStorage/<name>.json
```

```
/Library/Application Support/Mozilla/PKCS11Modules/<name>.json
```

For per-user visibility, store the manifest in:
```

~/Library/Application Support/Mozilla/NativeMessagingHosts/<name>.json
```

```
~/Library/Application Support/Mozilla/ManagedStorage/<name>.json
```

```
~/Library/Application Support/Mozilla/PKCS11Modules/<name>.json
```

### Linux

For global visibility, store the manifest in either:
```

/usr/lib/mozilla/native-messaging-hosts/<name>.json
```

```
/usr/lib/mozilla/managed-storage/<name>.json
```

```
/usr/lib/mozilla/pkcs11-modules/<name>.json
```

or:
```

/usr/lib64/mozilla/native-messaging-hosts/<name>.json
```

```
/usr/lib64/mozilla/managed-storage/<name>.json
```

```
/usr/lib64/mozilla/pkcs11-modules/<name>.json
```

For per-user visibility, store the manifest in:
```

~/.mozilla/native-messaging-hosts/<name>.json
```

```
~/.mozilla/managed-storage/<name>.json
```

```
~/.mozilla/pkcs11-modules/<name>.json
```


Work with the Cookies API - Mozilla

Work with the Cookies API
=========================

With the Cookies API your extensions have access to capabilities similar to those used by websites to store and read cookies. The API's features give extensions the ability to store information on a site-by-site basis. So, as we shall see in the example, you could store details of a user's choice of background color for a site. Then, when the user revisits the site, your extension can use the API's ability to get details about cookies and read them to recover the user's choice and apply it to the website.

**Note:** The behavior of cookies can be controlled using the `privacy.websites` `cookieConfig` property. This property controls whether and how cookies are accepted or whether all cookies are treated as session cookies.

Permissions
-----------

To use the Cookies API you need to request both the `"cookies"` permission and host permissions for the protocols, domains, or websites you want to access or use `"<all_urls>"` to access any protocol and domain. The way in which you define your host permission string affects your extension's ability to read, write, and delete cookies.

| Host permission string | Read | Write/Delete |
| Secure | Non-secure | Secure | Non-secure |
| `"http://*.example.com/"` | No | Main and sub domains, with any path | Main and sub domains, with any path | Main and sub domains, with any path |
| `"https://www.example.com/"` | www.example.com or .example.com with any path, but no subdomains | www.example.com or .example.com with any path, but no subdomains | www.example.com or .example.com with any path, but no subdomains | www.example.com or .example.com with any path, but no subdomains |
| `"*://*.example.com/"` | Main and sub domains, with any path | Main and sub domains, with any path | Main and sub domains, with any path | Main and sub domains, with any path |
| `"<all_urls>"` | Any domain with any path | Any domain with any path | Any domain with any path | Any domain with any path |

Cookie stores
-------------

Firefox provides three types of cookie store:

* The default store, which stores cookies from normal browsing.
* Private browsing mode stores, which stores cookies created during a private browsing session. These stores and any cookies they contain are removed when the related private browsing window closes.

**Note:**
 Only visible if `extension.isAllowedIncognitoAccess()` returns true. Safari doesn't support access to private cookies.
* Container tabs stores, which stores cookies for each contextual identity in Firefox. Contextual identities enable a user to maintain multiple identities within one browser window. This is useful if, for example, you've a company and personal email account on Gmail. With contextual identities, you can open one tab against a personal identity and a second tab against a business identity. Each tab can then sign into Google mail with a different username, and the two accounts can be used side-by-side. For more information, see Security/Contextual Identity Project/Containers in the Mozilla wiki.

You can find out what cookie stores are available using `cookies.getAllCookieStores`, which returns an object containing the ID of each cookie store and a list of the IDs of the tabs using each cookie store.

Example walkthrough
-------------------

The example extension cookie-bg-picker allows its user to pick a color and icon that are applied to the background of a site's web pages. These choices are saved on a per-site basis using `cookies.set`. When a page from the site is opened, `cookies.get` reads any earlier choice, and the extension applies it to the web page. A reset option removes the background icon and color from the site as well as the cookie, using `cookies.remove`. It also uses `cookies.onChanged` to listen for changes to cookies, sending details of the change to the console.

This video shows the extension in action:

This example also uses the Tabs and Runtime APIs, but we'll discuss those features only in passing.

### manifest.json

The key feature of the manifest.json file relating to the use of the Cookies API is the permissions request:

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

Here, the extension requests permission to use the Cookies API (`"cookies"`) with all websites (`"<all_urls>"`). This enables the extension to save the background color icon choice for any website.

### Scripts—bgpicker.js

The extension's UI uses a toolbar button (`browserAction`) implemented with bgpicker.html that calls bgpicker.js. Together these allow the user to select the icon and enter the color they want to apply as the site background. They also provide the option to clear those settings.

bgpicker.js handles the selection of icon or entry of a color for the background in separate functions.

To handle the icon buttons the script first gathers all the class names used for the buttons in the HTML file:

```js
let bgBtns = document.querySelectorAll(".bg-container button");
```

It then loops through all the buttons assigning them their image and creating an onclick listener for each button:

```js
for (let i = 0; i < bgBtns.length; i++) {
  let imgName = bgBtns[i].getAttribute('class');
  let bgImg = 'url(\'images/' + imgName + '.png\')';
  bgBtns[i].style.backgroundImage = bgImg;

  bgBtns[i].onclick = (e) => {
```

When a button is clicked, its corresponding listener function gets the button class name and then the icon path which it passes to the page's content script (updatebg.js) using a message. The content script then applies the icon to the web page's background. Meanwhile, bgpicker.js stores the details of the icon applied to the background in a cookie:

```js
cookieVal.image = fullURL;
browser.cookies.set({
  url: tabs[0].url,
  name: "bgpicker",
  value: JSON.stringify(cookieVal),
});
```

The color setting is handled in a similar way, triggered by a listener on the color input field. When a color is entered the active tab is discovered and the color selection details sent, using a message, to the page's content script to be applied to the web page background. Then the color selection is added to the cookie:

```js
    cookieVal.color = currColor;
    browser.cookies.set({
    url: tabs[0].url,
    name: "bgpicker",
    value: JSON.stringify(cookieVal)
```

When the user clicks the reset button, which has been assigned to the variable reset:

```js
let reset = document.querySelector(".color-reset button");
```

`reset.onclick` first finds the active tab. Then, using the tab's ID it passes a message to the page's content script (updatebg.js) to get it to remove the icon and color from the page. The function then clears the cookie values (so the old values aren't carried forward and written onto a cookie created for a new icon or color selection on the same page) before removing the cookie:

```js
    cookieVal = { image : '',
                  color : '' };
    browser.cookies.remove({
    url: tabs[0].url,
    name: "bgpicker"
```

Also, so you can see what is going on with the cookies, the script reports on all changes to cookies in the console:

```js
browser.cookies.onChanged.addListener((changeInfo) => {
  console.log(`Cookie changed:\n
 \* Cookie: ${JSON.stringify(changeInfo.cookie)}\n
 \* Cause: ${changeInfo.cause}\n
 \* Removed: ${changeInfo.removed}`);
});
```

### Scripts—background.js

A background script (background.js) provides for the possibility that the user has chosen a background icon and color for the website in an earlier session. The script listens for changes in the active tab, either the user switching between tabs or changing the URL of the page displayed in the tab. When either of these events happen, `cookieUpdate()` is called. `cookieUpdate()` in turn uses `getActiveTab()` to get the active tab ID. The function can then check whether a cookie for the extension exists, using the tab's URL:

```js
let gettingCookies = browser.cookies.get({
  url: tabs[0].url,
  name: "bgpicker",
});
```

If the `"bgpicker"` cookie exists for the website, the details of the icon and color selected earlier are retrieved and passed to the content script updatebg.js using messages:

```js
gettingCookies.then((cookie) => {
  if (cookie) {
    let cookieVal = JSON.parse(cookie.value);
    browser.tabs.sendMessage(tabs[0].id, { image: cookieVal.image });
    browser.tabs.sendMessage(tabs[0].id, { color: cookieVal.color });
  }
});
```

Other features
--------------

In addition to the APIs mentioned so far, the Cookies API also offers `cookies.getAll`. This function takes the details object to specify filters for the selected cookies and returns an array of `cookies.Cookie` objects that match the filter criteria.

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

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

* Cookies API reference.
* List-cookies example.