andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 1 | # Mojo in Chromium |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 2 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 3 | **THIS DOCUIMENT IS A WORK IN PROGRESS.** As long as this notice exists, you |
| 4 | should probably ignore everything below it. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 5 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 6 | 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++ |
| 8 | and be familiar with Chromium's multi-process architecture as well as common |
| 9 | concepts used throughout Chromium such as smart pointers, message loops, |
| 10 | callback binding, and so on. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 11 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 12 | [TOC] |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 13 | |
| 14 | ## Should I Bother Reading This? |
| 15 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 16 | 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._, |
| 18 | `foo_messages.h` files, message filters, and the suite of `IPC_MESSAGE_*` macros |
| 19 | -- **is on the verge of deprecation.** |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 20 | |
| 21 | ## Why Mojo? |
| 22 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 23 | Mojo provides IPC primitives for pushing messages and data around between |
| 24 | transferrable endpoints which may or may not cross process boundaries; it |
| 25 | simplifies threading with regard to IPC; it standardizes message serialization |
| 26 | in a way that's resilient to versioning issues; and it can be used with relative |
| 27 | ease and consistency across a number of languages including C++, Java, and |
| 28 | `JavaScript` -- all languages which comprise a significant share of Chromium |
| 29 | code. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 30 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 31 | The messaging protocol doesn't strictly need to be used for IPC though, and |
| 32 | there are some higher-level reasons for this adoption and for the specific |
| 33 | approach to integration outlined in this document. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 34 | |
| 35 | ### Code Health |
| 36 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 37 | At the moment we have fairly weak separation between components, with DEPS being |
| 38 | the strongest line of defense against increasing complexity. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 39 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 40 | A component Foo might hold a reference to some bit of component Bar's internal |
| 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 46 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 50 | |
| 51 | ### Modularity |
| 52 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 56 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 62 | |
| 63 | ### Mandoline |
| 64 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 70 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 71 | ## Mojo Overview |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 72 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 73 | This section provides a general overview of Mojo and some of its API features. |
| 74 | You can probably skip straight to |
| 75 | [Your First Mojo Application](#Your-First-Mojo-Application) if you just want to |
| 76 | get to some practical sample code. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 77 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 78 | The Mojo Embedder Development Kit (EDK) provides a suite of low-level IPC |
| 79 | primitives: **message pipes**, **data pipes**, and **shared buffers**. We'll |
| 80 | focus primarily on message pipes and the C++ bindings API in this document. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 81 | |
| 82 | _TODO: Java and JS bindings APIs should also be covered here._ |
| 83 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 84 | ### Message Pipes |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 85 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 86 | A message pipe is a lightweight primitive for reliable, bidirectional, queued |
| 87 | transfer of relatively small packets of data. Every pipe endpoint is identified |
| 88 | by a **handle** -- a unique process-wide integer identifying the endpoint to the |
| 89 | EDK. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 90 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 91 | A single message across a pipe consists of a binary payload and an array of zero |
| 92 | or more handles to be transferred. A pipe's endpoints may live in the same |
| 93 | process or in two different processes. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 94 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 95 | Pipes are easy to create. The `mojo::MessagePipe` type (see |
| 96 | `/third_party/mojo/src/mojo/public/cpp/system/message_pipe.h`) provides a nice |
| 97 | class wrapper with each endpoint represented as a scoped handle type (see |
| 98 | members `handle0` and `handle1` and the definition of |
| 99 | `mojo::ScopedMessagePipeHandle`). In the same header you can find |
| 100 | `WriteMessageRaw` and `ReadMessageRaw` definitions. These are in theory all one |
| 101 | needs to begin pushing things from one endpoint to the other. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 102 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 103 | While it's worth being aware of `mojo::MessagePipe` and the associated raw I/O |
| 104 | functions, you will rarely if ever have a use for them. Instead you'll typically |
| 105 | use bindings code generated from mojom interface definitions, along with the |
| 106 | public bindings API which mostly hides the underlying pipes. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 107 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 108 | ### Mojom Bindings |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 109 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 114 | |
| 115 | ``` |
| 116 | // frobinator.mojom |
| 117 | module frob; |
| 118 | interface Frobinator { |
| 119 | Frobinate(); |
| 120 | }; |
| 121 | ``` |
| 122 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 123 | This can be used to generate bindings for a very simple `Frobinator` interface. |
| 124 | Bindings are generated at build time and will match the location of the mojom |
| 125 | source file itself, mapped into the generated output directory for your Chromium |
| 126 | build. In this case one can expect to find files named `frobinator.mojom.js`, |
| 127 | `frobinator.mojom.cc`, `frobinator.mojom.h`, _etc._ |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 128 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 129 | The C++ header (`frobinator.mojom.h`) generated from this mojom will define a |
| 130 | pure virtual class interface named `frob::Frobinator` with a pure virtual method |
| 131 | of signature `void Frobinate()`. Any class which implements this interface is |
| 132 | effectively a `Frobinator` service. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 133 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 134 | ### C++ Bindings API |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 135 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 136 | Before we see an example implementation and usage of the Frobinator, there are a |
| 137 | handful of interesting bits in the public C++ bindings API you should be |
| 138 | familiar with. These complement generated bindings code and generally obviate |
| 139 | any need to use a `mojo::MessagePipe` directly. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 140 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 141 | In all of the cases below, `T` is the type of a generated bindings class |
| 142 | interface, such as the `frob::Frobinator` discussed above. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 143 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 144 | #### `mojo::InterfacePtr<T>` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 145 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 146 | Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 147 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 148 | `mojo::InterfacePtr<T>` is a typed proxy for a service of type `T`, which can be |
| 149 | bound to a message pipe endpoint. This class implements every interface method |
| 150 | on `T` by serializing a message (encoding the method call and its arguments) and |
| 151 | writing it to the pipe (if bound.) This is the standard way for C++ code to talk |
| 152 | to any Mojo service. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 153 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 154 | For illustrative purposes only, we can create a message pipe and bind an |
| 155 | `InterfacePtr` to one end as follows: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 156 | |
| 157 | ``` |
| 158 | mojo::MessagePipe pipe; |
| 159 | mojo::InterfacePtr<frob::Frobinator> frobinator; |
| 160 | frobinator.Bind( |
| 161 | mojo::InterfacePtrInfo<frob::Frobinator>(pipe.handle0.Pass(), 0u)); |
| 162 | ``` |
| 163 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 164 | You could then call `frobinator->Frobinate()` and read the encoded `Frobinate` |
| 165 | message from the other side of the pipe (`handle1`.) You most likely don't want |
| 166 | to do this though, because as you'll soon see there's a nicer way to establish |
| 167 | service pipes. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 168 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 169 | #### `mojo::InterfaceRequest<T>` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 170 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 171 | Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 172 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 177 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 183 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 184 | As with `InterfacePtr<T>`, we can manually bind an `InterfaceRequest<T>` to a |
| 185 | pipe endpoint: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 186 | |
| 187 | ``` |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 188 | mojo::MessagePipe pipe; |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 189 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 190 | mojo::InterfacePtr<frob::Frobinator> frobinator; |
| 191 | frobinator.Bind( |
| 192 | mojo::InterfacePtrInfo<frob::Frobinator>(pipe.handle0.Pass(), 0u)); |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 193 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 194 | mojo::InterfaceRequest<frob::Frobinator> frobinator_request; |
| 195 | frobinator_request.Bind(pipe.handle1.Pass()); |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 196 | ``` |
| 197 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 202 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 203 | #### `mojo::GetProxy<T>` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 204 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 205 | Defined in |
| 206 | `/third_party/mojo/src/mojo/public/cpp/bindings/interface`_request.h`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 207 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 208 | `mojo::GetProxy<T>` is the function you will most commonly use to create a new |
| 209 | message pipe. Its signature is as follows: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 210 | |
| 211 | ``` |
| 212 | template <typename T> |
| 213 | mojo::InterfaceRequest<T> GetProxy(mojo::InterfacePtr<T>* ptr); |
| 214 | ``` |
| 215 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 220 | |
| 221 | ``` |
| 222 | mojo::InterfacePtr<frob::Frobinator> frobinator; |
| 223 | mojo::InterfaceRequest<frob::Frobinator> frobinator_request = |
| 224 | mojo::GetProxy(&frobinator); |
| 225 | ``` |
| 226 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 227 | #### `mojo::Binding<T>` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 228 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 229 | Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/binding.h`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 230 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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>`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 237 | |
| 238 | A common usage pattern looks something like this: |
| 239 | |
| 240 | ``` |
| 241 | #include "components/frob/public/interfaces/frobinator.mojom.h" |
| 242 | #include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" |
| 243 | #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" |
| 244 | |
| 245 | class FrobinatorImpl : public frob::Frobinator { |
| 246 | public: |
| 247 | FrobinatorImpl(mojo::InterfaceRequest<frob::Frobinator> request) |
| 248 | : binding_(this, request.Pass()) {} |
| 249 | ~FrobinatorImpl() override {} |
| 250 | |
| 251 | private: |
| 252 | // frob::Frobinator: |
| 253 | void Frobinate() override { /* ... */ } |
| 254 | |
| 255 | mojo::Binding<frob::Frobinator> binding_; |
| 256 | }; |
| 257 | ``` |
| 258 | |
| 259 | And then we could write some code to test this: |
| 260 | |
| 261 | ``` |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 262 | // Fun fact: The bindings generator emits a type alias like this for every |
| 263 | // interface type. frob::FrobinatorPtr is an InterfacePtr<frob::Frobinator>. |
| 264 | frob::FrobinatorPtr frobinator; |
| 265 | scoped_ptr<FrobinatorImpl> impl( |
| 266 | new FrobinatorImpl(mojo::GetProxy(&frobinator))); |
| 267 | frobinator->Frobinate(); |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 268 | ``` |
| 269 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 270 | This will _eventually_ call `FrobinatorImpl::Frobinate()`. "Eventually," because |
| 271 | the sequence of events when `frobinator->Frobinate()` is called is roughly as |
| 272 | follows: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 273 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 274 | 1. A new message buffer is allocated and filled with an encoded 'Frobinate' |
| 275 | message. |
| 276 | 1. The EDK is asked to write this message to the pipe endpoint owned by the |
| 277 | `FrobinatorPtr`. |
| 278 | 1. If the call didn't happen on the Mojo IPC thread for this process, EDK hops |
| 279 | to the Mojo IPC thread. |
| 280 | 1. The EDK writes the message to the pipe. In this case the pipe endpoints live |
| 281 | in the same process, so this essentially a glorified `memcpy`. If they lived |
| 282 | in different processes this would be the point at which the data moved |
| 283 | across a real IPC channel. |
| 284 | 1. The EDK on the other end of the pipe is awoken on the Mojo IPC thread and |
| 285 | alerted to the message arrival. |
| 286 | 1. The EDK reads the message. |
| 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 293 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 296 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 297 | #### `mojo::StrongBinding<T>` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 298 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 299 | Defined in `third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 300 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 307 | |
| 308 | ## The Mojo Shell |
| 309 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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.) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 313 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 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 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 338 | Definitions for these interfaces can be found in |
| 339 | `/mojo/application/public/interfaces`. Also note that `mojo::URLRequest` is a |
| 340 | Mojo struct defined in |
| 341 | `/mojo/services/network/public/interfaces/url_loader.mojom`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 342 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 346 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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.) |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 350 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 354 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 362 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 363 | ### Mojo Applications |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 364 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 370 | |
| 371 | ``` |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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 | }; |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 381 | ``` |
| 382 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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/application/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). |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 389 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 394 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 399 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 400 | ### Other IPC Primitives |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 401 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 408 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 409 | ## Your First Mojo Application |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 410 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 414 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 415 | ### Hello, world! |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 416 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 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. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 423 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 424 | First create a new `//components/hello` directory. Inside this directory we're |
| 425 | going to add the following files: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 426 | |
| 427 | **components/hello/main.cc** |
| 428 | ``` |
| 429 | #include "base/logging.h" |
| 430 | #include "third_party/mojo/src/mojo/public/c/system/main.h" |
| 431 | |
| 432 | MojoResult MojoMain(MojoHandle shell_handle) { |
| 433 | LOG(ERROR) << "Hello, world!"; |
| 434 | return MOJO_RESULT_OK; |
| 435 | }; |
| 436 | ``` |
| 437 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 438 | **components/hello/BUILD.gn** |
| 439 | ``` |
| 440 | import("//mojo/public/mojo_application.gni") |
| 441 | |
| 442 | mojo_native_application("hello") { |
| 443 | sources = [ |
| 444 | "main.cc", |
| 445 | ] |
| 446 | deps = [ |
| 447 | "//base", |
| 448 | "//mojo/environment:chromium", |
| 449 | ] |
| 450 | } |
| 451 | ``` |
| 452 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 453 | For the sake of this example you'll also want to add your component as a |
| 454 | dependency somewhere in your local checkout to ensure its build files are |
| 455 | generated. The easiest thing to do there is probably to add a dependency on |
| 456 | `"//components/hello"` in the `"gn_all"` target of the top-level `//BUILD.gn`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 457 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 458 | Assuming you have a GN output directory at `out_gn/Debug`, you can build the |
| 459 | Mojo runner along with your shiny new app: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 460 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 461 | ninja -C out_gn/Debug mojo_runner components/hello |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 462 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 463 | In addition to the `mojo_runner` executable, this will produce a new binary at |
| 464 | `out_gn/Debug/hello/hello.mojo`. This binary is essentially a shared library |
| 465 | which exports your `MojoMain` function. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 466 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 467 | `mojo_runner` takes an application URL as its only argument and runs the |
| 468 | corresponding application. In its current state it resolves `mojo`-scheme URLs |
| 469 | such that `"mojo:foo"` maps to the file `"foo/foo.mojo"` relative to the |
| 470 | `mojo_runner` path (_i.e._ your output directory.) This means you can run your |
| 471 | new app with the following command: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 472 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 473 | out_gn/Debug/mojo_runner mojo:hello |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 474 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 475 | You should see our little `"Hello, world!"` error log followed by a hanging |
| 476 | application. You can `^C` to kill it. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 477 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 478 | ### Exposing Services |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 479 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 480 | An app that prints `"Hello, world!"` isn't terribly interesting. At a bare |
| 481 | minimum your app should implement `mojo::ApplicationDelegate` and expose at |
| 482 | least one service to connecting applications. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 483 | |
| 484 | Let's update `main.cc` with the following contents: |
| 485 | |
| 486 | **components/hello/main.cc** |
| 487 | ``` |
| 488 | #include "components/hello/hello_app.h" |
| 489 | #include "mojo/application/public/cpp/application_runner.h" |
| 490 | #include "third_party/mojo/src/mojo/public/c/system/main.h" |
| 491 | |
| 492 | MojoResult MojoMain(MojoHandle shell_handle) { |
| 493 | mojo::ApplicationRunner runner(new hello::HelloApp); |
| 494 | return runner.Run(shell_handle); |
| 495 | }; |
| 496 | ``` |
| 497 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 498 | This is a pretty typical looking `MojoMain`. Most of the time this is all you |
| 499 | want -- a `mojo::ApplicationRunner` constructed over a |
| 500 | `mojo::ApplicationDelegate` instance, `Run()` with the pipe handle received from |
| 501 | the shell. We'll add some new files to the app as well: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 502 | |
| 503 | **components/hello/public/interfaces/greeter.mojom** |
| 504 | ``` |
| 505 | module hello; |
| 506 | interface Greeter { |
| 507 | Greet(string name) => (string greeting); |
| 508 | }; |
| 509 | ``` |
| 510 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 511 | Note the new arrow syntax on the `Greet` method. This indicates that the caller |
| 512 | expects a response from the service. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 513 | |
| 514 | **components/hello/public/interfaces/BUILD.gn** |
| 515 | ``` |
| 516 | import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") |
| 517 | |
| 518 | mojom("interfaces") { |
| 519 | sources = [ |
| 520 | "greeter.mojom", |
| 521 | ] |
| 522 | } |
| 523 | ``` |
| 524 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 525 | **components/hello/hello_app.h** |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 526 | ``` |
| 527 | #ifndef COMPONENTS_HELLO_HELLO_APP_H_ |
| 528 | #define COMPONENTS_HELLO_HELLO_APP_H_ |
| 529 | |
| 530 | #include "base/macros.h" |
| 531 | #include "components/hello/public/interfaces/greeter.mojom.h" |
| 532 | #include "mojo/application/public/cpp/application_delegate.h" |
| 533 | #include "mojo/application/public/cpp/interface_factory.h" |
| 534 | |
| 535 | namespace hello { |
| 536 | |
| 537 | class HelloApp : public mojo::ApplicationDelegate, |
| 538 | public mojo::InterfaceFactory<Greeter> { |
| 539 | public: |
| 540 | HelloApp(); |
| 541 | ~HelloApp() override; |
| 542 | |
| 543 | private: |
| 544 | // mojo::ApplicationDelegate: |
| 545 | bool ConfigureIncomingConnection( |
| 546 | mojo::ApplicationConnection* connection) override; |
| 547 | |
| 548 | // mojo::InterfaceFactory<Greeter>: |
| 549 | void Create(mojo::ApplicationConnection* connection, |
| 550 | mojo::InterfaceRequest<Greeter> request) override; |
| 551 | |
| 552 | DISALLOW_COPY_AND_ASSIGN(HelloApp); |
| 553 | }; |
| 554 | |
| 555 | } // namespace hello |
| 556 | |
| 557 | #endif // COMPONENTS_HELLO_HELLO_APP_H_ |
| 558 | ``` |
| 559 | |
| 560 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 561 | **components/hello/hello_app.cc** |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 562 | ``` |
| 563 | #include "base/macros.h" |
| 564 | #include "components/hello/hello_app.h" |
| 565 | #include "mojo/application/public/cpp/application_connection.h" |
| 566 | #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h" |
| 567 | #include "third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h" |
| 568 | |
| 569 | namespace hello { |
| 570 | |
| 571 | namespace { |
| 572 | |
| 573 | class GreeterImpl : public Greeter { |
| 574 | public: |
| 575 | GreeterImpl(mojo::InterfaceRequest<Greeter> request) |
| 576 | : binding_(this, request.Pass()) { |
| 577 | } |
| 578 | |
| 579 | ~GreeterImpl() override {} |
| 580 | |
| 581 | private: |
| 582 | // Greeter: |
| 583 | void Greet(const mojo::String& name, const GreetCallback& callback) override { |
| 584 | callback.Run("Hello, " + std::string(name) + "!"); |
| 585 | } |
| 586 | |
| 587 | mojo::StrongBinding<Greeter> binding_; |
| 588 | |
| 589 | DISALLOW_COPY_AND_ASSIGN(GreeterImpl); |
| 590 | }; |
| 591 | |
| 592 | } // namespace |
| 593 | |
| 594 | HelloApp::HelloApp() { |
| 595 | } |
| 596 | |
| 597 | HelloApp::~HelloApp() { |
| 598 | } |
| 599 | |
| 600 | bool HelloApp::ConfigureIncomingConnection( |
| 601 | mojo::ApplicationConnection* connection) { |
| 602 | connection->AddService<Greeter>(this); |
| 603 | return true; |
| 604 | } |
| 605 | |
| 606 | void HelloApp::Create( |
| 607 | mojo::ApplicationConnection* connection, |
| 608 | mojo::InterfaceRequest<Greeter> request) { |
| 609 | new GreeterImpl(request.Pass()); |
| 610 | } |
| 611 | |
| 612 | } // namespace hello |
| 613 | ``` |
| 614 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 615 | And finally we need to update our app's `BUILD.gn` to add some new sources and |
| 616 | dependencies: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 617 | |
| 618 | **components/hello/BUILD.gn** |
| 619 | ``` |
| 620 | import("//mojo/public/mojo_application.gni") |
| 621 | |
| 622 | source_set("lib") { |
| 623 | sources = [ |
| 624 | "hello_app.cc", |
| 625 | "hello_app.h", |
| 626 | ] |
| 627 | deps = [ |
| 628 | "//base", |
| 629 | "//components/hello/public/interfaces", |
| 630 | "//mojo/application/public/cpp", |
| 631 | "//mojo/environment:chromium", |
| 632 | ] |
| 633 | } |
| 634 | |
| 635 | mojo_native_application("hello") { |
| 636 | sources = [ |
| 637 | "main.cc", |
| 638 | ], |
| 639 | deps = [ ":lib" ] |
| 640 | } |
| 641 | ``` |
| 642 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 643 | Note that we build the bulk of our application sources as a static library |
| 644 | separate from the `MojoMain` definition. Following this convention is |
| 645 | particularly useful for Chromium integration, as we'll see later. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 646 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 647 | There's a lot going on here and it would be useful to familiarize yourself with |
| 648 | the definitions of `mojo::ApplicationDelegate`, `mojo::ApplicationConnection`, |
| 649 | and `mojo::InterfaceFactory<T>`. The TL;DR though is that if someone connects to |
| 650 | this app and requests a service named `"hello::Greeter"`, the app will create a |
| 651 | new `GreeterImpl` and bind it to that request pipe. From there the connecting |
| 652 | app can call `Greeter` interface methods and they'll be routed to that |
| 653 | `GreeterImpl` instance. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 654 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 655 | Although this appears to be a more interesting application, we need some way to |
| 656 | actually connect and test the behavior of our new service. Let's write an app |
| 657 | test! |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 658 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 659 | ### App Tests |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 660 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 661 | App tests run inside a test application, giving test code access to a shell |
| 662 | which can connect to one or more applications-under-test. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 663 | |
| 664 | First let's introduce some test code: |
| 665 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 666 | **components/hello/hello_apptest.cc** |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 667 | ``` |
| 668 | #include "base/bind.h" |
| 669 | #include "base/callback.h" |
| 670 | #include "base/logging.h" |
| 671 | #include "base/macros.h" |
| 672 | #include "base/run_loop.h" |
| 673 | #include "components/hello/public/interfaces/greeter.mojom.h" |
| 674 | #include "mojo/application/public/cpp/application_impl.h" |
| 675 | #include "mojo/application/public/cpp/application_test_base.h" |
| 676 | |
| 677 | namespace hello { |
| 678 | namespace { |
| 679 | |
| 680 | class HelloAppTest : public mojo::test::ApplicationTestBase { |
| 681 | public: |
| 682 | HelloAppTest() {} |
| 683 | ~HelloAppTest() override {} |
| 684 | |
| 685 | void SetUp() override { |
| 686 | ApplicationTestBase::SetUp(); |
| 687 | mojo::URLRequestPtr app_url = mojo::URLRequest::New(); |
| 688 | app_url->url = "mojo:hello"; |
| 689 | application_impl()->ConnectToService(app_url.Pass(), &greeter_); |
| 690 | } |
| 691 | |
| 692 | Greeter* greeter() { return greeter_.get(); } |
| 693 | |
| 694 | private: |
| 695 | GreeterPtr greeter_; |
| 696 | |
| 697 | DISALLOW_COPY_AND_ASSIGN(HelloAppTest); |
| 698 | }; |
| 699 | |
| 700 | void ExpectGreeting(const mojo::String& expected_greeting, |
| 701 | const base::Closure& continuation, |
| 702 | const mojo::String& actual_greeting) { |
| 703 | EXPECT_EQ(expected_greeting, actual_greeting); |
| 704 | continuation.Run(); |
| 705 | }; |
| 706 | |
| 707 | TEST_F(HelloAppTest, GreetWorld) { |
| 708 | base::RunLoop loop; |
| 709 | greeter()->Greet("world", base::Bind(&ExpectGreeting, "Hello, world!", |
| 710 | loop.QuitClosure())); |
| 711 | loop.Run(); |
| 712 | } |
| 713 | |
| 714 | } // namespace |
| 715 | } // namespace hello |
| 716 | ``` |
| 717 | |
| 718 | We also need to add a new rule to `//components/hello/BUILD.gn`: |
| 719 | |
| 720 | ``` |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 721 | mojo_native_application("apptests") { |
| 722 | output_name = "hello_apptests" |
| 723 | testonly = true |
| 724 | sources = [ |
| 725 | "hello_apptest.cc", |
| 726 | ] |
| 727 | deps = [ |
| 728 | "//base", |
| 729 | "//mojo/application/public/cpp:test_support", |
| 730 | ] |
| 731 | public_deps = [ |
| 732 | "//components/hello/public/interfaces", |
| 733 | ] |
| 734 | data_deps = [ ":hello" ] |
| 735 | } |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 736 | ``` |
| 737 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 738 | Note that the `//components/hello:apptests` target does **not** have a binary |
| 739 | dependency on either `HelloApp` or `GreeterImpl` implementations; instead it |
| 740 | depends only on the component's public interface definitions. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 741 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 742 | The `data_deps` entry ensures that `hello.mojo` is up-to-date when `apptests` is |
| 743 | built. This is desirable because the test connects to `"mojo:hello"` which will |
| 744 | in turn load `hello.mojo` from disk. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 745 | |
| 746 | You can now build the test suite: |
| 747 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 748 | ninja -C out_gn/Debug components/hello:apptests |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 749 | |
| 750 | and run it: |
| 751 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 752 | out_gn/Debug/mojo_runner mojo:hello_apptests |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 753 | |
| 754 | You should see one test (`HelloAppTest.GreetWorld`) passing. |
| 755 | |
| 756 | One particularly interesting bit of code in this test is in the `SetUp` method: |
| 757 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 758 | mojo::URLRequestPtr app_url = mojo::URLRequest::New(); |
| 759 | app_url->url = "mojo:hello"; |
| 760 | application_impl()->ConnectToService(app_url.Pass(), &greeter_); |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 761 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 762 | `ConnectToService` is a convenience method provided by `mojo::ApplicationImpl`, |
| 763 | and it's essentially a shortcut for calling out to the shell's |
| 764 | `ConnectToApplication` method with the given application URL (in this case |
| 765 | `"mojo:hello"`) and then connecting to a specific service provided by that app |
| 766 | via its `ServiceProvider`'s `ConnectToService` method. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 767 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 768 | Note that generated interface bindings include a constant string to identify |
| 769 | each interface by name; so for example the generated `hello::Greeter` type |
| 770 | defines a static C string: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 771 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 772 | const char hello::Greeter::Name_[] = "hello::Greeter"; |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 773 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 774 | This is exploited by the definition of |
| 775 | `mojo::ApplicationConnection::ConnectToService<T>`, which uses `T::Name_` as the |
| 776 | name of the service to connect to. The type `T` in this context is inferred from |
| 777 | the `InterfacePtr<T>*` argument. You can inspect the definition of |
| 778 | `ConnectToService` in `/mojo/application/public/cpp/application_connection.h` |
| 779 | for additional clarity. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 780 | |
| 781 | We could have instead written this code as: |
| 782 | |
| 783 | ``` |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 784 | mojo::URLRequestPtr app_url = mojo::URLRequest::New(); |
| 785 | app_url->url = "mojo::hello"; |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 786 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 787 | mojo::ServiceProviderPtr services; |
| 788 | application_impl()->shell()->ConnectToApplication( |
| 789 | app_url.Pass(), mojo::GetProxy(&services), |
| 790 | // We pass a null provider since we aren't exposing any of our own |
| 791 | // services to the target app. |
| 792 | mojo::ServiceProviderPtr()); |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 793 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 794 | mojo::InterfaceRequest<hello::Greeter> greeter_request = |
| 795 | mojo::GetProxy(&greeter_); |
| 796 | services->ConnectToService(hello::Greeter::Name_, |
| 797 | greeter_request.PassMessagePipe()); |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 798 | ``` |
| 799 | |
| 800 | The net result is the same, but 3-line version seems much nicer. |
| 801 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 802 | ## Chromium Integration |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 803 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 804 | Up until now we've been using `mojo_runner` to load and run `.mojo` binaries |
| 805 | dynamically. While this model is used by Mandoline and may eventually be used in |
| 806 | Chromium as well, Chromium is at the moment confined to running statically |
| 807 | linked application code. This means we need some way to register applications |
| 808 | with the browser's Mojo shell. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 809 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 810 | It also means that, rather than using the binary output of a |
| 811 | `mojo_native_application` target, some part of Chromium must link against the |
| 812 | app's static library target (_e.g._, `"//components/hello:lib"`) and register a |
| 813 | URL handler to teach the shell how to launch an instance of the app. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 814 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 815 | When registering an app URL in Chromium it probably makes sense to use the same |
| 816 | mojo-scheme URL used for the app in Mandoline. For example the media renderer |
| 817 | app is referenced by the `"mojo:media"` URL in both Mandoline and Chromium. In |
| 818 | Mandoline this resolves to a dynamically-loaded `.mojo` binary on disk, but in |
| 819 | Chromium it resolves to a static application loader linked into Chromium. The |
| 820 | net result is the same in both cases: other apps can use the shell to connect to |
| 821 | `"mojo:media"` and use its services. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 822 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 823 | This section explores different ways to register and connect to `"mojo:hello"` |
| 824 | in Chromium. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 825 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 826 | ### In-Process Applications |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 827 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 828 | Applications can be set up to run within the browser process via |
| 829 | `ContentBrowserClient::RegisterInProcessMojoApplications`. This method populates |
| 830 | a mapping from URL to `base::Callback<scoped_ptr<mojo::ApplicationDelegate>()>` |
| 831 | (_i.e._, a factory function which creates a new `mojo::ApplicationDelegate` |
| 832 | instance), so registering a new app means adding an entry to this map. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 833 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 834 | Let's modify `ChromeContentBrowserClient::RegisterInProcessMojoApplications` |
| 835 | (in `//chrome/browser/chrome_content_browser_client.cc`) by adding the following |
| 836 | code: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 837 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 838 | apps->insert(std::make_pair(GURL("mojo:hello"), |
| 839 | base::Bind(&HelloApp::CreateApp))); |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 840 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 841 | you'll also want to add the following convenience method to your `HelloApp` |
| 842 | definition in `//components/hello/hello_app.h`: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 843 | |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 844 | static scoped_ptr<mojo::ApplicationDelegate> HelloApp::CreateApp() { |
| 845 | return scoped_ptr<mojo::ApplicationDelegate>(new HelloApp); |
| 846 | } |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 847 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 848 | This introduces a dependency from `//chrome/browser` on to |
| 849 | `//components/hello:lib`, which you can add to the `"browser"` target's deps in |
| 850 | `//chrome/browser/BUILD.gn`. You'll of course also need to include |
| 851 | `"components/hello/hello_app.h"` in `chrome_content_browser_client.cc`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 852 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 853 | That's it! Now if an app comes to the shell asking to connect to `"mojo:hello"` |
| 854 | and app is already running, it'll get connected to our `HelloApp` and have |
| 855 | access to the `Greeter` service. If the app wasn't already running, it will |
| 856 | first be launched on a new thread. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 857 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 858 | ### Connecting From the Browser |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 859 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 860 | We've already seen how apps can connect to each other using their own private |
| 861 | shell proxy, but the vast majority of Chromium code doesn't yet belong to a Mojo |
| 862 | application. So how do we use an app's services from arbitrary browser code? We |
| 863 | use `content::MojoAppConnection`, like this: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 864 | |
| 865 | ``` |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 866 | #include "base/bind.h" |
| 867 | #include "base/logging.h" |
| 868 | #include "components/hello/public/interfaces/greeter.mojom.h" |
| 869 | #include "content/public/browser/mojo_app_connection.h" |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 870 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 871 | void LogGreeting(const mojo::String& greeting) { |
| 872 | LOG(INFO) << greeting; |
| 873 | } |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 874 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 875 | void GreetTheWorld() { |
| 876 | scoped_ptr<content::MojoAppConnection> connection = |
| 877 | content::MojoAppConnection::Create("mojo:hello", |
| 878 | content::kBrowserMojoAppUrl); |
| 879 | hello::GreeterPtr greeter; |
| 880 | connection->ConnectToService(&greeter); |
| 881 | greeter->Greet("world", base::Bind(&LogGreeting)); |
| 882 | } |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 883 | ``` |
| 884 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 885 | A `content::MojoAppConnection`, while not thread-safe, may be created and safely |
| 886 | used on any single browser thread. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 887 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 888 | You could add the above code to a new browsertest to convince yourself that it |
| 889 | works. In fact you might want to take a peek at |
| 890 | `MojoShellTest.TestBrowserConnection` (in |
| 891 | `/content/browser/mojo_shell_browsertest.cc`) which registers and tests an |
| 892 | in-process Mojo app. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 893 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 894 | Finally, note that `MojoAppConnection::Create` takes two URLs. The first is the |
| 895 | target app URL, and the second is the source URL. Since we're not really a Mojo |
| 896 | app, but we are still trusted browser code, the shell will gladly use this URL |
| 897 | as the `requestor_url` when establishing an incoming connection to the target |
| 898 | app. This allows browser code to masquerade as a Mojo app at the given URL. |
| 899 | `content::kBrowserMojoAppUrl` (which is presently `"system:content_browser"`) is |
| 900 | a reasonable default choice when a more specific app identity isn't required. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 901 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 902 | ### Out-of-Process Applications |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 903 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 904 | If an app URL isn't registered for in-process loading, the shell assumes it must |
| 905 | be an out-of-process application. If the shell doesn't already have a known |
| 906 | instance of the app running, a new utility process is launched and the |
| 907 | application request is passed onto it. Then if the app URL is registered in the |
| 908 | utility process, the app will be loaded there. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 909 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 910 | Similar to in-process registration, a URL mapping needs to be registered in |
| 911 | `ContentUtilityClient::RegisterMojoApplications`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 912 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 913 | Once again you can take a peek at `/content/browser/mojo_shell_browsertest.cc` |
| 914 | for an end-to-end example of testing an out-of-process Mojo app from browser |
| 915 | code. Note that `content_browsertests` runs on `content_shell`, which uses |
| 916 | `ShellContentUtilityClient` as defined |
| 917 | `/content/shell/utility/shell_content_utility_client.cc`. This code registers a |
| 918 | common OOP test app. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 919 | |
| 920 | ## Unsandboxed Out-of-Process Applications |
| 921 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 922 | By default new utility processes run in a sandbox. If you want your Mojo app to |
| 923 | run out-of-process and unsandboxed (which you **probably do not**), you can |
| 924 | register its URL via |
| 925 | `ContentBrowserClient::RegisterUnsandboxedOutOfProcessMojoApplications`. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 926 | |
| 927 | ## Connecting From `RenderFrame` |
| 928 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 929 | We can also connect to Mojo apps from a `RenderFrame`. This is made possible by |
| 930 | `RenderFrame`'s `GetServiceRegistry()` interface. The `ServiceRegistry` can be |
| 931 | used to acquire a shell proxy and in turn connect to an app like so: |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 932 | |
| 933 | ``` |
| 934 | void GreetWorld(content::RenderFrame* frame) { |
| 935 | mojo::ShellPtr shell; |
| 936 | frame->GetServiceRegistry()->ConnectToRemoteService( |
| 937 | mojo::GetProxy(&shell)); |
| 938 | |
| 939 | mojo::URLRequestPtr request = mojo::URLRequest::New(); |
| 940 | request->url = "mojo:hello"; |
| 941 | |
| 942 | mojo::ServiceProviderPtr hello_services; |
| 943 | shell->ConnectToApplication( |
| 944 | request.Pass(), mojo::GetProxy(&hello_services), nullptr); |
| 945 | |
| 946 | hello::GreeterPtr greeter; |
| 947 | hello_services->ConnectToService( |
| 948 | hello::Greeter::Name_, mojo::GetProxy(&greeter).PassMessagePipe()); |
| 949 | } |
| 950 | ``` |
| 951 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 952 | It's important to note that connections made through the frame's shell proxy |
| 953 | will appear to come from the frame's `SiteInstance` URL. For example, if the |
| 954 | frame has loaded `https://example.com/`, `HelloApp`'s incoming |
| 955 | `mojo::ApplicationConnection` in this case will have a remote application URL of |
| 956 | `"https://example.com/"`. This allows apps to expose their services to web |
| 957 | frames on a per-origin basis if needed. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 958 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 959 | ### Connecting From Java |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 960 | |
| 961 | TODO |
| 962 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 963 | ### Connecting From `JavaScript` |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 964 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 965 | This is still a work in progress and might not really take shape until the |
| 966 | Blink+Chromium merge. In the meantime there are some end-to-end WebUI examples |
| 967 | in `/content/browser/webui/web_ui_mojo_browsertest.cc`. In particular, |
| 968 | `WebUIMojoTest.ConnectToApplication` connects from a WebUI frame to a test app |
| 969 | running in a new utility process. |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 970 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 971 | ## FAQ |
andybons | 3322f76 | 2015-08-24 21:37:09 | [diff] [blame] | 972 | |
andybons | ad92aa3 | 2015-08-31 02:27:44 | [diff] [blame^] | 973 | Nothing here yet! |