Bug 1423425 Initial draft of WebExtensions API documentation draft
authorAndrew Swan <aswan@mozilla.com>
Tue, 05 Dec 2017 18:54:34 -0800
changeset 712814 d054c53fa16a78ca5c55ea6db380659546a25952
parent 712813 e9cb757a18f1c97667c2717e5d0f1cb91ec28964
child 744156 8da553e2ebc496328235ab0f3afd1e43a973f384
push id93447
push useraswan@mozilla.com
push dateMon, 18 Dec 2017 20:54:53 +0000
bugs1423425
milestone59.0a1
Bug 1423425 Initial draft of WebExtensions API documentation 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,