blob: f98a39228d0b8d46d7d2610bef0439833ba1e8ce [file] [log] [blame] [view]
Ken Rockotab035122019-02-06 00:35:241# Converting Legacy IPC to Mojo
2
3[TOC]
4
5## Overview
6
7A number of IPC messages sent (primarily between the browser and renderer
8processes) are still defined using Chrome's old IPC system in `//ipc`. This
9system uses
10[`base::Pickle`](https://cs.chromium.org/chromium/src/base/pickle.h?rcl=8b7842262ee1239b1f3ae20b9c851748ef0b9a8b&l=128)
11as the basis for message serialization and is supported by a number if `IPC_*`
12preprocessor macros defined in `//ipc` and used around the source tree.
13
14There is an ongoing, distributed effort to get these messages converted to Mojo
15interface messages. Messages that still need to be converted are tracked in two
16spreadsheets:
17
18- [Chrome IPC to Mojo migration](https://docs.google.com/spreadsheets/d/1pGWX_wxGdjAVtQOmlDDfhuIc3Pbwg9FtvFXAXYu7b7c/edit#gid=0) for non-web platform messages
19- [Mojoifying Platform Features](https://docs.google.com/spreadsheets/d/1VIINt17Dg2cJjPpoJ_HY3HI0uLpidql-1u8pBJtpbGk/edit#gid=1603373208) for web platform messages
20
21This document is concerned primarily with rote conversion of legacy IPC messages
22to Mojo interface messages. If you are considering more holistic refactoring and
23better isolation of an entire subsystem of the browser, you may consider
24[servicifying](servicification.md) the feature instead of merely converting its
25IPCs.
26
27See other [Mojo & Services](/docs/README.md#Mojo-Services) documentation
28for introductory guides, API references, and more.
29
30## Legacy IPC Concepts
31
32Each Content child process has a single **`IPC::Channel`** implementation going
33between it and the browser process, and this is used as the sole two-way FIFO
34to send legacy IPC messages between the processes.
35
36There are two fundamental types of legacy IPC messages: **control** messages,
37defined via `IPC_MESSAGE_CONTROLn` macros (where `n` is some small integer) and
38**routed** messages defined via `IPC_MESSAGE_ROUTEDn` macros.
39
40Control messages generally go between a browser-side process host (*e.g.*,
41`RenderProcessHost` or `GpuProcessHost`) and the child-side `ChildThreadImpl`
42subclass. All of these classes implement `IPC::Sender` and thus have a `Send`
43method for sending a control message to their remote counterpart, and they
44implement `IPC::Listener` to receive incoming control messages via
45`OnMessageReceived`.
46
47Routed messages are relegated to **routes** which have arbitrary meaning
48determined by their use within a given process. For example, renderers use
49routes to isolate messages scoped to individual render frames, and so such
50routed messages will travel between a `RenderFrameHostImpl` and its
51corresponding `RenderFrameImpl`, both of which also implement `IPC::Sender` and
52`IPC::Listener`.
53
54## Mojo Interfaces as Routes
55
56Routed messages in the old IPC system always carry a **routing ID** to identify
57to the receiving endpoint which routed object (*e.g.* which `RenderFrameImpl`
58or `RenderViewImpl` or whatever) the message is targeting. Each endpoint is thus
59required to do some additional book-keeping to track what each routing ID means.
60
61Mojo interfaces obviate the need for routing IDs, as new "routes" can be
62established by simply creating a new interface pipe and passing one endpoint to
63something which knows how to bind it.
64
65When thinking about an IPC message conversion to Mojo, it's important to
66consider whether the message is a control message or a routed message, as this
67determines where you might find an existing Mojo interface to carry your
68message, or where you will want to add a new end-to-end Mojo interface for that
69purpose. This can mean the difference between a single per-process interface
70going between each `RenderProcessHostImpl` and its corresponding
71`RenderThreadImpl`, vs a per-frame interface going between each
72`RenderFrameHostImpl` and its corresponding `RenderFrameImpl`.
73
74## Ordering Considerations
75
76One **very important** consideration when doing IPC conversions is the relative
77ordering of IPC-driven operations. With the old IPC system, because every
78message between two processes is globally ordered, it is quite easy for parts
79of the system to (intentionally or often unintentionally) rely on strict
80ordering guarantees.
81
82For example, imagine a `WebContentsObserver` in the browser processes observes
83a frame navigation and immediately sends an IPC message to the frame to
84configure some new behavior. The implementation may be inadvertently relying on
85this message arriving *before* some other tangentially related message sent to
86the same frame shortly after the same navigation event.
87
88Mojo does not (and in fact cannot) make any strict ordering guarantees between
89separate message pipes, as message pipes may be freely moved across process
90boundaries and thus cannot necessarily share a common FIFO at all times.
91
92If the two messages described above were moved to separate Mojo interfaces on
93separate message pipes, renderer behavior could break as the first message may
94arrive after the second message.
95
96The best solution to this problem is to rethink the IPC surface and/or
97implementation on either side to eliminate ordering dependencies between two
98interfaces that otherwise seem to be logically distinct. Failing that, Mojo's
99solution to this problem is to support
100[**associated interfaces**](/mojo/public/tools/bindings/README.md#Associated-Interfaces).
101In a nutshell, these allow multiple distinct interfaces to be multiplexed over
102a shared message pipe.
103
104## Channel-Associated Interfaces
105
106The previous section mentions **associated interfaces** as a general-purpose
107solution for establishing a mutual FIFO between multiple logical Mojo interfaces
108by having them share a single message pipe.
109
110In Chrome, the `IPC::Channel` which carries all legacy IPC messages between
111two processes is itself a Mojo message pipe. We provide a mechanism for
112associating arbitrary Mojo interfaces with this pipe, which means messages can
113be converted to Mojo while preserving strict FIFO with respect to other legacy
114IPC messages. Such interfaces are designated in Chrome parlance as
115**Channel-associated interfaces**.
116
117*** aside
118**NOTE:** Channel-associated interface acquisition is not constrained by the
119Service Manager in any way, so security reviewers need to be careful to inspect
120new additions and uses of such interfaces.
121***
122
123Usage of Channel-associated interfaces should be rare but is considered a
124reasonable intermediate solution for incremental IPC conversions where it would
125be too risky or noisy to convert a large IPC surface all at once, but it would
126also be impossible to split the IPC surface between legacy IPC and a dedicated
127Mojo interface pipe without introducing timing bugs.
128
129At this point in Chrome's development, practical usage of Channel-associated
130interfaces is restricted to the `IPC::Channel` between the browser process and
131a renderer process, as this is the most complex IPC surface with the most
132implicit ordering dependencies. A few simple APIs exist to support this.
133
134`RenderProcessHostImpl` owns an `IPC::Channel` to its corresponding
135`RenderThreadImpl` in the render process. This object has a
136`GetRemoteAssociatedInterfaces` method which can be used to pass arbitrary
137associated interface requests:
138
139``` cpp
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40140mojo::PendingAssociatedRemote<magic::mojom::GoatTeleporter> teleporter;
141channel_->GetRemoteAssociatedInterfaces()->GetInterface(teleporter.BindNewEndpointAndPassReceiver());
Ken Rockotab035122019-02-06 00:35:24142
143// These messages are all guaranteed to arrive in the same order they were sent.
144channel_->Send(new FooMsg_SomeLegacyIPC);
145teleporter->TeleportAllGoats();
146channel_->Send(new FooMsg_AnotherLegacyIPC);
147```
148
149Likewise, `ChildThreadImpl` has an `IPC::Channel` that can be used in the same
150way to send such messages back to the browser.
151
152To receive and bind incoming Channel-associated interface requests, the above
153objects also implement `IPC::Listener::OnAssociatedInterfaceRequest`.
154
155For supplementation of routed messages, both `RenderFrameHostImpl` and
156`RenderFrameImpl` define a `GetRemoteAssociatedInterfaces` method which works
157like the one on `IPC::Channel`, and both objects also implement
158`IPC::Listener::OnAssociatedInterfaceRequest` for processing incoming associated
159interface requests specific to their own frame.
160
161There are some example conversion CLs which use Channel-associated interfaces
162[here](https://codereview.chromium.org/2381493003) and
163[here](https://codereview.chromium.org/2400313002).
164
165## Deciding How to Approach a Conversion
166
167There are a few questions you should ask before embarking upon any IPC message
168conversion journey, and there are many potential approaches to consider. The
169right one depends on context.
170
171Note that this section assumes the message is traveling between the browser
172process and a renderer process. Other cases are rare and developers may wish to
173consult
174[[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
175before proceeding with them. Otherwise, apply the following basic algorithm to
176decide how to proceed:
177
178- General note: If the message is a reply to some other message (typically these
179 take a "request ID" argument), see the note about message replies at the
180 bottom of this section.
181- Consider whether or not the message makes sense as part of the IPC surface of
182 a new or existing service somewhere in `//services` or `//chrome/services`,
183 *etc.* This is less and less likely to be the case as time goes on, as many
184 remaining IPC conversions are quite narrowly dealing with specific
185 browser/renderer details rather than the browser's supporting subsystems. If
186 defining a new service, you may wish to consult some of the other
187 [Mojo &amp; Services documentation](/docs/README.md#Mojo-Services) first.
188- If the message is an `IPC_MESSAGE_CONTROL` message:
189 - If there are likely to be strict ordering requirements between this
190 message and other legacy IPC or Channel-associated interface messages,
191 consider using a new or existing
192 [Channel-associated interface](#Channel-Associated-Interfaces) between
193 `RenderProcessHostImpl` and `RenderThreadImpl`.
194 - If the message is sent from a renderer to the browser:
195 - If an existing interface is bound by `RenderProcessHostImpl` and
196 requested through `RenderThread`'s Connector and seems to be a good
197 fit for the message, add the equivalent Mojo message to that
198 interface.
199 - If no such interface exists, consider adding one for this message and
200 any related messages.
201 - If the message is sent from the browser to a renderer:
202 - If an existing interface is bound by `RenderThreadImpl` and requested
203 through a `BrowserContext` Connector referencing a specific
204 `RenderProcessHost` [identity](https://cs.chromium.org/chromium/src/content/public/browser/render_process_host.h?rcl=1497b88b7d6400a2a5cced258df03d53800d7848&l=327),
205 and the interface seems to be a good fit for the message, add the
206 equivalent Mojo message to that interface.
207 - If no such interface exists, consider adding one for this message and
208 any related messages.
209- If the message is an `IPC_MESSAGE_ROUTED` message:
210 - Determine what the routing endpoints are. If they are
211 `RenderFrameHostImpl` and `RenderFrameImpl`:
212 - If there are likely to be strict ordering requirements between this
213 message and other legacy IPC or Channel-associated interface messages,
214 consider using a new or existing
215 [Channel-associated interface](#Channel-Associated-Interfaces) between
216 `RenderFrameHostImpl` and `RenderFrameImpl`.
217 - If the message is sent from a renderer to the browser:
218 - If an existing interface is bound by `RenderFrameHostImpl` and
Oksana Zhuravlovab685db62020-02-21 20:14:01219 acquired via `RenderFrame::GetBrowserInterfaceBroker` and the interface seems
Ken Rockotab035122019-02-06 00:35:24220 to be a good fit for this message, add the equivalent Mojo message
221 to that interface.
Oksana Zhuravlovacf170052019-09-23 19:56:27222 - If no such interface exists, consider adding one and registering it
223 with `RenderFrameHostImpl`'s `BrowserInterfaceBroker`. See the
Ken Rockotab035122019-02-06 00:35:24224 [simple example](/docs/mojo_and_services.md#Example_Defining-a-New-Frame-Interface)
Oksana Zhuravlovacf170052019-09-23 19:56:27225 in the "Intro to Mojo & Services" document.
Ken Rockotab035122019-02-06 00:35:24226 - If the message is sent from the browser to a renderer, consider
227 adding a Mojo equivalent to the `content.mojom.Frame` interface
228 defined
229 [here](https://cs.chromium.org/chromium/src/content/common/frame.mojom?rcl=138b66744ee9ee853cbb0ae8437b71eaa1fafaa9&l=42).
230 - If the routing endpoints are **not** frame objects (for example, they may
231 be `RenderView`/`RenderViewHost` objects), this is a special case which
232 does not yet have an easy conversion approach readily available. Contact
233 [[email protected]](https://groups.google.com/a/chromium.org/forum#!forum/chromium-mojo)
234 to propose or discuss options.
235
Oksana Zhuravlova355fa642019-02-15 22:21:04236*** aside
237**NOTE**: If you are converting a sync IPC, see the section on
238[Synchronous Calls](/mojo/public/cpp/bindings/README.md#Synchronous-Calls)
239in the Mojo documentation.
240***
241
Ken Rockotab035122019-02-06 00:35:24242### Dealing With Replies
243
244If the message is a **reply**, meaning it has a "request ID" which correlates it
245to a prior message in the opposite direction, consider converting the
246**request** message following the algorithm above. Unlike with legacy IPC, Mojo
247messages support replies as a first-class concept. So for example if you have:
248
249``` cpp
250IPC_CONTROL_MESSAGE2(FooHostMsg_DoTheThing,
251 int /* request_id */,
252 std::string /* name */);
253IPC_CONTROL_MESSAGE2(FooMsg_DidTheThing,
254 int /* request_id */,
255 bool /* success */);
256```
257
258You should consider defining an interface `Foo` which is bound in
259`RenderProcessHostImpl` and acquired from `RenderThreadImpl`, with the following
260mojom definition:
261
262``` cpp
263interface Foo {
264 DoTheThing(string name) => (bool success);
265};
266```
Oksana Zhuravlova87b225a2019-03-07 01:08:03267See [Receiving responses](/mojo/public/cpp/bindings/README.md#receiving-responses)
268for more information.
Ken Rockotab035122019-02-06 00:35:24269
270## Repurposing `IPC::ParamTraits` and `IPC_STRUCT*` Invocations
271
272Occasionally it is useful to do partial IPC conversions, where you want to
273convert a message to a Mojo interface method but you don't want to necessarily
274convert every structure passed by the message. In this case, you can leverage
275Mojo's
Oksana Zhuravlovaa77a9a12021-05-07 18:02:10276[type-mapping](https://chromium.googlesource.com/chromium/src/+/main/mojo/public/cpp/bindings/README.md#Type-Mapping)
Ken Rockotab035122019-02-06 00:35:24277system to repurpose existing `IPC::ParamTraits`.
278
279*** aside
280**NOTE**: Although in some cases `IPC::ParamTraits<T>` specializations are
281defined manually in library code, the `IPC_STRUCT*` macro helpers also define
282`IPC::ParamTraits<T>` specializations under the hood. All advice in this section
283pertains to both kinds of definitions.
284***
285
286If a mojom struct is declared without a struct body and is tagged with
287`[Native]`, and a corresponding typemap is provided for the struct, the emitted
288C++ bindings will -- as if by magic -- replace the mojom type with the
289typemapped C++ type and will internally use the existing `IPC::ParamTraits<T>`
290specialization for that type in order to serialize and deserialize the struct.
291
292For example, given the
293[`resource_messages.h`](https://cs.chromium.org/chromium/src/content/common/resource_messages.h?rcl=2e7a430d8d88222c04ab3ffb0a143fa85b3cec5b&l=215) header
294which defines an IPC mapping for `content::ResourceRequest`:
295
296``` cpp
297IPC_STRUCT_TRAITS_BEGIN(content::ResourceRequest)
298 IPC_STRUCT_TRAITS_MEMBER(method)
299 IPC_STRUCT_TRAITS_MEMBER(url)
300 // ...
301IPC_STRUCT_TRAITS_END()
302```
303
304and the
305[`resource_request.h`](https://cs.chromium.org/chromium/src/content/common/resource_request.h?rcl=dce9e476a525e4ff0304787935dc1a8c38392ac8&l=32) header
306which actually defines the `content::ResourceRequest` type:
307
308``` cpp
309namespace content {
310
311struct CONTENT_EXPORT ResourceRequest {
312 // ...
313};
314
315} // namespace content
316```
317
318we can declare a corresponding "native" mojom struct:
319
320``` cpp
321module content.mojom;
322
323[Native]
324struct URLRequest;
325```
326
327and add a typemap like
328[`url_request.typemap`](https://cs.chromium.org/chromium/src/content/common/url_request.typemap?rcl=4b5963fa744a706398f8f06a4cbbf70d7fa3213d)
329to define how to map between them:
330
331``` python
332mojom = "//content/public/common/url_loader.mojom"
333public_headers = [ "//content/common/resource_request.h" ]
334traits_headers = [ "//content/common/resource_messages.h" ]
335...
336type_mappings = [ "content.mojom.URLRequest=content::ResourceRequest" ]
337```
338
339Note specifically that public_headers includes the definition of the native C++
340type, and traits_headers includes the definition of the legacy IPC traits.
341
342As a result of all this, other mojom files can now reference
343`content.mojom.URLRequest` as a type for method parameters and other struct
344fields, and the generated C++ bindings will represent those values exclusively
345as `content::ResourceRequest` objects.
346
347This same basic approach can be used to leverage existing `IPC_ENUM_TRAITS` for
348invocations for `[Native]` mojom enum aliases.
349
350*** aside
351**NOTE:** Use of `[Native]` mojom definitions is strictly limited to C++
352bindings. If a mojom message depends on such definitions, it cannot be sent or
353received by other language bindings. This feature also depends on continued
354support for legacy IPC serialization and all uses of it should therefore be
355treated as technical debt.
356***
357
Oksana Zhuravlova4f3692b2019-02-08 21:00:58358## Blink-Specific Advice
359
360### Variants
361Let's assume we have a mojom file such as this:
362
363``` cpp
364module example.mojom;
365
366interface Foo {
367 SendData(string param1, array<int32> param2);
368};
369```
370
371The following GN snippet will generate two concrete targets: `example` and
372`example_blink`:
373
374```
375mojom("example") {
376 sources = [ "example.mojom" ]
377}
378```
379
380The target `example` will generate Chromium-style C++ bindings using STL types:
381
382``` cpp
383// example.mojom.h
384namespace example {
385namespace mojom {
386
387class Example {
388 virtual void SendArray(const std::string& param1, const std::vector<int32_t>& param2) = 0;
389}
390
391} // namespace mojom
392} // namespace example
393```
394
395The target `example_blink` will generate Blink-style C++ bindings using WTF types:
396
397``` cpp
398// example.mojom-blink.h
399namespace example {
400namespace mojom {
401namespace blink {
402
403class Example {
404 virtual void SendArray(const WTF::String& param1, const WTF::Vector<int32_t>& param2) = 0;
405}
406
407} // namespace blink
408} // namespace mojom
409} // namespace example
410```
411
412Thanks to these separate sets of bindings no work is necessary to convert types
413between Blink-style code and Chromium-style code. It is handled automatically
414during message serialization and deserialization.
415
416For more information about variants, see
417[this section](/mojo/public/cpp/bindings/README.md#Variants) of the C++ bindings
418documentation.
419
420### Binding callbacks
421
422Mojo methods that return a value take an instance of `base::OnceCallback`.
423Use `WTF::Bind()` and an appropriate wrapper function depending on the type of
424object and the callback.
425
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40426For garbage-collected (Oilpan) classes owning the `mojo::Remote`, it is recommended
Oksana Zhuravlova6fac3d132019-02-19 23:58:33427to use `WrapWeakPersistent(this)` for connection error handlers since they
428are not guaranteed to get called in a finite time period (wrapping the object
429with `WrapPersistent` in this case would cause memory leaks).
430
431If the response can be discarded in case the object is not alive by the time
432the response is received, use `WrapWeakPersistent(this)` for binding the response callback:
Oksana Zhuravlova4f3692b2019-02-08 21:00:58433
434``` cpp
Oksana Zhuravlova6fac3d132019-02-19 23:58:33435// src/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.cc
436sensor_.set_connection_error_handler(WTF::Bind(
437 &DeviceSensorEntry::HandleSensorError, WrapWeakPersistent(this)));
438sensor_->ConfigureReadingChangeNotifications(/*enabled=*/false);
439sensor_->AddConfiguration(
440 std::move(config), WTF::Bind(&DeviceSensorEntry::OnSensorAddConfiguration,
441 WrapWeakPersistent(this)));
442```
443
444Otherwise (for example, if the response callback is used to resolve a Promise),
445use `WrapPersistent(this)` to keep the object alive:
446
447``` cpp
448// src/third_party/blink/renderer/modules/nfc/nfc.cc
449ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
450...
451nfc_->CancelAllWatches(WTF::Bind(&NFC::OnRequestCompleted,
452 WrapPersistent(this),
453 WrapPersistent(resolver)));
Oksana Zhuravlova4f3692b2019-02-08 21:00:58454```
455
456Non-garbage-collected objects can use `WTF::Unretained(this)` for both response
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40457and error handler callbacks when the `mojo::Remote` is owned by the object bound
Oksana Zhuravlova4f3692b2019-02-08 21:00:58458to the callback or the object is guaranteed to outlive the Mojo connection for
459another reason. Otherwise a weak pointer should be used. However, it is not a
460common pattern since using Oilpan is recommended for all Blink code.
461
462### Implementing Mojo interfaces in Blink
463
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40464Only a `mojo::Receiver` or `mojo::ReceiverSet` should be used when implementing a
Oksana Zhuravlova4f3692b2019-02-08 21:00:58465Mojo interface in an Oilpan-managed object. The object must then have a pre-finalizer
466to close any open pipes when the object is about to be swept as lazy sweeping
467means that it may be invalid long before the destructor is called. This requires
468setup in both the object header and implementation.
469
470``` cpp
471// MyObject.h
472class MyObject : public GarbageCollected,
473 public example::mojom::blink::Example {
474 USING_PRE_FINALIZER(MyObject, Dispose);
475
476 public:
477 MyObject();
478 void Dispose();
479
480 // Implementation of example::mojom::blink::Example.
481
482 private:
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40483 mojo::Receiver<example::mojom::blink::Example> m_receiver{this};
Oksana Zhuravlova4f3692b2019-02-08 21:00:58484};
485
486// MyObject.cpp
487void MyObject::Dispose() {
Oksana Zhuravlova9f3b8ef2019-08-26 20:27:40488 m_receiver.Close();
Oksana Zhuravlova4f3692b2019-02-08 21:00:58489}
490```
491
492For more information about Blink's Garbage Collector, see
493[Blink GC API Reference](/third_party/blink/renderer/platform/heap/BlinkGCAPIReference.md).
494
495### Typemaps For Content and Blink Types
Ken Rockotab035122019-02-06 00:35:24496
497Using typemapping for messages that go between Blink and content browser code
498can sometimes be tricky due to things like dependency cycles or confusion over
499the correct place for some definition
500to live. There are some example CLs provided here, but feel free to also contact
501[[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
502with specific details if you encounter trouble.
503
504[This CL](https://codereview.chromium.org/2363533002) introduces a Mojom
505definition and typemap for `ui::WindowOpenDisposition` as a precursor to the
506IPC conversion below.
507
508The [follow-up CL](https://codereview.chromium.org/2363573002) uses that
509definition along with several other new typemaps (including native typemaps as
510described above) to convert the relatively large `ViewHostMsg_CreateWindow`
511message to Mojo.
512
513## Additional Support
514
515If this document was not helpful in some way, please post a message to your
516friendly
517[[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
518mailing list.