Bug 1453522 Document bundled webextension experiments r=kmag
authorAndrew Swan <aswan@mozilla.com>
Wed, 11 Apr 2018 16:44:07 -0700
changeset 413097 b0553a9a967ce855f4e147ba2e825b8114935f7e
parent 413096 f4d4a3fc7bb0fcab64c1df4f77e1060b0499b32b
child 413098 15332e1d2eda348b7081c8df9cd47dcca79e4401
push id33833
push useraiakab@mozilla.com
push dateFri, 13 Apr 2018 09:41:15 +0000
treeherdermozilla-central@260e4c83c8a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1453522
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1453522 Document bundled webextension experiments r=kmag Also cleaned up a few other loose ends on webextensions api docs. MozReview-Commit-ID: FnyqmM7NjqE
toolkit/components/extensions/docs/background.rst
toolkit/components/extensions/docs/basics.rst
toolkit/components/extensions/docs/events.rst
toolkit/components/extensions/docs/functions.rst
toolkit/components/extensions/docs/manifest.rst
--- a/toolkit/components/extensions/docs/background.rst
+++ b/toolkit/components/extensions/docs/background.rst
@@ -11,17 +11,21 @@ The implementation of a WebExtension API
 Browser internals are accessed using
 `XPCOM <https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM>`_
 or `ChromeOnly WebIDL features <https://developer.mozilla.org/en-US/docs/Mozilla/WebIDL_bindings#ChromeOnly>`_.
 
 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 elsewhere (*add links here*).
+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,
--- a/toolkit/components/extensions/docs/basics.rst
+++ b/toolkit/components/extensions/docs/basics.rst
@@ -136,9 +136,72 @@ This is specified by the ``manifest`` pr
 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`.
 
 WebExtensions Experiments
 -------------------------
-(*This section left blank pending some coming experiments changes*)
+A new API may also be implemented within an extension. An API implemented
+this way is called a WebExtension Experiment.  Experiments can be useful
+when actively developing a new API, as they do not require building
+Firefox locally. Note that extensions that include experiments cannot be
+signed by addons.mozilla.org.  They 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``.
+
+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.
--- a/toolkit/components/extensions/docs/events.rst
+++ b/toolkit/components/extensions/docs/events.rst
@@ -55,17 +55,17 @@ it in a child process, the wrapper in th
 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
 
-   class myapi extends ExtensionAPI {
+   this.myapi = class extends ExtensionAPI {
      getAPI(context) {
        return {
          myapi: {
            onSomething: new EventManager(context, "myapi.onSomething", fire => {
              const callback = value => {
                fire.async(value);
              };
              RegisterSomeInternalCallback(callback);
@@ -149,17 +149,17 @@ the ``extraParameters`` property.  For e
    ]
 
 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
 
-   class myapi extends ExtensionAPI {
+   this.myapi = class extends ExtensionAPI {
      getAPI(context) {
        return {
          myapi: {
            onSomething: new EventManager(context, "myapi.onSomething", (fire, minValue) => {
              const callback = value => {
                if (value >= minValue) 
                  fire.async(value);
                }
@@ -206,17 +206,17 @@ This can be defined in the schema with t
      }
    ]
 
 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
 
-   class myapi extends ExtensionAPI {
+   this.myapi = class extends ExtensionAPI {
      getAPI(context) {
        return {
          myapi: {
            onSomething: new EventManager(context, "myapi.onSomething", fire => {
              const callback = async (value) => {
                let rv = await fire.async(value);
                log(`The onSomething listener returned the string ${rv}`);
              };
@@ -258,17 +258,17 @@ that is loaded in a child process.  And 
 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
 
-   class myapi extends ExtensionAPI {
+   this.myapi = class extends ExtensionAPI {
      getAPI(context) {
        return {
          myapi: {
            onSomething: new EventManager(context, "myapi.onSomething", fire => {
              const listener = (value) => {
                fire.async(value);
              };
 
--- a/toolkit/components/extensions/docs/functions.rst
+++ b/toolkit/components/extensions/docs/functions.rst
@@ -92,17 +92,17 @@ 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
 
-   class myapi extends ExtensionAPI {
+   this.myapi = class extends ExtensionAPI {
      getAPI(context) {
        return {
          myapi: {
            add(x, y) { return x+y; }
          }
        }
      }
    }
@@ -122,17 +122,17 @@ Any other return type will be sent direc
 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
 
-   class myapi extends ExtensionAPI {
+   this.myapi = class extends ExtensionAPI {
      getAPI(context) {
        return {
          myapi: {
            doSomething() {
              if (cantDoSomething) {
                throw new ExtensionError("Cannot call doSomething at this time");
              }
              return something();
@@ -168,17 +168,17 @@ subclass that is loaded in the appropria
 (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
 
-   class myapi extends ExtensionAPI {
+   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 ...;
            }
--- a/toolkit/components/extensions/docs/manifest.rst
+++ b/toolkit/components/extensions/docs/manifest.rst
@@ -55,14 +55,14 @@ The JSON fragment above causes the WebEx
 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
 
-   class myapi extends ExtensionAPI {
+   this.myapi = class extends ExtensionAPI {
      onManifestEntry(name) {
        let value = this.extension.manifest.my_api_property;
        /* do something with value... */
      }
    }