Extensions built with WebExtension APIs are designed to be compatible with Chrome and Opera extensions. As far as possible, extensions written for those browsers should run on Firefox with minimal changes.
However, there are significant differences between Chrome, Firefox, and Edge. In particular:
Support for JavaScript APIs differs across browsers. See Browser support for JavaScript APIs for more details.
Support for manifest.json
keys differs across browsers. See the "Browser compatibility" section in the manifest.json
page for more details.
Javascript APIs:
chrome
namespace. (cf. Chrome bug 798169)browser
namespace.Asynchronous APIs:
The rest of this page summarizes these and other incompatibilities.
chrome
namespace. chrome.browserAction.setIcon({path: "path/to/icon.png"});
browser
namespace. browser.browserAction.setIcon({path: "path/to/icon.png"});
Many of the APIs are asynchronous.
runtime.lastError
to communicate errors. 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 );
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);
As a porting aid, the Firefox implementation of WebExtensions supports chrome
, using callbacks, as well as browser
, using promises. This means that many Chrome extensions will just work in Firefox without any changes.
Note: However, this is not part of the WebExtensions standard. and may not be supported by all compliant browsers.
If you choose to write your extension to use browser
and promises, then Firefox also provides a polyfill that will enable it to run in Chrome: https://github.com/mozilla/webextension-polyfill.
The page Browser support for JavaScript APIs includes compatibility tables for all APIs that have any support in Firefox. Where there are caveats around support for a given API item, this is indicated in these tables with an asterisk "*" and in the reference page for the API item, the caveats are explained.
These tables are generated from compatibility data stored as JSON files in GitHub.
The rest of this section describes compatibility issues that are not already captured in the tables.
These incompatibilities are present in notifications
:
For 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:
chrome.notifications.create()
callback function is not a sufficient delay to prevent this.Firefox's proxy
API followed a completely different design from Chrome's Proxy API.
Note: Because this API is incompatible with Chrome's proxy
API, the Firefox proxy API is only available through the browser
namespace.
These incompatibilities are present in tabs
:
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 :
tabs.query()
requires the "tabs"
permission.tabs.query()
It's possible without the "tabs"
permission, but will limit results to tabs whose URLs match host permissions.When calling tabs.remove()
:
tabs.remove()
promise is fulfilled after the beforeunload
event.beforeunload
.These incompatibilities are present in webRequest
:
http:
or https:
scheme.activeTab
permission does not allow intercepting network requests in the current tab. (See bug 1617479)webRequest.onAuthRequired
for proxy authorization. See the documentation for webRequest.onAuthRequired
.manifest.json
file must contain a web_accessible_resources
key with the URL of the extension page. Note: Any website may then link or redirect to that URL, and extensions should treat any input (POST data, for example) as if it came from an untrusted source, just as a normal web page should.
When using browser.webRequest.*
:
browser.webRequest.*
APIs allow returning Promises that resolves webRequest.BlockingResponse
asynchronously.webRequest.onAuthRequired
supports asynchronous webRequest.BlockingResponse
via supplying 'asyncBlocking'
.onFocusChanged
of the windows
API, will trigger multiple times for a given focus change.Chrome's declarativeContent API has not yet been implemented in Firefox:
declarativeContent.RequestContentScript
API (which is rarely used, and is unavailable in stable releases of Chrome).web_accessible_resources
, it is accessible as chrome-extension://«your-extension-id»/«path/to/resource»
. The extension ID is fixed for a given extension.moz-extension://«random-UUID»/«path/to/resource»
. This randomness can prevent you from doing a few things, such as add your specific extension's URL to another domain's CSP policy."key"
property to pin the extension ID across different machines. This is mainly useful when working with web_accessible_resources
.web_accessible_resources
, this property is unsupported.fetch()
) to a relative URL (like /api
), it will be sent to https://example.com/api
.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 bug 1208775.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 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.
The zoom level persists across page loads and navigation within the tab. in Chrome-based browsers
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 "*" and in the reference page for the key, the caveats are explained.
These tables are generated from compatibility data stored as JSON files in GitHub.
chrome-extension://«extensionID/»
(trailing slash required). This enables the app to identify the extension.Chrome passes two arguments:
allowed_extensions
.allowed_origins
instead.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
.
Extension 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.
© 2005–2021 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Chrome_incompatibilities