chromium/mojo/public/js/README.md

# Mojo JavaScript Bindings API

This document is a subset of the [Mojo documentation](/mojo/README.md).

*** note
TODO(crbug.com/40605290): this document mentions deprecated JavaScript bindings.

We need to update it to describe the new bindings (bindings_lite).
***

[TOC]

## Getting Started

The bindings API is defined in the `mojo` namespace and implemented in
`mojo_bindings.js`, which could be generated by the GN target
`//mojo/public/js:bindings`.

When a Mojom IDL file is processed by the bindings generator, JavaScript code is
emitted in a `.js` file with the name based on the input `.mojom` file. Suppose
we create the following Mojom file at
`//services/echo/public/interfaces/echo.mojom`:

```
module test.echo.mojom;

interface Echo {
  EchoInteger(int32 value) => (int32 result);
};
```

And a GN target to generate the bindings in
`//services/echo/public/interfaces/BUILD.gn`:

```
import("//mojo/public/tools/bindings/mojom.gni")

mojom("interfaces") {
  sources = [
    "echo.mojom",
  ]
}
```

Bindings are generated by building one of these implicitly generated targets
(where "foo" is the target name):

- `foo_js` JavaScript bindings; used as compile-time dependency.
- `foo_js_data_deps` JavaScript bindings; used as run-time dependency.

If we then build this target:

```
ninja -C out/r services/echo/public/interfaces:interfaces_js
```

This will produce several generated source files. The one relevant to JavaScript
bindings is:

```
out/gen/services/echo/public/interfaces/echo.mojom.js
```

In order to use the definitions in `echo.mojom`, you will need to include two
files in your html page using `<script>` tags:

- `mojo_bindings.js` **Note: This file must be included before any `.mojom.js`
  files.**
- `echo.mojom.js`

```html
<!doctype html>
<script src="URL/to/mojo_bindings.js"></script>
<script src="URL/to/echo.mojom.js"></script>
<script>
  const echoPtr = new test.echo.mojom.EchoPtr();
  const echoRequest = mojo.makeRequest(echoPtr);
  // ...
</script>
```

## Interfaces

Similar to the C++ bindings API, we have:

- `mojo.InterfacePtrInfo` and `mojo.InterfaceRequest` encapsulate two ends of a
  message pipe. They represent the client end and service end of an interface
  connection, respectively.
- For each Mojom interface `Foo`, there is a generated `FooPtr` class. It owns
  an `InterfacePtrInfo`; provides methods to send interface calls using the
  message pipe handle from the `InterfacePtrInfo`.
- `mojo.Binding` owns an `InterfaceRequest`. It listens on the message pipe
  handle and dispatches incoming messages to a user-defined interface
  implementation.

Let's consider the `echo.mojom` example above. The following shows how to create
an `Echo` interface connection and use it to make a call.

```html
<!doctype html>
<script src="URL/to/mojo_bindings.js"></script>
<script src="URL/to/echo.mojom.js"></script>
<script>
  function EchoImpl() {}
  EchoImpl.prototype.echoInteger = function (value) {
    return Promise.resolve({ result: value });
  };

  const echoServicePtr = new test.echo.mojom.EchoPtr();
  const echoServiceRequest = mojo.makeRequest(echoServicePtr);
  const echoServiceBinding = new mojo.Binding(
    test.echo.mojom.Echo,
    new EchoImpl(),
    echoServiceRequest,
  );
  echoServicePtr.echoInteger({ value: 123 }).then((response) => {
    console.log(`The result is ${response.value}`);
  });
</script>
```

### Interface Pointers and Requests

In the example above, `test.echo.mojom.EchoPtr` is an interface pointer class.
`EchoPtr` represents the client end of an interface connection. For method
`EchoInteger` in the `Echo` Mojom interface, there is a corresponding
`echoInteger` method defined in `EchoPtr`. (Please note that the format of the
generated method name is `camelCaseWithLowerInitial`.)

There are some control methods shared by all interface pointer classes. For
example, binding/extracting `InterfacePtrInfo`, setting connection error
handler, querying version information, etc. In order to avoid name collision,
they are defined in `mojo.InterfacePtrController` and exposed as the `ptr` field
of every interface pointer class.

In the example above, `echoServiceRequest` is an `InterfaceRequest` instance. It
represents the service end of an interface connection.

`mojo.makeRequest` creates a message pipe; populates the output argument (which
could be an `InterfacePtrInfo` or an interface pointer) with one end of the
pipe; returns the other end wrapped in an `InterfaceRequest` instance.

### Binding an InterfaceRequest

A `mojo.Binding` bridges an implementation of an interface and a message pipe
endpoint, dispatching incoming messages to the implementation.

In the example above, `echoServiceBinding` listens for incoming `EchoInteger`
method calls on the messsage pipe, and dispatches those calls to the `EchoImpl`
instance.

### Receiving Responses

Some Mojom interface methods expect a response, such as `EchoInteger`. The
corresponding JavaScript method returns a Promise. This Promise is resolved when
the service side sends back a response. It is rejected if the interface is
disconnected.

### Connection Errors

If a pipe is disconnected, both endpoints will be able to observe the connection
error (unless the disconnection is caused by closing/destroying an endpoint, in
which case that endpoint won't get such a notification). If there are remaining
incoming messages for an endpoint on disconnection, the connection error won't
be triggered until the messages are drained.

Pipe disconnecition may be caused by:

- Mojo system-level causes: process terminated, resource exhausted, etc.
- The bindings close the pipe due to a validation error when processing a
  received message.
- The peer endpoint is closed. For example, the remote side is a bound interface
  pointer and it is destroyed.

Regardless of the underlying cause, when a connection error is encountered on a
binding endpoint, that endpoint's **connection error handler** (if set) is
invoked. This handler may only be invoked _once_ as long as the endpoint is
bound to the same pipe. Typically clients and implementations use this handler
to do some kind of cleanup or recovery.

```js
// Assume echoServicePtr is already bound.
echoServicePtr.ptr.setConnectionErrorHandler(function () {
  DoImportantCleanUp();
});

// Assume echoServiceBinding is already bound:
echoServiceBinding.setConnectionErrorHandler(function () {
  DoImportantCleanUpToo();
});
```

**Note:** Closing one end of a pipe will eventually trigger a connection error
on the other end. However it's ordered with respect to any other event (_e.g._
writing a message) on the pipe. Therefore, it is safe to make an `echoInteger`
call on `echoServicePtr` and reset it immediately (which results in
disconnection), `echoServiceBinding` will receive the `echoInteger` call before
it observes the connection error.

## Associated Interfaces

An associated interface connection doesn't have its own underlying message pipe.
It is associated with an existing message pipe (i.e., interface connection).

Similar to the non-associated interface case, we have:

- `mojo.AssociatedInterfacePtrInfo` and `mojo.AssociatedInterfaceRequest`
  encapsulate a _route ID_, representing a logical connection over a message
  pipe.
- For each Mojom interface `Foo`, there is a generated `FooAssociatedPtr` class.
  It owns an `AssociatedInterfacePtrInfo`. It is the client side of an
  interface.
- `mojo.AssociatedBinding` owns an `AssociatedInterfaceRequest`. It listens on
  the connection and dispatches incoming messages to a user-defined interface
  implementation.

See
[this document](https://www.chromium.org/developers/design-documents/mojo/associated-interfaces)
for more details.

## Automatic and Manual Dependency Loading

By default, generated `.mojom.js` files automatically load Mojom dependencies.
For example, if `foo.mojom` imports `bar.mojom`, loading `foo.mojom.js` will
insert a `<script>` tag to load `bar.mojom.js`, if it hasn't been loaded.

The URL of `bar.mojom.js` is determined by:

- the path of `bar.mojom` relative to the position of `foo.mojom` at build time;
- the URL of `foo.mojom.js`.

For example, if at build time the two Mojom files are located at:

```
a/b/c/foo.mojom
a/b/d/bar.mojom
```

The URL of `foo.mojom.js` is:

```
http://example.org/scripts/b/c/foo.mojom.js
```

Then the URL of `bar.mojom.js` is supposed to be:

```
http://example.org/scripts/b/d/bar.mojom.js
```

If you would like `bar.mojom.js` to live at a different location, you need to
set `mojo.config.autoLoadMojomDeps` to `false` before loading `foo.mojom.js`,
and manually load `bar.mojom.js` yourself. Similarly, you need to turn off this
option if you merge `bar.mojom.js` and `foo.mojom.js` into a single file.

```html
<!-- Automatic dependency loading -->
<script src="http://example.org/scripts/mojo_bindings.js"></script>
<script src="http://example.org/scripts/b/c/foo.mojom.js"></script>

<!-- Manual dependency loading -->
<script src="http://example.org/scripts/mojo_bindings.js"></script>
<script>
  mojo.config.autoLoadMojomDeps = false;
</script>
<script src="http://example.org/scripts/b/d/bar.mojom.js"></script>
<script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
```

### Performance Tip: Avoid Loading the Same `.mojom.js` File Multiple Times

If `mojo.config.autoLoadMojomDeps` is set to `true` (which is the default
value), you might accidentally load the same `.mojom.js` file multiple times if
you are not careful. Although it doesn't cause fatal errors, it hurts
performance and therefore should be avoided.

```html
<!-- Assume that mojo.config.autoLoadMojomDeps is set to true: -->

<!-- No duplicate loading; recommended. -->
<script src="http://example.org/scripts/b/c/foo.mojom.js"></script>

<!-- No duplicate loading, although unnecessary. -->
<script src="http://example.org/scripts/b/d/bar.mojom.js"></script>
<script src="http://example.org/scripts/b/c/foo.mojom.js"></script>

<!-- Load bar.mojom.js twice; should be avoided. -->
<!-- when foo.mojom.js is loaded, it sees that bar.mojom.js is not yet loaded,
     so it inserts another <script> tag for bar.mojom.js. -->
<script src="http://example.org/scripts/b/c/foo.mojom.js"></script>
<script src="http://example.org/scripts/b/d/bar.mojom.js"></script>
```

If a `.mojom.js` file is loaded for a second time, a warnings will be showed
using `console.warn()` to bring it to developers' attention.

## Name Formatting

As a general rule, Mojom definitions follow the C++ formatting style. To make
the generated JavaScript bindings conforms to our JavaScript style guide, the
code generator does the following conversions:

| In Mojom               | In generated `.mojom.js` |
| ---------------------- | ------------------------ |
| `MethodLikeThis`       | `methodLikeThis`         |
| `parameter_like_this`  | `parameterLikeThis`      |
| `field_like_this`      | `fieldLikeThis`          |
| `name_space.like_this` | `nameSpace.likeThis`     |

## Downloads

As of Chrome 123.0.6309.0, prebuilt MojoJS archives are available for download
via
[Chrome for Testing (CfT) infrastructure](https://developer.chrome.com/blog/chrome-for-testing).
For every user-facing Chrome release, a correspondingly-versioned ZIP file
containing the relevant MojoJS bindings can be downloaded at the following URL:

```
https://storage.googleapis.com/chrome-for-testing-public/123.0.6309.0/mojojs.zip
```

Replace `123.0.6309.0` with the exact Chrome version you need. Any version in
[CfT’s `known-good-versions.json`](https://googlechromelabs.github.io/chrome-for-testing/known-good-versions.json)
greater than or equal to 123.0.6309.0 is guaranteed to have a corresponding
`mojojs.zip` download available.