summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/docs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /toolkit/components/extensions/docs
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/extensions/docs')
-rw-r--r--toolkit/components/extensions/docs/background.rst133
-rw-r--r--toolkit/components/extensions/docs/basics.rst275
-rw-r--r--toolkit/components/extensions/docs/events.rst609
-rw-r--r--toolkit/components/extensions/docs/functions.rst201
-rw-r--r--toolkit/components/extensions/docs/generate_webidl_from_jsonschema.rst94
-rw-r--r--toolkit/components/extensions/docs/generate_webidl_from_jsonschema_dataflow.drawio.svg4
-rw-r--r--toolkit/components/extensions/docs/incognito.rst78
-rw-r--r--toolkit/components/extensions/docs/index.rst33
-rw-r--r--toolkit/components/extensions/docs/lifecycle.rst60
-rw-r--r--toolkit/components/extensions/docs/manifest.rst68
-rw-r--r--toolkit/components/extensions/docs/other.rst140
-rw-r--r--toolkit/components/extensions/docs/reference.rst35
-rw-r--r--toolkit/components/extensions/docs/schema.rst145
-rw-r--r--toolkit/components/extensions/docs/webext-storage.rst227
-rw-r--r--toolkit/components/extensions/docs/webidl_bindings.rst246
-rw-r--r--toolkit/components/extensions/docs/webidl_bindings_backgroundWorker_apiRequestHandling.drawio.svg4
-rw-r--r--toolkit/components/extensions/docs/wiring_up_new_webidl_bindings.rst165
17 files changed, 2517 insertions, 0 deletions
diff --git a/toolkit/components/extensions/docs/background.rst b/toolkit/components/extensions/docs/background.rst
new file mode 100644
index 0000000000..5d5dcd06b9
--- /dev/null
+++ b/toolkit/components/extensions/docs/background.rst
@@ -0,0 +1,133 @@
+Background
+==========
+
+WebExtensions run in a sandboxed environment much like regular web content.
+The purpose of extensions is to enhance the browser in a way that
+regular content cannot -- WebExtensions APIs bridge this gap by exposing
+browser features to extensions in a way preserves safety, reliability,
+and performance.
+The implementation of a WebExtension API runs with
+:doc:`chrome privileges </dom/scriptSecurity/index>`.
+Browser internals are accessed using
+:ref:`XPCOM`
+or :doc:`ChromeOnly WebIDL features </dom/webIdlBindings/index>`.
+
+The rest of this documentation covers how API implementations interact
+with the implementation of WebExtensions.
+To expose some browser feature to WebExtensions, the first step is
+to design the API. Some high-level principles for API design
+are documented on the Mozilla wiki:
+
+- `Vision for WebExtensions <https://wiki.mozilla.org/WebExtensions/Vision>`_
+- `API Policies <https://wiki.mozilla.org/WebExtensions/policy>`_
+- `Process for creating new APIs <https://wiki.mozilla.org/WebExtensions/NewAPIs>`_
+
+Javascript APIs
+---------------
+Many WebExtension APIs are accessed directly from extensions through
+Javascript. Functions are the most common type of object to expose,
+though some extensions expose properties of primitive Javascript types
+(e.g., constants).
+Regardless of the exact method by which something is exposed,
+there are a few important considerations when designing part of an API
+that is accessible from Javascript:
+
+- **Namespace**:
+ Everything provided to extensions is exposed as part of a global object
+ called ``browser``. For compatibility with Google Chrome, many of these
+ features are also exposed on a global object called ``chrome``.
+ Functions and other objects are not exposed directly as properties on
+ ``browser``, they are organized into *namespaces*, which appear as
+ properties on ``browser``. For example, the
+ `tabs API <https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs>`_
+ uses a namespace called ``tabs``, so all its functions and other
+ properties appear on the object ``browser.tabs``.
+ For a new API that provides features via Javascript, the usual practice
+ is to create a new namespace with a concise but descriptive name.
+
+- **Environments**:
+ There are several different types of Javascript environments in which
+ extension code can execute: extension pages, content scripts, proxy
+ scripts, and devtools pages.
+ Extension pages include the background page, popups, and content pages
+ accessed via |getURL|_.
+ When creating a new Javascript feature the designer must choose
+ in which of these environments the feature will be available.
+ Most Javascript features are available in extension pages,
+ other environments have limited sets of API features available.
+
+.. |getURL| replace:: ``browser.runtime.getURL()``
+.. _getURL: https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/getURL
+
+- **Permissions**:
+ Many Javascript features are only present for extensions that
+ include an appropriate permission in the manifest.
+ The guidelines for when an API feature requires a permission are
+ described in (*citation needed*).
+
+The specific types of features that can be exposed via Javascript are:
+
+- **Functions**:
+ A function callable from Javascript is perhaps the most commonly
+ used feature in WebExtension APIs.
+ New API functions are asynchronous, returning a
+ `Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_. Even functions that do not return a result
+ use Promises so that errors can be indicated asynchronously
+ via a rejected Promise as opposed to a synchronously thrown Error.
+ This is due to the fact that extensions run in a child process and
+ many API functions require communication with the main process.
+ If an API function that needs to communicate in this way returned a
+ synchronous result, then all Javascript execution in the child
+ process would need to be paused until a response from the main process
+ was received. Even if a function could be implemented synchronously
+ within a child process, the standard practice is to make it
+ asynchronous so as not to constrain the implementation of the underlying
+ browser feature and make it impossible to move functionality out of the
+ child process.
+ Another consequence of functions using inter-process communication is
+ that the parameters to a function and its return value must all be
+ simple data types that can be sent between processes using the
+ `structured clone algorithm <https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm>`_.
+
+- **Events**:
+ Events complement functions (which allow an extension to call into
+ an API) by allowing an event within the browser to invoke a callback
+ in the extension.
+ Any time an API requires an extension to pass a callback function that
+ gets invoked some arbitrary number of times, that API method should be
+ defined as an event.
+
+Manifest Keys
+-------------
+In addition to providing functionality via Javascript, WebExtension APIs
+can also take actions based on the contents of particular properties
+in an extension's manifest (or even just the presence of a particular
+property).
+Manifest entries are used for features in which an extension specifies
+some static information that is used when an extension is installed or
+when it starts up (i.e., before it has the chance to run any code to use
+a Javascript API).
+An API may handle a manifest key and implement Javascript functionality,
+see the
+`browser action <https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/browserAction>`_
+API for an example.
+
+Other Considerations
+--------------------
+In addition to the guidelines outlined above,
+there are some other considerations when designing and implementing
+a WebExtension API:
+
+- **Cleanup**: A badly written WebExtension should not be able to permanently
+ leak any resources. In particular, any action from an extension that
+ causes a resource to be allocated within the browser should be
+ automatically cleaned up when the extension is disabled or uninstalled.
+ This is described in more detail in the section on :ref:`lifecycle`.
+
+- **Performance**: A new WebExtension API should not add any new overhead
+ to the browser when the API is not used. That is, the implementation
+ of the API should not be loaded at all unless it is actively used by
+ an extension. In addition, initialization should be delayed when
+ possible -- extensions ared started relatively early in the browser
+ startup process so any unnecessary work done during extension startup
+ contributes directly to sluggish browser startup.
diff --git a/toolkit/components/extensions/docs/basics.rst b/toolkit/components/extensions/docs/basics.rst
new file mode 100644
index 0000000000..35d61561e2
--- /dev/null
+++ b/toolkit/components/extensions/docs/basics.rst
@@ -0,0 +1,275 @@
+.. _basics:
+
+API Implementation Basics
+=========================
+This page describes some of the pieces involved when creating
+WebExtensions APIs. Detailed documentation about how these pieces work
+together to build specific features is in the next section.
+
+The API Schema
+--------------
+As described previously, a WebExtension runs in a sandboxed environment
+but the implementation of a WebExtensions API runs with full chrome
+privileges. API implementations do not directly interact with
+extensions' Javascript environments, that is handled by the WebExtensions
+framework. Each API includes a schema that describes all the functions,
+events, and other properties that the API might inject into an
+extension's Javascript environment.
+Among other things, the schema specifies the namespace into which
+an API should be injected, what permissions (if any) are required to
+use it, and in which contexts (e.g., extension pages, content scripts, etc)
+it should be available. The WebExtensions framework reads this schema
+and takes care of injecting the right objects into each extension
+Javascript environment.
+
+API schemas are written in JSON and are based on
+`JSON Schema <http://json-schema.org/>`_ with some extensions to describe
+API functions and events.
+The next section describes the format of the schema in detail.
+
+The ExtensionAPI class
+----------------------
+Every WebExtensions API is represented by an instance of the Javascript
+`ExtensionAPI <reference.html#extensionapi-class>`_ class.
+An instance of its API class is created every time an extension that has
+access to the API is enabled. Instances of this class contain the
+implementations of functions and events that are exposed to extensions,
+and they also contain code for handling manifest keys as well as other
+part of the extension lifecycle (e.g., updates, uninstalls, etc.)
+The details of this class are covered in a subsequent section, for now the
+important point is that this class contains all the actual code that
+backs a particular WebExtensions API.
+
+Built-in versus Experimental APIs
+---------------------------------
+A WebExtensions API can be built directly into the browser or it can be
+contained in a special type of extension called a privileged extension
+that defines a WebExtensions Experiment (i.e. experimental APIs).
+The API schema and the ExtensionAPI class are written in the same way
+regardless of how the API will be delivered, the rest of this section
+explains how to package a new API using these methods.
+
+Adding a built-in API
+---------------------
+Built-in WebExtensions APIs are loaded lazily. That is, the schema and
+accompanying code are not actually loaded and interpreted until an
+extension that uses the API is activated.
+To actually register the API with the WebExtensions framework, an entry
+must be added to the list of WebExtensions modules in one of the following
+files:
+
+- ``toolkit/components/extensions/ext-toolkit.json``
+- ``browser/components/extensions/ext-browser.json``
+- ``mobile/android/components/extensions/ext-android.json``
+
+Here is a sample fragment for a new API:
+
+.. code-block:: js
+
+ "myapi": {
+ "schema": "chrome://extensions/content/schemas/myapi.json",
+ "url": "chrome://extensions/content/ext-myapi.js",
+ "paths": [
+ ["myapi"],
+ ["anothernamespace", "subproperty"]
+ ],
+ "scopes": ["addon_parent"],
+ "permissions": ["myapi"],
+ "manifest": ["myapi_key"],
+ "events": ["update", "uninstall"]
+ }
+
+The ``schema`` and ``url`` properties are simply URLs for the API schema
+and the code implementing the API. The ``chrome:`` URLs in the example above
+are typically created by adding entries to ``jar.mn`` in the mozilla-central
+directory where the API implementation is kept. The standard locations for
+API implementations are:
+
+- ``toolkit/components/extensions``: This is where APIs that work in both
+ the desktop and mobile versions of Firefox (as well as potentially any
+ other applications built on Gecko) should go
+- ``browser/components/extensions``: APIs that are only supported on
+ Firefox for the desktop.
+- ``mobile/android/components/extensions``: APIs that are only supported
+ on Firefox for Android.
+
+Within the appropriate extensions directory, the convention is that the
+API schema is in a file called ``schemas/name.json`` (where *name* is
+the name of the API, typically the same as its namespace if it has
+Javascript visible features). The code for the ExtensionAPI class is put
+in a file called ``ext-name.js``. If the API has code that runs in a
+child process, that is conventionally put in a file called ``ext-c-name.js``.
+
+The remaining properties specify when an API should be loaded.
+The ``paths``, ``scopes``, and ``permissions`` properties together
+cause an API to be loaded when Javascript code in an extension references
+something beneath the ``browser`` global object that is part of the API.
+The ``paths`` property is an array of paths where each individual path is
+also an array of property names. In the example above, the sample API will
+be loaded if an extension references either ``browser.myapi`` or
+``browser.anothernamespace.subproperty``.
+
+A reference to a property beneath ``browser`` only causes the API to be
+loaded if it occurs within a scope listed in the ``scopes`` property.
+A scope corresponds to the combination of a Javascript environment
+(e.g., extension pages, content scripts, etc) and the process in which the
+API code should run (which is either the main/parent process, or a
+content/child process).
+Valid ``scopes`` are:
+
+- ``"addon_parent"``, ``"addon_child``: Extension pages
+
+- ``"content_parent"``, ``"content_child``: Content scripts
+
+- ``"devtools_parent"``, ``"devtools_child"``: Devtools pages
+
+The distinction between the ``_parent`` and ``_child`` scopes will be
+explained in further detail in following sections.
+
+A reference to a property only causes the API to be loaded if the
+extension referencing the property also has all the permissions listed
+in the ``permissions`` property.
+
+A WebExtensions API that is controlled by a manifest key can also be loaded
+when an extension that includes the relevant manifest key is activated.
+This is specified by the ``manifest`` property, which lists any manifest keys
+that should cause the API to be loaded.
+
+Finally, APIs can be loaded based on other events in the WebExtension
+lifecycle. These are listed in the ``events`` property and described in
+more detail in :ref:`lifecycle`.
+
+Adding Experimental APIs in Privileged Extensions
+-------------------------------------------------
+
+A new API may also be implemented within a privileged extension. An API
+implemented this way is called a WebExtensions Experiment (or simply an
+Experimental API). Experiments can be useful when actively developing a
+new API, as they do not require building Firefox locally. These extensions
+may be installed temporarily via ``about:debugging`` or, on browser that
+support it (current Nightly and Developer Edition), by setting the preference
+``xpinstall.signatures.required`` to ``false``. You may also set the
+preference ``extensions.experiments.enabled`` to ``true`` to install the
+addon normally and test across restart.
+
+.. note::
+ Out-of-tree privileged extensions cannot be signed by addons.mozilla.org.
+ A different pipeline is used to sign them with a privileged certificate.
+ You'll find more information in the `xpi-manifest repository on GitHub <https://github.com/mozilla-extensions/xpi-manifest>`_.
+
+Experimental APIs have a few limitations compared with built-in APIs:
+
+- Experimental APIs can (currently) only be exposed to extension pages,
+ not to devtools pages or to content scripts.
+- Experimental APIs cannot handle manifest keys (since the extension manifest
+ needs to be parsed and validated before experimental APIs are loaded).
+- Experimental APIs cannot use the static ``"update"`` and ``"uninstall"``
+ lifecycle events (since in general those may occur when an affected
+ extension is not active or installed).
+
+Experimental APIs are declared in the ``experiment_apis`` property in a
+WebExtension's ``manifest.json`` file. For example:
+
+.. code-block:: js
+
+ {
+ "manifest_version": 2,
+ "name": "Extension containing an experimental API",
+ "experiment_apis": {
+ "apiname": {
+ "schema": "schema.json",
+ "parent": {
+ "scopes": ["addon_parent"],
+ "paths": [["myapi"]],
+ "script": "implementation.js"
+ },
+
+ "child": {
+ "scopes": ["addon_child"],
+ "paths": [["myapi"]],
+ "script": "child-implementation.js"
+ }
+ }
+ }
+ }
+
+This is essentially the same information required for built-in APIs,
+just organized differently. The ``schema`` property is a relative path
+to a file inside the extension containing the API schema. The actual
+implementation details for the parent process and for child processes
+are defined in the ``parent`` and ``child`` properties of the API
+definition respectively. Inside these sections, the ``scope`` and ``paths``
+properties have the same meaning as those properties in the definition
+of a built-in API (though see the note above about limitations; the
+only currently valid values for ``scope`` are ``"addon_parent"`` and
+``"addon_child"``). The ``script`` property is a relative path to a file
+inside the extension containing the implementation of the API.
+
+The extension that includes an experiment defined in this way automatically
+gets access to the experimental API. An extension may also use an
+experimental API implemented in a different extension by including the
+string ``experiments.name`` in the ``permissions``` property in its
+``manifest.json`` file. In this case, the string name must be replace by
+the name of the API from the extension that defined it (e.g., ``apiname``
+in the example above.
+
+Globals available in the API scripts global
+-------------------------------------------
+
+The API scripts aren't loaded as an JSM and so:
+
+- they are not fully isolated from each other (and they are going to be
+ lazy loaded when the extension does use them for the first time) and
+ be executed in a per-process shared global scope)
+- the experimental APIs embedded in privileged extensions are executed
+ in a per-extension global (separate from the one used for the built-in APIs)
+
+The global scope where the API scripts are executed is pre-populated with
+some useful globals:
+
+- ``AppConstants``
+- ``console``
+- ``CC``, ``Ci``, ``Cr`` and ``Cu``
+- ``ChromeWorker``
+- ``extensions``, ``ExtensionAPI``, ``ExtensionCommon`` and ``ExtensionUtils``
+- ``global``
+- ``MatchGlob``, ``MatchPattern`` and ``MatchPatternSet``
+- ``Services``
+- ``StructuredCloneHolder``
+- ``XPCOMUtils``
+
+For a more complete and updated list of the globals available by default in
+all API scripts look to the following source:
+
+- `SchemaAPIManager _createExtGlobal method <https://searchfox.org/mozilla-central/search?q=symbol:SchemaAPIManager%23_createExtGlobal&redirect=false>`_
+- Only available in the parent Firefox process:
+ `toolkit/components/extensions/parent/ext-toolkit.js <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/parent/ext-toolkit.js>`_
+- Only available in the child Firefox process:
+ `toolkit/components/extensions/child/ext-toolkit.js <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/child/ext-toolkit.js>`_
+- Only available in the Desktop builds:
+ `browser/components/extensions/parent/ext-browser.js <https://searchfox.org/mozilla-central/source/browser/components/extensions/parent/ext-browser.js>`_
+- Only available in the Android builds:
+ `mobile/android/components/extensions/ext-android.js <https://searchfox.org/mozilla-central/source/mobile/android/components/extensions/ext-android.js>`_
+
+.. warning::
+ The extension API authors should never redefine these globals to avoid introducing potential
+ conflicts between API scripts (e.g. see `Bug 1697404 comment 3 <https://bugzilla.mozilla.org/show_bug.cgi?id=1697404#c3>`_
+ and `Bug 1697404 comment 4 <https://bugzilla.mozilla.org/show_bug.cgi?id=1697404#c4>`_).
+
+WebIDL Bindings
+---------------
+
+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 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.
+
+An high level view of the architecture and a more in depth details about the architecture process
+to create or modify WebIDL bindings for the WebExtensions API can be found here:
+
+.. toctree::
+ :maxdepth: 2
+
+ webidl_bindings
diff --git a/toolkit/components/extensions/docs/events.rst b/toolkit/components/extensions/docs/events.rst
new file mode 100644
index 0000000000..d494155ffc
--- /dev/null
+++ b/toolkit/components/extensions/docs/events.rst
@@ -0,0 +1,609 @@
+Implementing an event
+=====================
+Like a function, an event requires a definition in the schema and
+an implementation in Javascript inside an instance of ExtensionAPI.
+
+Declaring an event in the API schema
+------------------------------------
+The definition for a simple event looks like this:
+
+.. code-block:: json
+
+ [
+ {
+ "namespace": "myapi",
+ "events": [
+ {
+ "name": "onSomething",
+ "type": "function",
+ "description": "Description of the event",
+ "parameters": [
+ {
+ "name": "param1",
+ "description": "Description of the first callback parameter",
+ "type": "number"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+
+This fragment defines an event that is used from an extension with
+code such as:
+
+.. code-block:: js
+
+ browser.myapi.onSomething.addListener(param1 => {
+ console.log(`Something happened: ${param1}`);
+ });
+
+Note that the schema syntax looks similar to that for a function,
+but for an event, the ``parameters`` property specifies the arguments
+that will be passed to a listener.
+
+Implementing an event
+---------------------
+Just like with functions, defining an event in the schema causes
+wrappers to be automatically created and exposed to an extensions'
+appropriate Javascript contexts.
+An event appears to an extension as an object with three standard
+function properties: ``addListener()``, ``removeListener()``,
+and ``hasListener()``.
+Also like functions, if an API defines an event but does not implement
+it in a child process, the wrapper in the child process effectively
+proxies these calls to the implementation in the main process.
+
+A helper class called
+`EventManager <reference.html#eventmanager-class>`_ makes implementing
+events relatively simple. A simple event implementation looks like:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ myapi: {
+ onSomething: new EventManager({
+ context,
+ name: "myapi.onSomething",
+ register: fire => {
+ const callback = value => {
+ fire.async(value);
+ };
+ RegisterSomeInternalCallback(callback);
+ return () => {
+ UnregisterInternalCallback(callback);
+ };
+ }
+ }).api(),
+ }
+ }
+ }
+ }
+
+The ``EventManager`` class is usually just used directly as in this example.
+The first argument to the constructor is an ``ExtensionContext`` instance,
+typically just the object passed to the API's ``getAPI()`` function.
+The second argument is a name, it is used only for debugging.
+The third argument is the important piece, it is a function that is called
+the first time a listener is added for this event.
+This function is passed an object (``fire`` in the example) that is used to
+invoke the extension's listener whenever the event occurs. The ``fire``
+object has several different methods for invoking listeners, but for
+events implemented in the main process, the only valid method is
+``async()`` which executes the listener asynchronously.
+
+The event setup function (the function passed to the ``EventManager``
+constructor) must return a cleanup function,
+which will be called when the listener is removed either explicitly
+by the extension by calling ``removeListener()`` or implicitly when
+the extension Javascript context from which the listener was added is destroyed.
+
+In this example, ``RegisterSomeInternalCallback()`` and
+``UnregisterInternalCallback()`` represent methods for listening for
+some internal browser event from chrome privileged code. This is
+typically something like adding an observer using ``Services.obs`` or
+attaching a listener to an ``EventEmitter``.
+
+After constructing an instance of ``EventManager``, its ``api()`` method
+returns an object with with ``addListener()``, ``removeListener()``, and
+``hasListener()`` methods. This is the standard extension event interface,
+this object is suitable for returning from the extension's
+``getAPI()`` method as in the example above.
+
+Handling extra arguments to addListener()
+-----------------------------------------
+The standard ``addListener()`` method for events may accept optional
+addition parameters to allow extra information to be passed when registering
+an event listener. One common application of this parameter is for filtering,
+so that extensions that only care about a small subset of the instances of
+some event can avoid the overhead of receiving the ones they don't care about.
+
+Extra parameters to ``addListener()`` are defined in the schema with the
+the ``extraParameters`` property. For example:
+
+.. code-block:: json
+
+ [
+ {
+ "namespace": "myapi",
+ "events": [
+ {
+ "name": "onSomething",
+ "type": "function",
+ "description": "Description of the event",
+ "parameters": [
+ {
+ "name": "param1",
+ "description": "Description of the first callback parameter",
+ "type": "number"
+ }
+ ],
+ "extraParameters": [
+ {
+ "name": "minValue",
+ "description": "Only call the listener for values of param1 at least as large as this value.",
+ "type": "number"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+
+Extra parameters defined in this way are passed to the event setup
+function (the last parameter to the ``EventManager`` constructor.
+For example, extending our example above:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ myapi: {
+ onSomething: new EventManager({
+ context,
+ module: "myapi",
+ event: "onSomething",
+ register: (fire, minValue) => {
+ const callback = value => {
+ if (value >= minValue) {
+ fire.async(value);
+ }
+ };
+ RegisterSomeInternalCallback(callback);
+ return () => {
+ UnregisterInternalCallback(callback);
+ };
+ }
+ }).api()
+ }
+ }
+ }
+ }
+
+Handling listener return values
+-------------------------------
+Some event APIs allow extensions to affect event handling in some way
+by returning values from event listeners that are processed by the API.
+This can be defined in the schema with the ``returns`` property:
+
+.. code-block:: json
+
+ [
+ {
+ "namespace": "myapi",
+ "events": [
+ {
+ "name": "onSomething",
+ "type": "function",
+ "description": "Description of the event",
+ "parameters": [
+ {
+ "name": "param1",
+ "description": "Description of the first callback parameter",
+ "type": "number"
+ }
+ ],
+ "returns": {
+ "type": "string",
+ "description": "Description of how the listener return value is processed."
+ }
+ }
+ ]
+ }
+ ]
+
+And the implementation of the event uses the return value from ``fire.async()``
+which is a Promise that resolves to the listener's return value:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ myapi: {
+ onSomething: new EventManager({
+ context,
+ module: "myapi",
+ event: "onSomething",
+ register: fire => {
+ const callback = async (value) => {
+ let rv = await fire.async(value);
+ log(`The onSomething listener returned the string ${rv}`);
+ };
+ RegisterSomeInternalCallback(callback);
+ return () => {
+ UnregisterInternalCallback(callback);
+ };
+ }
+ }).api()
+ }
+ }
+ }
+ }
+
+Note that the schema ``returns`` definition is optional and serves only
+for documentation. That is, ``fire.async()`` always returns a Promise
+that resolves to the listener return value, the implementation of an
+event can just ignore this Promise if it doesn't care about the return value.
+
+Implementing an event in the child process
+------------------------------------------
+The reasons for implementing events in the child process are similar to
+the reasons for implementing functions in the child process:
+
+- Listeners for the event return a value that the API implementation must
+ act on synchronously.
+
+- Either ``addListener()`` or the listener function has one or more
+ parameters of a type that cannot be sent between processes.
+
+- The implementation of the event interacts with code that is only
+ accessible from a child process.
+
+- The event can be implemented substantially more efficiently in a
+ child process.
+
+The process for implementing an event in the child process is the same
+as for functions -- simply implement the event in an ExtensionAPI subclass
+that is loaded in a child process. And just as a function in a child
+process can call a function in the main process with
+`callParentAsyncFunction()`, events in a child process may subscribe to
+events implemented in the main process with a similar `getParentEvent()`.
+For example, the automatically generated event proxy in a child process
+could be written explicitly as:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ myapi: {
+ onSomething: new EventManager(
+ context,
+ name: "myapi.onSomething",
+ register: fire => {
+ const listener = (value) => {
+ fire.async(value);
+ };
+
+ let parentEvent = context.childManager.getParentEvent("myapi.onSomething");
+ parent.addListener(listener);
+ return () => {
+ parent.removeListener(listener);
+ };
+ }
+ }).api()
+ }
+ }
+ }
+ }
+
+Events implemented in a child process have some additional methods available
+to dispatch listeners:
+
+- ``fire.sync()`` This runs the listener synchronously and returns the
+ value returned by the listener
+
+- ``fire.raw()`` This runs the listener synchronously without cloning
+ the listener arguments into the extension's Javascript compartment.
+ This is used as a performance optimization, it should not be used
+ unless you have a detailed understanding of Javascript compartments
+ and cross-compartment wrappers.
+
+Event Listeners Persistence
+---------------------------
+
+Event listeners are persisted in some circumstances. Persisted event listeners can either
+block startup, and/or cause an Event Page or Background Service Worker to be started.
+
+The event listener must be registered synchronously in the top level scope
+of the background. Event listeners registered later, or asynchronously, are
+not persisted.
+
+Currently only WebRequestBlocking and Proxy events are able to block
+at startup, causing an addon to start earlier in Firefox startup. Whether
+a module can block startup is defined by a ``startupBlocking`` flag in
+the module definition files (``ext-toolkit.json`` or ``ext-browser.json``).
+As well, these are the only events persisted for persistent background scripts.
+
+Events implemented only in a child process, without a parent process counterpart,
+cannot be persisted.
+
+Persisted and Primed Event Listeners
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In terms of terminology:
+
+- **Persisted Event Listener** is the set of data (in particular API module, API event name
+ and the parameters passed along with addListener call if any) related to an event listener
+ that has been registered by an Event Page (or Background Service Worker) in a previous run
+ and being stored in the StartupCache data
+
+- **Primed Event Listener** is a "placeholder" event listener created, from the **Persisted Event Listener**
+ data found in the StartupCache, while the Event Page (or Background Service Worker) is not running
+ (either not started yet or suspended after the idle timeout was hit)
+
+ExtensionAPIPersistent and PERSISTENT_EVENTS
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Most of the WebExtensions APIs promise some API events, and it is likely that most of those events are also
+expected to be waking up the Event Page (or Background Service Worker) when emitted while the background
+extension context has not been started yet (or it was suspended after the idle timeout was hit).
+
+As part of implementing a WebExtensions API that is meant to persist all or some of its API event
+listeners:
+
+- the WebExtensions API namespace class should extend ``ExtensionAPIPersistent`` (instead of extending
+ the ``ExtensionAPI`` class)
+
+- the WebExtensions API namespace should have a ``PERSISTENT_EVENTS`` property, which is expected to be
+ set to an object defining methods. Each method should be named after the related API event name, which
+ are going to be called internally:
+
+ - while the extension Event Page (or Background Service Worker) isn't running (either never started yet
+ or suspended after the idle timeout). These methods are called by the WebExtensions internals to
+ create placeholder API event listeners in the parent process for each of the API event listeners
+ persisted for that extension. These placeholder listeners are internally referred to as
+ ``primed listeners``).
+
+ - while the extension Event Page (or Background Service Worker) is running (as well as for any other
+ extension context types they may have been created for the extension). These methods are called by the
+ WebExtensions internals to create the parent process callback that will be responsible for
+ forwarding the API events to the extension callbacks in the child processes.
+
+- in the ``getAPI`` method. For all the API namespace properties that represent API events returned by this method,
+ the ``EventManager`` instances created for each of the API events that is expected to persist its listeners
+ should include following options:
+
+ - ``module``, to be set to the API module name as listed in ``"ext-toolkit.json"`` / ``"ext-browser.json"``
+ / ``"ext-android.json"`` (which, in most cases, is the same as the API namespace name string).
+ - ``event``, to be set to the API event name string.
+ - ``extensionApi``, to be set to the ``ExtensionAPIPersistent`` class instance.
+
+Taking a look to some of the patches landed to introduce API event listener persistency on some of the existing
+API as part of introducing support for the Event Page may also be useful:
+
+- Bug-1748546_ ported the browserAction and pageAction API namespace implementations to
+ ``ExtensionAPIPersistent`` and, in particular, the changes applied to:
+
+ - ext-browserAction.js: https://hg.mozilla.org/integration/autoland/rev/08a3eaa8bce7
+ - ext-pageAction.js: https://hg.mozilla.org/integration/autoland/rev/ed616e2e0abb
+
+.. _Bug-1748546: https://bugzilla.mozilla.org/show_bug.cgi?id=1748546
+
+Follows an example of what has been described previously in a code snippet form:
+
+.. code-block:: js
+
+ this.myApiName = class extends ExtensionAPIPersistent {
+ PERSISTENT_EVENTS = {
+ // @param {object} options
+ // @param {object} options.fire
+ // @param {function} options.fire.async
+ // @param {function} options.fire.sync
+ // @param {function} options.fire.raw
+ // For primed listeners `fire.async`/`fire.sync`/`fire.raw` will
+ // collect the pending events to be send to the background context
+ // and implicitly wake up the background context (Event Page or
+ // Background Service Worker), or forward the event right away if
+ // the background context is running.
+ // @param {function} [options.fire.wakeup = undefined]
+ // For primed listeners, the `fire` object also provide a `wakeup` method
+ // which can be used by the primed listener to explicitly `wakeup` the
+ // background context (Event Page or Background Service Worker) and wait for
+ // it to be running (by awaiting on the Promise returned by wakeup to be
+ // resolved).
+ // @param {ProxyContextParent} [options.context=undefined]
+ // This property is expected to be undefined for primed listeners (which
+ // are created while the background extension context does not exist) and
+ // to be set to a ProxyContextParent instance (the same got by the getAPI
+ // method) when the method is called for a listener registered by a
+ // running extension context.
+ //
+ // @param {object} [apiEventsParams=undefined]
+ // The additional addListener parameter if any (some API events are allowing
+ // the extensions to pass some parameters along with the extension callback).
+ onMyEventName({ context, fire }, apiEventParams = undefined) {
+ const listener = (...) {
+ // Wake up the EventPage (or Background ServiceWorker).
+ if (fire.wakeup) {
+ await fire.wakeup();
+ }
+
+ fire.async(...);
+ }
+
+ // Subscribe a listener to an internal observer or event which will be notified
+ // when we need to call fire to either send the event to an extension context
+ // already running or wake up a suspended event page and accumulate the events
+ // to be fired once the extension context is running again and a callback registered
+ // back (which will be used to convert the primed listener created while
+ // the non persistent background extension context was not running yet)
+ ...
+ return {
+ unregister() {
+ // Unsubscribe a listener from an internal observer or event.
+ ...
+ }
+ convert(fireToExtensionCallback) {
+ // Convert gets called once the primed API event listener,
+ // created while the extension background context has been
+ // suspended, is being converted to a parent process API
+ // event listener callback that is responsible for forwarding the
+ // events to the child processes.
+ //
+ // The `fireToExtensionCallback` parameter is going to be the
+ // one that will emit the event to the extension callback (while
+ // the one got from the API event registrar method may be the one
+ // that is collecting the events to emit up until the background
+ // context got started up again).
+ fire = fireToExtensionCallback;
+ },
+ };
+ },
+ ...
+ };
+
+ getAPI(context) {
+ ...
+ return {
+ myAPIName: {
+ ...
+ onMyEventName: new EventManager({
+ context,
+ // NOTE: module is expected to be the API module name as listed in
+ // ext-toolkit.json / ext-browser.json / ext-android.json.
+ module: "myAPIName",
+ event: "onMyEventNAme",
+ extensionApi: this,
+ }),
+ },
+ };
+ }
+ };
+
+Testing Persisted API Event Listeners
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+- ``extension.terminateBackground()`` / ``extension.terminateBackground({ disableResetIdleForTest: true})``:
+
+ - The wrapper object returned by ``ExtensionTestUtils.loadExtension`` provides
+ a ``terminateBackground`` method which can be used to simulate an idle timeout,
+ by explicitly triggering the same idle timeout suspend logic handling the idle timeout.
+ - This method also accept an optional parameter, if set to ``{ disableResetIdleForTest: true}``
+ will forcefully suspend the background extension context and ignore all the
+ conditions that would reset the idle timeout due to some work still pending
+ (e.g. a ``NativeMessaging``'s ``Port`` still open, a ``StreamFilter`` instance
+ still active or a ``Promise`` from an API event listener call not yet resolved)
+
+- ``ExtensionTestUtils.testAssertions.assertPersistentListeners``:
+
+ - This test assertion helper can be used to more easily assert what should
+ be the persisted state of a given API event (e.g. assert it to not be
+ persisted, or to be persisted and/or primed)
+
+.. code-block:: js
+
+ assertPersistentListeners(extension, "browserAction", "onClicked", {
+ primed: false,
+ });
+ await extension.terminateBackground();
+ assertPersistentListeners(extension, "browserAction", "onClicked", {
+ primed: true,
+ });
+
+- ``extensions.background.idle.timeout`` preference determines how long to wait
+ (between API events being notified to the extension event page) before considering
+ the Event Page in the idle state and suspend it, in some xpcshell test this pref
+ may be set to 0 to reduce the amount of time the test will have to wait for the
+ Event Page to be suspended automatically
+
+- ``extension.eventPage.enabled`` pref is responsible for enabling/disabling
+ Event Page support for manifest_version 2 extension, technically it is
+ now set to ``true`` on all channels, but it would still be worth flipping it
+ to ``true`` explicitly in tests that are meant to cover Event Page behaviors
+ for manifest_version 2 test extension until the pref is completely removed
+ (mainly to make sure that if the pref would need to be flipped to false
+ for any reason, the tests will still be passing)
+
+Persisted Event listeners internals
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``ExtensionAPIPersistent`` class provides a way to quickly introduce API event
+listener persistency to a new WebExtensions API, and reduce the number of code
+duplication, the following section provide some more details about what the
+abstractions are doing internally in practice.
+
+WebExtensions APIs classes that extend the ``ExtensionAPIPersistent`` base class
+are still able to support non persisted listeners along with persisted ones
+(e.g. events that are persisting the listeners registered from an event page are
+already not persisting listeners registered from other extension contexts)
+and can mix persisted and non-persisted events.
+
+As an example in ``toolkit/components/extensions/parent/ext-runtime.js``` the two
+events ``onSuspend`` and ``onSuspendCanceled`` are expected to be never persisted
+nor primed (even for an event page) and so their ``EventManager`` instances
+receive the following options:
+
+- a ``register`` callback (instead of the one part of ``PERSISTED_EVENTS``)
+- a ``name`` string property (instead of the two separate ``module`` and ``event``
+ string properties that are used for ``EventManager`` instances from persisted
+ ones
+- no ``extensionApi`` property (because that is only needed for events that are
+ expected to persist event page listeners)
+
+In practice ``ExtensionAPIPersistent`` extends the ``ExtensionAPI`` class to provide
+a generic ``primeListeners`` method, which is the method responsible for priming
+a persisted listener when the event page has been suspended or not started yet.
+
+The ``primeListener`` method is expected to return an object with an ``unregister``
+and ``convert`` method, while the ``register`` callback passed to the ``EventManager``
+constructor is expected to return the ``unregister`` method.
+
+.. code-block:: js
+
+ function somethingListener(fire, minValue) => {
+ const callback = value => {
+ if (value >= minValue) {
+ fire.async(value);
+ }
+ };
+ RegisterSomeInternalCallback(callback);
+ return {
+ unregister() {
+ UnregisterInternalCallback(callback);
+ },
+ convert(_fire, context) {
+ fire = _fire;
+ }
+ };
+ }
+
+ this.myapi = class extends ExtensionAPI {
+ primeListener(extension, event, fire, params, isInStartup) {
+ if (event == "onSomething") {
+ // Note that we return the object with unregister and convert here.
+ return somethingListener(fire, ...params);
+ }
+ // If an event other than onSomething was requested, we are not returning
+ // anything for it, thus it would not be persistable.
+ }
+ getAPI(context) {
+ return {
+ myapi: {
+ onSomething: new EventManager({
+ context,
+ module: "myapi",
+ event: "onSomething",
+ register: (fire, minValue) => {
+ // Note that we return unregister here.
+ return somethingListener(fire, minValue).unregister;
+ }
+ }).api()
+ }
+ }
+ }
+ }
diff --git a/toolkit/components/extensions/docs/functions.rst b/toolkit/components/extensions/docs/functions.rst
new file mode 100644
index 0000000000..f1727aceed
--- /dev/null
+++ b/toolkit/components/extensions/docs/functions.rst
@@ -0,0 +1,201 @@
+Implementing a function
+=======================
+Implementing an API function requires at least two different pieces:
+a definition for the function in the schema, and Javascript code that
+actually implements the function.
+
+Declaring a function in the API schema
+--------------------------------------
+An API schema definition for a simple function looks like this:
+
+.. code-block:: json
+
+ [
+ {
+ "namespace": "myapi",
+ "functions": [
+ {
+ "name": "add",
+ "type": "function",
+ "description": "Adds two numbers together.",
+ "async": true,
+ "parameters": [
+ {
+ "name": "x",
+ "type": "number",
+ "description": "The first number to add."
+ },
+ {
+ "name": "y",
+ "type": "number",
+ "description": "The second number to add."
+ }
+ ]
+ }
+ ]
+ }
+ ]
+
+The ``type`` and ``description`` properties were described above.
+The ``name`` property is the name of the function as it appears in
+the given namespace. That is, the fragment above creates a function
+callable from an extension as ``browser.myapi.add()``.
+The ``parameters`` property describes the parameters the function takes.
+Parameters are specified as an array of Javascript types, where each
+parameter is a constrained Javascript value as described
+in the previous section.
+
+Each parameter may also contain additional properties ``optional``
+and ``default``. If ``optional`` is present it must be a boolean
+(and parameters are not optional by default so this property is typically
+only added when it has the value ``true``).
+The ``default`` property is only meaningful for optional parameters,
+it specifies the value that should be used for an optional parameter
+if the function is called without that parameter.
+An optional parameter without an explicit ``default`` property will
+receive a default value of ``null``.
+Although it is legal to create optional parameters at any position
+(i.e., optional parameters can come before required parameters), doing so
+leads to difficult to use functions and API designers are encouraged to
+use object-valued parameters with optional named properties instead,
+or if optional parameters must be used, to use them sparingly and put
+them at the end of the parameter list.
+
+.. XXX should we describe allowAmbiguousArguments?
+
+The boolean-valued ``async`` property specifies whether a function
+is asynchronous.
+For asynchronous functions,
+the WebExtensions framework takes care of automatically generating a
+`Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ and then resolving the Promise when the function
+implementation completes (or rejecting the Promise if the implementation
+throws an Error).
+Since extensions can run in a child process, any API function that is
+implemented (either partially or completely) in the parent process must
+be asynchronous.
+
+When a function is declared in the API schema, a wrapper for the function
+is automatically created and injected into appropriate extension Javascript
+contexts. This wrapper automatically validates arguments passed to the
+function against the formal parameters declared in the schema and immediately
+throws an Error if invalid arguments are passed.
+It also processes optional arguments and inserts default values as needed.
+As a result, API implementations generally do not need to write much
+boilerplate code to validate and interpret arguments.
+
+Implementing a function in the main process
+-------------------------------------------
+If an asynchronous function is not implemented in the child process,
+the wrapper generated from the schema automatically marshalls the
+function arguments, sends the request to the parent process,
+and calls the implementation there.
+When that function completes, the return value is sent back to the child process
+and the Promise for the function call is resolved with that value.
+
+Based on this, an implementation of the function we wrote the schema
+for above looks like this:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ myapi: {
+ add(x, y) { return x+y; }
+ }
+ }
+ }
+ }
+
+The implementations of API functions are contained in a subclass of the
+`ExtensionAPI <reference.html#extensionapi-class>`_ class.
+Each subclass of ExtensionAPI must implement the ``getAPI()`` method
+which returns an object with a structure that mirrors the structure of
+functions and events that the API exposes.
+The ``context`` object passed to ``getAPI()`` is an instance of
+`BaseContext <reference.html#basecontext-class>`_,
+which contains a number of useful properties and methods.
+
+If an API function implementation returns a Promise, its result will
+be sent back to the child process when the Promise is settled.
+Any other return type will be sent directly back to the child process.
+A function implementation may also raise an Error. But by default,
+an Error thrown from inside an API implementation function is not
+exposed to the extension code that called the function -- it is
+converted into generic errors with the message "An unexpected error occurred".
+To throw a specific error to extensions, use the ``ExtensionError`` class:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ myapi: {
+ doSomething() {
+ if (cantDoSomething) {
+ throw new ExtensionError("Cannot call doSomething at this time");
+ }
+ return something();
+ }
+ }
+ }
+ }
+ }
+
+The purpose of this step is to avoid bugs in API implementations from
+exposing details about the implementation to extensions. When an Error
+that is not an instance of ExtensionError is thrown, the original error
+is logged to the
+`Browser Console <https://developer.mozilla.org/en-US/docs/Tools/Browser_Console>`_,
+which can be useful while developing a new API.
+
+Implementing a function in a child process
+------------------------------------------
+Most functions are implemented in the main process, but there are
+occasionally reasons to implement a function in a child process, such as:
+
+- The function has one or more parameters of a type that cannot be automatically
+ sent to the main process using the structured clone algorithm.
+
+- The function implementation interacts with some part of the browser
+ internals that is only accessible from a child process.
+
+- The function can be implemented substantially more efficiently in
+ a child process.
+
+To implement a function in a child process, simply include an ExtensionAPI
+subclass that is loaded in the appropriate context
+(e.g, ``addon_child``, ``content_child``, etc.) as described in
+the section on :ref:`basics`.
+Code inside an ExtensionAPI subclass in a child process may call the
+implementation of a function in the parent process using a method from
+the API context as follows:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ getAPI(context) {
+ return {
+ myapi: {
+ async doSomething(arg) {
+ let result = await context.childManager.callParentAsyncFunction("anothernamespace.functionname", [arg]);
+ /* do something with result */
+ return ...;
+ }
+ }
+ }
+ }
+ }
+
+As you might expect, ``callParentAsyncFunction()`` calls the given function
+in the main process with the given arguments, and returns a Promise
+that resolves with the result of the function.
+This is the same mechanism that is used by the automatically generated
+function wrappers for asynchronous functions that do not have a
+provided implementation in a child process.
+
+It is possible to define the same function in both the main process
+and a child process and have the implementation in the child process
+call the function with the same name in the parent process.
+This is a common pattern when the implementation of a particular function
+requires some code in both the main process and child process.
diff --git a/toolkit/components/extensions/docs/generate_webidl_from_jsonschema.rst b/toolkit/components/extensions/docs/generate_webidl_from_jsonschema.rst
new file mode 100644
index 0000000000..ea29d6b7b6
--- /dev/null
+++ b/toolkit/components/extensions/docs/generate_webidl_from_jsonschema.rst
@@ -0,0 +1,94 @@
+Generating WebIDL definitions from WebExtensions API JSONSchema
+===============================================================
+
+In ``toolkit/components/extensions/webidl-api``, a python script named ``GenerateWebIDLBindings.py``
+helps to generation of the WebIDL definitions for the WebExtensions API namespaces based on the existing
+JSONSchema data.
+
+.. figure:: generate_webidl_from_jsonschema_dataflow.drawio.svg
+ :alt: Diagram of the GenerateWebIDLBindings.py script data flow
+
+..
+ This svg diagram has been created using https://app.diagrams.net,
+ the svg file also includes the source in the drawio format and so
+ it can be edited more easily by loading it back into app.diagrams.net
+ and then re-export from there (and include the updated drawio format
+ content into the exported svg file).
+
+Example: how to execute GenerateWebIDLBindings.py
+-------------------------------------------------
+
+As an example, the following shell command generates (or regenerates if one exists) the webidl bindings
+for the `runtime` API namespace:
+
+.. code-block:: sh
+
+ $ export SCRIPT_DIR="toolkit/components/extensions/webidl-api"
+ $ mach python $SCRIPT_DIR/GenerateWebIDLBindings.py -- runtime
+
+this command will generates a `.webdil` file named `dom/webidl/ExtensionRuntime.webidl`.
+
+.. warning::
+ This python script uses some python libraries part of mozilla-central ``mach`` command
+ and so it has to be executed using ``mach python`` and any command line options that has
+ to the passed to the ``GenerateWebIDLBindings.py`` script should be passed after the ``--``
+ one that ends ``mach python`` own command line options.
+
+* If a webidl file with the same name already exist, the python script will ask confirmation and
+ offer to print a diff of the changes (or just continue without changing the existing webidl file
+ if the content is exactly the same):
+
+.. code-block::
+
+ $ mach python $SCRIPT_DIR/GenerateWebIDLBindings.py -- runtime
+
+ Generating webidl definition for 'runtime' => dom/webidl/ExtensionRuntime.webidl
+ Found existing dom/webidl/ExtensionRuntime.webidl.
+
+ (Run again with --overwrite-existing to allow overwriting it automatically)
+
+ Overwrite dom/webidl/ExtensionRuntime.webidl? (Yes/No/Diff)
+ D
+ --- ExtensionRuntime.webidl--existing
+ +++ ExtensionRuntime.webidl--updated
+ @@ -24,6 +24,9 @@
+ [Exposed=(ServiceWorker), LegacyNoInterfaceObject]
+ interface ExtensionRuntime {
+ // API methods.
+ +
+ + [Throws, WebExtensionStub="Async"]
+ + any myNewMethod(boolean aBoolParam, optional Function callback);
+
+ [Throws, WebExtensionStub="Async"]
+ any openOptionsPage(optional Function callback);
+
+
+ Overwrite dom/webidl/ExtensionRuntime.webidl? (Yes/No/Diff)
+
+* By convention each WebExtensions API WebIDL binding is expected to be paired with C++ files
+ named ``ExtensionMyNamespace.h`` and ``ExtensionMyNamespace.cpp`` and located in
+ ``toolkit/components/extensions/webidl-api``:
+
+ * if no files with the expected names is found the python script will generate an initial
+ boilerplate files and will store them in the expected mozilla-central directory.
+ * The Firefox developers are responsible to fill this initial boilerplate as needed and
+ to apply the necessary changes (if any) when the webidl definitions are updated because
+ of changes to the WebExtensions APIs JSONSchema.
+
+``ExtensionWebIDL.conf`` config file
+------------------------------------
+
+TODO:
+
+* mention the role of the "webidl generation" script config file in handling
+ special cases (e.g. mapping types and method stubs)
+
+* notes on desktop-only APIs and API namespaces only partially available on Android
+
+
+``WebExtensionStub`` WebIDL extended attribute
+----------------------------------------------
+
+TODO:
+
+* mention the special webidl extended attribute used in the WebIDL definitions
diff --git a/toolkit/components/extensions/docs/generate_webidl_from_jsonschema_dataflow.drawio.svg b/toolkit/components/extensions/docs/generate_webidl_from_jsonschema_dataflow.drawio.svg
new file mode 100644
index 0000000000..aaa5a4c3e0
--- /dev/null
+++ b/toolkit/components/extensions/docs/generate_webidl_from_jsonschema_dataflow.drawio.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Do not edit this file with editors other than diagrams.net -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(230, 230, 230);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="911px" height="701px" viewBox="-0.5 -0.5 911 701" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2021-09-22T15:04:40.150Z&quot; agent=&quot;5.0 (X11)&quot; etag=&quot;wbbyr0k6o51B6i_UoEFs&quot; version=&quot;15.2.9&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;887lF9N4iZXDEKDicNfW&quot; name=&quot;GenerateWebIDLBindings Data Flow&quot;&gt;7Vxbc5s4FP41ntl9sIc75DGJk3QzybYz7vTytANGttVgYEGO7f76lUDCSAgMiSFOu9OZFIQ4gnO+cxce6dfr3V3ixqvHyAfBSFP83UifjjRNNW0L/0dG9vmIfaHnA8sE+nTSYWAGfwI6qNDRDfRByk1EURQgGPOD8ygMwRxxY26SRFt+2iIK+FVjdwkqA7O5G1RHv0Ifreioal0cLnwAcLmiSzuanV/w3PnTMok2IV1vpOk3FvmXX167jBZ90XTl+tG2NKTfjPTrJIpQfrTeXYOA8Jax7c79+M/TR9/7MbubaV9m8f1ysx3nxG673FK8YQJCdFrSBqX97AYbwNiQvSzaMwbj947J4SIAu0sisJF+BUKfHk7ngZumcI4HV2gd4AEVHy6iEFGoqAaZv4PoG71Gjr/jY2Vi0rMpAaPCTvbsJETJ/lv5pHQXOT3clp1x930CCVwDBBI66LvpCvj0CVryk/ImjTbJHDTMo3hCbrIETfR0J58IfA7RVFx3IMIPnOzxhO0BxyYF36qEYDaWgMBF8JnXA5eq07IgV6zwKYL4fTWFqr5tUg2him9cKDyJ/MXpXWVkCYQcRSBkCIRyzlQIYQS5+9K0mExIGx7Y4dfRL5TG5xLnmyo3Hx/kT8DOSjI4DGVa1EWjdIlGWQEiGITP+HCJMgTmQx4bWIIQJC4ChHwIEXQDNgU/hSfehsckxJroexEMQBIH2RLtCQumgCD3wfWwB+G03Q3gMiSmAOsT0birZ5AgiE30Jb2whr5PaFwlIIU/XS+jR9SSihwTN69G5lRiOQKy3FVhqq+jICI6HUYhaK/JhZnDzwV2nL5Qt0SfiTPtnE7Su5SJqgh6MzZOoo6awVG1L3gC0WKRAiTg9ySI1c3jPiDjfWY/idS2K4jALHYzq7jFYUWz8e9mbisyqpWFKgjCtKjdKdlPTZEYUFVT6qXDsbczL61a7S90bTZPYIxy6C3gksQ8WDeLaYlEJ71ajcSsQjz3U5RET0DQE7xCIAy1V1qZuHlAvJXELYnEVUci8f4EXpH3zQ6BMIVR+BV4f00fJkTKtTGVH80364xJx5TKyxn+4LGB3njsqBOT1ytbwmVmNcpcdvrist3sU0uctf7dkHg8Q+Q4zSB5iSeoRrzLVYpeZwpFUpUniLXxdh6tY6wZWQRyC5gQyckWeNAPxm4MyVuU9PQV67bw5K+gfkejiRyCVzD0YbhMJ/G+pZ9n8EwRIDCMS8E0GSoF10d9AdwBljTKfEN2Thdum+d0hrRu8WajAGoJ0LolMxu92Q114MTrkGx9L6VhxxKvQ671nUu15IlX2cn4YOFuggO4TpZsMT4dzbaozTiTZMsUcqSifNI12TIFF6ha7ZKtU0WLqvaWwFUagTsIANUzA5Zg20wx+W4NLFb3Y4SUgYElS5zPBFgli6g6VtkmqhMlp9q1IDUIVlmQ9s6MpcHSKBbmi264LaYNXV4BOnVlShMzUaO5MiW+oDC/n8qU2iLPP4mCvbRu+4Ia8TAhh/IutUgTy58v1SJd0CLDHtYz6MqwnuHNmhSDoLltt0I7LzTbtgDCl6LZ0YQuQks0D92t0KwhuhWyaL5TtyIriSib2C91Fo7VMH/pXkNhrU7Qa8DgtDlQnKjXMOZ7DWPT4in02GxoUfcg3I+7Wb3XM9sR82rWDT1WWlbFhOtkNSJNVvas8XvzfQBDHyT6CyrJh40QHzcIk2G1upSqhvnyCr/euVZnVrluD8v1agunKOk/7i8//TXJ68G1oui/pt+dqypf0TckXG7y3adnslNhsh+ti1p7hbfvus3VQVzM+GgtjU9/fa0WZb7cTp8mbH29ATdU3oBL7Lcmq/FXCqinA3kLHg7RWC9keRx/b8gsCa8qgejnvFs3DsBztosxna/A2m29K4b02dPGILVNU2wA+95eYPSqUATWJeZDG9S+1++CIiAetW1u1vZNi2ziK/AK94zvup99/Ht2naNCOS5vWVKSP+CvuN+iO674PEGvWlRZ1NCbU2KPU0LVFKRPKIprLMJvoft8aCdLW4ZVfVmZuSKOx8jDAvnfkFcCdf3MxKm3aMudVyCoigU+iTfUZDzsb1sgI/zmkaBew+TmSHBYZqlVKy8xAWW/j02BghPzDrujldBdgzRjb378YmNyEJt6DmJjV21hD58sIpQllP3F97LCX93O2OsHLEwlilEu3N9vZ2x3iTN1ZBUEpaXA+6sgyNRY6jiGLhRYDseqYsfM0bi2Nyertqj0HjEtbhrnX9dlOy5fgTyjhs/1/JQUE4etHVQjzsI1HOq1ExhWeDpgJNiZraYpmHBVotGsMz9IKNhQGCdsHplX8zge2derkTl9d9x2JjbPbummd20i2fben1moVsnbdGfvYfjDHXtuiu2ApiCwzj77SuVes1WQ8759aWcwCLuzVImDGNiXtvAPZ9E1NdrGmf05A12mNDWW6Fy7pnaNfF7dNe2P6xdtTJXQSF2Rz26wyzi7sk1n/lf6qxMD64atsr9V8UjbrfZE5e7rSVjMnpWE1fU7qF/KQbQXd+f+rDMx7ddL8YtlPj1unY0TPTtPH+Dz+HZ239Ax2wSifw8gG1m7cQxD8tVp1iqhdVcFRfhP/rkYOdvHJF5QGlPvA82mVbK6jILfeBX5bB3W5FcqoUu5qjNDG08esygZLP0svHERSqC3ObL5rN2zsiq0Qn2p8gebXUwu9ATLTPHz5kN+ss4q2qU1i3v+ZO+9jn5iBXDHBOuJm/0MC0wyhKJVc5mj+vx4sCzmk+kjKYoRbuhT83D2Gb+kPiVlTLkaRlh1FkG203aF1RWEFdVs3lo32PfDQmnYkn0IKLPMF92VGJ8efhsm3z53+AEe/eY/&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 600.5 305 L 600.5 295 L 655 295 Q 665 295 665 305 L 665 415 L 715.5 415 L 715.5 404.5 L 734.5 420 L 715.5 435.5 L 715.5 425 L 665 425 Q 655 425 655 415 L 655 305 Z" fill="none" stroke="#000000" stroke-linejoin="round" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="all"/><path d="M 715.5 415 L 715.5 404.5 L 734.5 420 L 715.5 435.5 L 715.5 425" fill="none" stroke="#000000" stroke-linejoin="flat" stroke-miterlimit="4" stroke-dasharray="3 3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 461px; margin-left: 681px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><div><b>generate initial</b></div><div><b>boilerplate</b></div></div></div></div></foreignObject><text x="681" y="465" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">generate initial...</text></switch></g><rect x="30" y="470" width="200" height="120" fill="#ffffff" stroke="#000000" pointer-events="all"/><rect x="30" y="570" width="180" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 580px; margin-left: 31px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><b>Script config file<br /></b></div></div></div></foreignObject><text x="120" y="584" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">Script config file&#xa;</text></switch></g><path d="M 52.5 480 L 187.5 480 L 187.5 548 Q 153.75 526.4 120 548 Q 86.25 569.6 52.5 548 L 52.5 492 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 508px; margin-left: 54px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">ExtensionWebIDL.conf</div></div></div></foreignObject><text x="120" y="512" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ExtensionWebIDL.conf</text></switch></g><path d="M 240 190 L 580 190 L 600 300 L 580 410 L 240 410 L 260 300 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 358px; height: 1px; padding-top: 300px; margin-left: 241px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;"><div style="font-size: 14px">toolkit/components/extensions/webidl-api/<br style="font-size: 14px" /></div><div style="font-size: 14px">GenerateWebIDLBindings.py</div></div></div></div></foreignObject><text x="420" y="304" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle" font-weight="bold">toolkit/components/extensions/webidl-api/...</text></switch></g><path d="M 415 160.5 L 425 160.5 L 425 170.5 L 435.5 170.5 L 420 189.5 L 404.5 170.5 L 415 170.5 Z" fill="#ffffff" stroke="#000000" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 469 449.5 L 459 449.5 L 459 429.5 L 448.5 429.5 L 464 410.5 L 479.5 429.5 L 469 429.5 Z" fill="#ffffff" stroke="#000000" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 135 469.5 L 125 469.5 L 125 455 Q 125 445 135 445 L 301.99 445 L 301.98 432.59 L 291.48 432.6 L 306.96 413.58 L 322.48 432.56 L 311.98 432.57 L 311.99 444.99 Q 312.01 455 301.99 455 L 135 455 Z" fill="#ffffff" stroke="#000000" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 301.98 432.59 L 291.48 432.6 L 306.96 413.58 L 322.48 432.56 L 311.98 432.57" fill="none" stroke="#000000" stroke-linejoin="flat" stroke-miterlimit="4" pointer-events="all"/><path d="M 220.5 305 L 220.5 295 L 240.5 295 L 240.5 284.5 L 259.5 300 L 240.5 315.5 L 240.5 305 Z" fill="#ffffff" stroke="#000000" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 600.5 305 L 600.5 295 L 655 295 L 655 175 Q 655 165 665 165 L 715.5 165 L 715.5 154.5 L 734.5 170 L 715.5 185.5 L 715.5 175 L 665 175 L 665 295 Q 665 305 655 305 Z" fill="#ffffff" stroke="#000000" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"/><path d="M 715.5 165 L 715.5 154.5 L 734.5 170 L 715.5 185.5 L 715.5 175" fill="none" stroke="#000000" stroke-linejoin="flat" stroke-miterlimit="4" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 141px; margin-left: 661px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><div><b>generate / update<br /></b></div></div></div></div></foreignObject><text x="661" y="145" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">generate / update&#xa;</text></switch></g><path d="M 735 115 C 735 106.72 773.06 100 820 100 C 842.54 100 864.16 101.58 880.1 104.39 C 896.04 107.21 905 111.02 905 115 L 905 225 C 905 233.28 866.94 240 820 240 C 773.06 240 735 233.28 735 225 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 905 115 C 905 123.28 866.94 130 820 130 C 773.06 130 735 123.28 735 115" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 745 145 L 895 145 L 895 187.5 Q 857.5 174 820 187.5 Q 782.5 201 745 187.5 L 745 152.5 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 163px; margin-left: 746px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">ExtensionMyAPI.webidl</div></div></div></foreignObject><text x="820" y="166" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ExtensionMyAPI.webidl</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 230px; margin-left: 731px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">dom/webidl</div></div></div></foreignObject><text x="820" y="234" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">dom/webidl</text></switch></g><rect x="290" y="0" width="260" height="160" fill="#ffffff" stroke="none" pointer-events="none"/><rect x="290" y="0" width="260" height="160" fill="#ffffff" stroke="#000000" pointer-events="none"/><path d="M 359 30 L 479 30 L 479 72.5 Q 449 59 419 72.5 Q 389 86 359 72.5 L 359 37.5 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 48px; margin-left: 360px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>Toolkit-level schema</div><div>files<br /></div></div></div></div></foreignObject><text x="419" y="51" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Toolkit-level schema...</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 248px; height: 1px; padding-top: 13px; margin-left: 295px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><font style="font-size: 14px"><b>WebExtension JSONSChema files<br /></b></font></div></div></div></foreignObject><text x="419" y="17" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">WebExtension JSONSChema files&#xa;</text></switch></g><path d="M 295 90 L 415 90 L 415 132.5 Q 385 119 355 132.5 Q 325 146 295 132.5 L 295 97.5 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 108px; margin-left: 296px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">Desktop-level schema<div>files<br /></div></div></div></div></foreignObject><text x="355" y="111" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Desktop-level schema...</text></switch></g><path d="M 425 90 L 545 90 L 545 132.5 Q 515 119 485 132.5 Q 455 146 425 132.5 L 425 97.5 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 108px; margin-left: 426px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>Mobile-level schema</div><div>files<br /></div></div></div></div></foreignObject><text x="485" y="111" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Mobile-level schema...</text></switch></g><rect x="0" y="240" width="220" height="120" fill="#ffffff" stroke="none" pointer-events="none"/><rect x="0" y="240" width="220" height="120" fill="#ffffff" stroke="#000000" pointer-events="none"/><rect x="17.5" y="270" width="180" height="60" rx="9" ry="9" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 300px; margin-left: 19px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>WebExtensions API</div><div> namespace name<br /></div></div></div></div></foreignObject><text x="108" y="304" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">WebExtensions API...</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 350px; margin-left: 21px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><b>Script CLI options<br /></b></div></div></div></foreignObject><text x="110" y="354" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">Script CLI options&#xa;</text></switch></g><rect x="384" y="450" width="160" height="160" fill="#ffffff" stroke="#000000" pointer-events="none"/><path d="M 394.5 460 L 533.5 460 L 533.5 502.5 Q 498.75 489 464 502.5 Q 429.25 516 394.5 502.5 L 394.5 467.5 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 137px; height: 1px; padding-top: 478px; margin-left: 396px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">ExtensionAPI.webidl.in</div></div></div></foreignObject><text x="464" y="481" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ExtensionAPI.webidl.in</text></switch></g><path d="M 397.75 520 L 530.25 520 L 530.25 562.5 Q 497.13 549 464 562.5 Q 430.88 576 397.75 562.5 L 397.75 527.5 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 131px; height: 1px; padding-top: 538px; margin-left: 399px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">ExtensionAPI.[cpp|h].in</div></div></div></foreignObject><text x="464" y="541" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ExtensionAPI.[cpp|h].in</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 600px; margin-left: 376px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div><b>Jinja-based templates</b></div></div></div></div></foreignObject><text x="465" y="604" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">Jinja-based templates</text></switch></g><path d="M 735 355 C 735 346.72 773.06 340 820 340 C 842.54 340 864.16 341.58 880.1 344.39 C 896.04 347.21 905 351.02 905 355 L 905 485 C 905 493.28 866.94 500 820 500 C 773.06 500 735 493.28 735 485 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 905 355 C 905 363.28 866.94 370 820 370 C 773.06 370 735 363.28 735 355" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 745 385.43 L 895 385.43 L 895 434 Q 857.5 418.57 820 434 Q 782.5 449.43 745 434 L 745 394 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 406px; margin-left: 746px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>ExtensionMyAPI.h/cpp</div></div></div></div></foreignObject><text x="820" y="409" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ExtensionMyAPI.h/cpp</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 474px; margin-left: 731px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">toolkit/components/extensions/webidl-api</div></div></div></foreignObject><text x="820" y="478" fill="#000000" font-family="Helvetica" font-size="14px" text-anchor="middle">toolkit/components/extensi...</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 342px; height: 1px; padding-top: 580px; margin-left: 5px;"><div style="box-sizing: border-box; font-size: 0px; text-align: left; max-height: 107px; overflow: hidden;"><div style="display: inline-block; font-size: 14px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><ul><li>mapping JSONSchema to WebIDL types <br /></li><li>mapping API method to webidl <b>WebExtensionStub</b> extended attribute<br /></li><li>mapping schema group (<i>toolkit, desktop, mobile</i>) to mozilla-central dir paths<br /></li></ul></div></div></div></foreignObject><text x="5" y="594" fill="#000000" font-family="Helvetica" font-size="14px">mapping JSONSchema to WebIDL types...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg> \ No newline at end of file
diff --git a/toolkit/components/extensions/docs/incognito.rst b/toolkit/components/extensions/docs/incognito.rst
new file mode 100644
index 0000000000..7df71e77c4
--- /dev/null
+++ b/toolkit/components/extensions/docs/incognito.rst
@@ -0,0 +1,78 @@
+.. _incognito:
+
+Incognito Implementation
+========================
+
+This page provides a high level overview of how incognito works in
+Firefox, primarily to help in understanding how to test the feature.
+
+The Implementation
+------------------
+
+The incognito value in manifest.json supports ``spanning`` and ``not_allowed``.
+The other value, ``split``, may be supported in the future. The default
+value is ``spanning``, however, by default access to private windows is
+not allowed. The user must turn on support, per extension, in ``about:addons``.
+
+Internally this is handled as a hidden extension permission called
+``internal:privateBrowsingAllowed``. This permission is reset when the
+extension is disabled or uninstalled. The permission is accessible in
+several ways:
+
+- extension.privateBrowsingAllowed
+- context.privateBrowsingAllowed (see BaseContext)
+- WebExtensionPolicy.privateBrowsingAllowed
+- WebExtensionPolicy.canAccessWindow(DOMWindow)
+
+Testing
+-------
+
+The goal of testing is to ensure that data from a private browsing session
+is not accessible to an extension without permission.
+
+In Firefox 67, the feature will initially be disabled, however the
+intention is to enable the feature on in 67. The pref controlling this
+is ``extensions.allowPrivateBrowsingByDefault``. When this pref is
+``true``, all extensions have access to private browsing and the manifest
+value ``not_allowed`` will produce an error. To enable incognito.not_allowed
+for tests you must flip the pref to false.
+
+Testing EventManager events
+---------------------------
+
+This is typically most easily handled by running a test with an extension
+that has permission, using ``incognitoOverride: spanning`` in the call to
+ExtensionTestUtils.loadExtension. You can then use a second extension
+without permission to try and catch any events that would typically be passed.
+
+If the events can happen without calls produced by an extension, you can
+also use BrowserTestUtils to open a private window, and use a non-permissioned
+extension to run tests against it.
+
+There are two utility functions in head.js, getIncognitoWindow and
+startIncognitoMonitorExtension, which are useful for some basic testing.
+
+Example: `browser_ext_windows_events.js <https://searchfox.org/mozilla-central/rev/78cd247b5d7a08832f87d786541d3e2204842e8e/browser/components/extensions/test/browser/browser_ext_windows_events.js>`_
+
+Testing API Calls
+-----------------
+
+This is easily done using an extension without permission. If you need
+an ID of a window or tab, use getIncognitoWindow. In most cases, the
+API call should throw an exception when the window is not accessible.
+There are some cases where API calls explicitly do not throw.
+
+Example: `browser_ext_windows_incognito.js <https://searchfox.org/mozilla-central/rev/78cd247b5d7a08832f87d786541d3e2204842e8e/browser/components/extensions/test/browser/browser_ext_windows_incognito.js>`_
+
+Privateness of window vs. tab
+-----------------------------
+
+Android does not currently support private windows. When a tab is available,
+the test should prefer tab over window.
+
+- PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser)
+- PrivateBrowsingUtils.isContentWindowPrivate(window)
+
+When WebExtensionPolicy is handy to use, you can directly check window access:
+
+- policy.canAccessWindow(window)
diff --git a/toolkit/components/extensions/docs/index.rst b/toolkit/components/extensions/docs/index.rst
new file mode 100644
index 0000000000..63a7c3685c
--- /dev/null
+++ b/toolkit/components/extensions/docs/index.rst
@@ -0,0 +1,33 @@
+WebExtensions API Development
+=============================
+
+This documentation covers the implementation of WebExtensions inside Firefox.
+Documentation about existing WebExtension APIs and how to use them
+to develop WebExtensions is available
+`on MDN <https://developer.mozilla.org/en-US/Add-ons/WebExtensions>`_.
+
+To use this documentation, you should already be familiar with
+WebExtensions, including
+`the anatomy of a WebExtension <https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension>`_
+and `permissions <https://developer.mozilla.org/en-US/Add-ons/WebExtensions/manifest.json/permissions>`_.
+You should also be familiar with concepts from
+`Firefox development <https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide>`_
+including `e10s <https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox>`_
+in particular.
+
+.. toctree::
+ :caption: WebExtension API Developers Guide
+ :maxdepth: 2
+
+ background
+ basics
+ schema
+ functions
+ events
+ manifest
+ lifecycle
+ incognito
+ webidl_bindings
+ webext-storage
+ other
+ reference
diff --git a/toolkit/components/extensions/docs/lifecycle.rst b/toolkit/components/extensions/docs/lifecycle.rst
new file mode 100644
index 0000000000..8f08b34b68
--- /dev/null
+++ b/toolkit/components/extensions/docs/lifecycle.rst
@@ -0,0 +1,60 @@
+.. _lifecycle:
+
+Managing the Extension Lifecycle
+================================
+The techniques described in previous pages allow a WebExtension API to
+be loaded and instantiated only when an extension that uses the API is
+activated.
+But there are a few other events in the extension lifecycle that an API
+may need to respond to.
+
+Extension Shutdown
+------------------
+APIs that allocate any resources (e.g., adding elements to the browser's
+user interface, setting up internal event listeners, etc.) must free
+these resources when the extension for which they are allocated is
+shut down. An API does this by using the ``callOnClose()``
+method on an `Extension <reference.html#extension-class>`_ object.
+
+Extension Uninstall and Update
+------------------------------
+In addition to resources allocated within an individual browser session,
+some APIs make durable changes such as setting preferences or storing
+data in the user's profile.
+These changes are typically not reverted when an extension is shut down,
+but when the extension is completely uninstalled (or stops using the API).
+To handle this, extensions can be notified when an extension is uninstalled
+or updated. Extension updates are a subtle case -- consider an API that
+makes some durable change based on the presence of a manifest property.
+If an extension uses the manifest key in one version and then is updated
+to a new version that no longer uses the manifest key,
+the ``onManifestEntry()`` method for the API is no longer called,
+but an API can examine the new manifest after an update to detect that
+the key has been removed.
+
+Handling lifecycle events
+-------------------------
+
+To be notified of update and uninstall events, an extension lists these
+events in the API manifest:
+
+.. code-block:: js
+
+ "myapi": {
+ "schema": "...",
+ "url": "...",
+ "events": ["update", "uninstall"]
+ }
+
+If these properties are present, the ``onUpdate()`` and ``onUninstall()``
+methods will be called for the relevant ``ExtensionAPI`` instances when
+an extension that uses the API is updated or uninstalled.
+
+Note that these events can be triggered on extensions that are inactive.
+For that reason, these events can only be handled by extension APIs that
+are built into the browser. Or, in other words, these events cannot be
+handled by APIs that are implemented in WebExtension experiments. If the
+implementation of an API relies on these events for correctness, the API
+must be built into the browser and not delivered via an experiment.
+
+.. Should we even document onStartup()? I think no...
diff --git a/toolkit/components/extensions/docs/manifest.rst b/toolkit/components/extensions/docs/manifest.rst
new file mode 100644
index 0000000000..194dc43a8d
--- /dev/null
+++ b/toolkit/components/extensions/docs/manifest.rst
@@ -0,0 +1,68 @@
+Implementing a manifest property
+================================
+Like functions and events, implementing a new manifest key requires
+writing a definition in the schema and extending the API's instance
+of ``ExtensionAPI``.
+
+The contents of a WebExtension's ``manifest.json`` are validated using
+a type called ``WebExtensionManifest`` defined in the namespace
+``manifest``.
+The first step when adding a new property is to extend the schema so
+that manifests containing the new property pass validation.
+This is done with the ``"$extend"`` property as follows:
+
+.. code-block:: js
+
+ [
+ "namespace": "manifest",
+ "types": [
+ {
+ "$extend": "WebExtensionManifest",
+ "properties": {
+ "my_api_property": {
+ "type": "string",
+ "optional": true,
+ ...
+ }
+ }
+ }
+ ]
+ ]
+
+The next step is to inform the WebExtensions framework that this API
+should be instantiated and notified when extensions that use the new
+manifest key are loaded.
+For built-in APIs, this is done with the ``manifest`` property
+in the API manifest (e.g., ``ext-toolkit.json``).
+Note that this property is an array so an extension can implement
+multiple properties:
+
+.. code-block:: js
+
+ "myapi": {
+ "schema": "...",
+ "url": "...",
+ "manifest": ["my_api_property"]
+ }
+
+The final step is to write code to handle the new manifest entry.
+The WebExtensions framework processes an extension's manifest when the
+extension starts up, this happens for existing extensions when a new
+browser session starts up and it can happen in the middle of a session
+when an extension is first installed or enabled, or when the extension
+is updated.
+The JSON fragment above causes the WebExtensions framework to load the
+API implementation when it encounters a specific manifest key while
+starting an extension, and then call its ``onManifestEntry()`` method
+with the name of the property as an argument.
+The value of the property is not passed, but the full manifest is
+available through ``this.extension.manifest``:
+
+.. code-block:: js
+
+ this.myapi = class extends ExtensionAPI {
+ onManifestEntry(name) {
+ let value = this.extension.manifest.my_api_property;
+ /* do something with value... */
+ }
+ }
diff --git a/toolkit/components/extensions/docs/other.rst b/toolkit/components/extensions/docs/other.rst
new file mode 100644
index 0000000000..85a9b6db41
--- /dev/null
+++ b/toolkit/components/extensions/docs/other.rst
@@ -0,0 +1,140 @@
+Utilities for implementing APIs
+===============================
+
+This page covers some utility classes that are useful for
+implementing WebExtension APIs:
+
+WindowManager
+-------------
+This class manages the mapping between the opaque window identifiers used
+in the `browser.windows <https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/windows>`__ API.
+See the reference docs `here <reference.html#windowmanager-class>`__.
+
+TabManager
+----------
+This class manages the mapping between the opaque tab identifiers used
+in the `browser.tabs <https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs>`__ API.
+See the reference docs `here <reference.html#tabmanager-class>`__.
+
+ExtensionSettingsStore
+----------------------
+ExtensionSettingsStore (ESS) is used for storing changes to settings that are
+requested by extensions, and for finding out what the current value
+of a setting should be, based on the precedence chain or a specific selection
+made (typically) by the user.
+
+When multiple extensions request to make a change to a particular
+setting, the most recently installed extension will be given
+precedence.
+
+It is also possible to select a specific extension (or no extension, which
+infers user-set) to control a setting. This will typically only happen via
+ExtensionPreferencesManager described below. When this happens, precedence
+control is not used until either a new extension is installed, or the controlling
+extension is disabled or uninstalled. If user-set is specifically chosen,
+precedence order will only be returned to by installing a new extension that
+takes control of the setting.
+
+ESS will manage what has control over a setting through any
+extension state changes (ie. install, uninstall, enable, disable).
+
+Notifications:
+^^^^^^^^^^^^^^
+
+"extension-setting-changed":
+****************************
+
+ When a setting changes an event is emitted via the apiManager. It contains
+ the following:
+
+ * *action*: one of select, remove, enable, disable
+
+ * *id*: the id of the extension for which the setting has changed, may be null
+ if the setting has returned to default or user set.
+
+ * *type*: The type of setting altered. This is defined by the module using ESS.
+ If the setting is controlled through the ExtensionPreferencesManager below,
+ the value will be "prefs".
+
+ * *key*: The name of the setting altered.
+
+ * *item*: The new value, if any that has taken control of the setting.
+
+
+ExtensionPreferencesManager
+---------------------------
+ExtensionPreferencesManager (EPM) is used to manage what extensions may control a
+setting that results in changing a preference. EPM adds additional logic on top
+of ESS to help manage the preference values based on what is in control of a
+setting.
+
+Defining a setting in an API
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A preference setting is defined in an API module by calling EPM.addSetting. addSetting
+allows the API to use callbacks that can handle setting preferences as needed. Since
+the setting is defined at runtime, the API module must be loaded as necessary by EPM
+to properly manage settings.
+
+In the api module definition (e.g. ext-toolkit.json), the api must use `"settings": true`
+so the management code can discover which API modules to load in order to manage a
+setting. See browserSettings[1] as an example.
+
+Settings that are exposed to the user in about:preferences also require special handling.
+We typically show that an extension is in control of the preference, and prevent changes
+to the setting. Some settings may allow the user to choose which extension (or none) has
+control of the setting.
+
+Preferences behavior
+^^^^^^^^^^^^^^^^^^^^
+
+To actually set a setting, the module must call EPM.setSetting. This is typically done
+via an extension API, such as browserSettings.settingName.set({ ...value data... }), though
+it may be done at other times, such as during extension startup or install in a modules
+onManifest handler.
+
+Preferences are not always changed when an extension uses an API that results in a call
+to EPM.setSetting. When setSetting is called, the values are stored by ESS (above), and if
+the extension currently has control, or the setting is controllable by the extension, then
+the preferences would be updated.
+
+The preferences would also potentially be updated when installing, enabling, disabling or
+uninstalling an extension, or by a user action in about:preferences (or other UI that
+allows controlling the preferences). If all extensions that use a preference setting are
+disabled or uninstalled, the prior user-set or default values would be returned to.
+
+An extension may watch for changes using the onChange api (e.g. browserSettings.settingName.onChange).
+
+[1] https://searchfox.org/mozilla-central/rev/04d8e7629354bab9e6a285183e763410860c5006/toolkit/components/extensions/ext-toolkit.json#19
+
+Notifications:
+^^^^^^^^^^^^^^
+
+"extension-setting-changed:*name*":
+***********************************
+
+ When a setting controlled by EPM changes an event is emitted via the apiManager. It contains
+ no other data. This is used primarily to implement the onChange API.
+
+ESS vs. EPM
+-----------
+An API may use ESS when it needs to allow an extension to store a setting value that
+affects how Firefox works, but does not result in setting a preference. An example
+is allowing an extension to change the newTab value in the newTab service.
+
+An API should use EPM when it needs to allow an extension to change a preference.
+
+Using ESS/EPM with experimental APIs
+------------------------------------
+
+Properly managing settings values depends on the ability to load any modules that
+define a setting. Since experimental APIs are defined inside the extension, there
+are situations where settings defined in experimental APIs may not be correctly
+managed. The could result in a preference remaining set by the extension after
+the extension is disabled or installed, especially when that state is updated during
+safe mode.
+
+Extensions making use of settings in an experimental API should practice caution,
+potentially unsetting the values when the extension is shutdown. Values used for
+the setting could be stored in the extensions locale storage, and restored into
+EPM when the extension is started again.
diff --git a/toolkit/components/extensions/docs/reference.rst b/toolkit/components/extensions/docs/reference.rst
new file mode 100644
index 0000000000..f88c0b872e
--- /dev/null
+++ b/toolkit/components/extensions/docs/reference.rst
@@ -0,0 +1,35 @@
+WebExtensions Javascript Component Reference
+============================================
+This page contains reference documentation for the individual classes
+used to implement WebExtensions APIs. This documentation is generated
+from jsdoc comments in the source code.
+
+ExtensionAPI class
+------------------
+.. js:autoclass:: ExtensionAPI
+ :members:
+
+Extension class
+---------------
+.. js:autoclass:: Extension
+ :members:
+
+EventManager class
+------------------
+.. js:autoclass:: EventManager
+ :members:
+
+BaseContext class
+-----------------
+.. js:autoclass:: BaseContext
+ :members:
+
+WindowManager class
+-------------------
+.. js:autoclass:: WindowManagerBase
+ :members:
+
+TabManager class
+----------------
+.. js:autoclass:: TabManagerBase
+ :members:
diff --git a/toolkit/components/extensions/docs/schema.rst b/toolkit/components/extensions/docs/schema.rst
new file mode 100644
index 0000000000..b55e918588
--- /dev/null
+++ b/toolkit/components/extensions/docs/schema.rst
@@ -0,0 +1,145 @@
+API Schemas
+===========
+Anything that a WebExtension API exposes to extensions via Javascript
+is described by the API's schema. The format of API schemas uses some
+of the same syntax as `JSON Schema <http://json-schema.org/>`_.
+JSON Schema provides a way to specify constraints on JSON documents and
+the same method is used by WebExtensions to specify constraints on,
+for example, parameters passed to an API function. But the syntax for
+describing functions, namespaces, etc. is all ad hoc. This section
+describes that syntax.
+
+An individual API schema consists of structured descriptions of
+items in one or more *namespaces* using a structure like this:
+
+.. code-block:: js
+
+ [
+ {
+ "namespace": "namespace1",
+ // declarations for namespace 1...
+ },
+ {
+ "namespace": "namespace2",
+ // declarations for namespace 2...
+ },
+ // other namespaces...
+ ]
+
+Most of the namespaces correspond to objects available to extensions
+Javascript code under the ``browser`` global. For example, entries in the
+namespace ``example`` are accessible to extension Javascript code as
+properties on ``browser.example``.
+The namespace ``"manifest"`` is handled specially, it describes the
+structure of WebExtension manifests (i.e., ``manifest.json`` files).
+Manifest schemas are explained in detail below.
+
+Declarations within a namespace look like:
+
+.. code-block:: js
+
+ {
+ "namespace": "namespace1",
+ "types": [
+ { /* type definition */ },
+ ...
+ ],
+ "properties": {
+ "NAME": { /* property definition */ },
+ ...
+ },
+ "functions": [
+ { /* function definition */ },
+ ...
+ ],
+ "events": [
+ { /* event definition */ },
+ ...
+ ]
+ }
+
+The four types of objects that can be defined inside a namespace are:
+
+- **types**: A type is a re-usable schema fragment. A common use of types
+ is to define in one place an object with a particular set of typed fields
+ that is used in multiple places in an API.
+
+- **properties**: A property is a fixed Javascript value available to
+ extensions via Javascript. Note that the format for defining
+ properties in a schema is different from the format for types, functions,
+ and events. The next subsection describes creating properties in detail.
+
+- **functions** and **events**:
+ These entries create functions and events respectively, which are
+ usable from Javascript by extensions. Details on how to implement
+ them are later in this section.
+
+Implementing a fixed Javascript property
+----------------------------------------
+A static property is made available to extensions via Javascript
+entirely from the schema, using a fragment like this one:
+
+.. code-block:: js
+
+ [
+ "namespace": "myapi",
+ "properties": {
+ "SOME_PROPERTY": {
+ "value": 24,
+ "description": "Description of my property here."
+ }
+ }
+ ]
+
+If a WebExtension API with this fragment in its schema is loaded for
+a particular extension context, that extension will be able to access
+``browser.myapi.SOME_PROPERTY`` and read the fixed value 24.
+The contents of ``value`` can be any JSON serializable object.
+
+Schema Items
+------------
+Most definitions of individual items in a schema have a common format:
+
+.. code-block:: js
+
+ {
+ "type": "SOME TYPE",
+ /* type-specific parameters... */
+ }
+
+Type-specific parameters will be described in subsequent sections,
+but there are some optional properties that can appear in many
+different types of items in an API schema:
+
+- ``description``: This string-valued property serves as documentation
+ for anybody reading or editing the schema.
+
+- ``permissions``: This property is an array of strings.
+ If present, the item in which this property appears is only made
+ available to extensions that have all the permissions listed in the array.
+
+- ``unsupported``: This property must be a boolean.
+ If it is true, the item in which it appears is ignored.
+ By using this property, a schema can define how a particular API
+ is intended to work, before it is implemented.
+
+- ``deprecated``: This property must be a boolean. If it is true,
+ any uses of the item in which it appears will cause a warning to
+ be logged to the browser console, to indicate to extension authors
+ that they are using a feature that is deprecated or otherwise
+ not fully supported.
+
+
+Describing constrained values
+-----------------------------
+There are many places where API schemas specify constraints on the type
+and possibly contents of some JSON value (e.g., the manifest property
+``name`` must be a string) or Javascript value (e.g., the first argument
+to ``browser.tabs.get()`` must be a non-negative integer).
+These items are defined using `JSON Schema <http://json-schema.org/>`_.
+Specifically, these items are specified by using one of the following
+values for the ``type`` property: ``boolean``, ``integer``, ``number``,
+``string``, ``array``, ``object``, or ``any``.
+Refer to the documentation and examples at the
+`JSON Schema site <http://json-schema.org/>`_ for details on how these
+items are defined in a schema.
diff --git a/toolkit/components/extensions/docs/webext-storage.rst b/toolkit/components/extensions/docs/webext-storage.rst
new file mode 100644
index 0000000000..23005bb679
--- /dev/null
+++ b/toolkit/components/extensions/docs/webext-storage.rst
@@ -0,0 +1,227 @@
+========================
+How webext storage works
+========================
+
+This document describes the implementation of the the `storage.sync` part of the
+`WebExtensions Storage APIs
+<https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/storage>`_.
+The implementation lives in the `toolkit/components/extensions/storage folder <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/storage>`_
+
+Ideally you would already know about Rust and XPCOM - `see this doc for more details <../../../../writing-rust-code/index.html>`_
+
+At a very high-level, the system looks like:
+
+.. mermaid::
+
+ graph LR
+ A[Extensions API]
+ A --> B[Storage JS API]
+ B --> C{magic}
+ C --> D[app-services component]
+
+Where "magic" is actually the most interesting part and the primary focus of this document.
+
+ Note: The general mechanism described below is also used for other Rust components from the
+ app-services team - for example, "dogear" uses a similar mechanism, and the sync engines
+ too (but with even more complexity) to manage the threads. Unfortunately, at time of writing,
+ no code is shared and it's not clear how we would, but this might change as more Rust lands.
+
+The app-services component `lives on github <https://github.com/mozilla/application-services/blob/main/components/webext-storage>`_.
+There are docs that describe `how to update/vendor this (and all) external rust code <../../../../build/buildsystem/rust.html>`_ you might be interested in.
+
+To set the scene, let's look at the parts exposed to WebExtensions first; there are lots of
+moving part there too.
+
+WebExtension API
+################
+
+The WebExtension API is owned by the addons team. The implementation of this API is quite complex
+as it involves multiple processes, but for the sake of this document, we can consider the entry-point
+into the WebExtension Storage API as being `parent/ext-storage.js <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/parent/ext-storage.js>`_
+
+This entry-point ends up using the implementation in the
+`ExtensionStorageSync JS class <https://searchfox.org/mozilla-central/rev/9028b0458cc1f432870d2996b186b0938dda734a/toolkit/components/extensions/ExtensionStorageSync.jsm#84>`_.
+This class/module has complexity for things like migration from the earlier Kinto-based backend,
+but importantly, code to adapt a callback API into a promise based one.
+
+Overview of the API
+###################
+
+At a high level, this API is quite simple - there are methods to "get/set/remove" extension
+storage data. Note that the "external" API exposed to the addon has subtly changed the parameters
+for this "internal" API, so there's an extension ID parameter and the JSON data has already been
+converted to a string.
+The semantics of the API are beyond this doc but are
+`documented on MDN <https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync>`_.
+
+As you will see in those docs, the API is promise-based, but the rust implementation is fully
+synchronous and Rust knows nothing about Javascript promises - so this system converts
+the callback-based API to a promise-based one.
+
+xpcom as the interface to Rust
+##############################
+
+xpcom is old Mozilla technology that uses C++ "vtables" to implement "interfaces", which are
+described in IDL files. While this traditionally was used to interface
+C++ and Javascript, we are leveraging existing support for Rust. The interface we are
+exposing is described in `mozIExtensionStorageArea.idl <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/storage/mozIExtensionStorageArea.idl>`_
+
+The main interface of interest in this IDL file is `mozIExtensionStorageArea`.
+This interface defines the functionality - and is the first layer in the sync to async model.
+For example, this interface defines the following method:
+
+.. code-block:: rust
+
+ interface mozIExtensionStorageArea : nsISupports {
+ ...
+ // Sets one or more key-value pairs specified in `json` for the
+ // `extensionId`...
+ void set(in AUTF8String extensionId,
+ in AUTF8String json,
+ in mozIExtensionStorageCallback callback);
+
+As you will notice, the 3rd arg is another interface, `mozIExtensionStorageCallback`, also
+defined in that IDL file. This is a small, generic interface defined as:
+
+.. code-block::
+
+ interface mozIExtensionStorageCallback : nsISupports {
+ // Called when the operation completes. Operations that return a result,
+ // like `get`, will pass a `UTF8String` variant. Those that don't return
+ // anything, like `set` or `remove`, will pass a `null` variant.
+ void handleSuccess(in nsIVariant result);
+
+ // Called when the operation fails.
+ void handleError(in nsresult code, in AUTF8String message);
+ };
+
+Note that this delivers all results and errors, so must be capable of handling
+every result type, which for some APIs may be problematic - but we are very lucky with this API
+that this simple XPCOM callback interface is capable of reasonably representing the return types
+from every function in the `mozIExtensionStorageArea` interface.
+
+(There's another interface, `mozIExtensionStorageListener` which is typically
+also implemented by the actual callback to notify the extension about changes,
+but that's beyond the scope of this doc.)
+
+*Note the thread model here is async* - the `set` call will return immediately, and later, on
+the main thread, we will call the callback param with the result of the operation.
+
+So under the hood, what happens is something like:
+
+.. mermaid::
+
+ sequenceDiagram
+ Extension->>ExtensionStorageSync: call `set` and give me a promise
+ ExtensionStorageSync->>xpcom: call `set`, supplying new data and a callback
+ ExtensionStorageSync-->>Extension: your promise
+ xpcom->>xpcom: thread magic in the "bridge"
+ xpcom-->>ExtensionStorageSync: callback!
+ ExtensionStorageSync-->>Extension: promise resolved
+
+So onto the thread magic in the bridge!
+
+webext_storage_bridge
+#####################
+
+The `webext_storage_bridge <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/storage/webext_storage_bridge>`_
+is a Rust crate which, as implied by the name, is a "bridge" between this Javascript/XPCOM world to
+the actual `webext-storage <https://github.com/mozilla/application-services/tree/main/components/webext-storage>`_ crate.
+
+lib.rs
+------
+
+Is the entry-point - it defines the xpcom "factory function" -
+an `extern "C"` function which is called by xpcom to create the Rust object
+implementing `mozIExtensionStorageArea` using existing gecko support.
+
+area.rs
+-------
+
+This module defines the interface itself. For example, inside that file you will find:
+
+.. code-block:: rust
+
+ impl StorageSyncArea {
+ ...
+
+ xpcom_method!(
+ set => Set(
+ ext_id: *const ::nsstring::nsACString,
+ json: *const ::nsstring::nsACString,
+ callback: *const mozIExtensionStorageCallback
+ )
+ );
+ /// Sets one or more key-value pairs.
+ fn set(
+ &self,
+ ext_id: &nsACString,
+ json: &nsACString,
+ callback: &mozIExtensionStorageCallback,
+ ) -> Result<()> {
+ self.dispatch(
+ Punt::Set {
+ ext_id: str::from_utf8(&*ext_id)?.into(),
+ value: serde_json::from_str(str::from_utf8(&*json)?)?,
+ },
+ callback,
+ )?;
+ Ok(())
+ }
+
+
+Of interest here:
+
+* `xpcom_method` is a Rust macro, and part of the existing xpcom integration which already exists
+ in gecko. It declares the xpcom vtable method described in the IDL.
+
+* The `set` function is the implementation - it does string conversions and the JSON parsing
+ on the main thread, then does the work via the supplied callback param, `self.dispatch` and a `Punt`.
+
+* The `dispatch` method dispatches to another thread, leveraging existing in-tree `moz_task <https://searchfox.org/mozilla-central/source/xpcom/rust/moz_task>`_ support, shifting the `Punt` to another thread and making the callback when done.
+
+Punt
+----
+
+`Punt` is a whimsical name somewhat related to a "bridge" - it carries things across and back.
+
+It is a fairly simple enum in `punt.rs <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/storage/webext_storage_bridge/src/punt.rs>`_.
+It's really just a restatement of the API we expose suitable for moving across threads. In short, the `Punt` is created on the main thread,
+then sent to the background thread where the actual operation runs via a `PuntTask` and returns a `PuntResult`.
+
+There's a few dances that go on, but the end result is that `inner_run() <https://searchfox.org/mozilla-central/source/toolkit/components/extensions/storage/webext_storage_bridge/src/punt.rs>`_
+gets executed on the background thread - so for `Set`:
+
+.. code-block:: rust
+
+ Punt::Set { ext_id, value } => {
+ PuntResult::with_change(&ext_id, self.store()?.get()?.set(&ext_id, value)?)?
+ }
+
+Here, `self.store()` is a wrapper around the actual Rust implementation from app-services with
+various initialization and mutex dances involved - see `store.rs`.
+ie, this function is calling our Rust implementation and stashing the result in a `PuntResult`
+
+The `PuntResult` is private to that file but is a simple struct that encapsulates both
+the actual result of the function (also a set of changes to send to observers, but that's
+beyond this doc).
+
+Ultimately, the `PuntResult` ends up back on the main thread once the call is complete
+and arranges to callback the JS implementation, which in turn resolves the promise created in `ExtensionStorageSync.jsm`
+
+End result:
+-----------
+
+.. mermaid::
+
+ sequenceDiagram
+ Extension->>ExtensionStorageSync: call `set` and give me a promise
+ ExtensionStorageSync->>xpcom - bridge main thread: call `set`, supplying new data and a callback
+ ExtensionStorageSync-->>Extension: your promise
+ xpcom - bridge main thread->>moz_task worker thread: Punt this
+ moz_task worker thread->>webext-storage: write this data to the database
+ webext-storage->>webext-storage: done: result/error and observers
+ webext-storage-->>moz_task worker thread: ...
+ moz_task worker thread-->>xpcom - bridge main thread: PuntResult
+ xpcom - bridge main thread-->>ExtensionStorageSync: callback!
+ ExtensionStorageSync-->>Extension: promise resolved
diff --git a/toolkit/components/extensions/docs/webidl_bindings.rst b/toolkit/components/extensions/docs/webidl_bindings.rst
new file mode 100644
index 0000000000..93556e9296
--- /dev/null
+++ b/toolkit/components/extensions/docs/webidl_bindings.rst
@@ -0,0 +1,246 @@
+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.jsm <https://searchfox.org/mozilla-central/search?q=symbol:Schemas%23inject&redirect=false>`_),
+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:
+
+ - :doc:`/dom/bindings/webidl/index`
+ - :doc:`/dom/webIdlBindings/index`
+
+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.
+
+.. _webidl: https://phabricator.services.mozilla.com/tag/webidl/
+
+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_.
+
+.. _ExtensionBrowser.webidl: https://searchfox.org/mozilla-central/source/dom/webidl/ExtensionBrowser.webidl
+.. _ExtensionBrowser.cpp: https://searchfox.org/mozilla-central/source/toolkit/components/extensions/webidl-api/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.
+
+.. _1713877: https://bugzilla.mozilla.org/1713877
+
+Background Service Workers API Request Handling
+-----------------------------------------------
+
+.. figure:: webidl_bindings_backgroundWorker_apiRequestHandling.drawio.svg
+ :alt: High Level Diagram of the Background Service Worker API Request Handling
+
+..
+ This svg diagram has been created using https://app.diagrams.net,
+ the svg file also includes the source in the drawio format and so
+ it can be edited more easily by loading it back into app.diagrams.net
+ and then re-export from there (and include the updated drawio format
+ content into the exported svg file).
+
+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 "privilged 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:
+
+.. toctree::
+ :maxdepth: 2
+
+ generate_webidl_from_jsonschema
+
+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:
+
+.. toctree::
+ :maxdepth: 2
+
+ wiring_up_new_webidl_bindings
+
+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:
+
+.. code-block:: js
+
+ 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:
+
+.. code-block::
+
+ 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):
+
+.. code-block:: js
+
+ 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:
+
+.. code-block::
+
+ mach mochitest --tag sw-webextensions path/to/test/file.js
diff --git a/toolkit/components/extensions/docs/webidl_bindings_backgroundWorker_apiRequestHandling.drawio.svg b/toolkit/components/extensions/docs/webidl_bindings_backgroundWorker_apiRequestHandling.drawio.svg
new file mode 100644
index 0000000000..ff1fc003ff
--- /dev/null
+++ b/toolkit/components/extensions/docs/webidl_bindings_backgroundWorker_apiRequestHandling.drawio.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Do not edit this file with editors other than diagrams.net -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(230, 230, 230);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1051px" height="702px" viewBox="-0.5 -0.5 1051 702" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2021-09-23T13:14:59.464Z&quot; agent=&quot;5.0 (X11)&quot; etag=&quot;SdliZ2VSr7bXlonuACqN&quot; version=&quot;15.3.0&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;FUdiw6MIiBahmvHCE1FN&quot; name=&quot;WebIDL Bindings Background Service Worker API Request handling&quot;&gt;7VtZc9o6FP41zPQ+tIM3lkcgtKWThQmZSXJfOgIroMS2qCwTyK+/ki3vAispi7ntZAjWsTafc75zPsmiYQzc9TcClosrbEOnoTftdcO4aOi61rTa7ItLNpGk3W1FgjlBtqiUCiboDcYthTRANvRzFSnGDkXLvHCGPQ/OaE4GCMGv+WpP2MmPugRzWBJMZsApS++RTRdCqjWb6Y3vEM0XYuiOJW5MwexlTnDgifEaujFs8b/otgvivkR9fwFs/JoRGcOGMSAY0+jKXQ+gw3Ubqy1q93XL3WTeBHpUpcFmcGeOvId/f01+Pnr48Uf7Z+/ps25G3ayAE8D4OVoO67BvoxVXsYPmXnij9SvgU+3P2HiQpGV2Neffo/EgbsomEbaObojHp5tY5WxezLqs0Gc6WXLhzMEBm2b/dYEonCzBjAtfmcMx2YK6Ditp7DKejJhC3HVWB0ItK0goXGdEQiffIHYhJRtWRdztGMI+wn+1lii/ZrwhtuEi6whCBoQDzpOuUyOwC2GHd9ikW1aYzVxWFDGhCzzHHnCGqbQf+iHkvTZZKa1zifFSKO8ZUroR+AMBxXnVbtWkjwMygzumG4MWkDmkO+oZUT3+LDvtQqADKFrl4bl3Jcd+ssXvI88drin0fIQ9VrM3HrH/HnChH/rnFlcv9fHpHk5HF5es7Wf2YZNllfr/xBWnpNi0Ajt5Q1fgZQ/waOXRYViK6GgdCh2acV7wMBThoWm1woehgg8Xv40SjIQAUQTFLfwVQJ9+B57thLlEEQxldD2MBzdXAlw/JueDK1OvG7DKTKBoXmG1ku78V+Q6wIP70UzCDGPNdMua0ZsSzSRCmWrEcLeMQgJvzma+dbyEsGUt0ZGNZ+SHAw4jJR6gsM99yS8ZJHnUj9uorYLKWBQ4RYmDYglYomtJKksrbG2yE2EV7W+mz0z/d5ul6phVXNSBT7TMRJORRvbuQFIxOlsksOg0AStofyWZR69o5kOyQjN4j8kLJCPvCasqiMx9xaoeJi7Txhu0e2Gj5qdb1uJ+d/grd8iEZSepCJQMxVTOykNjGH2OdMRU1xNiF9l2lIShj97ANOyIB9klRh4NsWP1G9YF74nlXT9KwbxjnxL8AgfYwYzrX3h4S4TZGc2Uw45m5aKAKQkCbUkQ0A4WjzsqWM/lvyK5/LAlk/XVmdrSLGQQiS3lCeRQnLW51Za7gjRhgyO44rkqCiYD7HGjDRbIsd8J9VTCJoFslqX4g3o2X9PEwSTcUZkHLuSmVAyRCzh74TaExEU+5wn+78WgIrWINwhYKMYBraZmbCKIGY17b9xonIqKsWoPdCWhJ++lD9rh3K11iiUSUyHZPPD2X6y4+Ci6CwsX61xpI0ofX1ppqlsPcWyvydpKU9p8iPcNFFdCYVBgJP0KeGCerqmqV1CQM3xOV5uFLs5nMdVSWUy1jrmYMv4UCJqKEDRqBsGKfe+PQ/ASc740Ho3cpfM7GxsFWGa7PSNgFtbWVkcxOR5u+7D9hwDTUs2NZr2AeZK3H3W2T6dW9kke8ET2yVknNdbp7BNTj5rYJ553LrEVs8ACu9PAr84A+18ttTr5hGBK3raaknxgHIyonTgfHM2fO2dK1JS2wT5C1NLN6L9cLYRdEZpd68RcTT/Jq95tXEA7GDbjFFKda/RaYVMvb2uOCV5vhOtDvpHIpsGPT5zYtzuFDQLTan+ReLc081iHyjyn8e41opFzty1RTIguu059mxc2WUfPtjoCJnRVTLTqhQldhokZ9P0B9uwAUV/s3tcMEdIts+PiwTwtHvQsHjRVPOhHWi/Gbn52OaJVhYdxpI26AULTTg4I2VKuRD4Z9/zMEu5172o4GfcGwy/PvjJxzZ/davrBdOYAf/cbvDpR1uL2Ylv13dvh9v27EpsVo5pn9/jB9UbyHtwG/iJUm6aookpwZxRgSZ4/linHADHCmL/mT/WfnLgS+u9oBb1GYU20SlVb2ZFe7CgKe6WO9nWuyiwz2f+x2bRmp0LdqnYzT2y2ihepkhNjz4FP0dOmfGgsCXqxYNy7HV7fZSLf9B0bA7838u3NYDiZvGfogq+Wj/ZIz988IccpiNSPAMnCez4B7CHCdwunK2S/zuhKkGIeKsCbZX4vSawlkw4f7obXk9HN9busui+HGnwfXbKGzb+OlTpWMQTKDolpR/UspSP3JdtehMf/ovNhH/StLT2zbu8WBAK75v6yd99oF1xDxiqNo7qG0mmFkgGvAPL+TJc4SggpkVaJn+wpN7Fi+kPRiD2lv8Y1hv8B&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 820 70 C 796 70 790 90 809.2 94 C 790 102.8 811.6 122 827.2 114 C 838 130 874 130 886 114 C 910 114 910 98 895 90 C 910 74 886 58 865 66 C 850 54 826 54 820 70 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 90px; margin-left: 791px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div align="center">IPC</div></div></div></div></foreignObject><text x="850" y="94" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">IPC</text></switch></g><path d="M 140 270 L 373.63 270" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 378.88 270 L 371.88 273.5 L 373.63 270 L 371.88 266.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="20" y="240" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 270px; margin-left: 21px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>Extension API namespace</div><div>(WebIDL - C++)<br /></div></div></div></div></foreignObject><text x="80" y="274" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Extension API namesp...</text></switch></g><path d="M 500 270 L 573.63 270" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 578.88 270 L 571.88 273.5 L 573.63 270 L 571.88 266.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="380" y="240" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 270px; margin-left: 381px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>mozIExtensionAPI</div><div>RequestHandler<br /></div><div>(XPCOM - JS)<br /></div></div></div></div></foreignObject><text x="440" y="274" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">mozIExtensionAPI...</text></switch></g><path d="M 170 303 L 170 280 L 370 280 L 370 303" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 170 303 L 170 480 L 370 480 L 370 303" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 170 303 L 370 303" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="269.5" y="296">mozIExtensionAPIRequest</text></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 400px; margin-left: 187px;"><div style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"><div><ul><li>apiNamespace</li><li>apiName<br /></li><li>apiObjectType</li><li><div align="left">apiObjectId</div></li><li>callerSavedFrame</li><li>serviceWorkerInfo</li><li>args</li><li>normalizedArgs (R/W)<br /></li></ul></div></div></div></div></foreignObject><text x="187" y="404" fill="#000000" font-family="Helvetica" font-size="12px">apiNamespaceapiName...</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 320px; margin-left: 260px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"><div>(XPCOM - C++)</div></div></div></div></foreignObject><text x="260" y="324" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">(XPCOM - C++)</text></switch></g><path d="M 350 110 L 530 110 L 530 200 L 460 200 L 440 230 L 440 200 L 350 200 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 178px; height: 1px; padding-top: 155px; margin-left: 352px;"><div style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><ul><li>retrieve WorkerContextChild<br /></li><li>validate and normalize arguments</li><li>check permissions<br /></li></ul></div></div></div></foreignObject><text x="352" y="159" fill="#000000" font-family="Helvetica" font-size="12px">retrieve WorkerContextChild...</text></switch></g><path d="M 660 300 L 660 343.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 660 348.88 L 656.5 341.88 L 660 343.63 L 663.5 341.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="580" y="240" width="160" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 270px; margin-left: 581px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>WebIDL</div><div>ChildAPIManager</div><div>(extends ChildAPIManager)<br /></div></div></div></div></foreignObject><text x="660" y="274" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">WebIDL...</text></switch></g><path d="M 660 530 L 660 603.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 660 608.88 L 656.5 601.88 L 660 603.63 L 663.5 601.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="570" y="470" width="180" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 500px; margin-left: 571px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>WebIDL</div><div>ChildLocalAPIImpl<br /></div><div>(extends ChildLocalAPIImpl)<br /></div></div></div></div></foreignObject><text x="660" y="504" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">WebIDL...</text></switch></g><path d="M 660 380 L 660 463.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 660 468.88 L 656.5 461.88 L 660 463.63 L 663.5 461.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 640 365 L 530 365 L 530 378.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 530 383.88 L 526.5 376.88 L 530 378.63 L 533.5 376.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 680 365 L 773.63 365" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 778.88 365 L 771.88 368.5 L 773.63 365 L 771.88 361.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 660 350 L 680 365 L 660 380 L 640 365 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 530 445 L 530 640 L 563.63 640" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 568.88 640 L 561.88 643.5 L 563.63 640 L 561.88 636.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="440" y="385" width="180" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 415px; margin-left: 441px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>WebIDL</div><div>ChildObjectTypeIImpl<br /></div><div>(extends ChildLocalAPIImpl)<br /></div></div></div></div></foreignObject><text x="530" y="419" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">WebIDL...</text></switch></g><path d="M 850 347.5 L 850 281.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 850 276.12 L 853.5 283.12 L 850 281.37 L 846.5 283.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="780" y="347.5" width="140" height="35" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 365px; margin-left: 781px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">ProxyAPIImplementation</div></div></div></foreignObject><text x="850" y="369" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ProxyAPIImplementation</text></switch></g><path d="M 885 240 L 885 41.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 885 36.12 L 888.5 43.12 L 885 41.37 L 881.5 43.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="780" y="240" width="140" height="35" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 258px; margin-left: 781px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">ProcessConduitsChild</div></div></div></foreignObject><text x="850" y="261" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ProcessConduitsChild</text></switch></g><path d="M 815 35 L 815 233.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><path d="M 815 238.88 L 811.5 231.88 L 815 233.63 L 818.5 231.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><rect x="780" y="0" width="140" height="35" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 18px; margin-left: 781px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;">ProcessConduitsParent</div></div></div></foreignObject><text x="850" y="21" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ProcessConduitsParent</text></switch></g><rect x="570" y="610" width="180" height="60" fill="#ffffff" stroke="#000000" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 640px; margin-left: 571px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div>ext-APINAMESPACE.js</div><div>ExtensionAPI subclass<br /></div></div></div></div></foreignObject><text x="660" y="644" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">ext-APINAMESPACE.js...</text></switch></g><path d="M 160 700 L 160 100" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><path d="M 1040 100 L 0 100" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 70px; margin-left: 951px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div align="justify"><b>PARENT</b></div><div align="justify"><b>PROCESS</b></div></div></div></div></foreignObject><text x="995" y="74" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">PARENT...</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 140px; margin-left: 941px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div><b>EXTENSIONS</b></div><div align="justify"><b>CHILD PROCESS</b></div></div></div></div></foreignObject><text x="995" y="144" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">EXTENSIONS...</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 130px; margin-left: 95px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"><div><b>DOM Worker</b></div><div><b> Thread</b></div></div></div></div></foreignObject><text x="95" y="134" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">DOM Worker...</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 130px; margin-left: 161px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: normal; overflow-wrap: normal;"><div><b>Main</b></div><div><b> Thread</b></div></div></div></div></foreignObject><text x="205" y="134" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Main...</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg> \ No newline at end of file
diff --git a/toolkit/components/extensions/docs/wiring_up_new_webidl_bindings.rst b/toolkit/components/extensions/docs/wiring_up_new_webidl_bindings.rst
new file mode 100644
index 0000000000..59088d7079
--- /dev/null
+++ b/toolkit/components/extensions/docs/wiring_up_new_webidl_bindings.rst
@@ -0,0 +1,165 @@
+Wiring up new WebExtensions WebIDL files into mozilla-central
+=============================================================
+
+Add a new entry in ``dom/bindings/Bindings.conf``
+-------------------------------------------------
+
+New WebIDL bindings should be added as new entries in ``dom/bindings/Bindings.conf``. The new entry should be
+added in alphabetic order and nearby the other WebExtensions API bindings already listed in this config file
+(look for the ``ExtensionBrowser`` webidl definition and the other existing WebIDL bindings related to the
+WebExtensions APIs):
+
+.. code-block::
+
+ # WebExtension API
+ ...
+ 'ExtensionRuntime': {
+ 'headerFile': 'mozilla/extensions/ExtensionRuntime.h',
+ 'nativeType': 'mozilla::extensions::ExtensionRuntime',
+ },
+
+.. warning::
+
+ `mach build` will fail if the entries in `dom/bindings/Bindings.conf` are not in alphabetic order,
+ or if the `headerFile` referenced does not exist yet.
+
+Add a new entry in ``dom/webidl/moz.build``
+-------------------------------------------
+
+The new ``.webidl`` file has to be also listed in "dom/webidl/moz.build", it should be added in
+
+- the existing group of ``WEBIDL_FILES`` entries meant specifically for the WebExtensions API bindings
+- or in the group of ``PREPROCESSED_WEBIDL_FILES`` entries meant specifically for the WebExtensions
+ API bindings, **if the generated `.webidl` includes preprocessing macros** (e.g. when part of an API
+ is not available in all builds, e.g. subset of APIs that are only available in Desktop builds).
+
+.. code-block::
+
+ # WebExtensions API.
+ WEBIDL_FILES += [
+ ...
+ "ExtensionRuntime.webidl"
+ ...
+ ]
+
+ PREPROCESSED_WEBIDL_FILES += [
+ ...
+ ]
+
+.. warning::
+
+ The group of PREPROCESSED_WERBIDL_FILES meant to list WebExtensions APIs ``.webidl`` files
+ may not exist yet (one will be added right after the existing `WEBIDL_FILES` when the first
+ preprocessed `.webidl` will be added).
+
+
+Add new entries in ``toolkit/components/extensions/webidl-api/moz.build``
+-------------------------------------------------------------------------
+
+The new C++ files for the WebExtensions API binding needs to be added to ``toolkit/components/extensions/webidl-api/moz.build``
+to make them part of the build, The new ``.cpp`` file has to be added into the ``UNIFIED_SOURCES`` group
+where the other WebIDL bindings are being listed. Similarly, the new ``.h`` counterpart has to be added to
+``EXPORTS.mozilla.extensions`` (which ensures that the header file will be placed into the path set earlier
+in ``dom/bindings/Bindings.conf``):
+
+.. code-block::
+
+ # WebExtensions API namespaces.
+ UNIFIED_SOURCES += [
+ ...
+ "ExtensionRuntime.cpp",
+ ...
+ ]
+
+ EXPORTS.mozilla.extensions += [
+ ...
+ "ExtensionRuntime.h",
+ ...
+ ]
+
+Wiring up the new API into ``dom/webidl/ExtensionBrowser.webidl``
+-----------------------------------------------------------------
+
+To make the new WebIDL bindings part of the ``browser`` global, a new attribute has to be added to
+``dom/webidl/ExtensionBrowser.webidl``:
+
+.. code-block::
+
+ // `browser.runtime` API namespace.
+ [Replaceable, SameObject, BinaryName="GetExtensionRuntime",
+ Func="mozilla::extensions::ExtensionRuntime::IsAllowed"]
+ readonly attribute ExtensionRuntime runtime;
+
+.. note::
+ ``chrome`` is defined as an alias of the ``browser`` global, and so by adding the new attribute
+ into ``ExtensionBrowser` the same attribute will also be available in the ``chrome`` global.
+ Unlike the "Privileged JS"-based WebExtensions API, the ``chrome`` and ``browser`` APIs are
+ exactly the same and a the async methods return a Promise if no callback has been passed
+ (similarly to Safari versions where the WebExtensions APIs are supported).
+
+The additional attribute added into ``ExtensionBrowser.webidl`` will require some addition to the ``ExtensionBrowser``
+C++ class as defined in ``toolkit/components/extensions/webidl-api/ExtensionBrowser.h``:
+
+- the definition of a new corresponding **public method** (by convention named ``GetExtensionMyNamespace``)
+- a ``RefPtr`` as a new **private data member named** (by convention named ``mExtensionMyNamespace``)
+
+.. code-block::
+
+ ...
+ namespace extensions {
+
+ ...
+ class ExtensionRuntime;
+ ...
+
+ class ExtensionBrowser final : ... {
+ ...
+ RefPtr<ExtensionRuntime> mExtensionRuntime;
+ ...
+
+ public:
+ ...
+ ExtensionRuntime* GetExtensionRuntime();
+ }
+ ...
+
+
+And then in its ``toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp`` counterpart:
+
+- the implementation of the new public method
+- the addition of the new private member data ``RefPtr`` in the ``NS_IMPL_CYCLE_COLLECTION_UNLINK``
+ and ``NS_IMPL_CYCLE_COLLECTION_TRAVERSE`` macros
+
+.. code-block::
+
+ ...
+ #include "mozilla/extensions/ExtensionRuntime.h"
+ ...
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ExtensionBrowser)
+ ...
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mExtensionRuntime)
+ ...
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ExtensionBrowser)
+ ...
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExtensionRuntime)
+ ...
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+ ...
+
+ ExtensionRuntime* ExtensionBrowser::GetExtensionRuntime() {
+ if (!mExtensionRuntime) {
+ mExtensionRuntime = new ExtensionRuntime(mGlobal, this);
+ }
+
+ return mExtensionRuntime
+ }
+
+.. warning::
+
+ Forgetting to add the new ``RefPtr`` into the cycle collection traverse and unlink macros
+ will not result in a build error, but it will result into a leak.
+
+ Make sure to don't forget to double-check these macros, especially if some tests are failing
+ because of detected shutdown leaks.