1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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
|