Extension API Specifications

Summary

Extension APIs are specified in JSON or IDL files. These files describe the API and the functions, events, types, and properties it contains. This specification is used for multiple purposes, including setting up the API’s bindings (the JavaScript end points that are exposed to extensions), generating strong types and conversion for extension API objects, creating the public documentation, generating JS externs, and registering extension API functions.

Applications

Bindings

The Extension API specifications generate the JSON objects that describe the extension APIs. These JSON objects are then used in the creation of the extension API bindings to set up the functions, events, and properties that are exposed to the extensions in JavaScript. For more information, see the bindings documentation.

Type Generation

Extension API implementations use strong types in the browser process, generated from the arguments passed by the renderer. Both the type definitions themselves and the conversion code to generate between the serialized base::Values and strong types are generated based on the API specification. Additionally, we also generate constants for enums and event names used in extension APIs.

Documentation

The public documentation hosted at https://developer.chrome.com/extensions is largely generated from the API specifications. Everything from the function descriptions to parameter ordering to exposed types and enums is generated from the specification. Because this becomes our public documentation, special care should be given to ensure that descriptions for events, functions, properties, and arguments are clear and well-formed.

JS Externs

JS externs, which are used with Closure Compilation, are generated from the API specifications as well. These are used both internally within Chrome (e.g., in our settings WebUI code) as well as externally by developers writing extensions.

Structure

The API specification consists of four different sections: functions, events, types, and properties. Functions describe the API functions exposed to extensions. Events describe the different events that extensions can register listeners for. Types are used to define object types to use in either functions or events. Finally, properties are used to expose certain properties on the API object itself.

Compilation

The extension API compilation is triggered as part of the normal compilation process (e.g., ninja -C out/debug chrome). The schemas are “compiled” by the JSON Schema Compiler, which is implemented in src/tools/json_schema_compiler. As part of this step, both IDL and JSON extension API specifications are converted to the same object, which can be output as a JSON object. This API object is then used in each of the compilation steps (bindings, type generation, documentation, JS externs, and function registration).

Compilation steps can be selective, since not all APIs need to be included in all compilation steps. For instance, some APIs are implemented entirely in the renderer, and thus do not need to have strong types or function registration generated. See the JSON Schema Compiler for more information.

File Location

Extension APIs are defined in two main locations: src/chrome/common/extensions/api and src/extensions/common/api. These represent two different layers in the extensions system. The chrome layer is for concepts that are purely chrome-related, whereas the extensions layer is for concepts that may apply to other embedders (such as AppShell or others).

If an API does not rely on Chrome-specific concepts (things like profiles and tabs are examples of Chrome-specific topics), it should go at the /extensions layer. The default location is the /extensions layer (though this was added later, and, as a result, many APIs that wouldn't otherwise need to be defined at the the /chrome layer, are).

Adding a New File

To add a new API, create a new IDL or JSON file that defines the API and add it to the appropriate BUILD.gn groups. Files can be included in different targets depending on their platform availability (e.g., an API may be only available on ChromeOS) and whether they need to be included in various compilation steps (e.g., an API may not need generated function registration).

Adding to GN Files

The GN targets that you include the file in depend on which applications it should be used in. Unfortunately, the targets aren‘t as standardized as they should be, so you’ll have to trace them to their GN action usage. (We'd like to fix this at some point.)

The GN actions correspond as below:

generated_json_strings: This action generates the bundled JSON strings for APIs, which are used to set up the extension bindings in the renderer.

function_registration: This action generates code to automatically register the extension function implementations with the ExtensionFunctionRegistry.

generated_types: This action generates the strong types used in the browser process and the conversion to and from these types and base::Values.

Most APIs leverage all of these (and are typically called schema_sources or schema_files in the .gni files). Others want to omit certain steps; these are added to other groups in the .gn files.

If in doubt, you probably want to add yours to the “basic” group. Feel free to reach out to an extensions OWNER with any questions.

IDL vs JSON

Extension APIs can be specified in either IDL or JSON. During compilation, all files are converted to JSON objects. The benefit to using a JSON file is that it is more clear what the JSON object output will look like (since it’s simply the result of parsing the file). IDL, on the other hand, is typically much more readable - especially when an API will have many methods or long descriptions. However, IDL is not as fully-featured as JSON in terms of accepted properties on different nodes.

Promise Based Function Returns

Extension functions can be made to both accept a callback as a final parameter, or return a promise if the callback is omitted. For JSON schemas, instead of specifying the callback as a function at the end of the parameter list, it is explicitly defined as an asynchronous return using the returns_async key on the function itself. For IDL scheamas, any function where the final argument of the parameters is a callback type will automatically be considered as supporting promises, unless the function has the [doesNotSupportPromises=“crbug.com/id”] extended attribute. This extended attribute shouldn‘t be added to any new APIs and only exists for a few legacy APIs which can’t support promises for various reasons.