Manifest V3 migration guide

Manifest V3 became generally available in Firefox 109 after being available as a developer preview from Firefox 101. This page details what's changed and how you adapt your extensions to take advantage of Manifest V3.

See the Developing extensions for Firefox for Android page for additional guidance if you plan to support Firefox for Android.

What is Manifest V3?

Manifest V3 (MV3) is the umbrella term for several foundational changes to the WebExtensions API in Firefox. The name refers to the declared manifest_version key in each extension’s manifest.json file.

The Manifest V3 changes apply to extensions for Safari, Firefox, and Chromium-based browsers – such as Chrome, Edge, and Opera. While the goal is to maintain a high degree of compatibility between the Firefox, Safari, and Chromium extension platforms, our implementation diverges where we think it matters and where our values point to a different direction.

This article discusses the changes introduced with Manifest V3 in Firefox and highlights where they diverge from the Chrome and Safari implementation.

Manifest V3 changes

This section details the Manifest V3 changes made to Firefox.

Manifest version

The manifest.json key manifest_version accepts 3. To use Manifest V3, update your manifest.json file like this:

"manifest_version": 3

Search extensions icons

Search extensions must use local icons. This change prevents the unnecessary network pings that result from accessing remote icons.

To accommodate this change, provide a local icon and define it in your manifest.json chrome_settings_overrides manifest key like this:

"chrome_settings_overrides": {
  "search_provider": {
    "name": "Search engine",
    "search_url": "https://www.searchengine.com/search/?q={searchTerms}",
    "keyword": "search",
    "favicon_url": "/imager/favicon.ico"
  }
}

Host permissions

In Manifest V2, host permissions are defined in the manifest.json keys permissions or optional_permissions. The host permissions defined in the manifest.json keys permissions are displayed to the user on installation and granted to the extension permanently.

In Manifest V3, host permissions are defined in the host_permissions and optional_host_permissions keys. With Manifest V3 extensions, users can grant or revoke any host permissions on an ad hoc basis.

Up to Firefox 126, Manifest V3 host permissions were not granted on installation and were not displayed to the user. From Firefox 127, host permissions listed in host_permissions and content_scripts are displayed in the install prompt and granted on installation. However, if an extension update grants new host permissions, these are not shown to the user (see Firefox bug 1893232).

As users can revoke host permissions, your extension should check for the availability of any expected host permissions and request them if necessary.

See Requested permissions and user prompts in the host_permissions documentation for more information.

Move all host permission specifications to the manifest.json key host_permissions like this:

"permissions": [
  "tabs",
  "notifications"
],
"optional_permissions": [
  "geolocation"
],
"host_permissions": [
  "http://www.mysite.com/",
  "*://*.example.org/*"
]

Browser action

The features available under the manifest.json key browser_action and the browserAction API have moved to a new action key and API. Also, the _execute_action special shortcut is introduced.

As the old and new key and API are otherwise identical, the changes needed are relatively straightforward and are as follows:

  • rename the manifest.json key 'browser_action' to 'action' and remove any reference to browser_style, like this:
    "action": {
      "default_icon": {
        "16": "button/geo-16.png",
        "32": "button/geo-32.png"
      },
      "default_title": "Whereami?",
      "default_popup": "popup/geo.html",
      "theme_icons": [{
        "light": "icons/geo-16-light.png",
        "dark": "icons/geo-16.png",
        "size": 16
      }, {
        "light": "icons/geo-32-light.png",
        "dark": "icons/geo-32.png",
        "size": 32
      }]
    }
  • update API references from browser.browserAction to browser.action.
  • if used, change _execute_browser_action to _execute_action in the commands manifest key and in the menu.create and menu.update API methods (or their aliases contextMenus.create and contextMenus.update).

If the user changes the shortcut of the _execute_browser_action command, it is automatically carried over to the _execute_action command when an extension migrates from Manifest V2 to V3. This was implemented in Chrome 111 and Firefox 127.

In Chromium and Safari, the Browser Action and Page Action APIs are unified into the Action API, page_action is merged into the renamed action key, and the _execute_page_action special shortcut is replaced by _execute_action. Firefox retains the page action API, key, and special shortcut.

browser_style

In Manifest Version 3, browser_style: true is no longer supported in the options_ui, action, page_action, and sidebar_action manifest keys.

The goal of this property was to enable extension UI components to take on the browser's style. However, it only partially worked as intended. As a consequence, it has been deprecated for Manifest V3. Therefore, remove any references from the manifest keys.

In Manifest Version 2, browser_style defaults to true for options_ui and sidebar_action. Therefore, unless you had set "browser_style": false, confirm that the appearance of options_ui and sidebar_action match your intended design.

See Manifest V3 migration for browser_style for more information.

Scripting API

The Scripting API takes over the features of tabs.insertCSS(), tabs.removeCSS(), and tabs.executeScript() and adds capabilities to register, update, and unregister content scripts at runtime.

Also, the code parameter is removed so that arbitrary strings can no longer be executed. This API requires the scripting permission. So, you need to move any arbitrary strings executed as scripts to files and rewrite your code to use the Scripting API.

Event-driven background scripts

Firefox has extended support for background scripts by enabling non-persistent background pages (aka Event Pages) for Manifest V2 and V3. Using non-persistent background scripts greatly reduces your extension use of browser resources. However, MV3 removes support for persistent background pages.

To migrate your extension to using non-persistent background pages, you need to:

  • Update your manifest.json background key to remove the "persistent" property or set it to false. This feature is also supported in MV2 from Firefox 106.
  • Ensure listeners are at the top-level and use the synchronous pattern.
  • Record state changes in local storage.
  • Change timers to alarms.
  • Switch from using extension.getBackgroundPage to call a function from the background page, to runtime.getBackgroundPage.
  • Place menu creation using menus.create or its alias contextMenus.create in a runtime.onInstalled listener. Also, note that the menus.onClicked event or its alias contextMenus.onClicked must be used to handle menu entry click events from an event page, instead of the onclick parameter of the contextMenus.create or contextMenus.update methods. If the onclick property of menus.create or its alias contextMenus.create are used from a call originating from an event page, they throw synchronously.

Safari also supports event-driven background scripts, however, Chromium has adopted service workers instead.

Firefox supports non-persistent background pages from Firefox 106. In Firefox 105 and earlier, event pages are run as if they are a persistent background page.

More information on the migration process can be found on the background script page on MDN.

Event pages and backward-compatibility

This section is only relevant if your extension supports Firefox 105 and earlier.

An extension designed as a non-persistent background page works even when event pages are not supported (i.e., in Firefox 105 and earlier) with one exception: the registration of context menus. In an event page, context menus persist across restarts, while they do not in persistent background pages.

If the recommendation to register menus in runtime.onInstalled is followed, these menus are removed after a browser restart in Firefox 105 and earlier. To work around this issue, you could unconditionally call browser.contextMenus.create. When the menu exists, the browser.runtime.lastError property is set when the (optional) create callback is called.

browser.contextMenus.create(
  {
    id: "my-menu",
    // etc.
  },
  () => {
    // TODO: Do not forget to read the "browser.runtime.lastError" property to
    // avoid warnings about an uncaught error when the menu item was created
    // before ("ID already exists: my-menu").
  }
);

If the initialization of the menu is expensive or requires complex logic, an alternative is to check whether event pages are supported and, if so, run the logic less frequently than at every wakeup of the event page (e.g., with runtime.onInstalled or runtime.onStartup).

You can detect the availability of event pages using the characteristic that an error is thrown synchronously when onclick is passed to contextMenus.create in an event page.

The following code shows how to use such a test to register menus.

let eventPagesSupported = true;
try {
  // Firefox throws a synchronous error when onclick is passed in an event page.
  browser.contextMenus.create({ id: "test-menu", onclick: () => {} });
  eventPagesSupported = false;
  browser.contextMenus.remove("test-menu");
} catch (err) {
  // Firefox 106+ error: Property "onclick" cannot be used in menus.create, replace with an "onClicked" event listener.
}
async function registerMyMenus() {
  browser.contextMenus.create({ id: "my-menu", /* etc. */ });
}
if (eventPagesSupported) {
  browser.runtime.onInstalled.addListener(registerMyMenus);
} else {
  registerMyMenus();
}

Content security policies

Content security policy (CSP) in the content_security_policy manifest.json key is changing to use the extension_pages property.

Therefore, you need to move the extension’s CSP to the manifest.json key to extension_pages, like this:

"content_security_policy": {
 "extension_pages": "default-src 'self'"
}

Manifest V3 has a more restrictive content security policy than Manifest V2, this may require further changes in your pages.

Mozilla’s long-standing add-on policies prohibit remote code execution. In keeping with these policies, the content_security_policy field no longer supports sources permitting remote code in script-related directives, such as script-src or ’unsafe-eval’. The only permitted values for the script-src directive are ’self’ and ’wasm-unsafe-eval’. ’wasm-unsafe-eval’ must be specified in the CSP if an extension is to use WebAssembly. In Manifest V3, content scripts are subject to the same CSP as other parts of the extension.

Historically, a custom extension CSP required object-src to be specified. This is not required in Manifest V3 (and was removed from Manifest V2 in Firefox 106). See object-src in the content_security_policy documentation). This change makes it easier for extensions to customize the CSP with minimal boilerplate.

Web Accessible Resources

Web accessible resources are available only to the sites and extensions specified in the manifest. In Manifest V3, Firefox supports the extension_ids, matches, and resources properties to specify the packaged resources you want to make available. Firefox does not support the use_dynamic_url property.

To migrate your extension, rewrite the manifest.json key ‘web_accessible_resources’ to specify the sites and extensions that can access the resources.

Features already supported by Firefox

As part of its Manifest V3 implementation, Chromium introduces promise support to many methods with the goal of eventually supporting promises on all appropriate methods. This will provide for greater compatibility between Firefox and Chrome extensions, given that Firefox already supports promises when using the browser.* namespace.

In Manifest v2, Firefox extensions support the use of the chrome.* namespace with APIs that provide asynchronous event handling using callbacks. In Manifest V3, Firefox supports promises for asynchronous events in the chrome.* namespace.

Extension version in the manifest

The format of the top-level manifest.json version key in Firefox has evolved and became simpler: letters and other previously allowed symbols are no longer accepted. The value must be a string with 1 to 4 numbers separated by dots (e.g., 1.2.3.4). Each number can have up to 9 digits and leading zeros before another digit are not allowed (e.g., 2.01 is forbidden, but 0.2, 2.0.1, and 2.1 are allowed).

Migration checklist

  • Update the manifest.json key manifest_version to 3.
  • If your extension adds a search engine, add a local icon and reference it in the manifest.json key chrome_settings_overrides.search_provider.favicon_url.
  • Remove any host permissions from the manifest.json keys permissions and optional_permissions and add them to the host_permissions key. Remember that host_permissions is treated as an optional permission in Firefox and Safari but granted at install in Chrome.
  • Remove references to browser_style from the manifest.json keys browser_action, options_ui, page_action, and sidebar_action.
  • If browser_style:false was not specified in options_ui and sidebar_action, confirm that their appearance has not changed.
  • Rename the manifest.json key browser_action to action and update any API references from browser.browserAction to browser.action.
  • Convert background pages to be non-persistent.
  • Move the extension’s CSP to the manifest.json key content_security_policy.extension_pages and update the CSP to conform to Manifest V3 requirements.
  • Move any arbitrary strings executed as scripts to files and update your code to use the Scripting API.
  • Rename the deprecated manifest.json key applications to browser_specific_settings.
  • The add-on ID is required to publish your extension. Make sure to add one in the manifest.json key browser_specific_settings.gecko.id.
  • Ensure that the top-level manifest.json key version is a string of numbers separated by up to 3 dots. For details, see version format.