| Index: docs/mojo_in_chromium.md
|
| diff --git a/docs/mojo_in_chromium.md b/docs/mojo_in_chromium.md
|
| deleted file mode 100644
|
| index c9e71c8dae35f6820cca345214d35f110d0bc556..0000000000000000000000000000000000000000
|
| --- a/docs/mojo_in_chromium.md
|
| +++ /dev/null
|
| @@ -1,342 +0,0 @@
|
| -# Mojo in Chromium
|
| -
|
| -This document is intended to serve as a Mojo primer for Chromium developers. No
|
| -prior knowledge of Mojo is assumed.
|
| -
|
| -[TOC]
|
| -
|
| -## Should I Bother Reading This?
|
| -
|
| -If you're planning to build a Chromium feature that needs IPC and you aren't
|
| -already using Mojo, YES! **Legacy IPC is deprecated.**
|
| -
|
| -## Why Mojo?
|
| -
|
| -TL;DR: The long-term intent is to refactor Chromium into a large set of smaller
|
| -services.
|
| -
|
| -We can be smarter about:
|
| -
|
| - * Which services we bring up or don't
|
| - * How we isolate these services to improve security and stability
|
| - * Which binary features we ship to one user or another
|
| -
|
| -A more robust messaging layer opens the door for a number of interesting
|
| -possibilities; in particular it allows us to integrate a large number of
|
| -components without link-time interdependencies, and it breaks down the growing
|
| -number of interesting cross-language boundaries across the codebase.
|
| -
|
| -Much has been learned from using Chromium IPC and maintaining Chromium
|
| -dependencies in anger over the past several years and we feel there's now a
|
| -significant opportunity to make life easier for developers, and to help them
|
| -build more and better features, faster, and with much less cost to users.
|
| -
|
| -## Mojo Overview
|
| -
|
| -The Mojo system API provides a small suite of low-level IPC primitives:
|
| -**message pipes**, **data pipes**, and **shared buffers**. On top of this API
|
| -we've built higher-level bindings APIs to simplify messaging for consumers
|
| -writing C++, Java, or JavaScript code.
|
| -
|
| -This document focuses primarily on using C++ bindings with message pipes, which
|
| -is likely to be the most common usage encountered by Chromium developers.
|
| -
|
| -### Message Pipes
|
| -
|
| -A message pipe is a lightweight primitive for reliable bidirectional transfer of
|
| -relatively small packets of data. Unsurprisingly a pipe has two endpoints, and
|
| -either endpoint may be transferred over another message pipe.
|
| -
|
| -Because we bootstrap a primordial message pipe between the browser process and
|
| -each child process, this in turn means that you can create a new pipe and
|
| -ultimately send either end to any process, and the two ends will still be
|
| -able to talk to each other seamlessly and exclusively. Goodbye, routing IDs!
|
| -
|
| -While message pipes can carry arbitrary packets of unstructured data we
|
| -generally use them in conjunction with generated bindings to ensure a
|
| -consistent, well-defined, versioned message structure on all endpoints.
|
| -
|
| -### Mojom
|
| -
|
| -Mojom is the IDL for Mojo interfaces. Given a `.mojom` file, the bindings
|
| -generator outputs bindings for all three of the currently supported languages.
|
| -
|
| -For example:
|
| -
|
| -```
|
| -// src/components/frob/public/interfaces/frobinator.mojom
|
| -module frob.mojom;
|
| -
|
| -interface Frobinator {
|
| - Frobinate();
|
| -};
|
| -```
|
| -
|
| -would generate the following outputs:
|
| -
|
| -```
|
| -out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.cc
|
| -out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.h
|
| -out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.js
|
| -out/Debug/gen/components/frob/public/interfaces/frobinator.mojom.srcjar
|
| -...
|
| -```
|
| -
|
| -The generated code hides away all the details of serializing and deserializing
|
| -messages on either end of a pipe.
|
| -
|
| -The C++ header (`frobinator.mojom.h`) defines an abstract class for each
|
| -mojom interface specified. Namespaces are derived from the `module` name.
|
| -
|
| -**NOTE:** Chromium convention for component `foo`'s module name is `foo.mojom`.
|
| -This means all mojom-generated C++ typenames for component `foo` will live in
|
| -the `foo::mojom` namespace to avoid collisions with non-generated typenames.
|
| -
|
| -In this example the generated `frob::mojom::Frobinator` has a single
|
| -pure virtual function:
|
| -
|
| -```cpp
|
| -namespace frob {
|
| -
|
| -class Frobinator {
|
| - public:
|
| - virtual void Frobinate() = 0;
|
| -};
|
| -
|
| -} // namespace frob
|
| -```
|
| -
|
| -To create a `Frobinator` service, one simply implements `foo::Frobinator` and
|
| -provides a means of binding pipes to it.
|
| -
|
| -### Binding to Pipes
|
| -
|
| -Let's look at some sample code:
|
| -
|
| -```cpp
|
| -// src/components/frob/frobinator_impl.cc
|
| -
|
| -#include "components/frob/public/interfaces/frobinator.mojom.h"
|
| -#include "mojo/public/cpp/bindings/binding.h"
|
| -#include "mojo/public/cpp/bindings/interface_request.h"
|
| -
|
| -namespace frob {
|
| -
|
| -class FrobinatorImpl : public mojom::Frobinator {
|
| - public:
|
| - FrobinatorImpl(mojom::FrobinatorRequest request)
|
| - : binding_(this, std::move(request)) {}
|
| - ~FrobinatorImpl() override {}
|
| -
|
| - // mojom::Frobinator:
|
| - void Frobinate() override { DLOG(INFO) << "I can't stop frobinating!"; }
|
| -
|
| - private:
|
| - mojo::Binding<mojom::Frobinator> binding_;
|
| -};
|
| -
|
| -} // namespace frob
|
| -```
|
| -
|
| -The first thing to note is that `mojo::Binding<T>` *binds* one end of a message
|
| -pipe to an implementation of a service. This means it watches that end of the
|
| -pipe for incoming messages; it knows how to decode messages for interface `T`,
|
| -and it dispatches them to methods on the bound `T` implementation.
|
| -
|
| -`mojom::FrobinatorRequest` is a generated type alias for
|
| -`mojo::InterfaceRequest<mojom::Frobinator>` and is essentially semantic sugar
|
| -for a strongly-typed message pipe endpoint. A common way to create new message
|
| -pipes is via the `GetProxy` call defined in `interface_request.h`:
|
| -
|
| -```cpp
|
| -mojom::FrobinatorPtr proxy;
|
| -mojom::FrobinatorRequest request = mojo::GetProxy(&proxy);
|
| -```
|
| -
|
| -This creates a new message pipe with one end owned by `proxy` and the other end
|
| -owned by `request`. It has the nice property of attaching common type
|
| -information to each end of the pipe.
|
| -
|
| -Note that `InterfaceRequest<T>` doesn't actually **do** anything. It just scopes
|
| -a pipe endpoint and associates it with an interface type at compile time. As
|
| -such, other typed service binding primitives such as `mojo::Binding<T>` take
|
| -these objects as input when they need an endpoint to bind to.
|
| -
|
| -`mojom::FrobinatorPtr` is a generated type alias for
|
| -`mojo::InterfacePtr<mojom::Frobinator>`. An `InterfacePtr<T>` scopes a message
|
| -pipe endpoint as well, but it also internally implements every method on `T` by
|
| -serializing a corresponding message and writing it to the pipe.
|
| -
|
| -Hence we can put this together to talk to a `FrobinatorImpl` over a pipe:
|
| -
|
| -```cpp
|
| -frob:mojom::FrobinatorPtr frobinator;
|
| -frob::FrobinatorImpl impl(GetProxy(&frobinator));
|
| -
|
| -// Tada!
|
| -frobinator->Frobinate();
|
| -```
|
| -
|
| -Behind the scenes this serializes a message corresponding to the `Frobinate`
|
| -request and writes it to one end of the pipe. Eventually (and incidentally,
|
| -very soon after), `impl`'s internal `mojo::Binding` will decode this message and
|
| -dispatch a call to `impl.Frobinate()`.
|
| -
|
| -**NOTE:** In this example the service and client are in the same process, and
|
| -this works just fine. If they were in different processes (see the example below
|
| -in [Exposing Services in Chromium](#Exposing-Services-in-Chromium)), the call
|
| -to `Frobinate()` would look exactly the same!
|
| -
|
| -### Responding to Requests
|
| -
|
| -A common idiom in Chromium IPC is to keep track of IPC requests with some kind
|
| -of opaque identifier (i.e. an integer *request ID*) so that you can later
|
| -respond to a specific request using some nominally related message in the other
|
| -direction.
|
| -
|
| -This is baked into mojom interface definitions. We can extend our `Frobinator`
|
| -service like so:
|
| -
|
| -```
|
| -module frob.mojom;
|
| -
|
| -interface Frobinator {
|
| - Frobinate();
|
| - GetFrobinationLevels() => (int min, int max);
|
| -};
|
| -```
|
| -
|
| -and update our implementation:
|
| -
|
| -```cpp
|
| -class FrobinatorImpl : public mojom::Frobinator {
|
| - public:
|
| - // ...
|
| -
|
| - // mojom::Frobinator:
|
| - void Frobinate() override { /* ... */ }
|
| - void GetFrobinationLevels(const GetFrobinationLevelsCallback& callback) {
|
| - callback.Run(1, 42);
|
| - }
|
| -};
|
| -```
|
| -
|
| -When the service implementation runs `callback`, the response arguments are
|
| -serialized and sent back over the pipe. The proxy on the other end knows how to
|
| -read this response and will in turn dispatch it to a callback on that end:
|
| -
|
| -```cpp
|
| -void ShowLevels(int min, int max) {
|
| - DLOG(INFO) << "Frobinator min=" << min << " max=" << max;
|
| -}
|
| -
|
| -// ...
|
| -
|
| - mojom::FrobinatorPtr frobinator;
|
| - FrobinatorImpl impl(GetProxy(&frobinator));
|
| -
|
| - frobinator->GetFrobinatorLevels(base::Bind(&ShowLevels));
|
| -```
|
| -
|
| -This does what you'd expect.
|
| -
|
| -## Exposing Services in Chromium
|
| -
|
| -There are a number of ways one might expose services across various surfaces of
|
| -the browser. One common approach now is to use a
|
| -[`content::ServiceRegistry` (link)](https://goo.gl/uEhx06). These come in
|
| -pairs generally spanning a process boundary, and they provide primitive service
|
| -registration and connection interfaces. For one example, [every
|
| -`RenderFrameHost` has a `ServiceRegistry`](https://goo.gl/4YR3j5), as does
|
| -[every corresponding `RenderFrame`](https://goo.gl/YhrgXa). These registries are
|
| -intertwined.
|
| -
|
| -The gist is that you can add a service to the local side of the registry -- it's
|
| -just a mapping from interface name to factory function -- or you can connect by
|
| -name to services registered on the remote side.
|
| -
|
| -**NOTE:** In this context the "factory function" is simply a callback which
|
| -takes a pipe endpoint and does something with it. It's expected that you'll
|
| -either bind it to a service implementation of some kind or you will close it, effectively rejecting the connection request.
|
| -
|
| -We can build a simple browser-side `FrobinatorImpl` service that has access to a
|
| -`BrowserContext` for any frame which connects to it:
|
| -
|
| -```cpp
|
| -#include "base/macros.h"
|
| -#include "components/frob/public/interfaces/frobinator.mojom.h"
|
| -#include "content/public/browser/browser_context.h"
|
| -#include "mojo/public/cpp/system/interface_request.h"
|
| -#include "mojo/public/cpp/system/strong_binding.h"
|
| -
|
| -namespace frob {
|
| -
|
| -class FrobinatorImpl : public mojom::Frobinator {
|
| - public:
|
| - FrobinatorImpl(content::BrowserContext* context,
|
| - mojom::FrobinatorRequest request)
|
| - : context_(context), binding_(this, std::move(request)) {}
|
| - ~FrobinatorImpl() override {}
|
| -
|
| - // A factory function to use in conjunction with ServiceRegistry.
|
| - static void Create(content::BrowserContext* context,
|
| - mojom::FrobinatorRequest request) {
|
| - // See comment below for why this doesn't leak.
|
| - new FrobinatorImpl(context, std::move(request));
|
| - }
|
| -
|
| - private:
|
| - // mojom::Frobinator:
|
| - void Frobinate() override { /* ... */ }
|
| -
|
| - content::BrowserContext* context_;
|
| -
|
| - // A StrongBinding is just like a Binding, except that it takes ownership of
|
| - // its bound implementation and deletes itself (and the impl) if and when the
|
| - // bound pipe encounters an error or is closed on the other end.
|
| - mojo::StrongBinding<mojom::Frobinator> binding_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(FrobinatorImpl);
|
| -};
|
| -
|
| -} // namespace frob
|
| -```
|
| -
|
| -Now somewhere in the browser we register the Frobinator service with each
|
| -`RenderFrameHost` ([this](https://goo.gl/HEFn63) is a popular spot):
|
| -
|
| -```cpp
|
| -frame_host->GetServiceRegistry()->AddService<frob::mojom::Frobinator>(
|
| - base::Bind(
|
| - &frob::FrobinatorImpl::Create,
|
| - base::Unretained(frame_host->GetProcess()->GetBrowserContext())));
|
| -```
|
| -
|
| -And in the render process we can now do something like:
|
| -
|
| -```cpp
|
| -mojom::FrobinatorPtr frobinator;
|
| -render_frame->GetServiceRegistry()->ConnectToRemoteService(
|
| - mojo::GetProxy(&frobinator));
|
| -
|
| -// It's IPC!
|
| -frobinator->Frobinate();
|
| -```
|
| -
|
| -There are now plenty of concrete examples of Mojo usage in the Chromium tree.
|
| -Poke around at existing mojom files and see how their implementions are built
|
| -and connected.
|
| -
|
| -## Mojo in Blink
|
| -
|
| -*TODO*
|
| -
|
| -This is a work in progress. TL;DR: We'll also soon begin using Mojo services
|
| -from Blink so that the platform layer can consume browser services
|
| -directly via Mojo. The long-term goal there is to eliminate `content/renderer`.
|
| -
|
| -## Questions, Discussion, etc.
|
| -
|
| -A good place to find highly concentrated doses of people who know and care
|
| -about Mojo in Chromium would be the [chromium-mojo](https://goo.gl/A4ebWB)
|
| -mailing list[.](https://goo.gl/L70ihQ)
|
|
|