blob: 612ba5ab2b23f89e15b35e5db3112f35f1c01822 [file] [log] [blame] [view]
Ken Rockotab035122019-02-06 00:35:241# Intro to Mojo & Services
2
3[TOC]
4
5## Overview
6
7This document contains the minimum amount of information needed for a developer
8to start using Mojo effectively in Chromium, with example Mojo interface usage,
9service definition and hookup, and a brief overview of the Content layer's core
10services.
11
12See other [Mojo & Services](/docs/README.md#Mojo-Services) documentation
13for introductory guides, API references, and more.
14
15## Mojo Terminology
16
17A **message pipe** is a pair of **endpoints**. Each endpoint has a queue of
18incoming messages, and writing a message at one endpoint effectively enqueues
19that message on the other (**peer**) endpoint. Message pipes are thus
20bidirectional.
21
22A **mojom** file describes **interfaces**, which are strongly-typed collections
23of **messages**. Each interface message is roughly analogous to a single proto
24message, for developers who are familiar with Google protobufs.
25
26Given a mojom interface and a message pipe, one of the endpoints
Darwin Huangb4bd2452019-10-08 22:56:0427can be designated as a **`Remote`** and is used to *send* messages described by
28the interface. The other endpoint can be designated as a **`Receiver`** and is used
Ken Rockotab035122019-02-06 00:35:2429to *receive* interface messages.
30
31*** aside
32NOTE: The above generalization is a bit oversimplified. Remember that the
33message pipe is still bidirectional, and it's possible for a mojom message to
Darwin Huangb4bd2452019-10-08 22:56:0434expect a reply. Replies are sent from the `Receiver` endpoint and received by the
35`Remote` endpoint.
Ken Rockotab035122019-02-06 00:35:2436***
37
Darwin Huangb4bd2452019-10-08 22:56:0438The `Receiver` endpoint must be associated with (*i.e.* **bound** to) an
Ken Rockotab035122019-02-06 00:35:2439**implementation** of its mojom interface in order to process received messages.
40A received message is dispatched as a scheduled task invoking the corresponding
41interface method on the implementation object.
42
Darwin Huangb4bd2452019-10-08 22:56:0443Another way to think about all this is simply that **a `Remote` makes
Ken Rockotab035122019-02-06 00:35:2444calls on a remote implementation of its interface associated with a
Darwin Huangb4bd2452019-10-08 22:56:0445corresponding remote `Receiver`.**
Ken Rockotab035122019-02-06 00:35:2446
47## Example: Defining a New Frame Interface
48
49Let's apply this to Chrome. Suppose we want to send a "Ping" message from a
50render frame to its corresponding `RenderFrameHostImpl` instance in the browser
51process. We need to define a nice mojom interface for this purpose, create a
52pipe to use that interface, and then plumb one end of the pipe to the right
53place so the sent messages can be received and processed there. This section
54goes through that process in detail.
55
56### Defining the Interface
57
58The first step involves creating a new `.mojom` file with an interface
59definition, like so:
60
61``` cpp
62// src/example/public/mojom/ping_responder.mojom
63module example.mojom;
64
65interface PingResponder {
66 // Receives a "Ping" and responds with a random integer.
Ken Rockota0cb6cf92019-03-26 16:40:4267 Ping() => (int32 random);
Ken Rockotab035122019-02-06 00:35:2468};
69```
70
71This should have a corresponding build rule to generate C++ bindings for the
72definition here:
73
74``` python
75# src/example/public/mojom/BUILD.gn
Ken Rockota0cb6cf92019-03-26 16:40:4276import("//mojo/public/tools/bindings/mojom.gni")
Ken Rockotab035122019-02-06 00:35:2477mojom("mojom") {
78 sources = [ "ping_responder.mojom" ]
79}
80```
81
82### Creating the Pipe
83
84Now let's create a message pipe to use this interface.
85
86*** aside
87As a general rule and as a matter of convenience when
Darwin Huangb4bd2452019-10-08 22:56:0488using Mojo, the *client* of an interface (*i.e.* the `Remote` side) is
Ken Rockotab035122019-02-06 00:35:2489typically the party who creates a new pipe. This is convenient because the
Darwin Huangb4bd2452019-10-08 22:56:0490`Remote` may be used to start sending messages immediately without waiting
Ken Rockotab035122019-02-06 00:35:2491for the InterfaceRequest endpoint to be transferred or bound anywhere.
92***
93
94This code would be placed somewhere in the renderer:
95
96```cpp
Darwin Huangb4bd2452019-10-08 22:56:0497// src/third_party/blink/example/public/ping_responder.h
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:4098mojo::Remote<example::mojom::PingResponder> ping_responder;
99mojo::PendingReceiver<example::mojom::PingResponder> receiver =
100 ping_responder.BindNewPipeAndPassReceiver();
Ken Rockotab035122019-02-06 00:35:24101```
102
Darwin Huangb4bd2452019-10-08 22:56:04103In this example, ```ping_responder``` is the `Remote`, and ```receiver```
104is a `PendingReceiver`, which is a `Receiver` precursor that will eventually
105be turned into a `Receiver`. `BindNewPipeAndPassReceiver` is the most common way to create
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40106a message pipe: it yields the `PendingReceiver` as the return
Ken Rockotab035122019-02-06 00:35:24107value.
108
109*** aside
Darwin Huangb4bd2452019-10-08 22:56:04110NOTE: A `PendingReceiver` doesn't actually **do** anything. It is an
Ken Rockotab035122019-02-06 00:35:24111inert holder of a single message pipe endpoint. It exists only to make its
112endpoint more strongly-typed at compile-time, indicating that the endpoint
Darwin Huangb4bd2452019-10-08 22:56:04113expects to be bound by a `Receiver` of the same interface type.
Ken Rockotab035122019-02-06 00:35:24114***
115
116### Sending a Message
117
Darwin Huangb4bd2452019-10-08 22:56:04118Finally, we can call the `Ping()` method on our `Remote` to send a message:
Ken Rockotab035122019-02-06 00:35:24119
120```cpp
Darwin Huangb4bd2452019-10-08 22:56:04121// src/third_party/blink/example/public/ping_responder.h
Ken Rockotab035122019-02-06 00:35:24122ping_responder->Ping(base::BindOnce(&OnPong));
123```
124
125*** aside
Darwin Huangb4bd2452019-10-08 22:56:04126**IMPORTANT:** If we want to receive the response, we must keep the
Ken Rockotab035122019-02-06 00:35:24127`ping_responder` object alive until `OnPong` is invoked. After all,
128`ping_responder` *owns* its message pipe endpoint. If it's destroyed then so is
129the endpoint, and there will be nothing to receive the response message.
130***
131
132We're almost done! Of course, if everything were this easy, this document
133wouldn't need to exist. We've taken the hard problem of sending a message from
134a renderer process to the browser process, and transformed it into a problem
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40135where we just need to take the `receiver` object from above and pass it to the
Darwin Huangb4bd2452019-10-08 22:56:04136browser process somehow where it can be turned into a `Receiver` that dispatches
Ken Rockotab035122019-02-06 00:35:24137its received messages.
138
Darwin Huangb4bd2452019-10-08 22:56:04139### Sending a `PendingReceiver` to the Browser
Ken Rockotab035122019-02-06 00:35:24140
Darwin Huangb4bd2452019-10-08 22:56:04141It's worth noting that `PendingReceiver`s (and message pipe endpoints in general)
Ken Rockotab035122019-02-06 00:35:24142are just another type of object that can be freely sent over mojom messages.
Darwin Huangb4bd2452019-10-08 22:56:04143The most common way to get a `PendingReceiver` somewhere is to pass it as a
Ken Rockotab035122019-02-06 00:35:24144method argument on some other already-connected interface.
145
146One such interface which we always have connected between a renderer's
147`RenderFrameImpl` and its corresponding `RenderFrameHostImpl` in the browser
148is
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40149[`BrowserInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/browser_interface_broker.mojom).
150This interface is a factory for acquiring other interfaces. Its `GetInterface`
151method takes a `GenericPendingReceiver`, which allows passing arbitrary
152interface receivers.
Ken Rockotab035122019-02-06 00:35:24153
154``` cpp
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40155interface BrowserInterfaceBroker {
156 GetInterface(mojo_base.mojom.GenericPendingReceiver receiver);
Ken Rockotab035122019-02-06 00:35:24157}
158```
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40159Since `GenericPendingReceiver` can be implicitly constructed from any specific
Darwin Huangb4bd2452019-10-08 22:56:04160`PendingReceiver`, it can call this method with the `receiver` object it created
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40161earlier via `BindNewPipeAndPassReceiver`:
Ken Rockotab035122019-02-06 00:35:24162
163``` cpp
164RenderFrame* my_frame = GetMyFrame();
Oksana Zhuravlovad4f1f5c2019-11-14 05:57:11165my_frame->GetBrowserInterfaceBroker().GetInterface(std::move(receiver));
Ken Rockotab035122019-02-06 00:35:24166```
167
Darwin Huangb4bd2452019-10-08 22:56:04168This will transfer the `PendingReceiver` endpoint to the browser process
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40169where it will be received by the corresponding `BrowserInterfaceBroker`
Ken Rockotab035122019-02-06 00:35:24170implementation. More on that below.
171
172### Implementing the Interface
173
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40174Finally, we need a browser-side implementation of our `PingResponder` interface.
Ken Rockotab035122019-02-06 00:35:24175
176```cpp
177#include "example/public/mojom/ping_responder.mojom.h"
178
179class PingResponderImpl : example::mojom::PingResponder {
180 public:
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40181 explicit PingResponderImpl(mojo::PendingReceiver<example::mojom::PingResponder> receiver)
182 : receiver_(this, std::move(receiver)) {}
Ken Rockotab035122019-02-06 00:35:24183
184 // example::mojom::PingResponder:
185 void Ping(PingCallback callback) override {
186 // Respond with a random 4, chosen by fair dice roll.
187 std::move(callback).Run(4);
188 }
189
190 private:
Charlie Hud4c0fe82019-10-08 19:48:13191 mojo::Receiver<example::mojom::PingResponder> receiver_;
Ken Rockotab035122019-02-06 00:35:24192
193 DISALLOW_COPY_AND_ASSIGN(PingResponderImpl);
194};
195```
196
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40197`RenderFrameHostImpl` owns an implementation of `BrowserInterfaceBroker`.
198When this implementation receives a `GetInterface` method call, it calls
199the handler previously registered for this specific interface.
Ken Rockotab035122019-02-06 00:35:24200
201``` cpp
202// render_frame_host_impl.h
203class RenderFrameHostImpl
204 ...
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40205 void GetPingResponder(mojo::PendingReceiver<example::mojom::PingResponder> receiver);
Ken Rockotab035122019-02-06 00:35:24206 ...
207 private:
208 ...
209 std::unique_ptr<PingResponderImpl> ping_responder_;
210 ...
211};
212
213// render_frame_host_impl.cc
214void RenderFrameHostImpl::GetPingResponder(
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40215 mojo::PendingReceiver<example::mojom::PingResponder> receiver) {
216 ping_responder_ = std::make_unique<PingResponderImpl>(std::move(receiver));
217}
218
219// browser_interface_binders.cc
220void PopulateFrameBinders(RenderFrameHostImpl* host,
221 service_manager::BinderMap* map) {
222...
223 // Register the handler for PingResponder.
224 map->Add<example::mojom::PingResponder>(base::BindRepeating(
225 &RenderFrameHostImpl::GetPingResponder, base::Unretained(host)));
Ken Rockotab035122019-02-06 00:35:24226}
227```
228
229And we're done. This setup is sufficient to plumb a new interface connection
230between a renderer frame and its browser-side host object!
231
232Assuming we kept our `ping_responder` object alive in the renderer long enough,
233we would eventually see its `OnPong` callback invoked with the totally random
234value of `4`, as defined by the browser-side implementation above.
235
236## Services Overview &amp; Terminology
237The previous section only scratches the surface of how Mojo IPC is used in
238Chromium. While renderer-to-browser messaging is simple and possibly the most
239prevalent usage by sheer code volume, we are incrementally decomposing the
240codebase into a set of services with a bit more granularity than the traditional
241Content browser/renderer/gpu/utility process split.
242
243A **service** is a self-contained library of code which implements one or more
244related features or behaviors and whose interaction with outside code is done
Ken Rockot216eb5d2020-02-19 17:09:55245*exclusively* through Mojo interface connections, typically brokered by the
246browser process.
Ken Rockotab035122019-02-06 00:35:24247
Ken Rockot216eb5d2020-02-19 17:09:55248Each service defines and implements a main Mojo interface which can be used
249by the browser to manage an instance of the service.
Ken Rockotab035122019-02-06 00:35:24250
251## Example: Building a Simple Out-of-Process Service
252
Ken Rockot216eb5d2020-02-19 17:09:55253There are multiple steps typically involved to get a new service up and running
254in Chromium:
Ken Rockotab035122019-02-06 00:35:24255
Ken Rockot216eb5d2020-02-19 17:09:55256- Define the main service interface and implementation
257- Hook up the implementation in out-of-process code
258- Write some browser logic to launch a service process
Ken Rockotab035122019-02-06 00:35:24259
260This section walks through these steps with some brief explanations. For more
261thorough documentation of the concepts and APIs used herein, see the
Ken Rockotab035122019-02-06 00:35:24262[Mojo](/mojo/README.md) documentation.
263
264### Defining the Service
265
266Typically service definitions are placed in a `services` directory, either at
267the top level of the tree or within some subdirectory. In this example, we'll
268define a new service for use by Chrome specifically, so we'll define it within
269`//chrome/services`.
270
271We can create the following files. First some mojoms:
272
273``` cpp
Ken Rockot216eb5d2020-02-19 17:09:55274// src/chrome/services/math/public/mojom/math_service.mojom
Ken Rockotab035122019-02-06 00:35:24275module math.mojom;
276
Ken Rockot216eb5d2020-02-19 17:09:55277interface MathService {
Ken Rockotab035122019-02-06 00:35:24278 Divide(int32 dividend, int32 divisor) => (int32 quotient);
279};
280```
281
282``` python
283# src/chrome/services/math/public/mojom/BUILD.gn
Ken Rockota0cb6cf92019-03-26 16:40:42284import("//mojo/public/tools/bindings/mojom.gni")
Ken Rockotab035122019-02-06 00:35:24285
286mojom("mojom") {
287 sources = [
Ken Rockot216eb5d2020-02-19 17:09:55288 "math_service.mojom",
Ken Rockotab035122019-02-06 00:35:24289 ]
290}
291```
292
Ken Rockot216eb5d2020-02-19 17:09:55293Then the actual `MathService` implementation:
Ken Rockotab035122019-02-06 00:35:24294
295``` cpp
296// src/chrome/services/math/math_service.h
Ken Rockotab035122019-02-06 00:35:24297#include "base/macros.h"
Ken Rockot216eb5d2020-02-19 17:09:55298#include "chrome/services/math/public/mojom/math_service.mojom.h"
Ken Rockotab035122019-02-06 00:35:24299
300namespace math {
301
Ken Rockot216eb5d2020-02-19 17:09:55302class MathService : public mojom::MathService {
Ken Rockotab035122019-02-06 00:35:24303 public:
Ken Rockot216eb5d2020-02-19 17:09:55304 explicit MathService(mojo::PendingReceiver<mojom::MathService> receiver);
Ken Rockotab035122019-02-06 00:35:24305 ~MathService() override;
306
307 private:
Ken Rockot216eb5d2020-02-19 17:09:55308 // mojom::MathService:
Ken Rockotab035122019-02-06 00:35:24309 void Divide(int32_t dividend,
310 int32_t divisor,
311 DivideCallback callback) override;
312
Ken Rockot216eb5d2020-02-19 17:09:55313 mojo::Receiver<mojom::MathService> receiver_;
Ken Rockotab035122019-02-06 00:35:24314
315 DISALLOW_COPY_AND_ASSIGN(MathService);
316};
317
318} // namespace math
319```
320
321``` cpp
322// src/chrome/services/math/math_service.cc
323#include "chrome/services/math/math_service.h"
324
325namespace math {
326
Ken Rockot216eb5d2020-02-19 17:09:55327MathService::MathService(mojo::PendingReceiver<mojom::MathService> receiver)
328 : receiver_(this, std::move(receiver)) {}
Ken Rockotab035122019-02-06 00:35:24329
330MathService::~MathService() = default;
331
Ken Rockotab035122019-02-06 00:35:24332void MathService::Divide(int32_t dividend,
333 int32_t divisor,
334 DivideCallback callback) {
335 // Respond with the quotient!
Oksana Zhuravlova0941c08d2019-05-03 20:46:33336 std::move(callback).Run(dividend / divisor);
Ken Rockotab035122019-02-06 00:35:24337}
338
339} // namespace math
340```
341
342``` python
343# src/chrome/services/math/BUILD.gn
344
345source_set("math") {
346 sources = [
347 "math.cc",
348 "math.h",
349 ]
350
351 deps = [
352 "//base",
353 "//chrome/services/math/public/mojom",
Ken Rockotab035122019-02-06 00:35:24354 ]
355}
356```
357
Ken Rockot216eb5d2020-02-19 17:09:55358Now we have a fully defined `MathService` implementation that we can make
359available in- or out-of-process.
Ken Rockotab035122019-02-06 00:35:24360
361### Hooking Up the Service Implementation
362
Ken Rockot216eb5d2020-02-19 17:09:55363For an out-of-process Chrome service, we simply register a factory function
364in [`//chrome/utility/services.cc`](https://cs.chromium.org/chromium/src/chrome/utility/services.cc).
Ken Rockotab035122019-02-06 00:35:24365
366``` cpp
Ken Rockot216eb5d2020-02-19 17:09:55367auto RunMathService(mojo::PendingReceiver<math::mojom::MathService> receiver) {
368 return std::make_unique<math::MathService>(std::move(receiver));
Ken Rockotab035122019-02-06 00:35:24369}
Ken Rockot216eb5d2020-02-19 17:09:55370
371mojo::ServiceFactory* GetMainThreadServiceFactory() {
372 // Existing factories...
373 static base::NoDestructor<mojo::ServiceFactory> factory {
374 RunFilePatcher,
375 RunUnzipper,
376
377 // We add our own factory to this list
378 RunMathService,
379 //...
Ken Rockotab035122019-02-06 00:35:24380```
381
Ken Rockot216eb5d2020-02-19 17:09:55382With this done, it is now possible for the browser process to launch new
383out-of-process instances of MathService.
Ken Rockotab035122019-02-06 00:35:24384
Ken Rockot216eb5d2020-02-19 17:09:55385### Launching the Service
386
387If you're running your service in-process, there's really nothing interesting
388left to do. You can instantiate the service implementation just like any other
389object, yet you can also talk to it via a Mojo Remote as if it were
390out-of-process.
391
392To launch an out-of-process service instance after the hookup performed in the
393previous section, use Content's
394[`ServiceProcessHost`](https://cs.chromium.org/chromium/src/content/public/browser/service_process_host.h?rcl=e7a1f6c9a24f3151c875598174a05167fb12c5d5&l=47)
395API:
Ken Rockotab035122019-02-06 00:35:24396
397``` cpp
Ken Rockot216eb5d2020-02-19 17:09:55398mojo::Remote<math::mojom::MathService> math_service =
399 content::ServiceProcessHost::Launch<math::mojom::MathService>(
400 content::ServiceProcessHost::LaunchOptions()
401 .WithSandboxType(content::SandboxType::kUtility)
402 .WithDisplayName("Math!")
403 .Pass());
Ken Rockotab035122019-02-06 00:35:24404```
405
Ken Rockot216eb5d2020-02-19 17:09:55406Except in the case of crashes, the launched process will live as long as
407`math_service` lives. As a corollary, you can force the process to be torn
408down by destroying (or resetting) `math_service`.
Ken Rockotab035122019-02-06 00:35:24409
Ken Rockot216eb5d2020-02-19 17:09:55410We can now perform an out-of-process division:
Ken Rockotab035122019-02-06 00:35:24411
412``` cpp
Ken Rockot216eb5d2020-02-19 17:09:55413// NOTE: As a client, we do not have to wait for any acknowledgement or
414// confirmation of a connection. We can start queueing messages immediately and
415// they will be delivered as soon as the service is up and running.
416math_service->Divide(
Ken Rockotab035122019-02-06 00:35:24417 42, 6, base::BindOnce([](int32_t quotient) { LOG(INFO) << quotient; }));
418```
Oksana Zhuravlova0941c08d2019-05-03 20:46:33419*** aside
Mario Sanchez Prada7dead3e2019-12-20 18:46:38420NOTE: To ensure the execution of the response callback, the
Ken Rockot216eb5d2020-02-19 17:09:55421`mojo::Remote<math::mojom::MathService>` object must be kept alive (see
Oksana Zhuravlova0941c08d2019-05-03 20:46:33422[this section](/mojo/public/cpp/bindings/README.md#A-Note-About-Endpoint-Lifetime-and-Callbacks)
423and [this note from an earlier section](#sending-a-message)).
424***
Ken Rockotab035122019-02-06 00:35:24425
Ken Rockotab035122019-02-06 00:35:24426## Content-Layer Services Overview
427
Ken Rockot216eb5d2020-02-19 17:09:55428### Interface Brokers
Ken Rockotab035122019-02-06 00:35:24429
Oksana Zhuravlovaee1afd12020-02-15 00:47:27430We define an explicit mojom interface with a persistent connection
431between a renderer's frame object and the corresponding
432`RenderFrameHostImpl` in the browser process.
Ken Rockotab035122019-02-06 00:35:24433This interface is called
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40434[`BrowserInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/browser_interface_broker.mojom?rcl=09aa5ae71649974cae8ad4f889d7cd093637ccdb&l=11)
435and is fairly easy to work with: you add a new method on `RenderFrameHostImpl`:
Ken Rockotab035122019-02-06 00:35:24436
437``` cpp
438void RenderFrameHostImpl::GetGoatTeleporter(
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40439 mojo::PendingReceiver<magic::mojom::GoatTeleporter> receiver) {
440 goat_teleporter_receiver_.Bind(std::move(receiver));
Ken Rockotab035122019-02-06 00:35:24441}
442```
443
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40444and register this method in `PopulateFrameBinders` function in `browser_interface_binders.cc`,
445which maps specific interfaces to their handlers in respective hosts:
446
447``` cpp
448// //content/browser/browser_interface_binders.cc
449void PopulateFrameBinders(RenderFrameHostImpl* host,
450 service_manager::BinderMap* map) {
451...
452 map->Add<magic::mojom::GoatTeleporter>(base::BindRepeating(
453 &RenderFrameHostImpl::GetGoatTeleporter, base::Unretained(host)));
454}
455```
456
Oksana Zhuravlovaee1afd12020-02-15 00:47:27457TODO: add information about workers and embedders.
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40458
Ken Rockotab035122019-02-06 00:35:24459## Additional Support
460
461If this document was not helpful in some way, please post a message to your
462friendly
463[[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
464or
465[[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
466mailing list.