Writing a New Extension API

Overview

This document describes the procedure and general advice for writing the implementation of a new Extension API.

Before Writing (Much) Code

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.

Implementation Concepts

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.

Schemas

An extension schema defines the API, including the functions, events, and properties on the API.

Read more about schemas here.

Features

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.

API Functions

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.

API Events

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.

API Properties

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.

Extension Bindings

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.

Implementation Process

What is the best way to approach writing a new API implementation?

Development

Include a new OWNERS file

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.

Include //extensions OWNERS on CLs

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.

Start enabled on “trunk” or “canary”

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.

Writing Code

Now, it's time to actually write the code to implement the API!

Approaches

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.

Anti-Approaches

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.

Code Concepts

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.

Launching

Add and Verify Documentation

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>.

Adjust Features Files

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”.