Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame^] | 1 | # Converting Legacy IPC to Mojo |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | ## Overview |
| 6 | |
| 7 | A number of IPC messages sent (primarily between the browser and renderer |
| 8 | processes) are still defined using Chrome's old IPC system in `//ipc`. This |
| 9 | system uses |
| 10 | [`base::Pickle`](https://cs.chromium.org/chromium/src/base/pickle.h?rcl=8b7842262ee1239b1f3ae20b9c851748ef0b9a8b&l=128) |
| 11 | as the basis for message serialization and is supported by a number if `IPC_*` |
| 12 | preprocessor macros defined in `//ipc` and used around the source tree. |
| 13 | |
| 14 | There is an ongoing, distributed effort to get these messages converted to Mojo |
| 15 | interface messages. Messages that still need to be converted are tracked in two |
| 16 | spreadsheets: |
| 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 | |
| 21 | This document is concerned primarily with rote conversion of legacy IPC messages |
| 22 | to Mojo interface messages. If you are considering more holistic refactoring and |
| 23 | better isolation of an entire subsystem of the browser, you may consider |
| 24 | [servicifying](servicification.md) the feature instead of merely converting its |
| 25 | IPCs. |
| 26 | |
| 27 | See other [Mojo & Services](/docs/README.md#Mojo-Services) documentation |
| 28 | for introductory guides, API references, and more. |
| 29 | |
| 30 | ## Legacy IPC Concepts |
| 31 | |
| 32 | Each Content child process has a single **`IPC::Channel`** implementation going |
| 33 | between it and the browser process, and this is used as the sole two-way FIFO |
| 34 | to send legacy IPC messages between the processes. |
| 35 | |
| 36 | There are two fundamental types of legacy IPC messages: **control** messages, |
| 37 | defined via `IPC_MESSAGE_CONTROLn` macros (where `n` is some small integer) and |
| 38 | **routed** messages defined via `IPC_MESSAGE_ROUTEDn` macros. |
| 39 | |
| 40 | Control messages generally go between a browser-side process host (*e.g.*, |
| 41 | `RenderProcessHost` or `GpuProcessHost`) and the child-side `ChildThreadImpl` |
| 42 | subclass. All of these classes implement `IPC::Sender` and thus have a `Send` |
| 43 | method for sending a control message to their remote counterpart, and they |
| 44 | implement `IPC::Listener` to receive incoming control messages via |
| 45 | `OnMessageReceived`. |
| 46 | |
| 47 | Routed messages are relegated to **routes** which have arbitrary meaning |
| 48 | determined by their use within a given process. For example, renderers use |
| 49 | routes to isolate messages scoped to individual render frames, and so such |
| 50 | routed messages will travel between a `RenderFrameHostImpl` and its |
| 51 | corresponding `RenderFrameImpl`, both of which also implement `IPC::Sender` and |
| 52 | `IPC::Listener`. |
| 53 | |
| 54 | ## Mojo Interfaces as Routes |
| 55 | |
| 56 | Routed messages in the old IPC system always carry a **routing ID** to identify |
| 57 | to the receiving endpoint which routed object (*e.g.* which `RenderFrameImpl` |
| 58 | or `RenderViewImpl` or whatever) the message is targeting. Each endpoint is thus |
| 59 | required to do some additional book-keeping to track what each routing ID means. |
| 60 | |
| 61 | Mojo interfaces obviate the need for routing IDs, as new "routes" can be |
| 62 | established by simply creating a new interface pipe and passing one endpoint to |
| 63 | something which knows how to bind it. |
| 64 | |
| 65 | When thinking about an IPC message conversion to Mojo, it's important to |
| 66 | consider whether the message is a control message or a routed message, as this |
| 67 | determines where you might find an existing Mojo interface to carry your |
| 68 | message, or where you will want to add a new end-to-end Mojo interface for that |
| 69 | purpose. This can mean the difference between a single per-process interface |
| 70 | going 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 | |
| 76 | One **very important** consideration when doing IPC conversions is the relative |
| 77 | ordering of IPC-driven operations. With the old IPC system, because every |
| 78 | message between two processes is globally ordered, it is quite easy for parts |
| 79 | of the system to (intentionally or often unintentionally) rely on strict |
| 80 | ordering guarantees. |
| 81 | |
| 82 | For example, imagine a `WebContentsObserver` in the browser processes observes |
| 83 | a frame navigation and immediately sends an IPC message to the frame to |
| 84 | configure some new behavior. The implementation may be inadvertently relying on |
| 85 | this message arriving *before* some other tangentially related message sent to |
| 86 | the same frame shortly after the same navigation event. |
| 87 | |
| 88 | Mojo does not (and in fact cannot) make any strict ordering guarantees between |
| 89 | separate message pipes, as message pipes may be freely moved across process |
| 90 | boundaries and thus cannot necessarily share a common FIFO at all times. |
| 91 | |
| 92 | If the two messages described above were moved to separate Mojo interfaces on |
| 93 | separate message pipes, renderer behavior could break as the first message may |
| 94 | arrive after the second message. |
| 95 | |
| 96 | The best solution to this problem is to rethink the IPC surface and/or |
| 97 | implementation on either side to eliminate ordering dependencies between two |
| 98 | interfaces that otherwise seem to be logically distinct. Failing that, Mojo's |
| 99 | solution to this problem is to support |
| 100 | [**associated interfaces**](/mojo/public/tools/bindings/README.md#Associated-Interfaces). |
| 101 | In a nutshell, these allow multiple distinct interfaces to be multiplexed over |
| 102 | a shared message pipe. |
| 103 | |
| 104 | ## Channel-Associated Interfaces |
| 105 | |
| 106 | The previous section mentions **associated interfaces** as a general-purpose |
| 107 | solution for establishing a mutual FIFO between multiple logical Mojo interfaces |
| 108 | by having them share a single message pipe. |
| 109 | |
| 110 | In Chrome, the `IPC::Channel` which carries all legacy IPC messages between |
| 111 | two processes is itself a Mojo message pipe. We provide a mechanism for |
| 112 | associating arbitrary Mojo interfaces with this pipe, which means messages can |
| 113 | be converted to Mojo while preserving strict FIFO with respect to other legacy |
| 114 | IPC 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 |
| 119 | Service Manager in any way, so security reviewers need to be careful to inspect |
| 120 | new additions and uses of such interfaces. |
| 121 | *** |
| 122 | |
| 123 | Usage of Channel-associated interfaces should be rare but is considered a |
| 124 | reasonable intermediate solution for incremental IPC conversions where it would |
| 125 | be too risky or noisy to convert a large IPC surface all at once, but it would |
| 126 | also be impossible to split the IPC surface between legacy IPC and a dedicated |
| 127 | Mojo interface pipe without introducing timing bugs. |
| 128 | |
| 129 | At this point in Chrome's development, practical usage of Channel-associated |
| 130 | interfaces is restricted to the `IPC::Channel` between the browser process and |
| 131 | a renderer process, as this is the most complex IPC surface with the most |
| 132 | implicit 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 |
| 137 | associated interface requests: |
| 138 | |
| 139 | ``` cpp |
| 140 | magic::mojom::GoatTeleporterAssociatedPtr teleporter; |
| 141 | channel_->GetRemoteAssociatedInterfaces()->GetInterface(&teleporter); |
| 142 | |
| 143 | // These messages are all guaranteed to arrive in the same order they were sent. |
| 144 | channel_->Send(new FooMsg_SomeLegacyIPC); |
| 145 | teleporter->TeleportAllGoats(); |
| 146 | channel_->Send(new FooMsg_AnotherLegacyIPC); |
| 147 | ``` |
| 148 | |
| 149 | Likewise, `ChildThreadImpl` has an `IPC::Channel` that can be used in the same |
| 150 | way to send such messages back to the browser. |
| 151 | |
| 152 | To receive and bind incoming Channel-associated interface requests, the above |
| 153 | objects also implement `IPC::Listener::OnAssociatedInterfaceRequest`. |
| 154 | |
| 155 | For supplementation of routed messages, both `RenderFrameHostImpl` and |
| 156 | `RenderFrameImpl` define a `GetRemoteAssociatedInterfaces` method which works |
| 157 | like the one on `IPC::Channel`, and both objects also implement |
| 158 | `IPC::Listener::OnAssociatedInterfaceRequest` for processing incoming associated |
| 159 | interface requests specific to their own frame. |
| 160 | |
| 161 | There 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 | |
| 167 | There are a few questions you should ask before embarking upon any IPC message |
| 168 | conversion journey, and there are many potential approaches to consider. The |
| 169 | right one depends on context. |
| 170 | |
| 171 | Note that this section assumes the message is traveling between the browser |
| 172 | process and a renderer process. Other cases are rare and developers may wish to |
| 173 | consult |
| 174 | [[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) |
| 175 | before proceeding with them. Otherwise, apply the following basic algorithm to |
| 176 | decide 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 & 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 |
| 219 | acquired either via `RenderFrame::GetRemoteInterfaces` or |
| 220 | `RenderFrame::GetDocumentInterfaceBroker` and the interface seems |
| 221 | to be a good fit for this message, add the equivalent Mojo message |
| 222 | to that interface. |
| 223 | - If no such interface exists, consider adding one and exposing it |
| 224 | via a new getter method on `DocumentInterfaceBroker`. See the |
| 225 | [simple example](/docs/mojo_and_services.md#Example_Defining-a-New-Frame-Interface) |
| 226 | earlier in this document. |
| 227 | - If the message is sent from the browser to a renderer, consider |
| 228 | adding a Mojo equivalent to the `content.mojom.Frame` interface |
| 229 | defined |
| 230 | [here](https://cs.chromium.org/chromium/src/content/common/frame.mojom?rcl=138b66744ee9ee853cbb0ae8437b71eaa1fafaa9&l=42). |
| 231 | - If the routing endpoints are **not** frame objects (for example, they may |
| 232 | be `RenderView`/`RenderViewHost` objects), this is a special case which |
| 233 | does not yet have an easy conversion approach readily available. Contact |
| 234 | [[email protected]](https://groups.google.com/a/chromium.org/forum#!forum/chromium-mojo) |
| 235 | to propose or discuss options. |
| 236 | |
| 237 | ### Dealing With Replies |
| 238 | |
| 239 | If the message is a **reply**, meaning it has a "request ID" which correlates it |
| 240 | to a prior message in the opposite direction, consider converting the |
| 241 | **request** message following the algorithm above. Unlike with legacy IPC, Mojo |
| 242 | messages support replies as a first-class concept. So for example if you have: |
| 243 | |
| 244 | ``` cpp |
| 245 | IPC_CONTROL_MESSAGE2(FooHostMsg_DoTheThing, |
| 246 | int /* request_id */, |
| 247 | std::string /* name */); |
| 248 | IPC_CONTROL_MESSAGE2(FooMsg_DidTheThing, |
| 249 | int /* request_id */, |
| 250 | bool /* success */); |
| 251 | ``` |
| 252 | |
| 253 | You should consider defining an interface `Foo` which is bound in |
| 254 | `RenderProcessHostImpl` and acquired from `RenderThreadImpl`, with the following |
| 255 | mojom definition: |
| 256 | |
| 257 | ``` cpp |
| 258 | interface Foo { |
| 259 | DoTheThing(string name) => (bool success); |
| 260 | }; |
| 261 | ``` |
| 262 | |
| 263 | ## Repurposing `IPC::ParamTraits` and `IPC_STRUCT*` Invocations |
| 264 | |
| 265 | Occasionally it is useful to do partial IPC conversions, where you want to |
| 266 | convert a message to a Mojo interface method but you don't want to necessarily |
| 267 | convert every structure passed by the message. In this case, you can leverage |
| 268 | Mojo's |
| 269 | [type-mapping](https://chromium.googlesource.com/chromium/src/+/master/mojo/public/cpp/bindings/README.md#Type-Mapping) |
| 270 | system to repurpose existing `IPC::ParamTraits`. |
| 271 | |
| 272 | *** aside |
| 273 | **NOTE**: Although in some cases `IPC::ParamTraits<T>` specializations are |
| 274 | defined manually in library code, the `IPC_STRUCT*` macro helpers also define |
| 275 | `IPC::ParamTraits<T>` specializations under the hood. All advice in this section |
| 276 | pertains to both kinds of definitions. |
| 277 | *** |
| 278 | |
| 279 | If a mojom struct is declared without a struct body and is tagged with |
| 280 | `[Native]`, and a corresponding typemap is provided for the struct, the emitted |
| 281 | C++ bindings will -- as if by magic -- replace the mojom type with the |
| 282 | typemapped C++ type and will internally use the existing `IPC::ParamTraits<T>` |
| 283 | specialization for that type in order to serialize and deserialize the struct. |
| 284 | |
| 285 | For example, given the |
| 286 | [`resource_messages.h`](https://cs.chromium.org/chromium/src/content/common/resource_messages.h?rcl=2e7a430d8d88222c04ab3ffb0a143fa85b3cec5b&l=215) header |
| 287 | which defines an IPC mapping for `content::ResourceRequest`: |
| 288 | |
| 289 | ``` cpp |
| 290 | IPC_STRUCT_TRAITS_BEGIN(content::ResourceRequest) |
| 291 | IPC_STRUCT_TRAITS_MEMBER(method) |
| 292 | IPC_STRUCT_TRAITS_MEMBER(url) |
| 293 | // ... |
| 294 | IPC_STRUCT_TRAITS_END() |
| 295 | ``` |
| 296 | |
| 297 | and the |
| 298 | [`resource_request.h`](https://cs.chromium.org/chromium/src/content/common/resource_request.h?rcl=dce9e476a525e4ff0304787935dc1a8c38392ac8&l=32) header |
| 299 | which actually defines the `content::ResourceRequest` type: |
| 300 | |
| 301 | ``` cpp |
| 302 | namespace content { |
| 303 | |
| 304 | struct CONTENT_EXPORT ResourceRequest { |
| 305 | // ... |
| 306 | }; |
| 307 | |
| 308 | } // namespace content |
| 309 | ``` |
| 310 | |
| 311 | we can declare a corresponding "native" mojom struct: |
| 312 | |
| 313 | ``` cpp |
| 314 | module content.mojom; |
| 315 | |
| 316 | [Native] |
| 317 | struct URLRequest; |
| 318 | ``` |
| 319 | |
| 320 | and add a typemap like |
| 321 | [`url_request.typemap`](https://cs.chromium.org/chromium/src/content/common/url_request.typemap?rcl=4b5963fa744a706398f8f06a4cbbf70d7fa3213d) |
| 322 | to define how to map between them: |
| 323 | |
| 324 | ``` python |
| 325 | mojom = "//content/public/common/url_loader.mojom" |
| 326 | public_headers = [ "//content/common/resource_request.h" ] |
| 327 | traits_headers = [ "//content/common/resource_messages.h" ] |
| 328 | ... |
| 329 | type_mappings = [ "content.mojom.URLRequest=content::ResourceRequest" ] |
| 330 | ``` |
| 331 | |
| 332 | Note specifically that public_headers includes the definition of the native C++ |
| 333 | type, and traits_headers includes the definition of the legacy IPC traits. |
| 334 | |
| 335 | As a result of all this, other mojom files can now reference |
| 336 | `content.mojom.URLRequest` as a type for method parameters and other struct |
| 337 | fields, and the generated C++ bindings will represent those values exclusively |
| 338 | as `content::ResourceRequest` objects. |
| 339 | |
| 340 | This same basic approach can be used to leverage existing `IPC_ENUM_TRAITS` for |
| 341 | invocations for `[Native]` mojom enum aliases. |
| 342 | |
| 343 | *** aside |
| 344 | **NOTE:** Use of `[Native]` mojom definitions is strictly limited to C++ |
| 345 | bindings. If a mojom message depends on such definitions, it cannot be sent or |
| 346 | received by other language bindings. This feature also depends on continued |
| 347 | support for legacy IPC serialization and all uses of it should therefore be |
| 348 | treated as technical debt. |
| 349 | *** |
| 350 | |
| 351 | ## Typemaps For Content and Blink Types |
| 352 | |
| 353 | Using typemapping for messages that go between Blink and content browser code |
| 354 | can sometimes be tricky due to things like dependency cycles or confusion over |
| 355 | the correct place for some definition |
| 356 | to live. There are some example CLs provided here, but feel free to also contact |
| 357 | [[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) |
| 358 | with specific details if you encounter trouble. |
| 359 | |
| 360 | [This CL](https://codereview.chromium.org/2363533002) introduces a Mojom |
| 361 | definition and typemap for `ui::WindowOpenDisposition` as a precursor to the |
| 362 | IPC conversion below. |
| 363 | |
| 364 | The [follow-up CL](https://codereview.chromium.org/2363573002) uses that |
| 365 | definition along with several other new typemaps (including native typemaps as |
| 366 | described above) to convert the relatively large `ViewHostMsg_CreateWindow` |
| 367 | message to Mojo. |
| 368 | |
| 369 | ## Additional Support |
| 370 | |
| 371 | If this document was not helpful in some way, please post a message to your |
| 372 | friendly |
| 373 | [[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) |
| 374 | mailing list. |