tree: 7ccf00e3627d3f88b8a03da17057816e643b9f14 [path history] [tgz]
  1. cpp/
  2. mojom/
  3. COMMON_METADATA
  4. DEPS
  5. DIR_METADATA
  6. OWNERS
  7. README.md
chromeos/crosapi/README.md

This directory defines the ChromeOS API (crosapi). This is the communication protocol between lacros (web-browser on ChromeOS) and ash (user-space system executable on ChromeOS) for all new IPCs. Some existing IPCs might use Wayland or D-Bus to avoid unnecessary rewrites.

The ChromeOS API is eventually going to be stabilized and will need to tolerate several milestones worth of version skew between lacros and ash. In the long term, these interfaces will potentially need to support years of version skew. This means that any mojom files and their transitive dependencies must be relatively stable and backwards compatible. By default, mojom files in this directory should not include mojoms from any other directories unless they are marked [Stable].

Note: The mojom subdirectory contains the stable API. The cpp subdirectory holds helper c++ code, but is not part of the API surface itself.

Guidelines

When to use Crosapi vs Wayland?

Wayland should be used when implementing features that primarily deal with display and input. Wayland should be engaged when dealing with

  • Window management
  • Surface position, window state (fullscreen, minimized, etc)
  • Display management (scaling, display layout, etc)
  • Input Handling (keyboard, mouse, IME, etc)
  • Compositing
  • Handling of graphics primitives

Concerns that do not fall into these buckets are candidates for the mojo-based crosapi.

Clients should avoid mixing use of the two APIs for a given feature (and this should not be necessary in most circumstances). Ordering of messages is guaranteed within each crosapi interface and globally for wayland, however there is no such sequential guarantee of ordering between the two protocols.

Consider the example of a feature wanting to keep updated captures of graphical browser contents to improve user restore experience on the OS level.

Depending on requirements Wayland may be a good fit. Changes to browser contents can be detected by Ash via tracking damage incurred on Wayland surfaces. Frames produced by these surfaces could then be persisted for product use-cases by the OS.

However Wayland does not expose higher level browser logic or state. To avoid frequently updating stored captures for inconsequential updates it may instead make sense to notify the OS of the specific browser-side triggers (navigation, load completion for e.g.) and persist frames when these are received by the OS. Such an API must be added to the mojom crosapi.

Create a Crosapi subservice for your feature

Create a Crosapi subservice for your feature. Some examples of features include:

  • A service that restores the browser state.
  • Theme synchronization between ChromeOS and the browser.
  • Background job that collects browser metrics and reports it under a ChromeOS identifier.

Organize feature code into their own module/directory. For example, if there is a feature called Foo, create a new module called crosapi.foo.mojom. Create a new directory under chromeos/crosapi/mojom/ to contain the code for this module. It might make sense to create additional submodules to further organize the feature service.

Adding new fields to structs and new parameters to methods

Refer to Mojo’s versioning instructions. All new fields are required to be optionals with the exception of primitive numeric type. Primitive numeric types can be nullable or non-nullable, but special considerations need to be made about the semantics of non-nullable default values.

Refer to mojo’s backwards compatibility documentation when choosing between nullable and non-nullable primitive types. Prefer nullable primitives if you are uncertain about the consequences of using a non-nullable primitive.

Defining mojo enums

Always define enums as [Extensible] and include a kUnknown. This is important because the enum list might grow in the future which can cause issues when the versions mismatch. Example:

[Extensible]
enum Status {
    [Default] kUnknown = 0,
    kSuccess = 1,
    kFailed = 2,
}

This is important for the caller or receiver to understand if there is a possible unanticipated request or response. When a service receives an enum values that it does not recognize, it will automatically set the enum value to kUnknown. Some patterns to avoid.

// This pattern will turn a failure into success if there is a version mismatch
// and could lead to unexpected behaviours.
[Extensible]
enum Status {
    [Default] kSuccess = 0,
    kFailureType1 = 1,
    kFailureType2 = 2,
    kFailureType3 = 3,
}

// kNone is ambiguous here. Is kNone an okay value to set when a new type of hardware is
// introduced? Does kNone have a behaviour associated with it that is different from an
// unknown device type?
[Extensible]
enum DeviceType {
    [Default] kNone = 0,
    kMouse = 1,
    kKeyboard = 2,
    kOther = 3,
}

Instead, do:

// Unknown can be specially handled. Maybe the feature needs to be disabled in this
// case.
[Extensible]
enum Status {
    [Default] kUnknown = 0,
    kSuccess = 1,
    kFailureType1 = 2,
    kFailureType2 = 3,
    kFailureType3 = 4,
}

// If we receive an unknown device type, we might want to inform the user to update or that
// it is an unsupported device type.
[Extensible]
enum DeviceType {
    [Default] kUnknown = 0,
    kNone = 1,
    kMouse = 2,
    kKeyboard = 3,
    kOther = 4,
}