Bug 1423425 Initial draft of WebExtensions API documentation r=bsilverberg
☠☠ backed out by bb75be81efa6 ☠ ☠
authorAndrew Swan <aswan@mozilla.com>
Tue, 05 Dec 2017 18:54:34 -0800
changeset 448348 c3ac950b0b8a71e4e5fbb478cdceb6a2777d66c0
parent 448347 d64fd69a4b8866c93f67690d0357ccdda6a9ccdd
child 448349 229639982d5b964d1e6767b4d71c9355d1df88c0
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsilverberg
bugs1423425
milestone59.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 1423425 Initial draft of WebExtensions API documentation r=bsilverberg MozReview-Commit-ID: EwcE881XGfp
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/index.rst
toolkit/components/extensions/docs/lifecycle.rst
toolkit/components/extensions/docs/manifest.rst
toolkit/components/extensions/docs/other.rst
toolkit/components/extensions/docs/reference.rst
toolkit/components/extensions/docs/schema.rst
toolkit/components/extensions/moz.build
tools/docs/conf.py
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/docs/background.rst
@@ -0,0 +1,129 @@
+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
+`chrome privileges <https://developer.mozilla.org/en-US/docs/Security/Firefox_Security_Basics_For_Developers>`_.
+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*).
+
+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 Javscript 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.
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/docs/basics.rst
@@ -0,0 +1,144 @@
+.. _basics:
+
+API Implementation Basics
+=========================
+This page describes some of the pieces involved when creating
+WebExtension 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 WebExtension 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 WebExtension 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 WebExtension API.
+
+Built-in APIs versus Experiments
+--------------------------------
+A WebExtension API can be built directly into the browser or it can be
+contained in a special type of extension called a "WebExtension Experiment".
+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 WebExtension 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.js``
+  (*ugh android*)
+
+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 WebExtension 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`.
+
+WebExtensions Experiments
+-------------------------
+(*This section left blank pending some coming experiments changes*)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/docs/events.rst
@@ -0,0 +1,295 @@
+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",
+           "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 happend: ${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
+
+   class myapi extends ExtensionAPI {
+     getAPI(context) {
+       return {
+         myapi: {
+           onSomething: new EventManager(context, "myapi.onSomething", 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",
+           "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
+
+   class myapi extends ExtensionAPI {
+     getAPI(context) {
+       return {
+         myapi: {
+           onSomething: new EventManager(context, "myapi.onSomething", (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",
+           "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
+
+   class myapi 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}`);
+             };
+             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
+
+   class myapi extends ExtensionAPI {
+     getAPI(context) {
+       return {
+         myapi: {
+           onSomething: new EventManager(context, "myapi.onSomething", 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.
+
+
new file mode 100644
--- /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
+
+   class myapi 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
+
+   class myapi 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
+occassionally 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
+
+   class myapi 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.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/docs/index.rst
@@ -0,0 +1,31 @@
+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
+   other
+   reference
+    
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/docs/lifecycle.rst
@@ -0,0 +1,50 @@
+.. _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.
+
+To be notified of update and uninstall events, an extension lists these
+events in the API manifest:
+
+.. code-block:: json
+
+   "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.
+
+.. Should we even document onStartup()?  I think no...
new file mode 100644
--- /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:: json
+
+   [
+     "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:: json
+
+   "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
+
+   class myapi extends ExtensionAPI {
+     onManifestEntry(name) {
+       let value = this.extension.manifest.my_api_property;
+       /* do something with value... */
+     }
+   }
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/docs/other.rst
@@ -0,0 +1,24 @@
+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
+----------------------
+*XXX describe*
+
+ExtensionPreferencesManager
+---------------------------
+*XXX describe*
new file mode 100644
--- /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:
new file mode 100644
--- /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",
+       // delcarations 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:: json
+
+   [
+     "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.
--- a/toolkit/components/extensions/moz.build
+++ b/toolkit/components/extensions/moz.build
@@ -85,9 +85,11 @@ MOCHITEST_MANIFESTS += [
 MOCHITEST_CHROME_MANIFESTS += ['test/mochitest/chrome.ini']
 XPCSHELL_TESTS_MANIFESTS += [
     'test/xpcshell/native_messaging.ini',
     'test/xpcshell/xpcshell-e10s.ini',
     'test/xpcshell/xpcshell-remote.ini',
     'test/xpcshell/xpcshell.ini',
 ]
 
+SPHINX_TREES['webextensions'] = 'docs'
+
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/tools/docs/conf.py
+++ b/tools/docs/conf.py
@@ -39,17 +39,21 @@ extensions = [
     'sphinx.ext.napoleon',
     'sphinx.ext.todo',
     'mozbuild.sphinx',
     'sphinx_js',
 ]
 
 # JSDoc must run successfully for dirs specified, so running
 # tree-wide (the default) will not work currently.
-js_source_path = ['toolkit/mozapps/extensions']
+js_source_path = [
+    'browser/components/extensions',
+    'toolkit/components/extensions',
+    'toolkit/mozapps/extensions',
+]
 root_for_relative_js_paths = '.'
 jsdoc_config_path = 'tools/docs/jsdoc.json'
 
 templates_path = ['_templates']
 source_suffix = '.rst'
 source_suffix = ['.rst', '.md']
 source_parsers = {
    '.md': CommonMarkParser,