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.
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, 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.
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.
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.
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).
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).
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
Most APIs leverage all of these (and are typically called
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.
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.
Extension functions can be made to both accept a callback as a final parameter, or return a promise if the callback is omitted. In IDL schemas this is achieved by adding the
[supportsPromises] extended attribute in front of the function definition. 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.