WebIDL WebExtensions API Bindings

While on manifest_version: 2 all the extension globals (extension pages and content scripts) that lives on the main thread and the WebExtensions API bindings can be injected into the extension global from the JS privileged code part of the WebExtensions internals (See Schemas.inject defined in Schemas.sys.mjs), in manifest_version: 3 the extension will be able to declare a background service worker instead of a background page, and the existing WebExtensions API bindings can’t be injected into this new extension global, because it lives off of the main thread.

To expose WebExtensions API bindings to the WebExtensions background.service_worker global we are in the process of generating new WebIDL bindings for the WebExtensions API.

Warning

For more general in depth details about WebIDL in Gecko:

Review process on changes to webidl definitions

Note

When new webidl definitions are being introduced for a WebExtensions API, or existing ones need to be updated to stay in sync with changes applied to the JSONSchema definitions of the same WebExtensions API, the resulting patch will include a new or changed WebIDL located at dom/webidl and that part of the patch will require a mandatory review and sign-off from a peer part of the webidl phabricator review group.

This section includes a brief description about the special setup of the webidl files related to WebExtensions and other notes useful to the WebIDL peers that will be reviewing and signing off these webidl files.

How/Where are these webidl interfaces restricted to the extensions background service workers?

All the webidl interfaces related to the extensions API are only visible in specific extension globals: the WebExtensions background service worker (a service worker declared in the extension manifest.json file, through the background.service_worker manifest field).

All webidl interfaces related to the WebExtensions API interfaces are exposed through the ExtensionBrowser interface, which gets exposed into the ServiceWorkerGlobalScope through the ExtensionGlobalsMixin interface and restricted to the WebExtensions background service worker through the mozilla::extensions::ExtensionAPIAllowed helper function.

See ExtensionBrowser and ExtensionGlobalsMixin interfaces defined from ExtensionBrowser.webidl and mozilla::extensions::ExtensionAPIAllowed defined in ExtensionBrowser.cpp.

Why do all the webidl interfaces for WebExtensions API use LegacyNoInterfaceObject?

The existing WebExtensions API bindings are not exposing any constructor in the globals where they are available (e.g. the webidl bindings for the browser.alarms API namespace is defined by the ExtensionAlarms webidl interface, but there shouldn’t be any ExtensionAlarms constructor available as a global to extension code running in the background service worker).

A previous attempt to create W3C specs for the WebExtensions APIs described in WebIDL syntaxes (https://browserext.github.io/browserext) was also using the same NoInterfaceObject WebIDL attribute on the definitions of the API namespace with the same motivations (eg. see BrowserExtBrowserRuntime as defined here: https://browserext.github.io/browserext/#webidl-definition-4).

Bug 1713877 is tracking a followup to determine a long term replacement for the LegacyNoInterfaceObject attribute currently being used.

Background Service Workers API Request Handling

High Level Diagram of the Background Service Worker API Request Handling

Generating WebIDL definitions from WebExtensions API JSONSchema

WebIDL definitions for the extension APIs are being generated based on the WebExtensions API JSONSchema data (the same metadata used to generate the “privileged JS”-based API bindings).

Most of the API methods in generated WebIDL are meant to be implemented using stub methods shared between all WebExtensions API classes, a WebExtensionStub webidl extended attribute specify which shared stub method should be used when the related API method is called.

For more in depth details about how to generate or update webidl definition for an Extension API given its API namespace:

Wiring up new WebExtensions WebIDL files into mozilla-central

After a new WebIDL definition has been generated, there are a few more steps to ensure that the new WebIDL binding is wired up into mozilla-central build system and to be able to complete successfully a full Gecko build that include the new bindings.

For more in depth details about these next steps:

Testing WebExtensions WebIDL bindings

Once the WebIDL definition for an WebExtensions API namespace has been implemented and wired up, the following testing strategies are available to cover them as part of the WebExtensions testing suites:

toolkit/components/extensions/test/xpcshell/webidl-api

The xpcshell tests added to this group of xpcshell tests are meant to provide testing coverage related to lower level components and behaviors (e.g. when making changes to the shared C++ helpers defined in toolkit/components/extensions/webidl-api, or adding new ones).

These tests will often mock part of the internals and use a browser.mockExtensionAPI API namespace which is only available in tests and not mapped to any actual API implementation (instead it is being mocked in the test cases to recreate the scenario that the test case is meant to cover).

And so they are not meant to provide any guarantees in terms of consistency of the behavior of the two different bindings implementations (the new WebIDL bindings vs. the current implemented bindings), instead the other test suites listed in the sections below should be used for that purpose.

All tests in this directory are skipped in builds where the WebExtensions WebIDL API bindings are being disabled at build time (e.g. beta and release builds, where otherwise the test would permafail while riding the train once got on those builds).

toolkit/components/extensions/test/xpcshell/xpcshell-serviceworker.ini

When a new or existing xpcshell tests added to this xpcshell-test manifest, all test extensions will be generated with a background service worker instead of a background page.

Warning

Unless the background page or scripts are declared as part of the test extension manifest, the test file added to this manifest should be explicitly reviewed to make sure all tests are going to provide the expected test coverage in all modes.

Note

In a background script that runs in both a background page and a background service worker it may be necessary to run different code for part of the test, self !== self.window is a simple check that can be used to detect if the script is being executed as a background service worker.

Test tasks that should be skipped when running in “background service worker mode”, but temporarily until a followup fixes the underlying issue can use the ExtensionTestUtils.isInBackgroundServiceWorkerTests() in the optional add_task’s skip_if parameter:

add_task(
  {
    // TODO(Bug TBF): remove this once ...
    skip_if: () => ExtensionTestUtils.isInBackgroundServiceWorkerTests(),
  },
  async function test_someapi_under_scenario() {
   ...
  }
);

On the contrary if the test tasks is covering a scenario that is specific to a background page, and it would need to be permanently skipped while the background script is running as a service worker, it may be more appropriate to split out those tests in a separate test file not included in this manifest.

Warning

Make sure that all tests running in multiple modes (in-process, remote, and “background service worker mode”) do not assume that the WebIDL bindings and Background Service Worker are enabled and to skip them when appropriate, otherwise the test will become a permafailure once it gets to a channel where the “Extensions WebIDL API bindings” are disabled by default at build time (currently on beta and release).

While running the test files locally they will be executed once for each manifest file where they are included, to restrict the run to just the “background service workers mode” specify the sw-webextensions tag:

mach xpcshell-test --tag sw-webextensions path/to/test/file.js

toolkit/components/extensions/test/mochitest/mochitest-serviceworker.ini

Same as the xpcshell-serviceworker.ini manifest but for the mochitest-plain tests.

Warning

The same warnings described in the xpcshell-serviceworker.ini subsection do also apply to this manifest file.

Test tasks that should be skipped when not running in “background service worker mode” can be split into separate test file or skipped inside the add_task body, but mochitests’ add_task does not support a skip_if option and so that needs to be done manually (and it may be good to also log a message to make it visible when a test is skipped):

add_task(async function test_someapi_in_background_service_worker() {
  if (!ExtensionTestUtils.isInBackgroundServiceWorkerTests()) {
    is(
      ExtensionTestUtils.getBackgroundServiceWorkerEnabled(),
      false,
      "This test should only be skipped with background service worker disabled"
    )
    info("Test intentionally skipped on 'extensions.backgroundServiceWorker.enabled=false'");
    return;
  }


  ...
});

While executing the test files locally they will run once for each manifest file where they are included, to restrict the run to just the “background service workers mode” specify the sw-webextensions tag:

mach mochitest --tag sw-webextensions path/to/test/file.js