| # Mojo JavaScript Bindings API |
| This document is a subset of the [Mojo documentation](/mojo/README.md). |
| |
| [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> |
| |
| var echoPtr = new test.echo.mojom.EchoPtr(); |
| var 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}); |
| }; |
| |
| var echoServicePtr = new test.echo.mojom.EchoPtr(); |
| var echoServiceRequest = mojo.makeRequest(echoServicePtr); |
| var echoServiceBinding = new mojo.Binding(test.echo.mojom.Echo, |
| new EchoImpl(), |
| echoServiceRequest); |
| echoServicePtr.echoInteger({value: 123}).then(function(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 exmple, 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 |