Building a cross-browser extension

注意: This article discusses building cross-browser extensions for manifest v2. At the time of writing (December 2021), manifest v3 is being introduced by the major browser vendors. Manifest v3 is likely to change the way cross-browser extension development is undertaken. However, work on Manifest v3 is not complete. The major browser vendors are collaborating (with community members) to ease the development of a cross-browser extension in the W3C WebExtensions Community Group .

The introduction of the browser extensions API created a uniform landscape for the development of browser extensions. However, there are differences in the API implementations and the scope of coverage among the browsers that use the extensions API (the major ones being Chrome, Edge, Firefox, Opera, and Safari).

Maximizing the reach of your browser extension means developing it for at least two browsers, possibly more. This article looks at six of the main challenges faced when creating a cross-browser extension and suggests how to address these challenges.

Cross-platform extension coding hurdles

There are six areas you need to address when tackling a cross-platform extension:

  • API namespace
  • API asynchronous event handling
  • API function coverage
  • Manifest keys
  • Extension packaging
  • Extension publishing

API namespace

There are two API namespaces in use among the main browsers:

  • browser.* , the proposed standard for the extensions API used by Firefox and Safari.
  • chrome.* used by Chrome, Opera, and Edge.

Firefox also supports the chrome.* namespace for APIs that are compatible with Chrome, primarily to assist with porting . However, using the browser.* namespace is preferred. In addition to being the proposed standard, browser.* uses promises—a modern and convenient mechanism for handling asynchronous events.

Only in the most trivial extensions is namespace likely to be the only cross-platform issue that has to be addressed. Therefore, it’s rarely, if ever, helpful to address this issue alone. The best approach is to address this with asynchronous event handling.

API asynchronous event handling

There are two approaches to handling asynchronous events in use among the main browsers:

  • Promises , the proposed standard for the extensions API used by Firefox and Safari.
  • 回调 , used by Chrome, Edge, and Opera.

Firefox also supports callbacks for the APIs that support the chrome.* namespace. However, using promises (and the browser.* namespace) is recommended. Promises have been adopted as part of the proposed standard. It greatly simplifies asynchronous event handling, particularly where you need to chain events together.

注意: If you’re unfamiliar with the differences between these two methods, take a look at Getting to know asynchronous JavaScript: Callbacks, Promises and Async/Await or the MDN Using promises 页面。

The WebExtension browser API Polyfill

So, how do you take advantage of promises easily? The solution is to code for Firefox using promises and use the WebExtension browser API Polyfill to address Chrome, Opera, and Edge.

This polyfill addresses the API namespace and asynchronous event handling across Firefox, Chrome, Opera, and Edge.

To use the polyfill, install it into your development environment using npm or download it directly from GitHub releases .

Then, reference browser-polyfill.js in:

  • manifest.json , to make it available to background and content scripts.
  • HTML documents, such as browserAction popups or tab pages.
  • executeScript call in dynamically-injected content scripts loaded by tabs.executeScript , where it hasn’t been loaded using a content_scripts declaration in manifest.json .

So, for example, this manifest.json code makes the polyfill available to background scripts:

{
 // ...
 "background": {
   "scripts": [
     "browser-polyfill.js",
     "background.js"
   ]
 }
}

					

Your goal is to ensure that the polyfill executes in your extension before any other scripts that expect the browser.* API namespace execute.

注意: For more details and information on using the polyfill with a module bundler, see the project’s readme on GitHub.

There are other polyfill options. However, at the time of writing, none of the other options provide the coverage of the WebExtension browser API Polyfill. So, where you haven’t targeted Firefox as your first choice, your options are to accept the limitations of alternative polyfills, port to Firefox and add cross-browser support, or develop your own polyfill.

API function coverage

The differences in the API functions offered in each of the main browsers fall into three broad categories:

  1. Lack of support for an entire function. For example, at the time of writing, Edge didn’t support the browserSettings 函数。
  2. Variations in the support for features within a function. For example, at the time of writing, Firefox doesn’t support the notification function method notifications.onButtonClicked , while Firefox is the only browser that supports notifications.onShown .
  3. Proprietary functions, supporting browser-specific features. For example, at the time of writing, containers was a Firefox-specific feature supported by the contextualIdentities 函数。

Details about the support for the extension APIs among the main browsers and Firefox for Android and Safari on iOS can be found on the Mozilla Developer Network Browser support for JavaScript APIs page. Browser compatibility information is also included with each function and its methods, types, and events in the Mozilla Developer Network JavaScript API reference pages.

Handling API differences

A simple approach to addressing API differences is to limit the functions used in your extension to functions that offer the same functionality across your range of targeted browsers. In practice, this approach is likely to be too restrictive for most extensions.

Instead, where there are differences among the APIs, you should either offer alternative implementations or fallback functionality. (Remember: you may also need to do this to allow for differences in API support between versions of the same browser.)

The use of runtime checks on the availability of a function’s features is the recommended approach to implementing alternative or fallback functionality. The benefit of performing a runtime check is that you don’t need to update and redistribute the extension to take advantage of a function when it becomes available.

The following code enables you to perform a runtime check:

if (typeof <function> === "function") {
   // safe to use the function
}

					

Manifest keys

The differences in the manifest.json file keys supported by the main browsers fall broadly into three categories:

  1. Extension information attributes. For example, at the time of writing, Firefox and Opera include the developer key for details about the developer of the extension.
  2. Extension features. For example, at the time of writing, Chrome did not support the browser_specific_settings key.
  3. Key optionality. At the time of writing, generally, only "manifest_version" , "version" ,和 "name" are mandatory keys.

Browser compatibility information is included with each key in the Mozilla Developer Network manifest.json key reference pages .

As manifest.json files tend to change little—except for release numbers, which may differ between the various browsers—creating and editing a static version for each browser is usually the simplest approach.

Extension packaging

Packaging an extension for distribution through the browser extension stores is relatively straightforward. Firefox, Chrome, Edge, and Opera all use a simple zip format that requires the manifest.json file to be at the root of the zip package. Safari requires extensions to be packaged in a similar way to apps.

For details on packaging, refer to the guidance on the respective extension’s developer portals.

Extension publishing

Each of the major browsers maintains browser extension stores. Each store also reviews your extension to check for security vulnerabilities.

As a consequence, you need to approach adding and updating your extension for each store separately. In some cases, you can upload your extension using a utility.

This table summarizes the approach and features of each store:

Browser Registration fee Upload utility Pre-publication review process Account two-factor authentication
Chrome Yes Yes Automatic, less than an hour Yes
Edge No No No SLA provided Yes
Firefox No web-ext

Automatic, a few seconds.

A manual review of the extension takes place after publication, which may result in the extension being suspended where issues that need fixing are found.

Yes
Opera No No Manual, no SLA provided No
Safari Yes No Yes with, according to Apple, on average, 50% of apps reviewed in 24 hours and over 90% reviewed in 48 hours. Yes

Other considerations

Version numbering

The Firefox, Chrome, and Edge stores require that each uploaded version has a different version number. This means you cannot revert to an earlier version number if you come across issues in a release.

结论

When approaching a cross-platform extension development, the differences between extension API implementations can be addressed by targeting Firefox and using the WebExtension browser API Polyfill . Following this approach, you benefit from using API features that are closely aligned with the proposed extensions API standard and gain the simplicity of promises for asynchronous event handling.

The bulk of your cross-platform work is likely to focus on handling variations among the API features supported by the main browsers. Creating your manifest.json files should be relatively straightforward and something you can do manually. You will then need to account for the variations in the processes for submitting to each extension store.

可以使用 browser-extension-template to quickly set up a working project for building and publishing a browser extension.

Following the advice in this article, you should be able to create an extension that works well on all of the four main browsers, enabling you to deliver your extension features to more people.

Found a problem with this page?

最后修改: , 由 MDN 贡献者

  1. 浏览器扩展名
  2. 快速入门
    1. What are extensions?
    2. Your first extension
    3. Your second extension
    4. Anatomy of an extension
    5. Example extensions
    6. What next?
  3. 概念
    1. Using the JavaScript APIs
    2. Content scripts
    3. Match patterns
    4. Working with files
    5. 国际化
    6. Content Security Policy
    7. Native messaging
    8. Differences between API implementations
    9. Chrome incompatibilities
  4. 用户界面
    1. 用户界面
    2. Toolbar button
    3. Address bar button
    4. Sidebars
    5. Context menu items
    6. Options page
    7. Extension pages
    8. Notifications
    9. Address bar suggestions
    10. Developer tools panels
  5. 如何
    1. Intercept HTTP requests
    2. Modify a web page
    3. Insert external content
    4. Share objects with page scripts
    5. Add a button to the toolbar
    6. Implement a settings page
    7. Work with the Tabs API
    8. Work with the Bookmarks API
    9. Work with the Cookies API
    10. Work with contextual identities
    11. Interact with the clipboard
    12. Build a cross-browser extension
  6. Firefox differentiators
  7. JavaScript API
    1. Browser support for JavaScript APIs
    2. alarms
    3. bookmarks
    4. browserAction
    5. browserSettings
    6. browsingData
    7. captivePortal
    8. clipboard
    9. 命令
    10. contentScripts
    11. contextualIdentities
    12. Cookie
    13. devtools
    14. dns
    15. downloads
    16. events
    17. extension
    18. extensionTypes
    19. find
    20. history
    21. i18n
    22. identity
    23. idle
    24. management
    25. menus
    26. notifications
    27. omnibox
    28. pageAction
    29. permissions
    30. pkcs11
    31. privacy
    32. proxy
    33. runtime
    34. search
    35. sessions
    36. sidebarAction
    37. storage
    38. tabs
    39. theme
    40. topSites
    41. 类型
    42. userScripts
    43. webNavigation
    44. webRequest
    45. windows
  8. Manifest keys
    1. 介绍
    1. 作者
    2. background
    3. browser_action
    4. browser_specific_settings
    5. chrome_settings_overrides
    6. chrome_url_overrides
    7. 命令
    8. content_scripts
    9. content_security_policy
    10. default_locale
    11. description
    12. developer
    13. devtools_page
    14. dictionaries
    15. externally_connectable
    16. homepage_url
    17. icons
    18. incognito
    19. manifest_version
    20. name
    21. offline_enabled
    22. omnibox
    23. optional_permissions
    24. options_page
    25. options_ui
    26. page_action
    27. permissions
    28. protocol_handlers
    29. short_name
    30. sidebar_action
    31. storage
    32. theme
    33. theme_experiment
    34. user_scripts
    35. version
    36. version_name
    37. web_accessible_resources
  9. Extension Workshop
    1. Develop
    2. Publish
    3. Manage
    4. Enterprise
  10. Contact us
  11. Channels
    1. Add-ons blog
    2. Add-ons forum
    3. Add-ons chat