This article is a technical comparison of the Add-on SDK and WebExtensions technology. It's intended to help orient people who have an add-on that uses the SDK, and who are planning to port it to use WebExtension APIs.

Support for extensions using XUL/XPCOM or the Add-on SDK was removed in Firefox 57, released November 2017. As there is no supported version of Firefox enabling these technologies, this page will be removed by December 2020.

Beyond these broad similarities, there are a lot of differences in the details, and these are summarised in the following sections.

Manifest files

In both technologies you have a JSON manifest file in the extension's root directory. In the SDK this is called "package.json", while in WebExtensions it's called "manifest.json". Both files contain basic metadata such as the extension's name, description, and icons.

However, "manifest.json" includes many keys that define parts of the extension's capabilities and behavior, which in the SDK are more often defined in code. For example:

This makes developing extensions with WebExtension APIs more declarative and less programmatic, compared with SDK add-ons.

With the SDK you'll typically use jpm init to create a new package.json. The WebExtensions technology doesn't have an equivalent of jpm init, so you'll probably write the manifest from scratch or copy and adapt an existing one.

Persistent scripts

Both technologies have the concept of persistent scripts that stay loaded for the extension's lifetime, have access to privileged APIs, and can communicate with other parts of the extension such as content scripts.

With WebExtensions, these scripts are called "background scripts". You can define a set of scripts using the background manifest key, and they will all be loaded into the same document, which is a hidden, auto-generated, blank HTML page. You can also define your own custom document using the background key.

An important difference is that background scripts get a window global, with all the DOM objects you'd expect to be present on a window. This makes writing extensions more like writing web pages, with direct access to all the normal Web APIs like XMLHttpRequest or IndexedDB.

Also note that by default, extensions have a Content Security Policy applied to them. You can specify your own policy, but the default policy, among other things, disallows potentially unsafe practices such as the use of eval().

Learn more

Content scripts

In both the Add-on SDK and WebExtensions, persistent scripts can't directly access the content of web pages. Instead, extensions can attach content scripts to web pages. These scripts:

do get direct access to web content

don't have access to privileged APIs

can communicate with the persistent scripts using a messaging API.

In both technologies, there are two ways to attach scripts: you can automatically attach a set of scripts to pages whose URL matches a given pattern, or you can programmatically attach a script to the page hosted by a given tab. The way to do this is different in each technology, though:

In both technologies you can pass options to control when the script runs and whether it will be attached to subframes. WebExtensions don't include an equivalent of contentScriptOptions, though, so to pass configuration options to a content script in an extension, you would either have to send them in a message or store them in storage.local.

In both technologies, content scripts can communicate with persistent scripts using an asynchronous messaging API:

Panels and popups

Panels and popups are both transient dialogs specified using HTML, CSS, and JavaScript.

Unlike panels, popups are always attached to a button (either a browser action or a page action) and can't be displayed programmatically: they are only shown when the user clicks the button.

Also unlike panels, popup scripts get access to all the same APIs that background scripts do. They can even get direct access to the background page, via runtime.getBackgroundPage().

Settings

The Add-on SDK and WebExtensions both have some support for settings (sometimes also called options or preferences).

With the SDK you can define preferences using a preferences key in package.json. The user can see and change these preferences in the extension's entry in the Add-ons Manager. The extension in turn can listen for changes using the simple-prefs API.

With WebExtensions, you will have to implement your own UI for presenting settings, and your own code for persisting them for your extension. You do this by writing an HTML file that presents the settings UI, which can include a script for persisting the settings. The script gets access to all the WebExtensions APIs, and it's generally expected that you should use the storage API to persist settings.

You then assign the HTML file's URL to the options_ui key in manifest.json. Your settings page then appears in the extension's entry in the Add-ons Manager. The options page can also be programmatically opened with an API call to browser.runtime.openOptionsPage.

Note that WebExtensions does not provide an equivalent of the SDK's preferences/service API, which provides general access to browser settings. However, you can change some browser settings using the privacy and browserSettings APIs.

Learn more

Internationalization

The Add-on SDK and WebExtensions both include tools for localizing user-visible text. They offer mostly similar functionality:

Feature

Add-on SDK

WebExtensions

Strings in add-on scripts

Yes

Yes

Strings in content scripts

No

Yes

Strings in HTML

Yes

No

Strings in CSS

No

Yes

Title & description

Yes

Yes

Plural forms

Yes

No

Placeholders

Yes

Yes

In both systems, you supply localized strings as a collection of files, one for each locale.

To retrieve localized strings in extension code, there's a JavaScript API - l10n in the SDK and i18n in WebExtensions - that returns a localized string given an ID.

WebExtensions don't have direct support for localizing strings appearing in HTML, so you have to do this yourself, using JavaScript to retrieve localized strings and to replace the HTML with the localized version.

Learn more

Command-line tool

The Add-on SDK comes with a command-line tool, jpm, that you can use for testing and packaging extensions. There's an equivalent tool for WebExtensions, called web-ext. web-ext doesn't yet support all the same commands that jpm does, but it has the basics: run, build, and sign.

It's also now possible to install (and reload) SDK add-ons and extensions built with WebExtension APIs in Firefox from their source directory, without needing to package them as an XPI. See Temporary Installation in Firefox.

Learn more

JavaScript APIs

In both the SDK and WebExtensions, the main power of the extension comes from a set of dedicated JavaScript APIs. For most of the SDK high-level APIs, there is a WebExtensions equivalent.

One big limitation of WebExtensions compared with the SDK is that SDK add-ons can use require("chrome") to get access to the full range of XPCOM APIs in Firefox. This is not possible with WebExtensions.

With WebExtensions most APIs are made available already, with no need to import them:

browser.tabs.create({
"url": "https://developer.mozilla.org/"
});

For some WebExtension APIs, you need to ask permission first, using the permissions manifest.json key. In the example below, the extension will need to ask for the "tabs" permission if they want access to the tab's URL:

Use a content script that sends the selection data to the add-on. Alternatively, if you can use a contextmenu on a selection, the selection is contained in selectionText (see contextMenus.OnClickData).