OLD | NEW |
1 # Mojo in Chromium | 1 # Mojo in Chromium |
2 | 2 |
3 **THIS DOCUIMENT IS A WORK IN PROGRESS.** As long as this notice exists, you | |
4 should probably ignore everything below it. | |
5 | |
6 This document is intended to serve as a Mojo primer for Chromium developers. No | 3 This document is intended to serve as a Mojo primer for Chromium developers. No |
7 prior knowledge of Mojo is assumed, but you should have a decent grasp of C++ | 4 prior knowledge of Mojo is assumed, but you should have a decent grasp of C++ |
8 and be familiar with Chromium's multi-process architecture as well as common | 5 and be familiar with Chromium's multi-process architecture as well as common |
9 concepts used throughout Chromium such as smart pointers, message loops, | 6 concepts used throughout Chromium such as smart pointers, message loops, |
10 callback binding, and so on. | 7 callback binding, and so on. |
11 | 8 |
12 [TOC] | 9 [TOC] |
13 | 10 |
14 ## Should I Bother Reading This? | 11 ## Should I Bother Reading This? |
15 | 12 |
16 If you're planning to build a Chromium feature that needs IPC and you aren't | 13 If you're planning to build a Chromium feature that needs IPC and you aren't |
17 already using Mojo, you probably want to read this. **Legacy IPC** -- _i.e._, | 14 already using Mojo, YES! **Legacy IPC is deprecated.** |
18 `foo_messages.h` files, message filters, and the suite of `IPC_MESSAGE_*` macros | |
19 -- **is on the verge of deprecation.** | |
20 | 15 |
21 ## Why Mojo? | 16 ## Why Mojo? |
22 | 17 |
23 Mojo provides IPC primitives for pushing messages and data around between | 18 TL;DR: The long-term intent is to refactor Chromium into a large set of smaller |
24 transferrable endpoints which may or may not cross process boundaries; it | 19 services. |
25 simplifies threading with regard to IPC; it standardizes message serialization | 20 |
26 in a way that's resilient to versioning issues; and it can be used with relative | 21 We can be smarter about: |
27 ease and consistency across a number of languages including C++, Java, and | 22 |
28 `JavaScript` -- all languages which comprise a significant share of Chromium | 23 * Which services we bring up or don't |
29 code. | 24 * How we isolate these services to improve security and stability |
30 | 25 * Which binary features we ship to one user or another |
31 The messaging protocol doesn't strictly need to be used for IPC though, and | 26 |
32 there are some higher-level reasons for this adoption and for the specific | 27 A more robust messaging layer opens the door for a number of interesting |
33 approach to integration outlined in this document. | 28 possibilities; in particular it allows us to integrate a large number of |
34 | 29 components without link-time interdependencies, and it breaks down the growing |
35 ### Code Health | 30 number of interesting cross-language boundaries across the codebase. |
36 | 31 |
37 At the moment we have fairly weak separation between components, with DEPS being | 32 Much has been learned from using Chromium IPC and maintaining Chromium |
38 the strongest line of defense against increasing complexity. | 33 dependencies in anger over the past several years and we feel there's now a |
39 | 34 significant opportunity to make life easier for developers, and to help them |
40 A component Foo might hold a reference to some bit of component Bar's internal | 35 build more and better features, faster, and with much less cost to users. |
41 state, or it might expect Bar to initialize said internal state in some | |
42 particular order. These sorts of problems are reasonably well-mitigated by the | |
43 code review process, but they can (and do) still slip through the cracks, and | |
44 they have a noticeable cumulative effect on complexity as the code base | |
45 continues to grow. | |
46 | |
47 We think we can make a lasting positive impact on code health by establishing | |
48 more concrete boundaries between components, and this is something a library | |
49 like Mojo gives us an opportunity to do. | |
50 | |
51 ### Modularity | |
52 | |
53 In addition to code health -- which alone could be addressed in any number of | |
54 ways that don't involve Mojo -- this approach opens doors to build and | |
55 distribute parts of Chrome separately from the main binary. | |
56 | |
57 While we're not currently taking advantage of this capability, doing so remains | |
58 a long-term goal due to prohibitive binary size constraints in emerging mobile | |
59 markets. Many open questions around the feasibility of this goal should be | |
60 answered by the experimental Mandoline project as it unfolds, but the Chromium | |
61 project can be technically prepared for such a transition in the meantime. | |
62 | |
63 ### Mandoline | |
64 | |
65 The Mandoline project is producing a potential replacement for `src/content`. | |
66 Because Mandoline components are Mojo apps, and Chromium is now capable of | |
67 loading Mojo apps (somethings we'll discuss later), Mojo apps can be shared | |
68 between both projects with minimal effort. Developing your feature as or within | |
69 a Mojo application can mean you're contributing to both Chromium and Mandoline. | |
70 | 36 |
71 ## Mojo Overview | 37 ## Mojo Overview |
72 | 38 |
73 This section provides a general overview of Mojo and some of its API features. | 39 The Mojo system API provides a small suite of low-level IPC primitives: |
74 You can probably skip straight to | 40 **message pipes**, **data pipes**, and **shared buffers**. On top of this API |
75 [Your First Mojo Application](#Your-First-Mojo-Application) if you just want to | 41 we've built higher-level bindings APIs to simplify messaging for consumers |
76 get to some practical sample code. | 42 writing C++, Java, or JavaScript code. |
77 | 43 |
78 The Mojo Embedder Development Kit (EDK) provides a suite of low-level IPC | 44 This document focuses primarily on using C++ bindings with message pipes, which |
79 primitives: **message pipes**, **data pipes**, and **shared buffers**. We'll | 45 is likely to be the most common usage encountered by Chromium developers. |
80 focus primarily on message pipes and the C++ bindings API in this document. | |
81 | |
82 _TODO: Java and JS bindings APIs should also be covered here._ | |
83 | 46 |
84 ### Message Pipes | 47 ### Message Pipes |
85 | 48 |
86 A message pipe is a lightweight primitive for reliable, bidirectional, queued | 49 A message pipe is a lightweight primitive for reliable bidirectional transfer of |
87 transfer of relatively small packets of data. Every pipe endpoint is identified | 50 relatively small packets of data. Unsurprisingly a pipe has two endpoints, and |
88 by a **handle** -- a unique process-wide integer identifying the endpoint to the | 51 either endpoint may be transferred over another message pipe. |
89 EDK. | 52 |
90 | 53 Because we bootstrap a primordial message pipe between the browser process and |
91 A single message across a pipe consists of a binary payload and an array of zero | 54 each child process, this in turn means that you can create a new pipe and |
92 or more handles to be transferred. A pipe's endpoints may live in the same | 55 ultimately send either end to any any process, and the two ends will still be |
93 process or in two different processes. | 56 able to talk to each other seamlessly and exclusively. Goodbye, routing IDs! |
94 | 57 |
95 Pipes are easy to create. The `mojo::MessagePipe` type (see | 58 While message pipes can carry arbitrary packets of unstructured data, we |
96 `/third_party/mojo/src/mojo/public/cpp/system/message_pipe.h`) provides a nice | 59 generally use them in conjunction with generated bindings to ensure a |
97 class wrapper with each endpoint represented as a scoped handle type (see | 60 consistent, well-defined, versioned message structure on all endpoints. |
98 members `handle0` and `handle1` and the definition of | 61 |
99 `mojo::ScopedMessagePipeHandle`). In the same header you can find | 62 ### Mojom |
100 `WriteMessageRaw` and `ReadMessageRaw` definitions. These are in theory all one | 63 |
101 needs to begin pushing things from one endpoint to the other. | 64 Mojom is the IDL for Mojo interfaces. Given a `.mojom` file, the bindings |
102 | 65 generator outputs bindings for all three of the currently supported languages. |
103 While it's worth being aware of `mojo::MessagePipe` and the associated raw I/O | 66 |
104 functions, you will rarely if ever have a use for them. Instead you'll typically | 67 For example: |
105 use bindings code generated from mojom interface definitions, along with the | 68 |
106 public bindings API which mostly hides the underlying pipes. | 69 ``` |
107 | 70 // src/components/frob/public/interfaces/frobinator.mojom |
108 ### Mojom Bindings | 71 module frob.mojom; |
109 | 72 |
110 Mojom is the IDL for Mojo interfaces. When given a mojom file, the bindings | |
111 generator outputs a collection of bindings libraries for each supported | |
112 language. Mojom syntax is fairly straightforward (TODO: Link to a mojom language | |
113 spec?). Consider the example mojom file below: | |
114 | |
115 ``` | |
116 // frobinator.mojom | |
117 module frob; | |
118 interface Frobinator { | 73 interface Frobinator { |
119 Frobinate(); | 74 Frobinate(); |
120 }; | 75 }; |
121 ``` | 76 ``` |
122 | 77 |
123 This can be used to generate bindings for a very simple `Frobinator` interface. | 78 would generate the following outputs: |
124 Bindings are generated at build time and will match the location of the mojom | 79 |
125 source file itself, mapped into the generated output directory for your Chromium | 80 ``` |
126 build. In this case one can expect to find files named `frobinator.mojom.js`, | 81 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.cc |
127 `frobinator.mojom.cc`, `frobinator.mojom.h`, _etc._ | 82 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.h |
128 | 83 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.js |
129 The C++ header (`frobinator.mojom.h`) generated from this mojom will define a | 84 out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.srcjar |
130 pure virtual class interface named `frob::Frobinator` with a pure virtual method | 85 ... |
131 of signature `void Frobinate()`. Any class which implements this interface is | 86 ``` |
132 effectively a `Frobinator` service. | 87 |
133 | 88 The generated code hides away all the details of serializing and deserializing |
134 ### C++ Bindings API | 89 messages on either end of a pipe. |
135 | 90 |
136 Before we see an example implementation and usage of the Frobinator, there are a | 91 The C++ header (`frobinator.mojom.h`) defines an abstract class for each |
137 handful of interesting bits in the public C++ bindings API you should be | 92 mojom interface specified. Namespaces are derived from the `module` name. |
138 familiar with. These complement generated bindings code and generally obviate | 93 |
139 any need to use a `mojo::MessagePipe` directly. | 94 **NOTE:** Chromium convention for component `foo`'s module name is `foo.mojom`. |
140 | 95 This means all mojom-generated C++ typenames for component `foo` will live in |
141 In all of the cases below, `T` is the type of a generated bindings class | 96 the `foo::mojom` namespace to avoid collisions with non-generated typenames. |
142 interface, such as the `frob::Frobinator` discussed above. | 97 |
143 | 98 In this example the generated `frob::mojom::Frobinator` has a single |
144 #### `mojo::InterfacePtr<T>` | 99 pure virtual function: |
145 | 100 |
146 Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h`. | 101 ``` |
147 | 102 namespace frob { |
148 `mojo::InterfacePtr<T>` is a typed proxy for a service of type `T`, which can be | 103 |
149 bound to a message pipe endpoint. This class implements every interface method | 104 class Frobinator { |
150 on `T` by serializing a message (encoding the method call and its arguments) and | 105 public: |
151 writing it to the pipe (if bound.) This is the standard way for C++ code to talk | 106 virtual void Frobinate() = 0; |
152 to any Mojo service. | 107 }; |
153 | 108 |
154 For illustrative purposes only, we can create a message pipe and bind an | 109 } // namespace frob |
155 `InterfacePtr` to one end as follows: | 110 ``` |
156 | 111 |
157 ```cpp | 112 To create a `Frobinator` service, one simply implements `foo::Frobinator` and |
158 mojo::MessagePipe pipe; | 113 provides a means of binding pipes to it. |
159 mojo::InterfacePtr<frob::Frobinator> frobinator; | 114 |
160 frobinator.Bind( | 115 ### Binding to Pipes |
161 mojo::InterfacePtrInfo<frob::Frobinator>(pipe.handle0.Pass(), 0u)); | 116 |
162 ``` | 117 Let's look at some sample code: |
163 | 118 |
164 You could then call `frobinator->Frobinate()` and read the encoded `Frobinate` | 119 ``` |
165 message from the other side of the pipe (`handle1`.) You most likely don't want | 120 // src/components/frob/frobinator_impl.cc |
166 to do this though, because as you'll soon see there's a nicer way to establish | 121 |
167 service pipes. | |
168 | |
169 #### `mojo::InterfaceRequest<T>` | |
170 | |
171 Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h`. | |
172 | |
173 `mojo::InterfaceRequest<T>` is a typed container for a message pipe endpoint | |
174 that should _eventually_ be bound to a service implementation. An | |
175 `InterfaceRequest` doesn't actually _do_ anything, it's just a way of holding | |
176 onto an endpoint without losing interface type information. | |
177 | |
178 A common usage pattern is to create a pipe, bind one end to an | |
179 `InterfacePtr<T>`, and pass the other end off to someone else (say, over some | |
180 other message pipe) who is expected to eventually bind it to a concrete service | |
181 implementation. `InterfaceRequest<T>` is here for that purpose and is, as we'll | |
182 see later, a first-class concept in Mojom interface definitions. | |
183 | |
184 As with `InterfacePtr<T>`, we can manually bind an `InterfaceRequest<T>` to a | |
185 pipe endpoint: | |
186 | |
187 ```cpp | |
188 mojo::MessagePipe pipe; | |
189 | |
190 mojo::InterfacePtr<frob::Frobinator> frobinator; | |
191 frobinator.Bind( | |
192 mojo::InterfacePtrInfo<frob::Frobinator>(pipe.handle0.Pass(), 0u)); | |
193 | |
194 mojo::InterfaceRequest<frob::Frobinator> frobinator_request; | |
195 frobinator_request.Bind(pipe.handle1.Pass()); | |
196 ``` | |
197 | |
198 At this point we could start making calls to `frobinator->Frobinate()` as | |
199 before, but they'll just sit in queue waiting for the request side to be bound. | |
200 Note that the basic logic in the snippet above is such a common pattern that | |
201 there's a convenient API function which does it for us. | |
202 | |
203 #### `mojo::GetProxy<T>` | |
204 | |
205 Defined in | |
206 `/third_party/mojo/src/mojo/public/cpp/bindings/interface`_request.h`. | |
207 | |
208 `mojo::GetProxy<T>` is the function you will most commonly use to create a new | |
209 message pipe. Its signature is as follows: | |
210 | |
211 ```cpp | |
212 template <typename T> | |
213 mojo::InterfaceRequest<T> GetProxy(mojo::InterfacePtr<T>* ptr); | |
214 ``` | |
215 | |
216 This function creates a new message pipe, binds one end to the given | |
217 `InterfacePtr` argument, and binds the other end to a new `InterfaceRequest` | |
218 which it then returns. Equivalent to the sample code just above is the following | |
219 snippet: | |
220 | |
221 ```cpp | |
222 mojo::InterfacePtr<frob::Frobinator> frobinator; | |
223 mojo::InterfaceRequest<frob::Frobinator> frobinator_request = | |
224 mojo::GetProxy(&frobinator); | |
225 ``` | |
226 | |
227 #### `mojo::Binding<T>` | |
228 | |
229 Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/binding.h`. | |
230 | |
231 Binds one end of a message pipe to an implementation of service `T`. A message | |
232 sent from the other end of the pipe will be read and, if successfully decoded as | |
233 a `T` message, will invoke the corresponding call on the bound `T` | |
234 implementation. A `Binding<T>` must be constructed over an instance of `T` | |
235 (which itself usually owns said `Binding` object), and its bound pipe is usually | |
236 taken from a passed `InterfaceRequest<T>`. | |
237 | |
238 A common usage pattern looks something like this: | |
239 | |
240 ```cpp | |
241 #include "components/frob/public/interfaces/frobinator.mojom.h" | 122 #include "components/frob/public/interfaces/frobinator.mojom.h" |
242 #include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" | 123 #include "mojo/public/cpp/bindings/binding.h" |
243 #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" | 124 #include "mojo/public/cpp/bindings/interface_request.h" |
244 | 125 |
245 class FrobinatorImpl : public frob::Frobinator { | 126 namespace frob { |
| 127 |
| 128 class FrobinatorImpl : public mojom::Frobinator { |
246 public: | 129 public: |
247 FrobinatorImpl(mojo::InterfaceRequest<frob::Frobinator> request) | 130 FrobinatorImpl(mojo::InterfaceRequest<mojom::Frobinator> request) |
248 : binding_(this, request.Pass()) {} | 131 : binding_(this, std::move(request)) {} |
249 ~FrobinatorImpl() override {} | 132 ~FrobinatorImpl() override {} |
250 | 133 |
| 134 // mojom::Frobinator: |
| 135 void Frobinate() override { DLOG(INFO) << "I can't stop frobinating!"; } |
| 136 |
251 private: | 137 private: |
252 // frob::Frobinator: | 138 mojo::Binding<mojom::Frobinator> binding_; |
| 139 }; |
| 140 |
| 141 } // namespace frob |
| 142 ``` |
| 143 |
| 144 The first thing to note is that `mojo::Binding<T>` *binds* one end of a message |
| 145 pipe to an implementation of a service. This means it watches that end of the |
| 146 pipe for incoming messages; it knows how to decode messages for interface `T`, |
| 147 and it dispatches them to methods on the bound `T` implementation. |
| 148 |
| 149 `mojo::InterfaceRequest<T>` is essentially semantic sugar for a strongly-typed |
| 150 message pipe endpoint. A common way to create new message pipes is via the |
| 151 `GetProxy` call defined in `interface_request.h`: |
| 152 |
| 153 ``` |
| 154 mojom::FrobinatorPtr proxy; |
| 155 mojo::InterfaceRequest<mojom::Frobinator> request = mojo::GetProxy(&proxy); |
| 156 ``` |
| 157 |
| 158 This creates a new message pipe with one end owned by `proxy` and the other end |
| 159 owned by `request`. It has the nice property of attaching common type |
| 160 information to each end of the pipe. |
| 161 |
| 162 Note that `InterfaceRequest<T>` doesn't actually **do** anything. It just scopes |
| 163 a pipe endpoint and associates it with an interface type at compile time. As |
| 164 such, other typed service binding primitives such as `mojo::Binding<T>` take |
| 165 these objects as input when they need an endpoint to bind to. |
| 166 |
| 167 `mojom::FrobinatorPtr` is a generated type alias for |
| 168 `mojo::InterfacePtr<mojom::Frobinator>`. An `InterfacePtr<T>` scopes a message |
| 169 pipe endpoint as well, but it also internally implements every method on `T` by |
| 170 serializing a corresponding message and writing it to the pipe. |
| 171 |
| 172 Hence we can put this together to talk to a `FrobinatorImpl` over a pipe: |
| 173 |
| 174 ``` |
| 175 frob:mojom::FrobinatorPtr frobinator; |
| 176 frob::FrobinatorImpl impl(GetProxy(&frobinator)); |
| 177 |
| 178 // Tada! |
| 179 frobinator->Frobinate(); |
| 180 ``` |
| 181 |
| 182 Behind the scenes this serializes a message corresponding to the `Frobinate` |
| 183 request and writes it to one end of the pipe. Eventually (and, incidentally, |
| 184 very soon after), `impl`'s internal `mojo::Binding` will decode this message and |
| 185 dispatch a call to `impl.Frobinate()`. |
| 186 |
| 187 ### Responding to Requests |
| 188 |
| 189 A common idiom in Chromium IPC is to keep track of IPC requests with some kind |
| 190 of opaque identifier (i.e. an integer *request ID*) so that you can later |
| 191 respond to a specific request using some nominally related message in the other |
| 192 direction. |
| 193 |
| 194 This is baked into mojom interface definitions. We can extend our `Frobinator` |
| 195 service like so: |
| 196 |
| 197 ``` |
| 198 module frob.mojom; |
| 199 |
| 200 interface Frobinator { |
| 201 Frobinate(); |
| 202 GetFrobinationLevels() => (int min, int max); |
| 203 }; |
| 204 ``` |
| 205 |
| 206 and update our implementation: |
| 207 |
| 208 ``` |
| 209 class FrobinatorImpl : public mojom::Frobinator { |
| 210 public: |
| 211 // ... |
| 212 |
| 213 // mojom::Frobinator: |
253 void Frobinate() override { /* ... */ } | 214 void Frobinate() override { /* ... */ } |
254 | 215 void GetFrobinationLevels(const GetFrobinationLevelsCallback& callback) { |
255 mojo::Binding<frob::Frobinator> binding_; | 216 callback.Run(1, 42); |
256 }; | 217 } |
257 ``` | 218 }; |
258 | 219 ``` |
259 And then we could write some code to test this: | 220 |
260 | 221 When the service implementation runs `callback`, the response arguments are |
261 ```cpp | 222 serialized and sent back over the pipe. The proxy on the other end knows how to |
262 // Fun fact: The bindings generator emits a type alias like this for every | 223 read this response and will in turn dispatch it to a callback on that end: |
263 // interface type. frob::FrobinatorPtr is an InterfacePtr<frob::Frobinator>. | 224 |
264 frob::FrobinatorPtr frobinator; | 225 ``` |
265 scoped_ptr<FrobinatorImpl> impl( | 226 void ShowLevels(int min, int max) { |
266 new FrobinatorImpl(mojo::GetProxy(&frobinator))); | 227 DLOG(INFO) << "Frobinator min=" << min << " max=" << max; |
| 228 } |
| 229 |
| 230 // ... |
| 231 |
| 232 mojom::FrobinatorPtr frobinator; |
| 233 FrobinatorImpl impl(GetProxy(&frobinator)); |
| 234 |
| 235 frobinator->GetFrobinatorLevels(base::Bind(&ShowLevels)); |
| 236 ``` |
| 237 |
| 238 This does what you'd expect. |
| 239 |
| 240 ## Exposing Services in Chromium |
| 241 |
| 242 There are a number of ways one might expose services across various surfaces of |
| 243 the browser. One common approach now is to use a |
| 244 [`content::ServiceRegistry` (link)](https://goo.gl/uEhx06). These come in |
| 245 pairs generally spanning a process boundary, and they provide primitive service |
| 246 registration and connection interfaces. For one example, [every |
| 247 `RenderFrameHost` has a `ServiceRegistry`](https://goo.gl/4YR3j5), as does |
| 248 [every corresponding `RenderFrame`](https://goo.gl/YhrgXa). These registries are |
| 249 intertwined. |
| 250 |
| 251 The gist is that you can add a service to the local side of the registry -- it's |
| 252 just a mapping from interface name to factory function -- or you can connect by |
| 253 name to services registered on the remote side. |
| 254 |
| 255 **NOTE:** In this context the "factory function" is simply a callback which |
| 256 takes a pipe endpoint and does something with it. It's expected that you'll |
| 257 either bind it to a service implementation of some kind or you will close it, ef
fectively rejecting the connection request. |
| 258 |
| 259 We can build a simple browser-side `FrobinatorImpl` service that has access to a |
| 260 `BrowserContext` for any frame which connects to it: |
| 261 |
| 262 ``` |
| 263 #include "base/macros.h" |
| 264 #include "components/frob/public/interfaces/frobinator.mojom.h" |
| 265 #include "content/public/browser/browser_context.h" |
| 266 #inlcude "mojo/public/cpp/system/interface_request.h" |
| 267 #inlcude "mojo/public/cpp/system/message_pipe.h" |
| 268 #inlcude "mojo/public/cpp/system/strong_binding.h" |
| 269 |
| 270 namespace frob { |
| 271 |
| 272 class FrobinatorImpl : public mojom::Frobinator { |
| 273 public: |
| 274 FrobinatorImpl(content::BrowserContext* context, |
| 275 mojo::InterfaceRequest<mojom::Frobinator> request) |
| 276 : context_(context), binding_(this, std::move(request)) {} |
| 277 ~FrobinatorImpl() override {} |
| 278 |
| 279 // A factory function to use in conjunction with ServiceRegistry. |
| 280 static void Create(content::BrowserContext* context, |
| 281 mojo::InterfaceRequest<mojom::Frobinator> request) { |
| 282 // See comment below for why this doesn't leak. |
| 283 new FrobinatorImpl(context, |
| 284 mojo::MakeRequest<mojom::Frobinator>(std::move(pipe))); |
| 285 } |
| 286 |
| 287 private: |
| 288 // mojom::Frobinator: |
| 289 void Frobinate() override { /* ... */ } |
| 290 |
| 291 content::BrowserContext* context_; |
| 292 |
| 293 // A StrongBinding is just like a Binding, except that it takes ownership of |
| 294 // its bound implementation and deletes itself (and the impl) if and when the |
| 295 // bound pipe encounters an error or is closed on the other end. |
| 296 mojo::StrongBinding<mojom::Frobinator> binding_; |
| 297 |
| 298 DISALLOW_COPY_AND_ASSIGN(FrobinatorImpl); |
| 299 }; |
| 300 |
| 301 } // namespace frob |
| 302 ``` |
| 303 |
| 304 Now somewhere in the browser we register the Frobinator service with each |
| 305 `RenderFrameHost` ([this](https://goo.gl/HEFn63) is a popular spot): |
| 306 |
| 307 ``` |
| 308 frame_host->GetServiceRegistry()->AddService<frob::mojom::Frobinator>( |
| 309 base::Bind( |
| 310 &frob::FrobinatorImpl::Create, |
| 311 base::Unretained(frame_host->GetProcess()->GetBrowserContext()))); |
| 312 ``` |
| 313 |
| 314 And in the render process we can now do something like: |
| 315 |
| 316 ``` |
| 317 mojom::FrobinatorPtr frobinator; |
| 318 render_frame->GetServiceRegistry()->ConnectToRemoteService( |
| 319 mojo::GetProxy(&frobinator)); |
| 320 |
| 321 // It's IPC! |
267 frobinator->Frobinate(); | 322 frobinator->Frobinate(); |
268 ``` | 323 ``` |
269 | 324 |
270 This will _eventually_ call `FrobinatorImpl::Frobinate()`. "Eventually," because | 325 There are now plenty of concrete examples of Mojo usage in the Chromium tree. |
271 the sequence of events when `frobinator->Frobinate()` is called is roughly as | 326 Poke around at existing mojom files and see how their implementions are built |
272 follows: | 327 and connected. |
273 | 328 |
274 1. A new message buffer is allocated and filled with an encoded 'Frobinate' | 329 ## Mojo in Blink |
275 message. | 330 |
276 1. The EDK is asked to write this message to the pipe endpoint owned by the | 331 *TODO* |
277 `FrobinatorPtr`. | 332 |
278 1. If the call didn't happen on the Mojo IPC thread for this process, EDK hops | 333 This is a work in progress. TL;DR: We'll also soon begin using Mojo services |
279 to the Mojo IPC thread. | 334 from Blink so that the platform layer can consume browser services |
280 1. The EDK writes the message to the pipe. In this case the pipe endpoints live | 335 directly via Mojo. The long-term goal there is to eliminate `content/renderer`. |
281 in the same process, so this essentially a glorified `memcpy`. If they lived | 336 |
282 in different processes this would be the point at which the data moved | 337 ## Questions, Discussion, etc. |
283 across a real IPC channel. | 338 |
284 1. The EDK on the other end of the pipe is awoken on the Mojo IPC thread and | 339 A good place to find highly concentrated doses of people who know and care |
285 alerted to the message arrival. | 340 about Mojo in Chromium would be the [chromium-mojo](https://goo.gl/A4ebWB) |
286 1. The EDK reads the message. | 341 mailing list[.](https://goo.gl/L70ihQ) |
287 1. If the bound receiver doesn't live on the Mojo IPC thread, the EDK hops to | |
288 the receiver's thread. | |
289 1. The message is passed on to the receiver. In this case the receiver is | |
290 generated bindings code, via `Binding<T>`. This code decodes and validates | |
291 the `Frobinate` message. | |
292 1. `FrobinatorImpl::Frobinate()` is called on the bound implementation. | |
293 | |
294 So as you can see, the call to `Frobinate()` may result in up to two thread hops | |
295 and one process hop before the service implementation is invoked. | |
296 | |
297 #### `mojo::StrongBinding<T>` | |
298 | |
299 Defined in `third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h`. | |
300 | |
301 `mojo::StrongBinding<T>` is just like `mojo::Binding<T>` with the exception that | |
302 a `StrongBinding` takes ownership of the bound `T` instance. The instance is | |
303 destroyed whenever the bound message pipe is closed. This is convenient in cases | |
304 where you want a service implementation to live as long as the pipe it's | |
305 servicing, but like all features with clever lifetime semantics, it should be | |
306 used with caution. | |
307 | |
308 ## The Mojo Shell | |
309 | |
310 Both Chromium and Mandoline run a central **shell** component which is used to | |
311 coordinate communication among all Mojo applications (see the next section for | |
312 an overview of Mojo applications.) | |
313 | |
314 Every application receives a proxy to this shell upon initialization, and it is | |
315 exclusively through this proxy that an application can request connections to | |
316 other applications. The `mojo::Shell` interface provided by this proxy is | |
317 defined as follows: | |
318 | |
319 ``` | |
320 module mojo; | |
321 interface Shell { | |
322 ConnectToApplication(URLRequest application_url, | |
323 ServiceProvider&? services, | |
324 ServiceProvider? exposed_services); | |
325 QuitApplication(); | |
326 }; | |
327 ``` | |
328 | |
329 and as for the `mojo::ServiceProvider` interface: | |
330 | |
331 ``` | |
332 module mojo; | |
333 interface ServiceProvider { | |
334 ConnectToService(string interface_name, handle<message_pipe> pipe); | |
335 }; | |
336 ``` | |
337 | |
338 Definitions for these interfaces can be found in | |
339 `/mojo/shell/public/interfaces`. Also note that `mojo::URLRequest` is a | |
340 Mojo struct defined in | |
341 `/mojo/services/network/public/interfaces/url_loader.mojom`. | |
342 | |
343 Note that there's some new syntax in the mojom for `ConnectToApplication` above. | |
344 The '?' signifies a nullable value and the '&' signifies an interface request | |
345 rather than an interface proxy. | |
346 | |
347 The argument `ServiceProvider&? services` indicates that the caller should pass | |
348 an `InterfaceRequest<ServiceProvider>` as the second argument, but that it need | |
349 not be bound to a pipe (i.e., it can be "null" in which case it's ignored.) | |
350 | |
351 The argument `ServiceProvider? exposed_services` indicates that the caller | |
352 should pass an `InterfacePtr<ServiceProvider>` as the third argument, but that | |
353 it may also be null. | |
354 | |
355 `ConnectToApplication` asks the shell to establish a connection between the | |
356 caller and some other app the shell might know about. In the event that a | |
357 connection can be established -- which may involve the shell starting a new | |
358 instance of the target app -- the given `services` request (if not null) will be | |
359 bound to a service provider in the target app. The target app may in turn use | |
360 the passed `exposed_services` proxy (if not null) to request services from the | |
361 connecting app. | |
362 | |
363 ### Mojo Applications | |
364 | |
365 All code which runs in a Mojo environment, apart from the shell itself (see | |
366 above), belongs to one Mojo **application** or another**`**`**. The term | |
367 "application" in this context is a common source of confusion, but it's really a | |
368 simple concept. In essence an application is anything which implements the | |
369 following Mojom interface: | |
370 | |
371 ``` | |
372 module mojo; | |
373 interface Application { | |
374 Initialize(Shell shell, string url); | |
375 AcceptConnection(string requestor_url, | |
376 ServiceProvider&? services, | |
377 ServiceProvider? exposed_services, | |
378 string resolved_url); | |
379 OnQuitRequested() => (bool can_quit); | |
380 }; | |
381 ``` | |
382 | |
383 Of course, in Chromium and Mandoline environments this interface is obscured | |
384 from application code and applications should generally just implement | |
385 `mojo::ApplicationDelegate` (defined in | |
386 `/mojo/shell/public/cpp/application_delegate.h`.) We'll see a concrete | |
387 example of this in the next section, | |
388 [Your First Mojo Application](#Your-First-Mojo-Application). | |
389 | |
390 The takeaway here is that an application can be anything. It's not necessarily a | |
391 new process (though at the moment, it's at least a new thread). Applications can | |
392 connect to each other, and these connections are the mechanism through which | |
393 separate components expose services to each other. | |
394 | |
395 **NOTE##: This is not true in Chromium today, but it should be eventually. For | |
396 some components (like render frames, or arbitrary browser process code) we | |
397 provide APIs which allow non-Mojo-app-code to masquerade as a Mojo app and | |
398 therefore connect to real Mojo apps through the shell. | |
399 | |
400 ### Other IPC Primitives | |
401 | |
402 Finally, it's worth making brief mention of the other types of IPC primitives | |
403 Mojo provides apart from message pipes. A **data pipe** is a unidirectional | |
404 channel for pushing around raw data in bulk, and a **shared buffer** is | |
405 (unsurprisingly) a shared memory primitive. Both of these objects use the same | |
406 type of transferable handle as message pipe endpoints, and can therefore be | |
407 transferred across message pipes, potentially to other processes. | |
408 | |
409 ## Your First Mojo Application | |
410 | |
411 In this section, we're going to build a simple Mojo application that can be run | |
412 in isolation using Mandoline's `mojo_runner` binary. After that we'll add a | |
413 service to the app and set up a test suite to connect and test that service. | |
414 | |
415 ### Hello, world! | |
416 | |
417 So, you're building a new Mojo app and it has to live somewhere. For the | |
418 foreseeable future we'll likely be treating `//components` as a sort of | |
419 top-level home for new Mojo apps in the Chromium tree. Any component application | |
420 you build should probably go there. Let's create some basic files to kick things | |
421 off. You may want to start a new local Git branch to isolate any changes you | |
422 make while working through this. | |
423 | |
424 First create a new `//components/hello` directory. Inside this directory we're | |
425 going to add the following files: | |
426 | |
427 **components/hello/main.cc** | |
428 | |
429 ```cpp | |
430 #include "base/logging.h" | |
431 #include "third_party/mojo/src/mojo/public/c/system/main.h" | |
432 | |
433 MojoResult MojoMain(MojoHandle shell_handle) { | |
434 LOG(ERROR) << "Hello, world!"; | |
435 return MOJO_RESULT_OK; | |
436 }; | |
437 ``` | |
438 | |
439 **components/hello/BUILD.gn** | |
440 | |
441 ``` | |
442 import("//mojo/public/mojo_application.gni") | |
443 | |
444 mojo_native_application("hello") { | |
445 sources = [ | |
446 "main.cc", | |
447 ] | |
448 deps = [ | |
449 "//base", | |
450 "//mojo/environment:chromium", | |
451 ] | |
452 } | |
453 ``` | |
454 | |
455 For the sake of this example you'll also want to add your component as a | |
456 dependency somewhere in your local checkout to ensure its build files are | |
457 generated. The easiest thing to do there is probably to add a dependency on | |
458 `"//components/hello"` in the `"gn_all"` target of the top-level `//BUILD.gn`. | |
459 | |
460 Assuming you have a GN output directory at `out_gn/Debug`, you can build the | |
461 Mojo runner along with your shiny new app: | |
462 | |
463 ninja -C out_gn/Debug mojo_runner components/hello | |
464 | |
465 In addition to the `mojo_runner` executable, this will produce a new binary at | |
466 `out_gn/Debug/hello/hello.mojo`. This binary is essentially a shared library | |
467 which exports your `MojoMain` function. | |
468 | |
469 `mojo_runner` takes an application URL as its only argument and runs the | |
470 corresponding application. In its current state it resolves `mojo`-scheme URLs | |
471 such that `"mojo:foo"` maps to the file `"foo/foo.mojo"` relative to the | |
472 `mojo_runner` path (_i.e._ your output directory.) This means you can run your | |
473 new app with the following command: | |
474 | |
475 out_gn/Debug/mojo_runner mojo:hello | |
476 | |
477 You should see our little `"Hello, world!"` error log followed by a hanging | |
478 application. You can `^C` to kill it. | |
479 | |
480 ### Exposing Services | |
481 | |
482 An app that prints `"Hello, world!"` isn't terribly interesting. At a bare | |
483 minimum your app should implement `mojo::ApplicationDelegate` and expose at | |
484 least one service to connecting applications. | |
485 | |
486 Let's update `main.cc` with the following contents: | |
487 | |
488 **components/hello/main.cc** | |
489 | |
490 ```cpp | |
491 #include "components/hello/hello_app.h" | |
492 #include "mojo/shell/public/cpp/application_runner.h" | |
493 #include "third_party/mojo/src/mojo/public/c/system/main.h" | |
494 | |
495 MojoResult MojoMain(MojoHandle shell_handle) { | |
496 mojo::ApplicationRunner runner(new hello::HelloApp); | |
497 return runner.Run(shell_handle); | |
498 }; | |
499 ``` | |
500 | |
501 This is a pretty typical looking `MojoMain`. Most of the time this is all you | |
502 want -- a `mojo::ApplicationRunner` constructed over a | |
503 `mojo::ApplicationDelegate` instance, `Run()` with the pipe handle received from | |
504 the shell. We'll add some new files to the app as well: | |
505 | |
506 **components/hello/public/interfaces/greeter.mojom** | |
507 | |
508 ``` | |
509 module hello; | |
510 interface Greeter { | |
511 Greet(string name) => (string greeting); | |
512 }; | |
513 ``` | |
514 | |
515 Note the new arrow syntax on the `Greet` method. This indicates that the caller | |
516 expects a response from the service. | |
517 | |
518 **components/hello/public/interfaces/BUILD.gn** | |
519 | |
520 ``` | |
521 import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") | |
522 | |
523 mojom("interfaces") { | |
524 sources = [ | |
525 "greeter.mojom", | |
526 ] | |
527 } | |
528 ``` | |
529 | |
530 **components/hello/hello_app.h** | |
531 | |
532 ```cpp | |
533 #ifndef COMPONENTS_HELLO_HELLO_APP_H_ | |
534 #define COMPONENTS_HELLO_HELLO_APP_H_ | |
535 | |
536 #include "base/macros.h" | |
537 #include "components/hello/public/interfaces/greeter.mojom.h" | |
538 #include "mojo/shell/public/cpp/application_delegate.h" | |
539 #include "mojo/shell/public/cpp/interface_factory.h" | |
540 | |
541 namespace hello { | |
542 | |
543 class HelloApp : public mojo::ApplicationDelegate, | |
544 public mojo::InterfaceFactory<Greeter> { | |
545 public: | |
546 HelloApp(); | |
547 ~HelloApp() override; | |
548 | |
549 private: | |
550 // mojo::ApplicationDelegate: | |
551 bool ConfigureIncomingConnection( | |
552 mojo::ApplicationConnection* connection) override; | |
553 | |
554 // mojo::InterfaceFactory<Greeter>: | |
555 void Create(mojo::ApplicationConnection* connection, | |
556 mojo::InterfaceRequest<Greeter> request) override; | |
557 | |
558 DISALLOW_COPY_AND_ASSIGN(HelloApp); | |
559 }; | |
560 | |
561 } // namespace hello | |
562 | |
563 #endif // COMPONENTS_HELLO_HELLO_APP_H_ | |
564 ``` | |
565 | |
566 | |
567 **components/hello/hello_app.cc** | |
568 | |
569 ```cpp | |
570 #include "base/macros.h" | |
571 #include "components/hello/hello_app.h" | |
572 #include "mojo/shell/public/cpp/application_connection.h" | |
573 #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" | |
574 #include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" | |
575 | |
576 namespace hello { | |
577 | |
578 namespace { | |
579 | |
580 class GreeterImpl : public Greeter { | |
581 public: | |
582 GreeterImpl(mojo::InterfaceRequest<Greeter> request) | |
583 : binding_(this, request.Pass()) { | |
584 } | |
585 | |
586 ~GreeterImpl() override {} | |
587 | |
588 private: | |
589 // Greeter: | |
590 void Greet(const mojo::String& name, const GreetCallback& callback) override { | |
591 callback.Run("Hello, " + std::string(name) + "!"); | |
592 } | |
593 | |
594 mojo::StrongBinding<Greeter> binding_; | |
595 | |
596 DISALLOW_COPY_AND_ASSIGN(GreeterImpl); | |
597 }; | |
598 | |
599 } // namespace | |
600 | |
601 HelloApp::HelloApp() { | |
602 } | |
603 | |
604 HelloApp::~HelloApp() { | |
605 } | |
606 | |
607 bool HelloApp::ConfigureIncomingConnection( | |
608 mojo::ApplicationConnection* connection) { | |
609 connection->AddService<Greeter>(this); | |
610 return true; | |
611 } | |
612 | |
613 void HelloApp::Create( | |
614 mojo::ApplicationConnection* connection, | |
615 mojo::InterfaceRequest<Greeter> request) { | |
616 new GreeterImpl(request.Pass()); | |
617 } | |
618 | |
619 } // namespace hello | |
620 ``` | |
621 | |
622 And finally we need to update our app's `BUILD.gn` to add some new sources and | |
623 dependencies: | |
624 | |
625 **components/hello/BUILD.gn** | |
626 | |
627 ``` | |
628 import("//mojo/public/mojo_application.gni") | |
629 | |
630 source_set("lib") { | |
631 sources = [ | |
632 "hello_app.cc", | |
633 "hello_app.h", | |
634 ] | |
635 deps = [ | |
636 "//base", | |
637 "//components/hello/public/interfaces", | |
638 "//mojo/environment:chromium", | |
639 "//mojo/shell/public/cpp", | |
640 ] | |
641 } | |
642 | |
643 mojo_native_application("hello") { | |
644 sources = [ | |
645 "main.cc", | |
646 ], | |
647 deps = [ ":lib" ] | |
648 } | |
649 ``` | |
650 | |
651 Note that we build the bulk of our application sources as a static library | |
652 separate from the `MojoMain` definition. Following this convention is | |
653 particularly useful for Chromium integration, as we'll see later. | |
654 | |
655 There's a lot going on here and it would be useful to familiarize yourself with | |
656 the definitions of `mojo::ApplicationDelegate`, `mojo::ApplicationConnection`, | |
657 and `mojo::InterfaceFactory<T>`. The TL;DR though is that if someone connects to | |
658 this app and requests a service named `"hello::Greeter"`, the app will create a | |
659 new `GreeterImpl` and bind it to that request pipe. From there the connecting | |
660 app can call `Greeter` interface methods and they'll be routed to that | |
661 `GreeterImpl` instance. | |
662 | |
663 Although this appears to be a more interesting application, we need some way to | |
664 actually connect and test the behavior of our new service. Let's write an app | |
665 test! | |
666 | |
667 ### App Tests | |
668 | |
669 App tests run inside a test application, giving test code access to a shell | |
670 which can connect to one or more applications-under-test. | |
671 | |
672 First let's introduce some test code: | |
673 | |
674 **components/hello/hello_apptest.cc** | |
675 | |
676 ```cpp | |
677 #include "base/bind.h" | |
678 #include "base/callback.h" | |
679 #include "base/logging.h" | |
680 #include "base/macros.h" | |
681 #include "base/run_loop.h" | |
682 #include "components/hello/public/interfaces/greeter.mojom.h" | |
683 #include "mojo/shell/public/cpp/application_impl.h" | |
684 #include "mojo/shell/public/cpp/application_test_base.h" | |
685 | |
686 namespace hello { | |
687 namespace { | |
688 | |
689 class HelloAppTest : public mojo::test::ApplicationTestBase { | |
690 public: | |
691 HelloAppTest() {} | |
692 ~HelloAppTest() override {} | |
693 | |
694 void SetUp() override { | |
695 ApplicationTestBase::SetUp(); | |
696 mojo::URLRequestPtr app_url = mojo::URLRequest::New(); | |
697 app_url->url = "mojo:hello"; | |
698 application_impl()->ConnectToService(app_url.Pass(), &greeter_); | |
699 } | |
700 | |
701 Greeter* greeter() { return greeter_.get(); } | |
702 | |
703 private: | |
704 GreeterPtr greeter_; | |
705 | |
706 DISALLOW_COPY_AND_ASSIGN(HelloAppTest); | |
707 }; | |
708 | |
709 void ExpectGreeting(const mojo::String& expected_greeting, | |
710 const base::Closure& continuation, | |
711 const mojo::String& actual_greeting) { | |
712 EXPECT_EQ(expected_greeting, actual_greeting); | |
713 continuation.Run(); | |
714 }; | |
715 | |
716 TEST_F(HelloAppTest, GreetWorld) { | |
717 base::RunLoop loop; | |
718 greeter()->Greet("world", base::Bind(&ExpectGreeting, "Hello, world!", | |
719 loop.QuitClosure())); | |
720 loop.Run(); | |
721 } | |
722 | |
723 } // namespace | |
724 } // namespace hello | |
725 ``` | |
726 | |
727 We also need to add a new rule to `//components/hello/BUILD.gn`: | |
728 | |
729 ``` | |
730 mojo_native_application("apptests") { | |
731 output_name = "hello_apptests" | |
732 testonly = true | |
733 sources = [ | |
734 "hello_apptest.cc", | |
735 ] | |
736 deps = [ | |
737 "//base", | |
738 "//mojo/shell/public/cpp:test_support", | |
739 ] | |
740 public_deps = [ | |
741 "//components/hello/public/interfaces", | |
742 ] | |
743 data_deps = [ ":hello" ] | |
744 } | |
745 ``` | |
746 | |
747 Note that the `//components/hello:apptests` target does **not** have a binary | |
748 dependency on either `HelloApp` or `GreeterImpl` implementations; instead it | |
749 depends only on the component's public interface definitions. | |
750 | |
751 The `data_deps` entry ensures that `hello.mojo` is up-to-date when `apptests` is | |
752 built. This is desirable because the test connects to `"mojo:hello"` which will | |
753 in turn load `hello.mojo` from disk. | |
754 | |
755 You can now build the test suite: | |
756 | |
757 ninja -C out_gn/Debug components/hello:apptests | |
758 | |
759 and run it: | |
760 | |
761 out_gn/Debug/mojo_runner mojo:hello_apptests | |
762 | |
763 You should see one test (`HelloAppTest.GreetWorld`) passing. | |
764 | |
765 One particularly interesting bit of code in this test is in the `SetUp` method: | |
766 | |
767 mojo::URLRequestPtr app_url = mojo::URLRequest::New(); | |
768 app_url->url = "mojo:hello"; | |
769 application_impl()->ConnectToService(app_url.Pass(), &greeter_); | |
770 | |
771 `ConnectToService` is a convenience method provided by `mojo::ApplicationImpl`, | |
772 and it's essentially a shortcut for calling out to the shell's | |
773 `ConnectToApplication` method with the given application URL (in this case | |
774 `"mojo:hello"`) and then connecting to a specific service provided by that app | |
775 via its `ServiceProvider`'s `ConnectToService` method. | |
776 | |
777 Note that generated interface bindings include a constant string to identify | |
778 each interface by name; so for example the generated `hello::Greeter` type | |
779 defines a static C string: | |
780 | |
781 const char hello::Greeter::Name_[] = "hello::Greeter"; | |
782 | |
783 This is exploited by the definition of | |
784 `mojo::ApplicationConnection::ConnectToService<T>`, which uses `T::Name_` as the | |
785 name of the service to connect to. The type `T` in this context is inferred from | |
786 the `InterfacePtr<T>*` argument. You can inspect the definition of | |
787 `ConnectToService` in `/mojo/shell/public/cpp/application_connection.h` | |
788 for additional clarity. | |
789 | |
790 We could have instead written this code as: | |
791 | |
792 ```cpp | |
793 mojo::URLRequestPtr app_url = mojo::URLRequest::New(); | |
794 app_url->url = "mojo::hello"; | |
795 | |
796 mojo::ServiceProviderPtr services; | |
797 application_impl()->shell()->ConnectToApplication( | |
798 app_url.Pass(), mojo::GetProxy(&services), | |
799 // We pass a null provider since we aren't exposing any of our own | |
800 // services to the target app. | |
801 mojo::ServiceProviderPtr()); | |
802 | |
803 mojo::InterfaceRequest<hello::Greeter> greeter_request = | |
804 mojo::GetProxy(&greeter_); | |
805 services->ConnectToService(hello::Greeter::Name_, | |
806 greeter_request.PassMessagePipe()); | |
807 ``` | |
808 | |
809 The net result is the same, but 3-line version seems much nicer. | |
810 | |
811 ## Chromium Integration | |
812 | |
813 Up until now we've been using `mojo_runner` to load and run `.mojo` binaries | |
814 dynamically. While this model is used by Mandoline and may eventually be used in | |
815 Chromium as well, Chromium is at the moment confined to running statically | |
816 linked application code. This means we need some way to register applications | |
817 with the browser's Mojo shell. | |
818 | |
819 It also means that, rather than using the binary output of a | |
820 `mojo_native_application` target, some part of Chromium must link against the | |
821 app's static library target (_e.g._, `"//components/hello:lib"`) and register a | |
822 URL handler to teach the shell how to launch an instance of the app. | |
823 | |
824 When registering an app URL in Chromium it probably makes sense to use the same | |
825 mojo-scheme URL used for the app in Mandoline. For example the media renderer | |
826 app is referenced by the `"mojo:media"` URL in both Mandoline and Chromium. In | |
827 Mandoline this resolves to a dynamically-loaded `.mojo` binary on disk, but in | |
828 Chromium it resolves to a static application loader linked into Chromium. The | |
829 net result is the same in both cases: other apps can use the shell to connect to | |
830 `"mojo:media"` and use its services. | |
831 | |
832 This section explores different ways to register and connect to `"mojo:hello"` | |
833 in Chromium. | |
834 | |
835 ### In-Process Applications | |
836 | |
837 Applications can be set up to run within the browser process via | |
838 `ContentBrowserClient::RegisterInProcessMojoApplications`. This method populates | |
839 a mapping from URL to `base::Callback<scoped_ptr<mojo::ApplicationDelegate>()>` | |
840 (_i.e._, a factory function which creates a new `mojo::ApplicationDelegate` | |
841 instance), so registering a new app means adding an entry to this map. | |
842 | |
843 Let's modify `ChromeContentBrowserClient::RegisterInProcessMojoApplications` | |
844 (in `//chrome/browser/chrome_content_browser_client.cc`) by adding the following | |
845 code: | |
846 | |
847 ```cpp | |
848 apps->insert(std::make_pair(GURL("mojo:hello"), | |
849 base::Bind(&HelloApp::CreateApp))); | |
850 ``` | |
851 | |
852 you'll also want to add the following convenience method to your `HelloApp` | |
853 definition in `//components/hello/hello_app.h`: | |
854 | |
855 ```cpp | |
856 static scoped_ptr<mojo::ApplicationDelegate> HelloApp::CreateApp() { | |
857 return scoped_ptr<mojo::ApplicationDelegate>(new HelloApp); | |
858 } | |
859 ``` | |
860 | |
861 This introduces a dependency from `//chrome/browser` on to | |
862 `//components/hello:lib`, which you can add to the `"browser"` target's deps in | |
863 `//chrome/browser/BUILD.gn`. You'll of course also need to include | |
864 `"components/hello/hello_app.h"` in `chrome_content_browser_client.cc`. | |
865 | |
866 That's it! Now if an app comes to the shell asking to connect to `"mojo:hello"` | |
867 and app is already running, it'll get connected to our `HelloApp` and have | |
868 access to the `Greeter` service. If the app wasn't already running, it will | |
869 first be launched on a new thread. | |
870 | |
871 ### Connecting From the Browser | |
872 | |
873 We've already seen how apps can connect to each other using their own private | |
874 shell proxy, but the vast majority of Chromium code doesn't yet belong to a Mojo | |
875 application. So how do we use an app's services from arbitrary browser code? We | |
876 use `content::MojoAppConnection`, like this: | |
877 | |
878 ```cpp | |
879 #include "base/bind.h" | |
880 #include "base/logging.h" | |
881 #include "components/hello/public/interfaces/greeter.mojom.h" | |
882 #include "content/public/browser/mojo_app_connection.h" | |
883 | |
884 void LogGreeting(const mojo::String& greeting) { | |
885 LOG(INFO) << greeting; | |
886 } | |
887 | |
888 void GreetTheWorld() { | |
889 scoped_ptr<content::MojoAppConnection> connection = | |
890 content::MojoAppConnection::Create("mojo:hello", | |
891 content::kBrowserMojoAppUrl); | |
892 hello::GreeterPtr greeter; | |
893 connection->ConnectToService(&greeter); | |
894 greeter->Greet("world", base::Bind(&LogGreeting)); | |
895 } | |
896 ``` | |
897 | |
898 A `content::MojoAppConnection`, while not thread-safe, may be created and safely | |
899 used on any single browser thread. | |
900 | |
901 You could add the above code to a new browsertest to convince yourself that it | |
902 works. In fact you might want to take a peek at | |
903 `MojoShellTest.TestBrowserConnection` (in | |
904 `/content/browser/mojo_shell_browsertest.cc`) which registers and tests an | |
905 in-process Mojo app. | |
906 | |
907 Finally, note that `MojoAppConnection::Create` takes two URLs. The first is the | |
908 target app URL, and the second is the source URL. Since we're not really a Mojo | |
909 app, but we are still trusted browser code, the shell will gladly use this URL | |
910 as the `requestor_url` when establishing an incoming connection to the target | |
911 app. This allows browser code to masquerade as a Mojo app at the given URL. | |
912 `content::kBrowserMojoAppUrl` (which is presently `"system:content_browser"`) is | |
913 a reasonable default choice when a more specific app identity isn't required. | |
914 | |
915 ### Out-of-Process Applications | |
916 | |
917 If an app URL isn't registered for in-process loading, the shell assumes it must | |
918 be an out-of-process application. If the shell doesn't already have a known | |
919 instance of the app running, a new utility process is launched and the | |
920 application request is passed onto it. Then if the app URL is registered in the | |
921 utility process, the app will be loaded there. | |
922 | |
923 Similar to in-process registration, a URL mapping needs to be registered in | |
924 `ContentUtilityClient::RegisterMojoApplications`. | |
925 | |
926 Once again you can take a peek at `/content/browser/mojo_shell_browsertest.cc` | |
927 for an end-to-end example of testing an out-of-process Mojo app from browser | |
928 code. Note that `content_browsertests` runs on `content_shell`, which uses | |
929 `ShellContentUtilityClient` as defined | |
930 `/content/shell/utility/shell_content_utility_client.cc`. This code registers a | |
931 common OOP test app. | |
932 | |
933 ## Unsandboxed Out-of-Process Applications | |
934 | |
935 By default new utility processes run in a sandbox. If you want your Mojo app to | |
936 run out-of-process and unsandboxed (which you **probably do not**), you can | |
937 register its URL via | |
938 `ContentBrowserClient::RegisterUnsandboxedOutOfProcessMojoApplications`. | |
939 | |
940 ## Connecting From `RenderFrame` | |
941 | |
942 We can also connect to Mojo apps from a `RenderFrame`. This is made possible by | |
943 `RenderFrame`'s `GetServiceRegistry()` interface. The `ServiceRegistry` can be | |
944 used to acquire a shell proxy and in turn connect to an app like so: | |
945 | |
946 ```cpp | |
947 void GreetWorld(content::RenderFrame* frame) { | |
948 mojo::ShellPtr shell; | |
949 frame->GetServiceRegistry()->ConnectToRemoteService( | |
950 mojo::GetProxy(&shell)); | |
951 | |
952 mojo::URLRequestPtr request = mojo::URLRequest::New(); | |
953 request->url = "mojo:hello"; | |
954 | |
955 mojo::ServiceProviderPtr hello_services; | |
956 shell->ConnectToApplication( | |
957 request.Pass(), mojo::GetProxy(&hello_services), nullptr); | |
958 | |
959 hello::GreeterPtr greeter; | |
960 hello_services->ConnectToService( | |
961 hello::Greeter::Name_, mojo::GetProxy(&greeter).PassMessagePipe()); | |
962 } | |
963 ``` | |
964 | |
965 It's important to note that connections made through the frame's shell proxy | |
966 will appear to come from the frame's `SiteInstance` URL. For example, if the | |
967 frame has loaded `https://example.com/`, `HelloApp`'s incoming | |
968 `mojo::ApplicationConnection` in this case will have a remote application URL of | |
969 `"https://example.com/"`. This allows apps to expose their services to web | |
970 frames on a per-origin basis if needed. | |
971 | |
972 ### Connecting From Java | |
973 | |
974 TODO | |
975 | |
976 ### Connecting From `JavaScript` | |
977 | |
978 This is still a work in progress and might not really take shape until the | |
979 Blink+Chromium merge. In the meantime there are some end-to-end WebUI examples | |
980 in `/content/browser/webui/web_ui_mojo_browsertest.cc`. In particular, | |
981 `WebUIMojoTest.ConnectToApplication` connects from a WebUI frame to a test app | |
982 running in a new utility process. | |
983 | |
984 ## FAQ | |
985 | |
986 Nothing here yet! | |
OLD | NEW |