forked from mirrors/gecko-dev
		
	Also cleaned up a few other loose ends on webextensions api docs. MozReview-Commit-ID: FnyqmM7NjqE --HG-- extra : rebase_source : 6039a70c72790c14d8872e38e77e9596b7dac3f8
		
			
				
	
	
		
			201 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
	
		
			8 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
Implementing a function
 | 
						|
=======================
 | 
						|
Implementing an API function requires at least two different pieces:
 | 
						|
a definition for the function in the schema, and Javascript code that
 | 
						|
actually implements the function.
 | 
						|
 | 
						|
Declaring a function in the API schema
 | 
						|
--------------------------------------
 | 
						|
An API schema definition for a simple function looks like this:
 | 
						|
 | 
						|
.. code-block:: json
 | 
						|
 | 
						|
   [
 | 
						|
     {
 | 
						|
       "namespace": "myapi",
 | 
						|
       "functions": [
 | 
						|
         {
 | 
						|
           "name": "add",
 | 
						|
           "type": "function",
 | 
						|
           "description": "Adds two numbers together.",
 | 
						|
           "async": true,
 | 
						|
           "parameters": [
 | 
						|
             {
 | 
						|
               "name": "x",
 | 
						|
               "type": "number",
 | 
						|
               "description": "The first number to add."
 | 
						|
             },
 | 
						|
             {
 | 
						|
               "name": "y",
 | 
						|
               "type": "number",
 | 
						|
               "description": "The second number to add."
 | 
						|
             }
 | 
						|
           ]
 | 
						|
         }
 | 
						|
       ]
 | 
						|
     }
 | 
						|
   ]
 | 
						|
 | 
						|
The ``type`` and ``description`` properties were described above.
 | 
						|
The ``name`` property is the name of the function as it appears in
 | 
						|
the given namespace.  That is, the fragment above creates a function
 | 
						|
callable from an extension as ``browser.myapi.add()``.
 | 
						|
The ``parameters`` property describes the parameters the function takes.
 | 
						|
Parameters are specified as an array of Javascript types, where each
 | 
						|
parameter is a constrained Javascript value as described
 | 
						|
in the previous section.
 | 
						|
 | 
						|
Each parameter may also contain additional properties ``optional``
 | 
						|
and ``default``.  If ``optional`` is present it must be a boolean
 | 
						|
(and parameters are not optional by default so this property is typically
 | 
						|
only added when it has the value ``true``).
 | 
						|
The ``default`` property is only meaningful for optional parameters,
 | 
						|
it specifies the value that should be used for an optional parameter
 | 
						|
if the function is called without that parameter.
 | 
						|
An optional parameter without an explicit ``default`` property will
 | 
						|
receive a default value of ``null``.
 | 
						|
Although it is legal to create optional parameters at any position
 | 
						|
(i.e., optional parameters can come before required parameters), doing so
 | 
						|
leads to difficult to use functions and API designers are encouraged to
 | 
						|
use object-valued parameters with optional named properties instead,
 | 
						|
or if optional parameters must be used, to use them sparingly and put
 | 
						|
them at the end of the parameter list.
 | 
						|
 | 
						|
.. XXX should we describe allowAmbiguousArguments?
 | 
						|
 | 
						|
The boolean-valued ``async`` property specifies whether a function
 | 
						|
is asynchronous.
 | 
						|
For asynchronous functions,
 | 
						|
the WebExtensions framework takes care of automatically generating a
 | 
						|
`Promise <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise>`_ and then resolving the Promise when the function
 | 
						|
implementation completes (or rejecting the Promise if the implementation
 | 
						|
throws an Error).
 | 
						|
Since extensions can run in a child process, any API function that is
 | 
						|
implemented (either partially or completely) in the parent process must
 | 
						|
be asynchronous.
 | 
						|
 | 
						|
When a function is declared in the API schema, a wrapper for the function
 | 
						|
is automatically created and injected into appropriate extension Javascript
 | 
						|
contexts.  This wrapper automatically validates arguments passed to the
 | 
						|
function against the formal parameters declared in the schema and immediately
 | 
						|
throws an Error if invalid arguments are passed.
 | 
						|
It also processes optional arguments and inserts default values as needed.
 | 
						|
As a result, API implementations generally do not need to write much
 | 
						|
boilerplate code to validate and interpret arguments.
 | 
						|
 | 
						|
Implementing a function in the main process
 | 
						|
-------------------------------------------
 | 
						|
If an asynchronous function is not implemented in the child process,
 | 
						|
the wrapper generated from the schema automatically marshalls the
 | 
						|
function arguments, sends the request to the parent process,
 | 
						|
and calls the implementation there.
 | 
						|
When that function completes, the return value is sent back to the child process
 | 
						|
and the Promise for the function call is resolved with that value.
 | 
						|
 | 
						|
Based on this, an implementation of the function we wrote the schema
 | 
						|
for above looks like this:
 | 
						|
 | 
						|
.. code-block:: js
 | 
						|
 | 
						|
   this.myapi = class extends ExtensionAPI {
 | 
						|
     getAPI(context) {
 | 
						|
       return {
 | 
						|
         myapi: {
 | 
						|
           add(x, y) { return x+y; }
 | 
						|
         }
 | 
						|
       }
 | 
						|
     }
 | 
						|
   }
 | 
						|
 | 
						|
The implementations of API functions are contained in a subclass of the
 | 
						|
`ExtensionAPI <reference.html#extensionapi-class>`_ class.
 | 
						|
Each subclass of ExtensionAPI must implement the ``getAPI()`` method
 | 
						|
which returns an object with a structure that mirrors the structure of
 | 
						|
functions and events that the API exposes.
 | 
						|
The ``context`` object passed to ``getAPI()`` is an instance of
 | 
						|
`BaseContext <reference.html#basecontext-class>`_,
 | 
						|
which contains a number of useful properties and methods.
 | 
						|
 | 
						|
If an API function implementation returns a Promise, its result will
 | 
						|
be sent back to the child process when the Promise is settled.
 | 
						|
Any other return type will be sent directly back to the child process.
 | 
						|
A function implementation may also raise an Error.  But by default,
 | 
						|
an Error thrown from inside an API implementation function is not
 | 
						|
exposed to the extension code that called the function -- it is
 | 
						|
converted into generic errors with the message "An unexpected error occurred".
 | 
						|
To throw a specific error to extensions, use the ``ExtensionError`` class:
 | 
						|
 | 
						|
.. code-block:: js
 | 
						|
 | 
						|
   this.myapi = class extends ExtensionAPI {
 | 
						|
     getAPI(context) {
 | 
						|
       return {
 | 
						|
         myapi: {
 | 
						|
           doSomething() {
 | 
						|
             if (cantDoSomething) {
 | 
						|
               throw new ExtensionError("Cannot call doSomething at this time");
 | 
						|
             }
 | 
						|
             return something();
 | 
						|
           }
 | 
						|
         }
 | 
						|
       }
 | 
						|
     }
 | 
						|
   }
 | 
						|
 | 
						|
The purpose of this step is to avoid bugs in API implementations from
 | 
						|
exposing details about the implementation to extensions.  When an Error
 | 
						|
that is not an instance of ExtensionError is thrown, the original error
 | 
						|
is logged to the
 | 
						|
`Browser Console <https://developer.mozilla.org/en-US/docs/Tools/Browser_Console>`_,
 | 
						|
which can be useful while developing a new API.
 | 
						|
 | 
						|
Implementing a function in a child process
 | 
						|
------------------------------------------
 | 
						|
Most functions are implemented in the main process, but there are
 | 
						|
occasionally reasons to implement a function in a child process, such as:
 | 
						|
 | 
						|
- The function has one or more parameters of a type that cannot be automatically
 | 
						|
  sent to the main process using the structured clone algorithm.
 | 
						|
 | 
						|
- The function implementation interacts with some part of the browser
 | 
						|
  internals that is only accessible from a child process.
 | 
						|
 | 
						|
- The function can be implemented substantially more efficiently in
 | 
						|
  a child process.
 | 
						|
 | 
						|
To implement a function in a child process, simply include an ExtensionAPI
 | 
						|
subclass that is loaded in the appropriate context
 | 
						|
(e.g, ``addon_child``, ``content_child``, etc.) as described in
 | 
						|
the section on :ref:`basics`.
 | 
						|
Code inside an ExtensionAPI subclass in a child process may call the
 | 
						|
implementation of a function in the parent process using a method from
 | 
						|
the API context as follows:
 | 
						|
 | 
						|
.. code-block:: js
 | 
						|
 | 
						|
   this.myapi = class extends ExtensionAPI {
 | 
						|
     getAPI(context) {
 | 
						|
       return {
 | 
						|
         myapi: {
 | 
						|
           async doSomething(arg) {
 | 
						|
             let result = await context.childManager.callParentAsyncFunction("anothernamespace.functionname", [arg]);
 | 
						|
             /* do something with result */
 | 
						|
             return ...;
 | 
						|
           }
 | 
						|
         }
 | 
						|
       }
 | 
						|
     }
 | 
						|
   }
 | 
						|
 | 
						|
As you might expect, ``callParentAsyncFunction()`` calls the given function
 | 
						|
in the main process with the given arguments, and returns a Promise
 | 
						|
that resolves with the result of the function.
 | 
						|
This is the same mechanism that is used by the automatically generated
 | 
						|
function wrappers for asynchronous functions that do not have a
 | 
						|
provided implementation in a child process.
 | 
						|
 | 
						|
It is possible to define the same function in both the main process
 | 
						|
and a child process and have the implementation in the child process
 | 
						|
call the function with the same name in the parent process.
 | 
						|
This is a common pattern when the implementation of a particular function
 | 
						|
requires some code in both the main process and child process.
 |