File: mojo_guide.md

package info (click to toggle)
chromium 73.0.3683.75-1~deb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,792,156 kB
  • sloc: cpp: 13,473,466; ansic: 1,577,080; python: 898,539; javascript: 655,737; xml: 341,883; asm: 306,070; java: 289,969; perl: 80,911; objc: 67,198; sh: 43,184; cs: 27,853; makefile: 12,092; php: 11,064; yacc: 10,373; tcl: 8,875; ruby: 3,941; lex: 1,800; pascal: 1,473; lisp: 812; awk: 41; jsp: 39; sed: 19; sql: 3
file content (157 lines) | stat: -rw-r--r-- 6,087 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# Mojo For Chromium Developers

## Overview

This document contains the minimum amount of information needed for a developer
to start using Mojo in Chromium. For more detailed documentation on the C++
bindings, see [this link](/mojo/public/cpp/bindings/README.md).

## Terminology

A **message pipe** is a pair of **endpoints**. Each endpoint has a queue of
incoming messages, and writing a message to one endpoint effectively enqueues
that message on the other endpoint. Message pipes are thus bidirectional.

A **mojom** file describes **interfaces** which describe strongly typed message
structures, similar to proto files.

Given a **mojom interface** and a **message pipe**, the two **endpoints**
can be given the labels **InterfacePtr** and **Binding**. This now describes a
strongly typed **message pipe** which transports messages described by the
**mojom interface**. The **InterfacePtr** is the **endpoint** which "sends"
messages, and the **Binding** "receives" messages. Note that the **message
pipe** itself is still bidirectional, and it's possible for a message to have a
response callback, which the **InterfacePtr** would receive.

Another way to think of this is that an **InterfacePtr** is capable of making
remote calls on an implementation of the mojom interface associated with the
**Binding**.

The **Binding** itself is just glue that wires the endpoint's message queue up
to some implementation of the interface provided by the developer.

## Example

Let's apply this to Chrome. Let's say we want to send a "Ping" message from a
Browser to a Renderer. First we need to define the mojom interface.

```
module example.mojom;
interface PingResponder {
  // Receives a "Ping" and responds with a random integer.
  Ping() => (int random);
};
```

Now let's make a MessagePipe.
```cpp
example::mojom::PingResponderPtr ping_responder;
example::mojom::PingResponderRequest request = mojo::MakeRequest(&ping_responder);
```

In this example, ```ping_responder``` is the **InterfacePtr**, and ```request```
is an **InterfaceRequest**, which is a **Binding** precursor that will shortly
be turned into a **Binding**. Now we can send our Ping message.

```cpp
auto callback = base::Bind(&OnPong);
ping_responder->Ping(callback);
```

Important aside: If we want to receive the the response, we must keep the object
```ping_responder``` alive. After all, it's just a wrapper around a **Message
Pipe endpoint**, if it were to go away, there'd be nothing left to receive the
response.

We're done! Of course, if everything were this easy, this document wouldn't need
to exist. We've taken the hard problem of sending a message from the Browser to
a Renderer, and transformed it into a problem where we just need to take the
```request``` object, pass it to the Renderer, turn it into a **Binding**, and
implement the interface.

In Chrome, processes host services, and the services themselves are connected to
a Service Manager via **message pipes**. It's easy to pass ```request``` to the
appropriate Renderer using the Service Manager, but this requires explicitly
declaring our intentions via manifest files. For this example, we'll use the
content_browser service [manifest
file](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_browser_manifest.json)
and the content_renderer service [manifest
file](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_renderer_manifest.json).

```js
content_renderer_manifest.json:
...
  "interface_provider_specs": {
    "service_manager:connector": {
      "provides": {
        "cool_ping_feature": [
          "example::mojom::PingResponder"
        ]
      },
    },
...
```

```js
content_browser_manifest.json:
...
 "interface_provider_specs": {
    "service_manager:connector": {
      "requires": {
        "content_renderer": [ "cool_ping_feature" ],
      },
    },
  },
...
```

These changes indicate that the content_renderer service provides the interface
PingResponder, under the **capability** named "cool_ping_feature". And the
content_browser services intends to use this feature.
```content::BindInterface``` is a helper function that takes ```request``` and
sends it to the renderer process via the Service Manager.

```cpp
content::RenderProcessHost* host = GetRenderProcessHost();
content::BindInterface(host, std::move(request));
```

Putting this all together for the browser process:
```cpp
example::mojom::PingResponderPtr ping_responder;  // Make sure to keep this alive! Otherwise the response will never be received.
example::mojom::PingResponderRequest request = mojo::MakeRequest(&ping_responder);
ping_responder->Ping(base::BindOnce(&OnPong));
content::RenderProcessHost* host = GetRenderProcessHost();
content::BindInterface(host, std::move(request));
```

In the Renderer process, we need to write an implementation for PingResponder,
and ensure that a **Binding** is created using the transported ```request```. In a
standalone Mojo service, this would require us to implement
```service_manager::Service::OnBindInterface()```. In Chrome, this is abstracted
behind ```content::ConnectionFilters``` and
```service_manager::BinderRegistry```. This is typically done in
```RenderThreadImpl::Init```.

```cpp
class PingResponderImpl : mojom::PingResponder {
  void BindToInterface(example::mojom::PingResponderRequest request) {
    binding_.reset(
      new mojo::Binding<mojom::MemlogClient>(this, std::move(request)));
  }
  void Ping(PingCallback callback) { std::move(callback).Run(4); }
  std::unique_ptr<mojo::Binding<mojom::PingResponder>> binding_;
};

RenderThreadImpl::Init() {
...
  this->ping_responder = std::make_unique<PingResponderImpl>();
  auto registry = base::MakeUnique<service_manager::BinderRegistry>();

  // This makes the assumption that |this->ping_responder| will outlive |registry|.
  registry->AddInterface(base::Bind(&PingResponderImpl::BindToInterface), base::Unretained(this->ping_responder.get()));

  GetServiceManagerConnection()->AddConnectionFilter(
      base::MakeUnique<SimpleConnectionFilter>(std::move(registry)));
...
```