The WebExtension APIs aim to provide compatibility across all the main browsers, so extensions should run on any browser with minimal changes.
However, there are significant differences between Chrome (and Chromium-based browsers), Firefox, and Safari. In particular:
Support for WebExtension APIs differs across browsers. See Browser support for JavaScript APIs for details.
Support for manifest.json
keys differs across browsers. See the "Browser compatibility" section on the manifest.json
page for more details.
Extension API namespace:
browser
namespace. The chrome
namespace is also supported for compatibility with Chrome.chrome
namespace. (cf. Chrome bug 798169)Asynchronous APIs:
The rest of this page details these and other incompatibilities.
JavaScript APIs chrome.* and browser.* namespaceIn Firefox and Safari: The APIs are accessed using the browser
namespace.
browser.browserAction.setIcon({ path: "path/to/icon.png" });
In Chrome: The APIs are accessed using the chrome
namespace.
chrome.browserAction.setIcon({ path: "path/to/icon.png" });
In Firefox and Safari (all versions), and Chrome (starting from Manifest Version 3): Asynchronous APIs use promises to return values.
function logCookie(c) {
console.log(c);
}
function logError(e) {
console.error(e);
}
let setCookie = browser.cookies.set({
url: "https://developer.mozilla.org/",
});
setCookie.then(logCookie, logError);
In Chrome: In Manifest V2, asynchronous APIs use callbacks to return values and runtime.lastError
to communicate errors. In Manifest V3, callbacks are supported for backward compatibility, along with support for promises on most appropriate methods.
function logCookie(c) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
} else {
console.log(c);
}
}
chrome.cookies.set({ url: "https://developer.mozilla.org/" }, logCookie);
As a porting aid, the Firefox implementation of WebExtensions supports chrome
using callbacks and browser
using promises. This means that many Chrome extensions work in Firefox without changes.
Note: The browser
namespace is supported by Firefox and Safari. Chrome does not offer the browser
namespace, until Chrome bug 798169 is resolved.
If you choose to write your extension to use browser
and promises, Firefox provides a polyfill that should enable it to run in Chrome: https://github.com/mozilla/webextension-polyfill.
The Browser support for JavaScript APIs page includes compatibility tables for all APIs that have any support in Firefox. Where there are caveats regarding support for an API method, property, type, or event, this is indicated in these tables with an asterisk "*". Selecting the asterisk expands the table to display a note explaining the caveat.
The tables are generated from compatibility data stored as JSON files in GitHub.
The rest of this section describes the main compatibility issues you may need to consider when building a cross-browser extension. Also, remember to check the browser compatibility tables, as they may contain additional compatibility information.
Notifications APIFor notifications.create()
, with type "basic"
:
iconUrl
is optional.iconUrl
is required.When the user clicks on a notification:
If you call notifications.create()
more than once in rapid succession:
notifications.create()
callback function is not a sufficient delay to prevent this.Firefox and Chrome include a Proxy API. However, the design of these two APIs is incompatible.
proxy.ProxyConfig
object. Depending on Chrome's proxy settings, the settings may contain proxy.ProxyRules
or a proxy.PacScript
. Proxies are set using the proxy.settings property. See chrome.proxy for more information on the API.Firefox and Chrome provide incompatible APIs for working with a sidebar.
sidebar_action
manifest key and manipulated with the sidebarAction
API.side_panel
manifest key. The sidePanel
API then enables panels to be manipulated.When using tabs.executeScript()
or tabs.insertCSS()
:
To work cross-browser, you can specify the path as an absolute URL, starting at the extension's root, like this:
/path/to/script.js
When calling tabs.remove()
:
tabs.remove()
promise is fulfilled after the beforeunload
event.beforeunload
.In Firefox:
Requests can be redirected only if their original URL uses the http:
or https:
scheme.
The activeTab
permission does not allow for intercepting network requests in the current tab. (See bug 1617479)
Events are not fired for system requests (for example, extension upgrades or search bar suggestions).
webRequest.onAuthRequired
for proxy authorization. See the documentation for webRequest.onAuthRequired
.If an extension wants to redirect a public (e.g., HTTPS) URL to an extension page, the extension's manifest.json
file must contain a web_accessible_resources
key with the URL of the extension page.
Note: Any website may link or redirect to that URL, and extensions should treat any input (POST data, for example) as if it came from an untrusted source, as a normal web page should.
Some of the browser.webRequest.*
APIs allow for returning Promises that resolves webRequest.BlockingResponse
asynchronously.
In Chrome: Only webRequest.onAuthRequired
supports asynchronous webRequest.BlockingResponse
by supplying 'asyncBlocking'
, through a callback instead of a Promise.
onFocusChanged
of the windows
API triggers multiple times for a focus change.declarativeContent.RequestContentScript
API (which is rarely used and is unavailable in stable releases of Chrome).moz-extension://«random-UUID»/«path»
. This randomness can prevent you from doing things, such as adding your extension's URL to another domain's CSP policy.web_accessible_resources
, it is accessible as chrome-extension://«your-extension-id»/«path»
. The extension ID is fixed for an extension.web_accessible_resources
, this property is unsupported. Firefox extensions can fix their extension ID through the browser_specific_settings.gecko.id
manifest key (see browser_specific_settings.gecko)."key"
property to pin the extension ID across different machines. This is mainly useful when working with web_accessible_resources
.fetch()
) to a relative URL (like /api
), it is sent to https://example.com/api
.window
(Firefox bug 1208775). More specifically, the global scope (globalThis
) is composed of standard JavaScript features as usual, plus window
as the prototype of the global scope. Most DOM APIs are inherited from the page through window
, through Xray vision to shield the content script from modifications by the web page. A content script may encounter JavaScript objects from its global scope or Xray-wrapped versions from the web page.window
, and the available DOM APIs are generally independent of the web page (other than sharing the underlying DOM). Content scripts cannot directly access JavaScript objects from the web page.eval
runs code in the context of the content script and window.eval
runs code in the context of the page. See Using eval
in content scripts.eval
and window.eval
always runs code in the context of the content script, not in the context of the page.this.{variableName}
in one script and then attempting to access them using window.{variableName}
in another. This is a limitation created by the sandbox environment in Firefox. This limitation may be removed; see Firefox bug 1208775.In Firefox: Content scripts remain injected in a web page after the user has navigated away. However, window object properties are destroyed. For example, if a content script sets window.prop1 = "prop"
and the user then navigates away and returns to the page window.prop1
is undefined. This issue is tracked in Firefox bug 1525400.
To mimic the behavior of Chrome, listen for the pageshow and pagehide events. Then simulate the injection or destruction of the content script.
In Chrome: Content scripts are destroyed when the user navigates away from a web page. If the user clicks the back button to return to the page through history, the content script is injected into the web page.
The main manifest.json
page includes a table describing browser support for manifest.json
keys. Where there are caveats around support for a given key, this is indicated in the table with an asterisk "*". Selecting the asterisk expands the table to display a note explaining the caveat.
The tables are generated from compatibility data stored as JSON files in GitHub.
Native messaging Connection-based messaging argumentsOn Linux and Mac: Chrome passes one argument to the native app, which is the origin of the extension that started it, in the form of chrome-extension://«extensionID/»
(trailing slash required). This enables the app to identify the extension.
On Windows: Chrome passes two arguments:
allowed_extensions
.allowed_origins
.CreateProcess
, instead of ShellExecute
, to launch the additional process with the CREATE_BREAKAWAY_FROM_JOB
flag.Some extension APIs allow an extension to send data from one part of the extension to another, such as runtime.sendMessage()
, tabs.sendMessage()
, runtime.onMessage
, the postMessage()
method of runtime.port
, and tabs.executeScript()
.
The Structured clone algorithm supports more types than the JSON serialization algorithm. A notable exception are (DOM) objects with a toJSON
method. DOM objects are not cloneable nor JSON-serializable by default, but with a toJSON()
method, these can be JSON-serialized (but still not cloned with the structured cloning algorithm). Examples of JSON-serializable objects that are not structured cloneable include instances of URL
and PerformanceEntry
.
Extensions that rely on the toJSON()
method of the JSON serialization algorithm can use JSON.stringify()
followed by JSON.parse()
to ensure that a message can be exchanged because a parsed JSON value is always structurally cloneable.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4