OLD | NEW |
| (Empty) |
1 # Mojo in Chromium | |
2 | |
3 This document is intended to serve as a Mojo primer for Chromium developers. No | |
4 prior knowledge of Mojo is assumed. | |
5 | |
6 [TOC] | |
7 | |
8 ## Should I Bother Reading This? | |
9 | |
10 If you're planning to build a Chromium feature that needs IPC and you aren't | |
11 already using Mojo, YES! **Legacy IPC is deprecated.** | |
12 | |
13 ## Why Mojo? | |
14 | |
15 TL;DR: The long-term intent is to refactor Chromium into a large set of smaller | |
16 services. | |
17 | |
18 We can be smarter about: | |
19 | |
20 * Which services we bring up or don't | |
21 * How we isolate these services to improve security and stability | |
22 * Which binary features we ship to one user or another | |
23 | |
24 A more robust messaging layer opens the door for a number of interesting | |
25 possibilities; in particular it allows us to integrate a large number of | |
26 components without link-time interdependencies, and it breaks down the growing | |
27 number of interesting cross-language boundaries across the codebase. | |
28 | |
29 Much has been learned from using Chromium IPC and maintaining Chromium | |
30 dependencies in anger over the past several years and we feel there's now a | |
31 significant opportunity to make life easier for developers, and to help them | |
32 build more and better features, faster, and with much less cost to users. | |
33 | |
34 ## Mojo Overview | |
35 | |
36 The Mojo system API provides a small suite of low-level IPC primitives: | |
37 **message pipes**, **data pipes**, and **shared buffers**. On top of this API | |
38 we've built higher-level bindings APIs to simplify messaging for consumers | |
39 writing C++, Java, or JavaScript code. | |
40 | |
41 This document focuses primarily on using C++ bindings with message pipes, which | |
42 is likely to be the most common usage encountered by Chromium developers. | |
43 | |
44 ### Message Pipes | |
45 | |
46 A message pipe is a lightweight primitive for reliable bidirectional transfer of | |
47 relatively small packets of data. Unsurprisingly a pipe has two endpoints, and | |
48 either endpoint may be transferred over another message pipe. | |
49 | |
50 Because we bootstrap a primordial message pipe between the browser process and | |
51 each child process, this in turn means that you can create a new pipe and | |
52 ultimately send either end to any process, and the two ends will still be | |
53 able to talk to each other seamlessly and exclusively. Goodbye, routing IDs! | |
54 | |
55 While message pipes can carry arbitrary packets of unstructured data we | |
56 generally use them in conjunction with generated bindings to ensure a | |
57 consistent, well-defined, versioned message structure on all endpoints. | |
58 | |
59 ### Mojom | |
60 | |
61 Mojom is the IDL for Mojo interfaces. Given a `.mojom` file, the bindings | |
62 generator outputs bindings for all three of the currently supported languages. | |
63 | |
64 For example: | |
65 | |
66 ``` | |
67 // src/components/frob/public/interfaces/frobinator.mojom | |
68 module frob.mojom; | |
69 | |
70 interface Frobinator { | |
71 Frobinate(); | |
72 }; | |
73 ``` | |
74 | |
75 would generate the following outputs: | |
76 | |
77 ``` | |
78 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.cc | |
79 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.h | |
80 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.js | |
81 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.srcjar | |
82 ... | |
83 ``` | |
84 | |
85 The generated code hides away all the details of serializing and deserializing | |
86 messages on either end of a pipe. | |
87 | |
88 The C++ header (`frobinator.mojom.h`) defines an abstract class for each | |
89 mojom interface specified. Namespaces are derived from the `module` name. | |
90 | |
91 **NOTE:** Chromium convention for component `foo`'s module name is `foo.mojom`. | |
92 This means all mojom-generated C++ typenames for component `foo` will live in | |
93 the `foo::mojom` namespace to avoid collisions with non-generated typenames. | |
94 | |
95 In this example the generated `frob::mojom::Frobinator` has a single | |
96 pure virtual function: | |
97 | |
98 ```cpp | |
99 namespace frob { | |
100 | |
101 class Frobinator { | |
102 public: | |
103 virtual void Frobinate() = 0; | |
104 }; | |
105 | |
106 } // namespace frob | |
107 ``` | |
108 | |
109 To create a `Frobinator` service, one simply implements `foo::Frobinator` and | |
110 provides a means of binding pipes to it. | |
111 | |
112 ### Binding to Pipes | |
113 | |
114 Let's look at some sample code: | |
115 | |
116 ```cpp | |
117 // src/components/frob/frobinator_impl.cc | |
118 | |
119 #include "components/frob/public/interfaces/frobinator.mojom.h" | |
120 #include "mojo/public/cpp/bindings/binding.h" | |
121 #include "mojo/public/cpp/bindings/interface_request.h" | |
122 | |
123 namespace frob { | |
124 | |
125 class FrobinatorImpl : public mojom::Frobinator { | |
126 public: | |
127 FrobinatorImpl(mojom::FrobinatorRequest request) | |
128 : binding_(this, std::move(request)) {} | |
129 ~FrobinatorImpl() override {} | |
130 | |
131 // mojom::Frobinator: | |
132 void Frobinate() override { DLOG(INFO) << "I can't stop frobinating!"; } | |
133 | |
134 private: | |
135 mojo::Binding<mojom::Frobinator> binding_; | |
136 }; | |
137 | |
138 } // namespace frob | |
139 ``` | |
140 | |
141 The first thing to note is that `mojo::Binding<T>` *binds* one end of a message | |
142 pipe to an implementation of a service. This means it watches that end of the | |
143 pipe for incoming messages; it knows how to decode messages for interface `T`, | |
144 and it dispatches them to methods on the bound `T` implementation. | |
145 | |
146 `mojom::FrobinatorRequest` is a generated type alias for | |
147 `mojo::InterfaceRequest<mojom::Frobinator>` and is essentially semantic sugar | |
148 for a strongly-typed message pipe endpoint. A common way to create new message | |
149 pipes is via the `GetProxy` call defined in `interface_request.h`: | |
150 | |
151 ```cpp | |
152 mojom::FrobinatorPtr proxy; | |
153 mojom::FrobinatorRequest request = mojo::GetProxy(&proxy); | |
154 ``` | |
155 | |
156 This creates a new message pipe with one end owned by `proxy` and the other end | |
157 owned by `request`. It has the nice property of attaching common type | |
158 information to each end of the pipe. | |
159 | |
160 Note that `InterfaceRequest<T>` doesn't actually **do** anything. It just scopes | |
161 a pipe endpoint and associates it with an interface type at compile time. As | |
162 such, other typed service binding primitives such as `mojo::Binding<T>` take | |
163 these objects as input when they need an endpoint to bind to. | |
164 | |
165 `mojom::FrobinatorPtr` is a generated type alias for | |
166 `mojo::InterfacePtr<mojom::Frobinator>`. An `InterfacePtr<T>` scopes a message | |
167 pipe endpoint as well, but it also internally implements every method on `T` by | |
168 serializing a corresponding message and writing it to the pipe. | |
169 | |
170 Hence we can put this together to talk to a `FrobinatorImpl` over a pipe: | |
171 | |
172 ```cpp | |
173 frob:mojom::FrobinatorPtr frobinator; | |
174 frob::FrobinatorImpl impl(GetProxy(&frobinator)); | |
175 | |
176 // Tada! | |
177 frobinator->Frobinate(); | |
178 ``` | |
179 | |
180 Behind the scenes this serializes a message corresponding to the `Frobinate` | |
181 request and writes it to one end of the pipe. Eventually (and incidentally, | |
182 very soon after), `impl`'s internal `mojo::Binding` will decode this message and | |
183 dispatch a call to `impl.Frobinate()`. | |
184 | |
185 **NOTE:** In this example the service and client are in the same process, and | |
186 this works just fine. If they were in different processes (see the example below | |
187 in [Exposing Services in Chromium](#Exposing-Services-in-Chromium)), the call | |
188 to `Frobinate()` would look exactly the same! | |
189 | |
190 ### Responding to Requests | |
191 | |
192 A common idiom in Chromium IPC is to keep track of IPC requests with some kind | |
193 of opaque identifier (i.e. an integer *request ID*) so that you can later | |
194 respond to a specific request using some nominally related message in the other | |
195 direction. | |
196 | |
197 This is baked into mojom interface definitions. We can extend our `Frobinator` | |
198 service like so: | |
199 | |
200 ``` | |
201 module frob.mojom; | |
202 | |
203 interface Frobinator { | |
204 Frobinate(); | |
205 GetFrobinationLevels() => (int min, int max); | |
206 }; | |
207 ``` | |
208 | |
209 and update our implementation: | |
210 | |
211 ```cpp | |
212 class FrobinatorImpl : public mojom::Frobinator { | |
213 public: | |
214 // ... | |
215 | |
216 // mojom::Frobinator: | |
217 void Frobinate() override { /* ... */ } | |
218 void GetFrobinationLevels(const GetFrobinationLevelsCallback& callback) { | |
219 callback.Run(1, 42); | |
220 } | |
221 }; | |
222 ``` | |
223 | |
224 When the service implementation runs `callback`, the response arguments are | |
225 serialized and sent back over the pipe. The proxy on the other end knows how to | |
226 read this response and will in turn dispatch it to a callback on that end: | |
227 | |
228 ```cpp | |
229 void ShowLevels(int min, int max) { | |
230 DLOG(INFO) << "Frobinator min=" << min << " max=" << max; | |
231 } | |
232 | |
233 // ... | |
234 | |
235 mojom::FrobinatorPtr frobinator; | |
236 FrobinatorImpl impl(GetProxy(&frobinator)); | |
237 | |
238 frobinator->GetFrobinatorLevels(base::Bind(&ShowLevels)); | |
239 ``` | |
240 | |
241 This does what you'd expect. | |
242 | |
243 ## Exposing Services in Chromium | |
244 | |
245 There are a number of ways one might expose services across various surfaces of | |
246 the browser. One common approach now is to use a | |
247 [`content::ServiceRegistry` (link)](https://goo.gl/uEhx06). These come in | |
248 pairs generally spanning a process boundary, and they provide primitive service | |
249 registration and connection interfaces. For one example, [every | |
250 `RenderFrameHost` has a `ServiceRegistry`](https://goo.gl/4YR3j5), as does | |
251 [every corresponding `RenderFrame`](https://goo.gl/YhrgXa). These registries are | |
252 intertwined. | |
253 | |
254 The gist is that you can add a service to the local side of the registry -- it's | |
255 just a mapping from interface name to factory function -- or you can connect by | |
256 name to services registered on the remote side. | |
257 | |
258 **NOTE:** In this context the "factory function" is simply a callback which | |
259 takes a pipe endpoint and does something with it. It's expected that you'll | |
260 either bind it to a service implementation of some kind or you will close it, ef
fectively rejecting the connection request. | |
261 | |
262 We can build a simple browser-side `FrobinatorImpl` service that has access to a | |
263 `BrowserContext` for any frame which connects to it: | |
264 | |
265 ```cpp | |
266 #include "base/macros.h" | |
267 #include "components/frob/public/interfaces/frobinator.mojom.h" | |
268 #include "content/public/browser/browser_context.h" | |
269 #include "mojo/public/cpp/system/interface_request.h" | |
270 #include "mojo/public/cpp/system/strong_binding.h" | |
271 | |
272 namespace frob { | |
273 | |
274 class FrobinatorImpl : public mojom::Frobinator { | |
275 public: | |
276 FrobinatorImpl(content::BrowserContext* context, | |
277 mojom::FrobinatorRequest request) | |
278 : context_(context), binding_(this, std::move(request)) {} | |
279 ~FrobinatorImpl() override {} | |
280 | |
281 // A factory function to use in conjunction with ServiceRegistry. | |
282 static void Create(content::BrowserContext* context, | |
283 mojom::FrobinatorRequest request) { | |
284 // See comment below for why this doesn't leak. | |
285 new FrobinatorImpl(context, std::move(request)); | |
286 } | |
287 | |
288 private: | |
289 // mojom::Frobinator: | |
290 void Frobinate() override { /* ... */ } | |
291 | |
292 content::BrowserContext* context_; | |
293 | |
294 // A StrongBinding is just like a Binding, except that it takes ownership of | |
295 // its bound implementation and deletes itself (and the impl) if and when the | |
296 // bound pipe encounters an error or is closed on the other end. | |
297 mojo::StrongBinding<mojom::Frobinator> binding_; | |
298 | |
299 DISALLOW_COPY_AND_ASSIGN(FrobinatorImpl); | |
300 }; | |
301 | |
302 } // namespace frob | |
303 ``` | |
304 | |
305 Now somewhere in the browser we register the Frobinator service with each | |
306 `RenderFrameHost` ([this](https://goo.gl/HEFn63) is a popular spot): | |
307 | |
308 ```cpp | |
309 frame_host->GetServiceRegistry()->AddService<frob::mojom::Frobinator>( | |
310 base::Bind( | |
311 &frob::FrobinatorImpl::Create, | |
312 base::Unretained(frame_host->GetProcess()->GetBrowserContext()))); | |
313 ``` | |
314 | |
315 And in the render process we can now do something like: | |
316 | |
317 ```cpp | |
318 mojom::FrobinatorPtr frobinator; | |
319 render_frame->GetServiceRegistry()->ConnectToRemoteService( | |
320 mojo::GetProxy(&frobinator)); | |
321 | |
322 // It's IPC! | |
323 frobinator->Frobinate(); | |
324 ``` | |
325 | |
326 There are now plenty of concrete examples of Mojo usage in the Chromium tree. | |
327 Poke around at existing mojom files and see how their implementions are built | |
328 and connected. | |
329 | |
330 ## Mojo in Blink | |
331 | |
332 *TODO* | |
333 | |
334 This is a work in progress. TL;DR: We'll also soon begin using Mojo services | |
335 from Blink so that the platform layer can consume browser services | |
336 directly via Mojo. The long-term goal there is to eliminate `content/renderer`. | |
337 | |
338 ## Questions, Discussion, etc. | |
339 | |
340 A good place to find highly concentrated doses of people who know and care | |
341 about Mojo in Chromium would be the [chromium-mojo](https://goo.gl/A4ebWB) | |
342 mailing list[.](https://goo.gl/L70ihQ) | |
OLD | NEW |