| 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 |