[TOC]
This document describes the procedure and general advice for writing the implementation of a new Extension API.
Before you invest significant time and energy into writing a new Extension API, be sure to go through the API proposal process. This is important, as we may not accept each proposal for a new Extension API, and many require tweaks or changes. The proposal process is designed to reach consensus on both the general usefulness and appropriateness of an API, as well as the high-level shape it will take.
The proposal process is documented here.
Extension APIs are defined by their schema files. Access to these APIs is controlled entries in the features files. APIs are generally implemented through a combination of extension functions and events (and, occasionally, properties). APIs are exposed in JavaScript to the extension through extension bindings.
An extension schema defines the API, including the functions, events, and properties on the API.
Read more about schemas here.
Extension feature files control access to different APIs, and can restrict APIs (or specific methods) to different types of extensions, Chromium release channels, or even to specific extension IDs, as well as specify required permissions.
Read more about the features files here.
Extension functions are called by the extension in order to perform some action
in the Chromium browser. For instance, the chrome.tabs.create()
API function
is called by an extension to create a tab, and is implemented in C++ by the
TabsCreateFunction
, and instance of the ExtensionFunction
class. Generally,
each API function will map to an instance of the ExtensionFunction
class.
Read more about extension functions here.
Events are dispatched by Chrome to inform the extension of an occurrence. For
instance, the chrome.tabs.onCreated()
event is dispatched when a new tab is
created.
Read more about extension events here.
Properties on the API are exposed as JavaScript properties on the API object
itself. These are generally rare. Constants defined in the API (such as
chrome.tabs.TAB_ID_NONE
) are exposed automatically through the bindings layer.
More complex objects (such as the StorageArea
defined in chrome.storage
for
chrome.storage.local
, chrome.storage.sync
, and chrome.storage.managed
)
need to be defined in the API, and are constructed by the bindings layer.
The bindings system is responsible for creating the JavaScript entry points that extensions use to invoke extension APIs, according to the definitions in the schema and the features files. Most APIs should not need any special code in extension API bindings, and custom bindings are generally discouraged. Custom bindings are only required if an API has behavior that is unique enough to not be built into the general extension API system.
Read more about extension bindings here.
What is the best way to approach writing a new API implementation?
The proposal process requires a team to sign on for continued ownership and stewardship of an API. Any extension API that is not being designed and implemented by the core extensions team (and some that are) should have a separate OWNERS file.
Even though each API should have its own dedicated OWNERS, it's good practice to include an OWNER from //extensions/OWNERS to review the interaction with the core extensions system. We can offer guidance on the use of the different core concepts of API implementation, and ensure that the new code is following best practices.
During the development process, the extension API should start restricted to "trunk" or "canary" in the features files (ideally "trunk", which means it is only accessible when building from source; "canary" should only be used if it is necessary to have external testers for the verification of the API). Things tend to change during development, and we don't want an API to reach stable channel before it's ready or when it is likely to experience churn.
Now, it's time to actually write the code to implement the API!
As a general practice in Chromium, it's good to develop CLs that represent a full logical unit, complete with tests. This does not have to mean it has to be entirely complete - it may not even be reachable in production code. However, it should be clear to reviewers what the functionality is, and that it is tested and works as intended.
This applies to writing new APIs, as well. APIs should frequently be written piecemeal, in multiple CLs, with each CL having a logical unit of tested code. For example, a CL may include:
-
An entry in the API schema (for instance, a new function or event)
-
The implementation of the new capability (the implementation of that function or dispatching that event appropriately)
-
Tests for the capability (a unit test, API test, or both)
In this case, the logical unit is the new function or event, complete with tests.
For exceptionally large or complex APIs, even this may be too large of a first step, and smaller CLs may be required before even creating the API entry (for instance, a new API function may require changes elsewhere in Chromium to enable a new behavior).
This approach makes it easy for reviewers to review the CL in a reasonable period of time, and keeps development of the API moving.
The below are discouraged.
The All-in-One CL: In most cases, please do not try and fit an entire API implementation into a single CL. This typically results in a large, unwieldy CL that is difficult to review. Review times generally increase superlinearly (i.e., faster than linearly) with the number of lines added - a 200 line CL is usually more than 5x faster to review than a 1,000 line CL.
The Stubbed-Out CL: A common anti-approach is to add an API stub as the first CL, where that stub adds the entirety of the API surface and empty extension function implementations for each API function.
First, this makes it impossible to evaluate the correctness of the API implementation and usage of extension system concepts. Adding a new API method, intentionally, requires special review from API reviewers (who are familiar with the best practices and any common pitfalls). If a stub is used, this review is no longer useful.
Additionally, APIs may need to be slightly tweaked as a result of different implementation details. While most of these should be ironed out in the proposal process, some may still come up during the implementation review. Bundling the declaration with the implementation allows us to catch any changes that need to happen in the API surface during the primary review.
TODO(devlin): Incorporate the below into this article.
This article describes a number of different code concepts, and can be useful for some of the high-level approaches. Note that some of this is outdated.
Much of the documentation for extension APIs is auto-generated from the schema
files. This includes method signatures and descriptions and type descriptions.
If you don't require any additional documentation, the only required step is to
add a new template article in
chrome/common/extensions/docs/templates/public/extensions
. If you need
additional documentation, you can also add an article in
chrome/common/extensions/docs/templates/intros
.
To verify the documentation is correctly included and visible, run the preview
mode of the documentation server by running
chrome/common/extensions/docs/server2/preview.py
and visiting
localhost:8000/extensions/<apiName>
.
Once an API has been fully implemented, tested, and is, in fact, stable, the features file restriction can be lifted. Depending on the complexity of the API, it may also need periods of restriction in "dev" and/or "beta".