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