This document is a subset of the Mojo documentation.
*** note TODO(crbug.com/40605290): this document mentions deprecated JavaScript bindings.
We need to update it to describe the new bindings (bindings_lite).
[TOC]
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<!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>
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.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.
<!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>
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.
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.
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.
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:
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.
// 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.
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.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 for more details.
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:
bar.mojom relative to the position of foo.mojom at build time;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.
<!-- 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>
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.
<!-- 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.
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 |
As of Chrome 123.0.6309.0, prebuilt MojoJS archives are available for download via Chrome for Testing (CfT) infrastructure. 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
greater than or equal to 123.0.6309.0 is guaranteed to have a corresponding
mojojs.zip download available.