blob: b9919e22414936149e81fb477ef67bf7ee6843aa [file] [log] [blame] [view]
andybonsad92aa32015-08-31 02:27:441# Mojo in Chromium
andybons3322f762015-08-24 21:37:092
andybonsad92aa32015-08-31 02:27:443**THIS DOCUIMENT IS A WORK IN PROGRESS.** As long as this notice exists, you
4should probably ignore everything below it.
andybons3322f762015-08-24 21:37:095
andybonsad92aa32015-08-31 02:27:446This document is intended to serve as a Mojo primer for Chromium developers. No
7prior knowledge of Mojo is assumed, but you should have a decent grasp of C++
8and be familiar with Chromium's multi-process architecture as well as common
9concepts used throughout Chromium such as smart pointers, message loops,
10callback binding, and so on.
andybons3322f762015-08-24 21:37:0911
andybonsad92aa32015-08-31 02:27:4412[TOC]
andybons3322f762015-08-24 21:37:0913
14## Should I Bother Reading This?
15
andybonsad92aa32015-08-31 02:27:4416If you're planning to build a Chromium feature that needs IPC and you aren't
17already 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.**
andybons3322f762015-08-24 21:37:0920
21## Why Mojo?
22
andybonsad92aa32015-08-31 02:27:4423Mojo provides IPC primitives for pushing messages and data around between
24transferrable endpoints which may or may not cross process boundaries; it
25simplifies threading with regard to IPC; it standardizes message serialization
26in a way that's resilient to versioning issues; and it can be used with relative
27ease and consistency across a number of languages including C++, Java, and
28`JavaScript` -- all languages which comprise a significant share of Chromium
29code.
andybons3322f762015-08-24 21:37:0930
andybonsad92aa32015-08-31 02:27:4431The messaging protocol doesn't strictly need to be used for IPC though, and
32there are some higher-level reasons for this adoption and for the specific
33approach to integration outlined in this document.
andybons3322f762015-08-24 21:37:0934
35### Code Health
36
andybonsad92aa32015-08-31 02:27:4437At the moment we have fairly weak separation between components, with DEPS being
38the strongest line of defense against increasing complexity.
andybons3322f762015-08-24 21:37:0939
andybonsad92aa32015-08-31 02:27:4440A component Foo might hold a reference to some bit of component Bar's internal
41state, or it might expect Bar to initialize said internal state in some
42particular order. These sorts of problems are reasonably well-mitigated by the
43code review process, but they can (and do) still slip through the cracks, and
44they have a noticeable cumulative effect on complexity as the code base
45continues to grow.
andybons3322f762015-08-24 21:37:0946
andybonsad92aa32015-08-31 02:27:4447We think we can make a lasting positive impact on code health by establishing
48more concrete boundaries between components, and this is something a library
49like Mojo gives us an opportunity to do.
andybons3322f762015-08-24 21:37:0950
51### Modularity
52
andybonsad92aa32015-08-31 02:27:4453In addition to code health -- which alone could be addressed in any number of
54ways that don't involve Mojo -- this approach opens doors to build and
55distribute parts of Chrome separately from the main binary.
andybons3322f762015-08-24 21:37:0956
andybonsad92aa32015-08-31 02:27:4457While we're not currently taking advantage of this capability, doing so remains
58a long-term goal due to prohibitive binary size constraints in emerging mobile
59markets. Many open questions around the feasibility of this goal should be
60answered by the experimental Mandoline project as it unfolds, but the Chromium
61project can be technically prepared for such a transition in the meantime.
andybons3322f762015-08-24 21:37:0962
63### Mandoline
64
andybonsad92aa32015-08-31 02:27:4465The Mandoline project is producing a potential replacement for `src/content`.
66Because Mandoline components are Mojo apps, and Chromium is now capable of
67loading Mojo apps (somethings we'll discuss later), Mojo apps can be shared
68between both projects with minimal effort. Developing your feature as or within
69a Mojo application can mean you're contributing to both Chromium and Mandoline.
andybons3322f762015-08-24 21:37:0970
andybonsad92aa32015-08-31 02:27:4471## Mojo Overview
andybons3322f762015-08-24 21:37:0972
andybonsad92aa32015-08-31 02:27:4473This section provides a general overview of Mojo and some of its API features.
74You can probably skip straight to
75[Your First Mojo Application](#Your-First-Mojo-Application) if you just want to
76get to some practical sample code.
andybons3322f762015-08-24 21:37:0977
andybonsad92aa32015-08-31 02:27:4478The Mojo Embedder Development Kit (EDK) provides a suite of low-level IPC
79primitives: **message pipes**, **data pipes**, and **shared buffers**. We'll
80focus primarily on message pipes and the C++ bindings API in this document.
andybons3322f762015-08-24 21:37:0981
82_TODO: Java and JS bindings APIs should also be covered here._
83
andybonsad92aa32015-08-31 02:27:4484### Message Pipes
andybons3322f762015-08-24 21:37:0985
andybonsad92aa32015-08-31 02:27:4486A message pipe is a lightweight primitive for reliable, bidirectional, queued
87transfer of relatively small packets of data. Every pipe endpoint is identified
88by a **handle** -- a unique process-wide integer identifying the endpoint to the
89EDK.
andybons3322f762015-08-24 21:37:0990
andybonsad92aa32015-08-31 02:27:4491A single message across a pipe consists of a binary payload and an array of zero
92or more handles to be transferred. A pipe's endpoints may live in the same
93process or in two different processes.
andybons3322f762015-08-24 21:37:0994
andybonsad92aa32015-08-31 02:27:4495Pipes are easy to create. The `mojo::MessagePipe` type (see
96`/third_party/mojo/src/mojo/public/cpp/system/message_pipe.h`) provides a nice
97class wrapper with each endpoint represented as a scoped handle type (see
98members `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
101needs to begin pushing things from one endpoint to the other.
andybons3322f762015-08-24 21:37:09102
andybonsad92aa32015-08-31 02:27:44103While it's worth being aware of `mojo::MessagePipe` and the associated raw I/O
104functions, you will rarely if ever have a use for them. Instead you'll typically
105use bindings code generated from mojom interface definitions, along with the
106public bindings API which mostly hides the underlying pipes.
andybons3322f762015-08-24 21:37:09107
andybonsad92aa32015-08-31 02:27:44108### Mojom Bindings
andybons3322f762015-08-24 21:37:09109
andybonsad92aa32015-08-31 02:27:44110Mojom is the IDL for Mojo interfaces. When given a mojom file, the bindings
111generator outputs a collection of bindings libraries for each supported
112language. Mojom syntax is fairly straightforward (TODO: Link to a mojom language
113spec?). Consider the example mojom file below:
andybons3322f762015-08-24 21:37:09114
115```
116// frobinator.mojom
117module frob;
118interface Frobinator {
119 Frobinate();
120};
121```
122
andybonsad92aa32015-08-31 02:27:44123This can be used to generate bindings for a very simple `Frobinator` interface.
124Bindings are generated at build time and will match the location of the mojom
125source file itself, mapped into the generated output directory for your Chromium
126build. In this case one can expect to find files named `frobinator.mojom.js`,
127`frobinator.mojom.cc`, `frobinator.mojom.h`, _etc._
andybons3322f762015-08-24 21:37:09128
andybonsad92aa32015-08-31 02:27:44129The C++ header (`frobinator.mojom.h`) generated from this mojom will define a
130pure virtual class interface named `frob::Frobinator` with a pure virtual method
131of signature `void Frobinate()`. Any class which implements this interface is
132effectively a `Frobinator` service.
andybons3322f762015-08-24 21:37:09133
andybonsad92aa32015-08-31 02:27:44134### C++ Bindings API
andybons3322f762015-08-24 21:37:09135
andybonsad92aa32015-08-31 02:27:44136Before we see an example implementation and usage of the Frobinator, there are a
137handful of interesting bits in the public C++ bindings API you should be
138familiar with. These complement generated bindings code and generally obviate
139any need to use a `mojo::MessagePipe` directly.
andybons3322f762015-08-24 21:37:09140
andybonsad92aa32015-08-31 02:27:44141In all of the cases below, `T` is the type of a generated bindings class
142interface, such as the `frob::Frobinator` discussed above.
andybons3322f762015-08-24 21:37:09143
andybonsad92aa32015-08-31 02:27:44144#### `mojo::InterfacePtr<T>`
andybons3322f762015-08-24 21:37:09145
andybonsad92aa32015-08-31 02:27:44146Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/interface_ptr.h`.
andybons3322f762015-08-24 21:37:09147
andybonsad92aa32015-08-31 02:27:44148`mojo::InterfacePtr<T>` is a typed proxy for a service of type `T`, which can be
149bound to a message pipe endpoint. This class implements every interface method
150on `T` by serializing a message (encoding the method call and its arguments) and
151writing it to the pipe (if bound.) This is the standard way for C++ code to talk
152to any Mojo service.
andybons3322f762015-08-24 21:37:09153
andybonsad92aa32015-08-31 02:27:44154For illustrative purposes only, we can create a message pipe and bind an
155`InterfacePtr` to one end as follows:
andybons3322f762015-08-24 21:37:09156
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
andybonsad92aa32015-08-31 02:27:44164You could then call `frobinator->Frobinate()` and read the encoded `Frobinate`
165message from the other side of the pipe (`handle1`.) You most likely don't want
166to do this though, because as you'll soon see there's a nicer way to establish
167service pipes.
andybons3322f762015-08-24 21:37:09168
andybonsad92aa32015-08-31 02:27:44169#### `mojo::InterfaceRequest<T>`
andybons3322f762015-08-24 21:37:09170
andybonsad92aa32015-08-31 02:27:44171Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h`.
andybons3322f762015-08-24 21:37:09172
andybonsad92aa32015-08-31 02:27:44173`mojo::InterfaceRequest<T>` is a typed container for a message pipe endpoint
174that should _eventually_ be bound to a service implementation. An
175`InterfaceRequest` doesn't actually _do_ anything, it's just a way of holding
176onto an endpoint without losing interface type information.
andybons3322f762015-08-24 21:37:09177
andybonsad92aa32015-08-31 02:27:44178A 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
180other message pipe) who is expected to eventually bind it to a concrete service
181implementation. `InterfaceRequest<T>` is here for that purpose and is, as we'll
182see later, a first-class concept in Mojom interface definitions.
andybons3322f762015-08-24 21:37:09183
andybonsad92aa32015-08-31 02:27:44184As with `InterfacePtr<T>`, we can manually bind an `InterfaceRequest<T>` to a
185pipe endpoint:
andybons3322f762015-08-24 21:37:09186
187```
andybonsad92aa32015-08-31 02:27:44188mojo::MessagePipe pipe;
andybons3322f762015-08-24 21:37:09189
andybonsad92aa32015-08-31 02:27:44190mojo::InterfacePtr<frob::Frobinator> frobinator;
191frobinator.Bind(
192 mojo::InterfacePtrInfo<frob::Frobinator>(pipe.handle0.Pass(), 0u));
andybons3322f762015-08-24 21:37:09193
andybonsad92aa32015-08-31 02:27:44194mojo::InterfaceRequest<frob::Frobinator> frobinator_request;
195frobinator_request.Bind(pipe.handle1.Pass());
andybons3322f762015-08-24 21:37:09196```
197
andybonsad92aa32015-08-31 02:27:44198At this point we could start making calls to `frobinator->Frobinate()` as
199before, but they'll just sit in queue waiting for the request side to be bound.
200Note that the basic logic in the snippet above is such a common pattern that
201there's a convenient API function which does it for us.
andybons3322f762015-08-24 21:37:09202
andybonsad92aa32015-08-31 02:27:44203#### `mojo::GetProxy<T>`
andybons3322f762015-08-24 21:37:09204
andybonsad92aa32015-08-31 02:27:44205Defined in
206`/third_party/mojo/src/mojo/public/cpp/bindings/interface`_request.h`.
andybons3322f762015-08-24 21:37:09207
andybonsad92aa32015-08-31 02:27:44208`mojo::GetProxy<T>` is the function you will most commonly use to create a new
209message pipe. Its signature is as follows:
andybons3322f762015-08-24 21:37:09210
211```
212template <typename T>
213mojo::InterfaceRequest<T> GetProxy(mojo::InterfacePtr<T>* ptr);
214```
215
andybonsad92aa32015-08-31 02:27:44216This function creates a new message pipe, binds one end to the given
217`InterfacePtr` argument, and binds the other end to a new `InterfaceRequest`
218which it then returns. Equivalent to the sample code just above is the following
219snippet:
andybons3322f762015-08-24 21:37:09220
221```
222 mojo::InterfacePtr<frob::Frobinator> frobinator;
223 mojo::InterfaceRequest<frob::Frobinator> frobinator_request =
224 mojo::GetProxy(&frobinator);
225```
226
andybonsad92aa32015-08-31 02:27:44227#### `mojo::Binding<T>`
andybons3322f762015-08-24 21:37:09228
andybonsad92aa32015-08-31 02:27:44229Defined in `/third_party/mojo/src/mojo/public/cpp/bindings/binding.h`.
andybons3322f762015-08-24 21:37:09230
andybonsad92aa32015-08-31 02:27:44231Binds one end of a message pipe to an implementation of service `T`. A message
232sent from the other end of the pipe will be read and, if successfully decoded as
233a `T` message, will invoke the corresponding call on the bound `T`
234implementation. 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
236taken from a passed `InterfaceRequest<T>`.
andybons3322f762015-08-24 21:37:09237
238A 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
245class 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
259And then we could write some code to test this:
260
261```
andybonsad92aa32015-08-31 02:27:44262// Fun fact: The bindings generator emits a type alias like this for every
263// interface type. frob::FrobinatorPtr is an InterfacePtr<frob::Frobinator>.
264frob::FrobinatorPtr frobinator;
265scoped_ptr<FrobinatorImpl> impl(
266 new FrobinatorImpl(mojo::GetProxy(&frobinator)));
267frobinator->Frobinate();
andybons3322f762015-08-24 21:37:09268```
269
andybonsad92aa32015-08-31 02:27:44270This will _eventually_ call `FrobinatorImpl::Frobinate()`. "Eventually," because
271the sequence of events when `frobinator->Frobinate()` is called is roughly as
272follows:
andybons3322f762015-08-24 21:37:09273
andybonsad92aa32015-08-31 02:27:442741. A new message buffer is allocated and filled with an encoded 'Frobinate'
275 message.
2761. The EDK is asked to write this message to the pipe endpoint owned by the
277 `FrobinatorPtr`.
2781. If the call didn't happen on the Mojo IPC thread for this process, EDK hops
279 to the Mojo IPC thread.
2801. 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.
2841. The EDK on the other end of the pipe is awoken on the Mojo IPC thread and
285 alerted to the message arrival.
2861. The EDK reads the message.
2871. If the bound receiver doesn't live on the Mojo IPC thread, the EDK hops to
288 the receiver's thread.
2891. 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.
2921. `FrobinatorImpl::Frobinate()` is called on the bound implementation.
andybons3322f762015-08-24 21:37:09293
andybonsad92aa32015-08-31 02:27:44294So as you can see, the call to `Frobinate()` may result in up to two thread hops
295and one process hop before the service implementation is invoked.
andybons3322f762015-08-24 21:37:09296
andybonsad92aa32015-08-31 02:27:44297#### `mojo::StrongBinding<T>`
andybons3322f762015-08-24 21:37:09298
andybonsad92aa32015-08-31 02:27:44299Defined in `third_party/mojo/src/mojo/public/cpp/bindings/strong_binding.h`.
andybons3322f762015-08-24 21:37:09300
andybonsad92aa32015-08-31 02:27:44301`mojo::StrongBinding<T>` is just like `mojo::Binding<T>` with the exception that
302a `StrongBinding` takes ownership of the bound `T` instance. The instance is
303destroyed whenever the bound message pipe is closed. This is convenient in cases
304where you want a service implementation to live as long as the pipe it's
305servicing, but like all features with clever lifetime semantics, it should be
306used with caution.
andybons3322f762015-08-24 21:37:09307
308## The Mojo Shell
309
andybonsad92aa32015-08-31 02:27:44310Both Chromium and Mandoline run a central **shell** component which is used to
311coordinate communication among all Mojo applications (see the next section for
312an overview of Mojo applications.)
andybons3322f762015-08-24 21:37:09313
andybonsad92aa32015-08-31 02:27:44314Every application receives a proxy to this shell upon initialization, and it is
315exclusively through this proxy that an application can request connections to
316other applications. The `mojo::Shell` interface provided by this proxy is
317defined as follows:
andybons3322f762015-08-24 21:37:09318
319```
320module mojo;
321interface Shell {
322 ConnectToApplication(URLRequest application_url,
323 ServiceProvider&? services,
324 ServiceProvider? exposed_services);
325 QuitApplication();
326};
327```
328
329and as for the `mojo::ServiceProvider` interface:
330
331```
332module mojo;
333interface ServiceProvider {
334 ConnectToService(string interface_name, handle<message_pipe> pipe);
335};
336```
337
andybonsad92aa32015-08-31 02:27:44338Definitions for these interfaces can be found in
339`/mojo/application/public/interfaces`. Also note that `mojo::URLRequest` is a
340Mojo struct defined in
341`/mojo/services/network/public/interfaces/url_loader.mojom`.
andybons3322f762015-08-24 21:37:09342
andybonsad92aa32015-08-31 02:27:44343Note that there's some new syntax in the mojom for `ConnectToApplication` above.
344The '?' signifies a nullable value and the '&' signifies an interface request
345rather than an interface proxy.
andybons3322f762015-08-24 21:37:09346
andybonsad92aa32015-08-31 02:27:44347The argument `ServiceProvider&? services` indicates that the caller should pass
348an `InterfaceRequest<ServiceProvider>` as the second argument, but that it need
349not be bound to a pipe (i.e., it can be "null" in which case it's ignored.)
andybons3322f762015-08-24 21:37:09350
andybonsad92aa32015-08-31 02:27:44351The argument `ServiceProvider? exposed_services` indicates that the caller
352should pass an `InterfacePtr<ServiceProvider>` as the third argument, but that
353it may also be null.
andybons3322f762015-08-24 21:37:09354
andybonsad92aa32015-08-31 02:27:44355`ConnectToApplication` asks the shell to establish a connection between the
356caller and some other app the shell might know about. In the event that a
357connection can be established -- which may involve the shell starting a new
358instance of the target app -- the given `services` request (if not null) will be
359bound to a service provider in the target app. The target app may in turn use
360the passed `exposed_services` proxy (if not null) to request services from the
361connecting app.
andybons3322f762015-08-24 21:37:09362
andybonsad92aa32015-08-31 02:27:44363### Mojo Applications
andybons3322f762015-08-24 21:37:09364
andybonsad92aa32015-08-31 02:27:44365All code which runs in a Mojo environment, apart from the shell itself (see
366above), 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
368simple concept. In essence an application is anything which implements the
369following Mojom interface:
andybons3322f762015-08-24 21:37:09370
371```
andybonsad92aa32015-08-31 02:27:44372module mojo;
373interface 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};
andybons3322f762015-08-24 21:37:09381```
382
andybonsad92aa32015-08-31 02:27:44383Of course, in Chromium and Mandoline environments this interface is obscured
384from 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
387example of this in the next section,
388[Your First Mojo Application](#Your-First-Mojo-Application).
andybons3322f762015-08-24 21:37:09389
andybonsad92aa32015-08-31 02:27:44390The takeaway here is that an application can be anything. It's not necessarily a
391new process (though at the moment, it's at least a new thread). Applications can
392connect to each other, and these connections are the mechanism through which
393separate components expose services to each other.
andybons3322f762015-08-24 21:37:09394
andybonsad92aa32015-08-31 02:27:44395**NOTE##: This is not true in Chromium today, but it should be eventually. For
396some components (like render frames, or arbitrary browser process code) we
397provide APIs which allow non-Mojo-app-code to masquerade as a Mojo app and
398therefore connect to real Mojo apps through the shell.
andybons3322f762015-08-24 21:37:09399
andybonsad92aa32015-08-31 02:27:44400### Other IPC Primitives
andybons3322f762015-08-24 21:37:09401
andybonsad92aa32015-08-31 02:27:44402Finally, it's worth making brief mention of the other types of IPC primitives
403Mojo provides apart from message pipes. A **data pipe** is a unidirectional
404channel 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
406type of transferable handle as message pipe endpoints, and can therefore be
407transferred across message pipes, potentially to other processes.
andybons3322f762015-08-24 21:37:09408
andybonsad92aa32015-08-31 02:27:44409## Your First Mojo Application
andybons3322f762015-08-24 21:37:09410
andybonsad92aa32015-08-31 02:27:44411In this section, we're going to build a simple Mojo application that can be run
412in isolation using Mandoline's `mojo_runner` binary. After that we'll add a
413service to the app and set up a test suite to connect and test that service.
andybons3322f762015-08-24 21:37:09414
andybonsad92aa32015-08-31 02:27:44415### Hello, world!
andybons3322f762015-08-24 21:37:09416
andybonsad92aa32015-08-31 02:27:44417So, you're building a new Mojo app and it has to live somewhere. For the
418foreseeable future we'll likely be treating `//components` as a sort of
419top-level home for new Mojo apps in the Chromium tree. Any component application
420you build should probably go there. Let's create some basic files to kick things
421off. You may want to start a new local Git branch to isolate any changes you
422make while working through this.
andybons3322f762015-08-24 21:37:09423
andybonsad92aa32015-08-31 02:27:44424First create a new `//components/hello` directory. Inside this directory we're
425going to add the following files:
andybons3322f762015-08-24 21:37:09426
427**components/hello/main.cc**
428```
429#include "base/logging.h"
430#include "third_party/mojo/src/mojo/public/c/system/main.h"
431
432MojoResult MojoMain(MojoHandle shell_handle) {
433 LOG(ERROR) << "Hello, world!";
434 return MOJO_RESULT_OK;
435};
436```
437
andybons3322f762015-08-24 21:37:09438**components/hello/BUILD.gn**
439```
440import("//mojo/public/mojo_application.gni")
441
442mojo_native_application("hello") {
443 sources = [
444 "main.cc",
445 ]
446 deps = [
447 "//base",
448 "//mojo/environment:chromium",
449 ]
450}
451```
452
andybonsad92aa32015-08-31 02:27:44453For the sake of this example you'll also want to add your component as a
454dependency somewhere in your local checkout to ensure its build files are
455generated. 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`.
andybons3322f762015-08-24 21:37:09457
andybonsad92aa32015-08-31 02:27:44458Assuming you have a GN output directory at `out_gn/Debug`, you can build the
459Mojo runner along with your shiny new app:
andybons3322f762015-08-24 21:37:09460
andybons3322f762015-08-24 21:37:09461 ninja -C out_gn/Debug mojo_runner components/hello
andybons3322f762015-08-24 21:37:09462
andybonsad92aa32015-08-31 02:27:44463In 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
465which exports your `MojoMain` function.
andybons3322f762015-08-24 21:37:09466
andybonsad92aa32015-08-31 02:27:44467`mojo_runner` takes an application URL as its only argument and runs the
468corresponding application. In its current state it resolves `mojo`-scheme URLs
469such 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
471new app with the following command:
andybons3322f762015-08-24 21:37:09472
andybons3322f762015-08-24 21:37:09473 out_gn/Debug/mojo_runner mojo:hello
andybons3322f762015-08-24 21:37:09474
andybonsad92aa32015-08-31 02:27:44475You should see our little `"Hello, world!"` error log followed by a hanging
476application. You can `^C` to kill it.
andybons3322f762015-08-24 21:37:09477
andybonsad92aa32015-08-31 02:27:44478### Exposing Services
andybons3322f762015-08-24 21:37:09479
andybonsad92aa32015-08-31 02:27:44480An app that prints `"Hello, world!"` isn't terribly interesting. At a bare
481minimum your app should implement `mojo::ApplicationDelegate` and expose at
482least one service to connecting applications.
andybons3322f762015-08-24 21:37:09483
484Let'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
492MojoResult MojoMain(MojoHandle shell_handle) {
493 mojo::ApplicationRunner runner(new hello::HelloApp);
494 return runner.Run(shell_handle);
495};
496```
497
andybonsad92aa32015-08-31 02:27:44498This is a pretty typical looking `MojoMain`. Most of the time this is all you
499want -- a `mojo::ApplicationRunner` constructed over a
500`mojo::ApplicationDelegate` instance, `Run()` with the pipe handle received from
501the shell. We'll add some new files to the app as well:
andybons3322f762015-08-24 21:37:09502
503**components/hello/public/interfaces/greeter.mojom**
504```
505module hello;
506interface Greeter {
507 Greet(string name) => (string greeting);
508};
509```
510
andybonsad92aa32015-08-31 02:27:44511Note the new arrow syntax on the `Greet` method. This indicates that the caller
512expects a response from the service.
andybons3322f762015-08-24 21:37:09513
514**components/hello/public/interfaces/BUILD.gn**
515```
516import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni")
517
518mojom("interfaces") {
519 sources = [
520 "greeter.mojom",
521 ]
522}
523```
524
andybonsad92aa32015-08-31 02:27:44525**components/hello/hello_app.h**
andybons3322f762015-08-24 21:37:09526```
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
535namespace hello {
536
537class 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
andybonsad92aa32015-08-31 02:27:44561**components/hello/hello_app.cc**
andybons3322f762015-08-24 21:37:09562```
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
569namespace hello {
570
571namespace {
572
573class 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
594HelloApp::HelloApp() {
595}
596
597HelloApp::~HelloApp() {
598}
599
600bool HelloApp::ConfigureIncomingConnection(
601 mojo::ApplicationConnection* connection) {
602 connection->AddService<Greeter>(this);
603 return true;
604}
605
606void HelloApp::Create(
607 mojo::ApplicationConnection* connection,
608 mojo::InterfaceRequest<Greeter> request) {
609 new GreeterImpl(request.Pass());
610}
611
612} // namespace hello
613```
614
andybonsad92aa32015-08-31 02:27:44615And finally we need to update our app's `BUILD.gn` to add some new sources and
616dependencies:
andybons3322f762015-08-24 21:37:09617
618**components/hello/BUILD.gn**
619```
620import("//mojo/public/mojo_application.gni")
621
622source_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
635mojo_native_application("hello") {
636 sources = [
637 "main.cc",
638 ],
639 deps = [ ":lib" ]
640}
641```
642
andybonsad92aa32015-08-31 02:27:44643Note that we build the bulk of our application sources as a static library
644separate from the `MojoMain` definition. Following this convention is
645particularly useful for Chromium integration, as we'll see later.
andybons3322f762015-08-24 21:37:09646
andybonsad92aa32015-08-31 02:27:44647There's a lot going on here and it would be useful to familiarize yourself with
648the definitions of `mojo::ApplicationDelegate`, `mojo::ApplicationConnection`,
649and `mojo::InterfaceFactory<T>`. The TL;DR though is that if someone connects to
650this app and requests a service named `"hello::Greeter"`, the app will create a
651new `GreeterImpl` and bind it to that request pipe. From there the connecting
652app can call `Greeter` interface methods and they'll be routed to that
653`GreeterImpl` instance.
andybons3322f762015-08-24 21:37:09654
andybonsad92aa32015-08-31 02:27:44655Although this appears to be a more interesting application, we need some way to
656actually connect and test the behavior of our new service. Let's write an app
657test!
andybons3322f762015-08-24 21:37:09658
andybonsad92aa32015-08-31 02:27:44659### App Tests
andybons3322f762015-08-24 21:37:09660
andybonsad92aa32015-08-31 02:27:44661App tests run inside a test application, giving test code access to a shell
662which can connect to one or more applications-under-test.
andybons3322f762015-08-24 21:37:09663
664First let's introduce some test code:
665
andybonsad92aa32015-08-31 02:27:44666**components/hello/hello_apptest.cc**
andybons3322f762015-08-24 21:37:09667```
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
677namespace hello {
678namespace {
679
680class 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
700void 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
707TEST_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
718We also need to add a new rule to `//components/hello/BUILD.gn`:
719
720```
andybonsad92aa32015-08-31 02:27:44721mojo_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}
andybons3322f762015-08-24 21:37:09736```
737
andybonsad92aa32015-08-31 02:27:44738Note that the `//components/hello:apptests` target does **not** have a binary
739dependency on either `HelloApp` or `GreeterImpl` implementations; instead it
740depends only on the component's public interface definitions.
andybons3322f762015-08-24 21:37:09741
andybonsad92aa32015-08-31 02:27:44742The `data_deps` entry ensures that `hello.mojo` is up-to-date when `apptests` is
743built. This is desirable because the test connects to `"mojo:hello"` which will
744in turn load `hello.mojo` from disk.
andybons3322f762015-08-24 21:37:09745
746You can now build the test suite:
747
andybonsad92aa32015-08-31 02:27:44748 ninja -C out_gn/Debug components/hello:apptests
andybons3322f762015-08-24 21:37:09749
750and run it:
751
andybonsad92aa32015-08-31 02:27:44752 out_gn/Debug/mojo_runner mojo:hello_apptests
andybons3322f762015-08-24 21:37:09753
754You should see one test (`HelloAppTest.GreetWorld`) passing.
755
756One particularly interesting bit of code in this test is in the `SetUp` method:
757
andybons3322f762015-08-24 21:37:09758 mojo::URLRequestPtr app_url = mojo::URLRequest::New();
759 app_url->url = "mojo:hello";
760 application_impl()->ConnectToService(app_url.Pass(), &greeter_);
andybons3322f762015-08-24 21:37:09761
andybonsad92aa32015-08-31 02:27:44762`ConnectToService` is a convenience method provided by `mojo::ApplicationImpl`,
763and 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
766via its `ServiceProvider`'s `ConnectToService` method.
andybons3322f762015-08-24 21:37:09767
andybonsad92aa32015-08-31 02:27:44768Note that generated interface bindings include a constant string to identify
769each interface by name; so for example the generated `hello::Greeter` type
770defines a static C string:
andybons3322f762015-08-24 21:37:09771
andybons3322f762015-08-24 21:37:09772 const char hello::Greeter::Name_[] = "hello::Greeter";
andybons3322f762015-08-24 21:37:09773
andybonsad92aa32015-08-31 02:27:44774This is exploited by the definition of
775`mojo::ApplicationConnection::ConnectToService<T>`, which uses `T::Name_` as the
776name of the service to connect to. The type `T` in this context is inferred from
777the `InterfacePtr<T>*` argument. You can inspect the definition of
778`ConnectToService` in `/mojo/application/public/cpp/application_connection.h`
779for additional clarity.
andybons3322f762015-08-24 21:37:09780
781We could have instead written this code as:
782
783```
andybonsad92aa32015-08-31 02:27:44784mojo::URLRequestPtr app_url = mojo::URLRequest::New();
785app_url->url = "mojo::hello";
andybons3322f762015-08-24 21:37:09786
andybonsad92aa32015-08-31 02:27:44787mojo::ServiceProviderPtr services;
788application_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());
andybons3322f762015-08-24 21:37:09793
andybonsad92aa32015-08-31 02:27:44794mojo::InterfaceRequest<hello::Greeter> greeter_request =
795 mojo::GetProxy(&greeter_);
796services->ConnectToService(hello::Greeter::Name_,
797 greeter_request.PassMessagePipe());
andybons3322f762015-08-24 21:37:09798```
799
800The net result is the same, but 3-line version seems much nicer.
801
andybonsad92aa32015-08-31 02:27:44802## Chromium Integration
andybons3322f762015-08-24 21:37:09803
andybonsad92aa32015-08-31 02:27:44804Up until now we've been using `mojo_runner` to load and run `.mojo` binaries
805dynamically. While this model is used by Mandoline and may eventually be used in
806Chromium as well, Chromium is at the moment confined to running statically
807linked application code. This means we need some way to register applications
808with the browser's Mojo shell.
andybons3322f762015-08-24 21:37:09809
andybonsad92aa32015-08-31 02:27:44810It also means that, rather than using the binary output of a
811`mojo_native_application` target, some part of Chromium must link against the
812app's static library target (_e.g._, `"//components/hello:lib"`) and register a
813URL handler to teach the shell how to launch an instance of the app.
andybons3322f762015-08-24 21:37:09814
andybonsad92aa32015-08-31 02:27:44815When registering an app URL in Chromium it probably makes sense to use the same
816mojo-scheme URL used for the app in Mandoline. For example the media renderer
817app is referenced by the `"mojo:media"` URL in both Mandoline and Chromium. In
818Mandoline this resolves to a dynamically-loaded `.mojo` binary on disk, but in
819Chromium it resolves to a static application loader linked into Chromium. The
820net result is the same in both cases: other apps can use the shell to connect to
821`"mojo:media"` and use its services.
andybons3322f762015-08-24 21:37:09822
andybonsad92aa32015-08-31 02:27:44823This section explores different ways to register and connect to `"mojo:hello"`
824in Chromium.
andybons3322f762015-08-24 21:37:09825
andybonsad92aa32015-08-31 02:27:44826### In-Process Applications
andybons3322f762015-08-24 21:37:09827
andybonsad92aa32015-08-31 02:27:44828Applications can be set up to run within the browser process via
829`ContentBrowserClient::RegisterInProcessMojoApplications`. This method populates
830a mapping from URL to `base::Callback<scoped_ptr<mojo::ApplicationDelegate>()>`
831(_i.e._, a factory function which creates a new `mojo::ApplicationDelegate`
832instance), so registering a new app means adding an entry to this map.
andybons3322f762015-08-24 21:37:09833
andybonsad92aa32015-08-31 02:27:44834Let's modify `ChromeContentBrowserClient::RegisterInProcessMojoApplications`
835(in `//chrome/browser/chrome_content_browser_client.cc`) by adding the following
836code:
andybons3322f762015-08-24 21:37:09837
andybons3322f762015-08-24 21:37:09838 apps->insert(std::make_pair(GURL("mojo:hello"),
839 base::Bind(&HelloApp::CreateApp)));
andybons3322f762015-08-24 21:37:09840
andybonsad92aa32015-08-31 02:27:44841you'll also want to add the following convenience method to your `HelloApp`
842definition in `//components/hello/hello_app.h`:
andybons3322f762015-08-24 21:37:09843
andybons3322f762015-08-24 21:37:09844 static scoped_ptr<mojo::ApplicationDelegate> HelloApp::CreateApp() {
845 return scoped_ptr<mojo::ApplicationDelegate>(new HelloApp);
846 }
andybons3322f762015-08-24 21:37:09847
andybonsad92aa32015-08-31 02:27:44848This 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`.
andybons3322f762015-08-24 21:37:09852
andybonsad92aa32015-08-31 02:27:44853That's it! Now if an app comes to the shell asking to connect to `"mojo:hello"`
854and app is already running, it'll get connected to our `HelloApp` and have
855access to the `Greeter` service. If the app wasn't already running, it will
856first be launched on a new thread.
andybons3322f762015-08-24 21:37:09857
andybonsad92aa32015-08-31 02:27:44858### Connecting From the Browser
andybons3322f762015-08-24 21:37:09859
andybonsad92aa32015-08-31 02:27:44860We've already seen how apps can connect to each other using their own private
861shell proxy, but the vast majority of Chromium code doesn't yet belong to a Mojo
862application. So how do we use an app's services from arbitrary browser code? We
863use `content::MojoAppConnection`, like this:
andybons3322f762015-08-24 21:37:09864
865```
andybonsad92aa32015-08-31 02:27:44866#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"
andybons3322f762015-08-24 21:37:09870
andybonsad92aa32015-08-31 02:27:44871void LogGreeting(const mojo::String& greeting) {
872 LOG(INFO) << greeting;
873}
andybons3322f762015-08-24 21:37:09874
andybonsad92aa32015-08-31 02:27:44875void 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}
andybons3322f762015-08-24 21:37:09883```
884
andybonsad92aa32015-08-31 02:27:44885A `content::MojoAppConnection`, while not thread-safe, may be created and safely
886used on any single browser thread.
andybons3322f762015-08-24 21:37:09887
andybonsad92aa32015-08-31 02:27:44888You could add the above code to a new browsertest to convince yourself that it
889works. 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
892in-process Mojo app.
andybons3322f762015-08-24 21:37:09893
andybonsad92aa32015-08-31 02:27:44894Finally, note that `MojoAppConnection::Create` takes two URLs. The first is the
895target app URL, and the second is the source URL. Since we're not really a Mojo
896app, but we are still trusted browser code, the shell will gladly use this URL
897as the `requestor_url` when establishing an incoming connection to the target
898app. This allows browser code to masquerade as a Mojo app at the given URL.
899`content::kBrowserMojoAppUrl` (which is presently `"system:content_browser"`) is
900a reasonable default choice when a more specific app identity isn't required.
andybons3322f762015-08-24 21:37:09901
andybonsad92aa32015-08-31 02:27:44902### Out-of-Process Applications
andybons3322f762015-08-24 21:37:09903
andybonsad92aa32015-08-31 02:27:44904If an app URL isn't registered for in-process loading, the shell assumes it must
905be an out-of-process application. If the shell doesn't already have a known
906instance of the app running, a new utility process is launched and the
907application request is passed onto it. Then if the app URL is registered in the
908utility process, the app will be loaded there.
andybons3322f762015-08-24 21:37:09909
andybonsad92aa32015-08-31 02:27:44910Similar to in-process registration, a URL mapping needs to be registered in
911`ContentUtilityClient::RegisterMojoApplications`.
andybons3322f762015-08-24 21:37:09912
andybonsad92aa32015-08-31 02:27:44913Once again you can take a peek at `/content/browser/mojo_shell_browsertest.cc`
914for an end-to-end example of testing an out-of-process Mojo app from browser
915code. 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
918common OOP test app.
andybons3322f762015-08-24 21:37:09919
920## Unsandboxed Out-of-Process Applications
921
andybonsad92aa32015-08-31 02:27:44922By default new utility processes run in a sandbox. If you want your Mojo app to
923run out-of-process and unsandboxed (which you **probably do not**), you can
924register its URL via
925`ContentBrowserClient::RegisterUnsandboxedOutOfProcessMojoApplications`.
andybons3322f762015-08-24 21:37:09926
927## Connecting From `RenderFrame`
928
andybonsad92aa32015-08-31 02:27:44929We can also connect to Mojo apps from a `RenderFrame`. This is made possible by
930`RenderFrame`'s `GetServiceRegistry()` interface. The `ServiceRegistry` can be
931used to acquire a shell proxy and in turn connect to an app like so:
andybons3322f762015-08-24 21:37:09932
933```
934void 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
andybonsad92aa32015-08-31 02:27:44952It's important to note that connections made through the frame's shell proxy
953will appear to come from the frame's `SiteInstance` URL. For example, if the
954frame 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
957frames on a per-origin basis if needed.
andybons3322f762015-08-24 21:37:09958
andybonsad92aa32015-08-31 02:27:44959### Connecting From Java
andybons3322f762015-08-24 21:37:09960
961TODO
962
andybonsad92aa32015-08-31 02:27:44963### Connecting From `JavaScript`
andybons3322f762015-08-24 21:37:09964
andybonsad92aa32015-08-31 02:27:44965This is still a work in progress and might not really take shape until the
966Blink+Chromium merge. In the meantime there are some end-to-end WebUI examples
967in `/content/browser/webui/web_ui_mojo_browsertest.cc`. In particular,
968`WebUIMojoTest.ConnectToApplication` connects from a WebUI frame to a test app
969running in a new utility process.
andybons3322f762015-08-24 21:37:09970
andybonsad92aa32015-08-31 02:27:44971## FAQ
andybons3322f762015-08-24 21:37:09972
andybonsad92aa32015-08-31 02:27:44973Nothing here yet!