TODO(vtl): Reorganize this to be properly structured.

Mojom IDL

The Mojom IDL (interface definition language) is primarily used to describe interfaces to be used on message pipes. Below, we describe practical aspects of the Mojom language. Elsewhere, we describe the Mojom protocol. (TODO(vtl): Also, serialization format? Versioning?)

Text files written in Mojom IDL are given the .mojom extension by convention (and are usually referred to as Mojom/mojom/.mojom files). The Mojom bindings generator (TODO(vtl): link?) may be used to generate code in a variety of languages (including C++, Dart, and Go) from a Mojom file. Such generated code “implements” the things specified in the Mojom file, in a way that's appropriate for the particular target language.

Interfaces

A Mojom interface is (typically) used to describe communication on a message pipe. Typically, message pipes are created with a particular interface in mind, with one endpoint designated the client (which sends request messages and receives response messages) and the other designed that server or impl (which receives request messages and sends response messages).

For example, take the following Mojom interface declaration:

interface MyInterface {
  Foo(int32 a, string b);
  Bar() => (bool x, uint32 y);
  Baz() => ();
};

This specifies a Mojom interface in which the client may send three types of messages, namely Foo, Bar, and Baz (see the note below about names in Mojom). The first does not have a response message defined, whereas the latter two do. Whenever the server receives a Bar or Baz message, it must (eventually) send a (single) corresponding response message.

The Foo request message contains two pieces of data: a signed (two's complement) 32-bit integer called a and a Unicode string called b. On the “wire”, the message basically consists of metadata and a (serialized) struct (see below) containing a and b.

The Bar request message contains no data, so on the wire it‘s just metadata and an empty struct. It has a response message, containing a boolean value x and an unsigned 32-bit integer y, which on the wire consists of metadata and a struct with x and y. Each time the server receives a Bar message, it is supposed to (eventually) respond by sending the response message. (Note: The client may include as part of the request message’s metadata an identifier for the request; the response's metadata will then include this identifier, allowing it to match responses to requests.)

The Baz request message also contains no data. It requires a response, also containing no data. Note that even though the response has no data, a response message must nonetheless be sent, functioning as an “ack”. (Thus this is different from not having a response, as was the case for Foo.)

Structs

Mojom defines a way of serializing data structures (with the Mojom IDL providing a way of specifying those data structures). A Mojom struct is the basic unit of serialization. As we saw above, messages are basically just structs, with a small amount of additional metadata.

Here is a simple example of a struct declaration:

struct MyStruct {
  int32 a;
  string b;
};

We will discuss in greater detail how structs are declared later.

Names in Mojom

Names in Mojom are not important. Except in affecting compatibility at the level of source code (when generating bindings), names in a Mojom file may be changed arbitrarily without any effect on the “meaning” of the Mojom file (subject to basic language requirements, e.g., avoiding collisions with keywords and other names). E.g., the following is completely equivalent to the interface discussed above:

interface Something {
  One(int32 an_integer, string a_string);
  Two() => (bool a_boolean, uint32 an_unsigned);
  Three() => ();
};

The Something interface is compatible at a binary level with MyInterface. A client using the Something interface may communicate with a server implementing the MyInterface with no issues, and vice versa.

The reason for this is that elements (messages, parameters, struct members, etc.) are actually identified by ordinal value. They may be specified explicitly (using @123 notation; see below). If they are not specified explicitly, they are automatically assigned. (The ordinal values for each interface/struct/etc. must assign distinct values for each item, in a consecutive range starting at 0.)

Explicitly assigning ordinals allows Mojom files to be rearranged “physically” without changing their meaning. E.g., perhaps one would write:

interface MyInterface {
  Bar@1() => (bool x@0, uint32 y@1);
  Baz@2() => ();

  // Please don't use this in new code!
  FooDeprecated@0(int32 a@0, string b@1);
};

Ordinals also tie into the versioning scheme (TODO(vtl): link?), which allows Mojom files to be evolved in a backwards-compatible way. We will not discuss this matter further here.

TODO(vtl): Maybe mention exceptions to this in attributes (e.g., ServiceName).

Mojom files

A Mojom file consists of, in order:

  • an optional module declaration;
  • zero or more import statements (the order of these is not important); and
  • zero or more declarations of structs, interfaces, unions, enums, or consts (the order of these is not important). (These are all described further below.)

Additionally, C/C++-style comments are supported (i.e., single-line comments starting with // or multi-line comments of the form /* ... */).

As stated above, the order of struct/interface/union/enum/const declarations is not important. This is required to allow “cyclic” structures to be defined. Nonetheless, whenever possible, one should declare things before they are “used”. For example, the following is valid but not recommended:

// NOT recommended.

const MyEnum kMyConst = kMyOtherConst;
const MyEnum kMyOtherConst = A_VALUE;

enum MyEnum {
  A_VALUE,
  ANOTHER_VALUE,
};

Naming style

There is a standard style for naming things:

  • StudlyCaps (a.k.a. CapitalizedCamelCase) for: (struct, interface, union, and enum) type names and message (a.k.a. function or method) names;
  • unix_hacker_style for field names (in structs and unions) and “parameter” names;
  • ALL_CAPS_UNIX_HACKER_STYLE for enum value names; and
  • kStudlyCaps for const names.

Following this style is highly recommended, since code generators for various languages will expect this style, in order to transform the names into a more language-appropriate style.

Module statement

The Mojom module statement is just a way of logically grouping Mojom declarations. For example:

module my_module;

Mojom modules are similar to C++ namespaces (and the standard C++ code generator would put generated code into the my_module namespace), in that there is no implication that the file contains the entirety of the “module” definiton; multiple files may have the same module statement. (There is also no requirement that the module name have anything to do with the file path containing the Mojom file.)

Mojom module names are hierarchical in that they can be composed of multiple parts separated by .. For example:

module my_module.my_submodule;

struct MyStruct {
};

Name look-up is similar to C++: E.g., if the current module is my_module.my_submodule then MyStruct, my_submodule.MyStruct, and my_module.my_submodule.MyStruct all refer to the above struct, whereas if the current module is just my_module then only the latter two do.

Import declarations

An import declaration makes the declarations from another Mojom file available in the current Mojom file. Moreover, it operates transitively, in the sense that it also makes the imports of the imported file available, etc. The “argument” to the import statement is a path to a file. Tools that work with Mojom files are typically provided with a search path for importing files (just as a C++ compiler can be provided with an “include path”), for the purposes of resolving these paths. (TODO(vtl): This always includes the current Mojom file‘s path, right? Is the current path the first path that’s searched?)

For example:

module my_module;

import "path/to/another.mojom";
import "path/to/yet/a/different.mojom";

This makes the contents of the two mentioned Mojom files available, together with whatever they import, transitively. (Note that names are resolved in the way described in the previous section.)

Import cycles are not permitted (so, e.g., it would be an error if path/to/another.mojom imported the current Mojom file). However, it is entirely valid for Mojom files to be imported (transitively) multiple times (e.g., it is fine for path/to/another.mojom to also import path/to/yet/a/different.mojom).

Struct declarations

A Mojom struct declaration consists of a finite sequence of field declaration, each of which consists of a type, a name, and optionally a default value (if applicable for the given type). (If no default value is declared, then the default is the default value for the field type, typically 0, null, or similar.)

Additionally, a struct may contain enum and const declarations (TODO(vtl): why not struct/union/interface declarations?). While the order of the field declarations (with respect to one another) is important, the ordering of the enum/const declarations (with respect to both the field declarations and other enum/const declarations) is not. (But as before, we recommend declaring things before “use”.)

Here is an example with these elements:

struct Foo {
  const int8 kSomeConstant = 123;

  enum MyEnum {
    A_VALUE,
    ANOTHER_VALUE
  };

  int8 first_field = kSomeConstant;
  uint32 second_field = 123;
  MyEnum etc_etc = A_VALUE;
  float a;    // Default value is 0.
  string? b;  // Default value is null.
};

(Note that kSomeConstant may be referred to as Foo.kSomeConstant and, similarly, MyEnum as Foo.MyEnum. This is required outside of the Foo declaration.)

Interface declarations

TODO(vtl)

Union declarations

TODO(vtl)

Enum declarations

TODO(vtl)

Const declarations

TODO(vtl)

TODO(vtl): Write/(re)organize the sections below.

Data types

Primitive types

Standard types

Enum types

“Pointer” types

Nullability

Strings

Maps

Structs

Arrays

Unions

Handle types

Raw handle types

Interface types

Interface request types

Annotations

Pipelining