Update Mojo and services documentation
This is a large update to all Mojo and services documentation in the tree. Here are the changes in a nutshell: - Images for Mojo docs are upstreamed instead of being pulled from docs.google.com - Various minor updates, corrections, clarifications in core Mojo docs - Consolidation of documents in the tree. - Refresh of all service and Service Manager related documentation, removing lots of outdated information, adding clarifying definitions and examples of core concepts As of this CL, the relevant documentation in the tree is pared down to: - The mojo/ subtree itself - services/README.md - service guidelines - services/service_manager/README.md - general service manager and service API documentation, examples, etc - docs/mojo_and_services.md - intro to mojo & services for chromium developers - docs/mojo_ipc_conversion.md - advice for converting legacy IPCs to mojo interfaces - docs/servicification.md - advice for servicifying chromium features specifically Specifically this wipes out content/public/common/services.md, ipc/README.md, services/service_manager/service_manifests.md, and services/api_standards.md. Any still-relevant content that was in these docs has been merged into one of the docs listed above. Finally, the presence in docs/README.md has been cleaned up a bit. A new section for "Mojo & Services" is added with links to the above list of documents. Change-Id: I294a32025afdca62441d3605da51d714f3aebd00 TBR: jam@chromium.org Reviewed-on: https://chromium-review.googlesource.com/c/1441640 Commit-Queue: Ken Rockot <rockot@google.com> Reviewed-by: Oksana Zhuravlova <oksamyt@chromium.org> Cr-Commit-Position: refs/heads/master@{#629384}
This commit is contained in:
@ -1,65 +0,0 @@
|
||||
# Services in Content
|
||||
|
||||
The //content layer implements the core process model for Chrome, including
|
||||
major process types: `browser`, `renderer`, `gpu`, `utility`, `plugin`. From the
|
||||
perspective of the service manager, each process type is a service, and each
|
||||
process instance is an instantiation of that service. For a renderer process,
|
||||
its `service_manager::Identity` is constructed as follows:
|
||||
|
||||
```
|
||||
name: content_renderer
|
||||
userid: <guid, from BrowserContext that spawned this renderer>
|
||||
instance: <string, generated from the RenderProcesHost's ID>
|
||||
```
|
||||
|
||||
These services express the set of capabilities they expose to one another using
|
||||
service manifests (see [Service Manager README](https://chromium.googlesource.com/chromium/src/+/master/services/service_manager/README.md)). For //content, the service manifests live in
|
||||
`//content/public/app/mojo`.
|
||||
|
||||
Every `content::BrowserContext` has an instance group ID generated for it upon
|
||||
construction, and the services run with that BrowserContext use that instance
|
||||
group as part of their instance identity. Where there are multiple instances of
|
||||
the same service within the same instance group, the Identity's instance ID
|
||||
field is used for disambiguation.
|
||||
|
||||
Launching code for each process type is currently ad-hoc & specific per type,
|
||||
and lives in `//content/browser`. In the medium-long term, we'll work to
|
||||
generalize this and move it all into the service manager.
|
||||
Each content process type is launched by host code in `//content/browser`,
|
||||
though eventually all process launching will be moved to the service manager.
|
||||
|
||||
The canonical service for each process type is represented by an implementation
|
||||
of the `service_manager::Service` interface which lives on the IO thread. This
|
||||
implementation is shared, and is a detail of `content::ServiceManagerConnection`
|
||||
which you will find in `//content/public/common`. This implementation receives
|
||||
the `OnStart()` and `OnBindInterface()` calls from the service manager.
|
||||
|
||||
The rest of this document talks about things you might like to do and how to
|
||||
accomplish them.
|
||||
|
||||
### Expose Mojo interfaces from one of the existing content-provided services.
|
||||
|
||||
To expose interfaces at the service-level from one of the existing content-
|
||||
provided services, you will need to add a `content::ConnectionFilter` to the
|
||||
`content::ServiceManagerConnection` in the relevant process. See
|
||||
`//content/public/common/connection_filter.h`. You implement this interface to
|
||||
handle `OnBindInterface()` requests on the IO thread. You can construct a
|
||||
`service_manager::BinderRegistry` on any other thread and move it to the IO
|
||||
thread using `//content/public/common/connection_filter_impl.h`. When you add
|
||||
bind callbacks to the binder registry you can specify what task runner you
|
||||
would like incoming interface requests to be bound on.
|
||||
|
||||
### Expose Mojo interfaces at the frame level between browser & renderer.
|
||||
|
||||
You can add bind callbacks to the `service_manager::InterfaceRegistry` owned by
|
||||
the `RenderFrame` and the `RenderFrameHost`. See the various content client
|
||||
interfaces also for signals to embedders allowing them to add additional
|
||||
interfaces.
|
||||
|
||||
### Expose a named service from an existing process.
|
||||
|
||||
If you want to expose a named service (i.e. a service other than the ones
|
||||
provided by content) from a process provided by content, you can "embed" a
|
||||
service in one of the content-provided services. You do this by calling
|
||||
`AddEmbeddedService()` on `ServiceManagerConnection`.
|
||||
|
@ -340,7 +340,20 @@ used when committed.
|
||||
install Chromium OS on VMWare.
|
||||
* [User Data Directory](user_data_dir.md) - How the user data and cache
|
||||
directories are determined on all platforms.
|
||||
* [Mojo](../mojo/README.md) - IPC mechanism used by services.
|
||||
|
||||
### Mojo & Services
|
||||
* [Intro to Mojo & Services](mojo_and_services.md) - Quick introduction
|
||||
to Mojo and services in Chromium, with examples
|
||||
* [Mojo API Reference](/mojo/README.md) - Detailed reference documentation for
|
||||
all things Mojo
|
||||
* [The Service Manager & Services](/services/service_manager/README.md) -
|
||||
Services system overview, API references, example services
|
||||
* [Service Development Guidelines](/services/README.md) - Guidelines for
|
||||
service development in the Chromium tree
|
||||
* [Servicifying Chromium Features](servicification.md) - General advice for
|
||||
integrating new and existing subsystems into Chromium as services
|
||||
* [Converting Legacy IPC to Mojo](mojo_ipc_conversion.md) - Tips and common
|
||||
patterns for practical IPC conversion work
|
||||
|
||||
### Probably Obsolete
|
||||
* [TPM Quick Reference](tpm_quick_ref.md) - Trusted Platform Module notes.
|
||||
|
BIN
docs/images/mojo_binding_and_dispatch.png
Normal file
BIN
docs/images/mojo_binding_and_dispatch.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 21 KiB |
BIN
docs/images/mojo_message.png
Normal file
BIN
docs/images/mojo_message.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 4.9 KiB |
BIN
docs/images/mojo_pipe.png
Normal file
BIN
docs/images/mojo_pipe.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 4.7 KiB |
BIN
docs/images/mojo_stack.png
Normal file
BIN
docs/images/mojo_stack.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 18 KiB |
BIN
docs/images/mojo_sync_call_deadlock.png
Normal file
BIN
docs/images/mojo_sync_call_deadlock.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 23 KiB |
BIN
docs/images/mojo_sync_call_flow.png
Normal file
BIN
docs/images/mojo_sync_call_flow.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 28 KiB |
855
docs/mojo_and_services.md
Normal file
855
docs/mojo_and_services.md
Normal file
@ -0,0 +1,855 @@
|
||||
# Intro to Mojo & Services
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
|
||||
This document contains the minimum amount of information needed for a developer
|
||||
to start using Mojo effectively in Chromium, with example Mojo interface usage,
|
||||
service definition and hookup, and a brief overview of the Content layer's core
|
||||
services.
|
||||
|
||||
See other [Mojo & Services](/docs/README.md#Mojo-Services) documentation
|
||||
for introductory guides, API references, and more.
|
||||
|
||||
## Mojo Terminology
|
||||
|
||||
A **message pipe** is a pair of **endpoints**. Each endpoint has a queue of
|
||||
incoming messages, and writing a message at one endpoint effectively enqueues
|
||||
that message on the other (**peer**) endpoint. Message pipes are thus
|
||||
bidirectional.
|
||||
|
||||
A **mojom** file describes **interfaces**, which are strongly-typed collections
|
||||
of **messages**. Each interface message is roughly analogous to a single proto
|
||||
message, for developers who are familiar with Google protobufs.
|
||||
|
||||
Given a mojom interface and a message pipe, one of the endpoints
|
||||
can be designated as an **InterfacePtr** and is used to *send* messages described by
|
||||
the interface. The other endpoint can be designated as a **Binding** and is used
|
||||
to *receive* interface messages.
|
||||
|
||||
*** aside
|
||||
NOTE: The above generalization is a bit oversimplified. Remember that the
|
||||
message pipe is still bidirectional, and it's possible for a mojom message to
|
||||
expect a reply. Replies are sent from the Binding endpoint and received by the
|
||||
InterfacePtr endpoint.
|
||||
***
|
||||
|
||||
The Binding endpoint must be associated with (*i.e.* **bound** to) an
|
||||
**implementation** of its mojom interface in order to process received messages.
|
||||
A received message is dispatched as a scheduled task invoking the corresponding
|
||||
interface method on the implementation object.
|
||||
|
||||
Another way to think about all this is simply that **an InterfacePtr makes
|
||||
calls on a remote implementation of its interface associated with a
|
||||
corresponding remote Binding.**
|
||||
|
||||
## Example: Defining a New Frame Interface
|
||||
|
||||
Let's apply this to Chrome. Suppose we want to send a "Ping" message from a
|
||||
render frame to its corresponding `RenderFrameHostImpl` instance in the browser
|
||||
process. We need to define a nice mojom interface for this purpose, create a
|
||||
pipe to use that interface, and then plumb one end of the pipe to the right
|
||||
place so the sent messages can be received and processed there. This section
|
||||
goes through that process in detail.
|
||||
|
||||
### Defining the Interface
|
||||
|
||||
The first step involves creating a new `.mojom` file with an interface
|
||||
definition, like so:
|
||||
|
||||
``` cpp
|
||||
// src/example/public/mojom/ping_responder.mojom
|
||||
module example.mojom;
|
||||
|
||||
interface PingResponder {
|
||||
// Receives a "Ping" and responds with a random integer.
|
||||
Ping() => (int random);
|
||||
};
|
||||
```
|
||||
|
||||
This should have a corresponding build rule to generate C++ bindings for the
|
||||
definition here:
|
||||
|
||||
``` python
|
||||
# src/example/public/mojom/BUILD.gn
|
||||
import "mojo/public/tools/bindings/mojom.gni"
|
||||
mojom("mojom") {
|
||||
sources = [ "ping_responder.mojom" ]
|
||||
}
|
||||
```
|
||||
|
||||
### Creating the Pipe
|
||||
|
||||
Now let's create a message pipe to use this interface.
|
||||
|
||||
*** aside
|
||||
As a general rule and as a matter of convenience when
|
||||
using Mojo, the *client* of an interface (*i.e.* the InterfacePtr side) is
|
||||
typically the party who creates a new pipe. This is convenient because the
|
||||
InterfacePtr may be used to start sending messages immediately without waiting
|
||||
for the InterfaceRequest endpoint to be transferred or bound anywhere.
|
||||
***
|
||||
|
||||
This code would be placed somewhere in the renderer:
|
||||
|
||||
```cpp
|
||||
example::mojom::PingResponderPtr ping_responder;
|
||||
example::mojom::PingResponderRequest request =
|
||||
mojo::MakeRequest(&ping_responder);
|
||||
```
|
||||
|
||||
In this example, ```ping_responder``` is the InterfacePtr, and ```request```
|
||||
is an InterfaceRequest, which is a Binding precursor that will eventually
|
||||
be turned into a Binding. `mojo::MakeRequest` is the most common way to create
|
||||
a message pipe: it yields both endpoints as strongly-typed objects, with the
|
||||
`InterfacePtr` as an output argument and the `InterfaceRequest` as the return
|
||||
value.
|
||||
|
||||
*** aside
|
||||
NOTE: Every mojom interface `T` generates corresponding C++ type aliases
|
||||
`TPtr = InterfacePtr<T>` and `TRequest = InterfaceRequest<T>`. Chromium code
|
||||
almost exclusively uses these aliases instead of writing out the more verbose
|
||||
templated name.
|
||||
|
||||
Also note that an InterfaceRequest doesn't actually **do** anything. It is an
|
||||
inert holder of a single message pipe endpoint. It exists only to make its
|
||||
endpoint more strongly-typed at compile-time, indicating that the endpoint
|
||||
expects to be bound by a Binding of the same interface type.
|
||||
***
|
||||
|
||||
### Sending a Message
|
||||
|
||||
Finally, we can call the `Ping()` method on our InterfacePtr to send a message:
|
||||
|
||||
```cpp
|
||||
ping_responder->Ping(base::BindOnce(&OnPong));
|
||||
```
|
||||
|
||||
*** aside
|
||||
**IMPORTANT:** If we want to receive the the response, we must keep the
|
||||
`ping_responder` object alive until `OnPong` is invoked. After all,
|
||||
`ping_responder` *owns* its message pipe endpoint. If it's destroyed then so is
|
||||
the endpoint, and there will be nothing to receive the response message.
|
||||
***
|
||||
|
||||
We're almost done! Of course, if everything were this easy, this document
|
||||
wouldn't need to exist. We've taken the hard problem of sending a message from
|
||||
a renderer process to the browser process, and transformed it into a problem
|
||||
where we just need to take the `request` object from above and pass it to the
|
||||
browser process somehow where it can be turned into a Binding that dispatches
|
||||
its received messages.
|
||||
|
||||
### Sending an InterfaceRequest to the Browser
|
||||
|
||||
It's worth noting that InterfaceRequests (and message pipe endpoints in general)
|
||||
are just another type of object that can be freely sent over mojom messages.
|
||||
The most common way to get an InterfaceRequest somewhere is to pass it as a
|
||||
method argument on some other already-connected interface.
|
||||
|
||||
One such interface which we always have connected between a renderer's
|
||||
`RenderFrameImpl` and its corresponding `RenderFrameHostImpl` in the browser
|
||||
is
|
||||
[`DocumentInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/frame/document_interface_broker.mojom).
|
||||
We can update this definition to add support for our new PingResponder
|
||||
interface:
|
||||
|
||||
``` cpp
|
||||
interface DocumentInterfaceBroker {
|
||||
...
|
||||
|
||||
GetPingResponder(PingResponder& responder);
|
||||
}
|
||||
```
|
||||
|
||||
The `&` syntax is not a reference! In mojom it denotes an InterfaceRequest.
|
||||
Specifically in this case, the `GetPingResponder` takes a single
|
||||
`PingResponderRequest` argument. If the `&` were omitted, this would instead
|
||||
take a `PingResponderPtr`.
|
||||
|
||||
Now the renderer can call this method with the `request` object it created
|
||||
earlier via `mojo::MakeRequest`:
|
||||
|
||||
``` cpp
|
||||
RenderFrame* my_frame = GetMyFrame();
|
||||
my_frame->GetDocumentInterfaceBroker()->GetPingResponder(std::move(request));
|
||||
```
|
||||
|
||||
This will transfer the PingResponderRequest endpoint to the browser process
|
||||
where it will be received by the corresponding `DocumentInterfaceBroker`
|
||||
implementation. More on that below.
|
||||
|
||||
### Implementing the Interface
|
||||
|
||||
Finally, we need a browser-side implementation of our `PingResponder` interface
|
||||
as well as an implementation of the new
|
||||
`DocumentInterfaceBroker.GetPingResponder` message. Let's implement
|
||||
`PingResponder` first:
|
||||
|
||||
```cpp
|
||||
#include "example/public/mojom/ping_responder.mojom.h"
|
||||
|
||||
class PingResponderImpl : example::mojom::PingResponder {
|
||||
public:
|
||||
explicit PingResponderImpl(example::mojom::PingResponderRequest request)
|
||||
: binding_(this, std::move(request)) {}
|
||||
|
||||
// example::mojom::PingResponder:
|
||||
void Ping(PingCallback callback) override {
|
||||
// Respond with a random 4, chosen by fair dice roll.
|
||||
std::move(callback).Run(4);
|
||||
}
|
||||
|
||||
private:
|
||||
mojo::Binding<example::mojom::PingResponder> binding_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PingResponderImpl);
|
||||
};
|
||||
```
|
||||
|
||||
And conveniently `RenderFrameHostImpl` implements `DocumentInterfaceBroker`, and
|
||||
any calls made on the object returned by
|
||||
`RenderFrameImpl::GetDocumentInterfaceBroker()' will be routed directly to the
|
||||
`RenderFrameHostImpl`. So the only thing left to do is update
|
||||
`RenderFrameHostImpl` to implement `GetPingResponder`. If you forget to do this
|
||||
the compiler will complain anyway, because generated mojom interface methods are
|
||||
pure virtual methods in C++.
|
||||
|
||||
``` cpp
|
||||
// render_frame_host_impl.h
|
||||
class RenderFrameHostImpl
|
||||
...
|
||||
void GetPingResponder(example::mojom::PingResponderRequest request) override;
|
||||
...
|
||||
private:
|
||||
...
|
||||
std::unique_ptr<PingResponderImpl> ping_responder_;
|
||||
...
|
||||
};
|
||||
|
||||
// render_frame_host_impl.cc
|
||||
void RenderFrameHostImpl::GetPingResponder(
|
||||
example::mojom::PingResponderRequest request) {
|
||||
ping_responder_ = std::make_unique<PingResponderImpl>(std::move(request));
|
||||
}
|
||||
```
|
||||
|
||||
And we're done. This setup is sufficient to plumb a new interface connection
|
||||
between a renderer frame and its browser-side host object!
|
||||
|
||||
Assuming we kept our `ping_responder` object alive in the renderer long enough,
|
||||
we would eventually see its `OnPong` callback invoked with the totally random
|
||||
value of `4`, as defined by the browser-side implementation above.
|
||||
|
||||
## Services Overview & Terminology
|
||||
The previous section only scratches the surface of how Mojo IPC is used in
|
||||
Chromium. While renderer-to-browser messaging is simple and possibly the most
|
||||
prevalent usage by sheer code volume, we are incrementally decomposing the
|
||||
codebase into a set of services with a bit more granularity than the traditional
|
||||
Content browser/renderer/gpu/utility process split.
|
||||
|
||||
A **service** is a self-contained library of code which implements one or more
|
||||
related features or behaviors and whose interaction with outside code is done
|
||||
*exclusively* through Mojo interface connections facilitated by the **Service
|
||||
Manager.**
|
||||
|
||||
The **Service Manager** is a component which can run in a dedicated process
|
||||
or embedded within another process. Only one Service Manager exists globally
|
||||
across the system, and in Chromium the browser process runs an embedded Service
|
||||
Manager instance immediately on startup. The Service Manager spawns
|
||||
**service instances** on-demand, and it routes each interface request from a
|
||||
service instance to some destination instance of the Service Manager's choosing.
|
||||
|
||||
Each service instance implements the
|
||||
[**`Service`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h)
|
||||
interface to receive incoming interface requests brokered by the Service
|
||||
Manager, and each service instance has a
|
||||
[**`Connector`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h)
|
||||
it can use to issue interface requests to other services via the
|
||||
Service Manager.
|
||||
|
||||
Every service has a **manifest** which declares some static metadata about the
|
||||
service. This metadata is used by the Service Manager for various purposes,
|
||||
including as a declaration of what interfaces are exposed to other services in
|
||||
the system. This eases the security review process.
|
||||
|
||||
Inside its manifest every service declares its **service name**, used to
|
||||
identify instances of the service in the most general sense. Names are free-form
|
||||
and usually short strings which must be globally unique. Some services defined
|
||||
in Chromium today include `"device"`, `"identity"`, and `"network"` services.
|
||||
|
||||
For more complete and in-depth coverage of the concepts covered here and other
|
||||
related APIs, see the
|
||||
[Service Manager documentation](/services/service_manager/README.md).
|
||||
|
||||
## Example: Building a Simple Out-of-Process Service
|
||||
|
||||
There are multiple steps required to get a new service up and running in
|
||||
Chromium. You must:
|
||||
|
||||
- Define the `Service` implementation
|
||||
- Define the service's manifest
|
||||
- Tell Chromium's Service Manager about the manifest
|
||||
- Tell Chromium how to instantiate the `Service` implementation when it's needed
|
||||
|
||||
This section walks through these steps with some brief explanations. For more
|
||||
thorough documentation of the concepts and APIs used herein, see the
|
||||
[Service Manager](/services/service_manager/README.md) and
|
||||
[Mojo](/mojo/README.md) documentation.
|
||||
|
||||
### Defining the Service
|
||||
|
||||
Typically service definitions are placed in a `services` directory, either at
|
||||
the top level of the tree or within some subdirectory. In this example, we'll
|
||||
define a new service for use by Chrome specifically, so we'll define it within
|
||||
`//chrome/services`.
|
||||
|
||||
We can create the following files. First some mojoms:
|
||||
|
||||
``` cpp
|
||||
// src/chrome/services/math/public/mojom/constants.mojom
|
||||
module math.mojom;
|
||||
|
||||
// These are not used by the implementation directly, but will be used in
|
||||
// following sections.
|
||||
const string kServiceName = "math";
|
||||
const string kArithmeticCapability = "arithmetic";
|
||||
```
|
||||
|
||||
``` cpp
|
||||
// src/chrome/services/math/public/mojom/divider.mojom
|
||||
module math.mojom;
|
||||
|
||||
interface Divider {
|
||||
Divide(int32 dividend, int32 divisor) => (int32 quotient);
|
||||
};
|
||||
```
|
||||
|
||||
``` python
|
||||
# src/chrome/services/math/public/mojom/BUILD.gn
|
||||
import "mojo/public/tools/bindings/mojom.gni"
|
||||
|
||||
mojom("mojom") {
|
||||
sources = [
|
||||
"constants.mojom",
|
||||
"divider.mojom",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Then the actual `Service` implementation:
|
||||
|
||||
``` cpp
|
||||
// src/chrome/services/math/math_service.h
|
||||
#include "services/service_manager/public/cpp/service.h"
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "chrome/services/math/public/mojom/divider.mojom.h"
|
||||
|
||||
namespace math {
|
||||
|
||||
class MathService : public service_manager::Service,
|
||||
public mojom::Divider {
|
||||
public:
|
||||
explicit MathService(service_manager::mojom::ServiceRequest request);
|
||||
~MathService() override;
|
||||
|
||||
private:
|
||||
// service_manager::Service:
|
||||
void OnBindInterface(const service_manager::BindSourceInfo& source,
|
||||
const std::string& interface_name,
|
||||
mojo::ScopedMessagePipeHandle interface_pipe) override;
|
||||
|
||||
// mojom::Divider:
|
||||
void Divide(int32_t dividend,
|
||||
int32_t divisor,
|
||||
DivideCallback callback) override;
|
||||
|
||||
service_manager::ServiceBinding service_binding_;
|
||||
|
||||
// You could also use a Binding. We use BindingSet to conveniently allow
|
||||
// multiple clients to bind to the same instance of this class. See Mojo
|
||||
// C++ Bindings documentation for more information.
|
||||
mojo::BindingSet<mojom::Divider> divider_bindings_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MathService);
|
||||
};
|
||||
|
||||
} // namespace math
|
||||
```
|
||||
|
||||
``` cpp
|
||||
// src/chrome/services/math/math_service.cc
|
||||
#include "chrome/services/math/math_service.h"
|
||||
|
||||
namespace math {
|
||||
|
||||
MathService::MathService(service_manager::ServiceRequest request)
|
||||
: service_binding_(this, std::move(request)) {}
|
||||
|
||||
MathService::~MathService() = default;
|
||||
|
||||
void MathService::OnBindInterface(
|
||||
const service_manager::BindSourceInfo& source,
|
||||
const std::string& interface_name,
|
||||
mojo::ScopedMessagePipeHandle interface_pipe) {
|
||||
// Note that services typically use a service_manager::BinderRegistry if they
|
||||
// plan on handling many different interface request types.
|
||||
if (interface_name == mojom::Divider::Name_) {
|
||||
divider_bindings_.AddBinding(
|
||||
this, mojom::DividerRequest(std::move(interface_pipe)));
|
||||
}
|
||||
}
|
||||
|
||||
void MathService::Divide(int32_t dividend,
|
||||
int32_t divisor,
|
||||
DivideCallback callback) {
|
||||
// Respond with the quotient!
|
||||
callback.Run(dividend / divisor);
|
||||
}
|
||||
|
||||
} // namespace math
|
||||
```
|
||||
|
||||
``` python
|
||||
# src/chrome/services/math/BUILD.gn
|
||||
|
||||
source_set("math") {
|
||||
sources = [
|
||||
"math.cc",
|
||||
"math.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//chrome/services/math/public/mojom",
|
||||
"//services/service_manager/public/cpp",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Now we have a fully defined `math` service implementation, including a nice
|
||||
little `Divider` interface for clients to play with. Next we need to define the
|
||||
service's manifest to declare how the service can be used.
|
||||
|
||||
### Defining the Manifest
|
||||
Manifests are defined as
|
||||
[`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h)
|
||||
objects, typically built using a
|
||||
[`ManifestBuilder`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h). As a general rule, services should define their manifest
|
||||
in a dedicated `source_set` or `component` target under their `public/cpp`
|
||||
subdirectory (typically referred to as the service's **C++ client library**).
|
||||
|
||||
We can create the following files for this purpose:
|
||||
|
||||
``` cpp
|
||||
// src/chrome/services/math/public/cpp/manifest.h
|
||||
#include "services/service_manager/public/cpp/manifest.h"
|
||||
|
||||
namespace math {
|
||||
|
||||
const service_manager::Manifest& GetManifest();
|
||||
|
||||
} // namespace math
|
||||
```
|
||||
|
||||
``` cpp
|
||||
// src/chrome/services/math/public/cpp/manifest.cc
|
||||
#include "chrome/services/math/public/cpp/manifest.h"
|
||||
|
||||
#include "base/no_destructor.h"
|
||||
#include "chrome/services/math/public/mojom/constants.mojom.h"
|
||||
#include "chrome/services/math/public/mojom/divider.mojom.h"
|
||||
#include "services/service_manager/public/cpp/manifest_builder.h"
|
||||
|
||||
namespace math {
|
||||
|
||||
const service_manager::Manifest& GetManifest() {
|
||||
static base::NoDestructor<service_manager::Manifest> manifest{
|
||||
service_manager::ManifestBuilder()
|
||||
.WithServiceName(mojom::kServiceName)
|
||||
.ExposeCapability(
|
||||
mojom::kArithmeticCapability,
|
||||
service_manager::Manifest::InterfaceList<mojom::Divider>())
|
||||
.Build()};
|
||||
return *manifest
|
||||
}
|
||||
|
||||
} // namespace math
|
||||
```
|
||||
|
||||
We also need to define a build target for our manifest sources:
|
||||
|
||||
``` python
|
||||
# src/chrome/services/math/public/cpp/BUILD.gn
|
||||
|
||||
source_set("manifest") {
|
||||
sources = [
|
||||
"manifest.cc",
|
||||
"manifest.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//chrome/services/math/public/mojom",
|
||||
"//services/service_manager/public/cpp",
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The above `Manifest` definition declares that the service is named `math` and
|
||||
that it **exposes** a single **capability** named `arithmetic` which allows
|
||||
access to the `Divider` interface.
|
||||
|
||||
Another service may **require** this capability from its own manifest in order
|
||||
for the Service Manager to grant it access to a `Divider`. We'll see this a
|
||||
few sections below. First, let's get the manifest and service implementation
|
||||
registered with Chromium's Service Manager.
|
||||
|
||||
### Registering the Manifest
|
||||
|
||||
For the most common out-of-process service cases, we register service manifests
|
||||
by **packaging** them in Chrome. This can be done by augmenting the value
|
||||
returned by
|
||||
[`GetChromePackagedServiceManifests`](https://cs.chromium.org/chromium/src/chrome/app/chrome_packaged_service_manifests.cc?rcl=af43cabf3c01e28be437becb972a7eae44fd54e8&l=133).
|
||||
|
||||
We can add our manifest there:
|
||||
|
||||
``` cpp
|
||||
// Deep within src/chrome/app/chrome_packaged_service_manifests.cc...
|
||||
const std::vector<service_manager::Manifest>
|
||||
GetChromePackagedServiceManifests() {
|
||||
...
|
||||
math::GetManifest(),
|
||||
...
|
||||
```
|
||||
|
||||
And don't forget to add a GN dependency from
|
||||
`//chrome/app:packaged_service_manifests` onto
|
||||
`//chrome/services/math/public/cpp:manifest`!
|
||||
|
||||
We're almost done with service setup. The last step is to teach Chromium (and
|
||||
thus the Service Manager) how to launch an instance of our beautiful `math`
|
||||
service.
|
||||
|
||||
### Hooking Up the Service Implementation
|
||||
|
||||
There are two parts to this for an out-of-process Chrome service.
|
||||
|
||||
First, we need
|
||||
to inform the embedded Service Manager that this service is an out-of-process
|
||||
service. The goofiness of this part is a product of some legacy issues and it
|
||||
should be eliminated soon, but for now it just means teaching the Service
|
||||
Manager how to *label* the process it creates for this service (e.g. how the process will
|
||||
appear in the system task manager). We modify
|
||||
[`ChromeContentBrowserClient::RegisterOutOfProcessServices`](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=960886a7febcc2acccea7f797d3d5e03a344a12c&l=3766)
|
||||
for this:
|
||||
|
||||
``` cpp
|
||||
void ChromeContentBrowserClient::RegisterOutOfProcessServices(
|
||||
OutOfProcessServicesMap* services) {
|
||||
...
|
||||
|
||||
(*services)[math::mojom::kServiceName] =
|
||||
base::BindRepeating([]() -> base::string16 {
|
||||
return "Math Service";
|
||||
});
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
And finally, since nearly all out-of-process services run in a "utility" process
|
||||
today, we need to add a dependency on our actual `Service` implementation to
|
||||
Chrome's service spawning code within the utility process.
|
||||
|
||||
For this step we just modify
|
||||
[`ChromeContentUtilityClient::MaybeCreateMainThreadService`](https://cs.chromium.org/chromium/src/chrome/utility/chrome_content_utility_client.cc?rcl=7226adebd6e8d077d673a82acf1aab0790627178&l=261)
|
||||
by adding a block of code as follows:
|
||||
|
||||
``` cpp
|
||||
void ChromeContentUtilityClient::MaybeCreateMainThreadService(
|
||||
const std::string& service_name,
|
||||
service_manager::mojom::ServiceRequest request) {
|
||||
...
|
||||
|
||||
if (service_name == math::mojom::kServiceName)
|
||||
return std::make_unique<math::MathService>(std::move(request));
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
And we're done!
|
||||
|
||||
As one nice follow-up step, let's use our math service from the browser.
|
||||
|
||||
### Using the Service
|
||||
|
||||
We can grant the browser process access to our `Divider` interface by
|
||||
**requiring** the `math` service's `arithmetic` capability within the
|
||||
`content_browser` service manifest.
|
||||
|
||||
*** aside
|
||||
NOTE: See the following section for an elaboration on what `content_browser` is.
|
||||
For the sake of this example, it's magic.
|
||||
***
|
||||
|
||||
For Chrome-specific features such as our glorious new `math` service, we can
|
||||
amend the `content_browser` manifest by modifying
|
||||
[GetChromeContentBrowserOverlayManifest](https://cs.chromium.org/chromium/src/chrome/app/chrome_content_browser_overlay_manifest.cc?rcl=38db90321e8e3627b2f3165cdb051fa8d668af48&l=100)
|
||||
as follows:
|
||||
|
||||
``` cpp
|
||||
// src/chrome/app/chrome_content_browser_overlay_manifest.cc
|
||||
|
||||
...
|
||||
const service_manager::Manifest& GetChromeContentBrowserOverlayManifest() {
|
||||
...
|
||||
.RequireCapability(math::mojom::kServiceName,
|
||||
math::mojom::kArithmeticCapability)
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we can use the global `content_browser` instance's `Connector` to send
|
||||
an interface request to our service. This is accessible from the main thread of
|
||||
the browser process. Somewhere in `src/chrome/browser`, we can write:
|
||||
|
||||
``` cpp
|
||||
// This gives us the global content_browser's Connector
|
||||
service_manager::Connector* connector =
|
||||
content::ServiceManagerConnection::GetForProcess()->GetConnector();
|
||||
|
||||
// Recall from the earlier Mojo section that mojo::MakeRequest creates a new
|
||||
// message pipe for our interface. Connector passes the request endpoint to
|
||||
// the Service Manager along with the name of our target service, "math".
|
||||
math::mojom::DividerPtr divider;
|
||||
connector->BindInterface(math::mojom::kServiceName,
|
||||
mojo::MakeRequest(÷r));
|
||||
|
||||
// As a client, we do not have to wait for any acknowledgement or confirmation
|
||||
// of a connection. We can start queueing messages immediately and they will be
|
||||
// delivered as soon as the service is up and running.
|
||||
divider->Divide(
|
||||
42, 6, base::BindOnce([](int32_t quotient) { LOG(INFO) << quotient; }));
|
||||
```
|
||||
|
||||
This should successfully spawn a new process to run the `math` service if it's
|
||||
not already running, then ask it to do a division, and ultimately log the result
|
||||
after it's sent back to the browser process.
|
||||
|
||||
Finally it's worth reiterating that every service instance in the system has
|
||||
its own `Connector` and there's no reason we have to limit ourselves to
|
||||
`content_browser` as the client, as long as the appropriate manifest declares
|
||||
that it requires our `arithmetic` capability.
|
||||
|
||||
If we did not update the `content_browser` manifest overlay as we did in this
|
||||
example, the `Divide` call would never reach the `math` service (in fact the
|
||||
service wouldn't even be started) and instead we'd get an error message (or in
|
||||
developer builds, an assertion failure) informing us that the Service Manager
|
||||
blocked the `BindInterface` call.
|
||||
|
||||
## Content-Layer Services Overview
|
||||
|
||||
Apart from very early initialization steps in the browser process, every bit of
|
||||
logic in Chromium today is effectively running as part of one service instance
|
||||
or another.
|
||||
|
||||
Although we continue to migrate parts of the browser's privileged
|
||||
functionality to more granular services defined below the Content layer, the
|
||||
main services defined in Chromium today continue to model the Content layer's
|
||||
classical multiprocess architecture which defines a handful of
|
||||
**process types**: browser, renderer, gpu, utility, and plugin processes. For
|
||||
each of these process types, we now define corresponding services.
|
||||
|
||||
Manifest definitions for all of the following services can be found in
|
||||
`//content/public/app`.
|
||||
|
||||
### The Browser Service
|
||||
|
||||
`content_browser` is defined to encapsulate general-purpose browser process
|
||||
code. There are multiple instances of this service, all running within the
|
||||
singular browser process. There is one shared global instance as well an
|
||||
additional instance for each `BrowserContext` (*i.e.* per Chrome profile).
|
||||
|
||||
The global instance exists primarily so that arbitrary browser process code can
|
||||
reach various system services conveniently via a global `Connector` instance
|
||||
on the main thread.
|
||||
|
||||
Each instance associated with a `BrowserContext` is placed in an isolated
|
||||
instance group specific to that `BrowserContext`. This limits the service
|
||||
instances with which its `Connector` can make contact. These instances are
|
||||
used primarily to facilitate the spawning of other isolated per-profile service
|
||||
instances, such as renderers and plugins.
|
||||
|
||||
### The Renderer Service
|
||||
|
||||
A `content_renderer` instance is spawned in its own sandboxed process for every
|
||||
site-isolated instance of Blink we require. Instances are placed in the same
|
||||
instance group as the renderer's corresponding `BrowserContext`, *i.e.* the
|
||||
profile which navigated to the site being rendered.
|
||||
|
||||
Most interfaces used by `content_renderer` are not brokered through the Service
|
||||
Manager but instead are brokered through dedicated interfaces implemented by
|
||||
`content_browser`, with which each renderer maintains persistent connections.
|
||||
|
||||
### The GPU Service
|
||||
|
||||
Only a single instance of `content_gpu` exists at a time and it always runs in
|
||||
its own isolated, sandboxed process. This service hosts the code in content/gpu
|
||||
and whatever else Content's embedder adds to that for GPU support.
|
||||
|
||||
### The Plugin Service
|
||||
|
||||
`content_plugin` hosts a plugin in an isolated process. Similarly to
|
||||
`content_renderer` instances, each instance of `content_plugin` belongs to
|
||||
an instance group associated with a specific `BrowserContext`, and in general
|
||||
plugins get most of their functionality by talking directly to `content_browser`
|
||||
rather than brokering interface requests through the Service Manager.
|
||||
|
||||
### The Utility Service
|
||||
|
||||
`content_utility` exists only nominally today, as there is no remaining API
|
||||
surface within Content which would allow a caller to explicitly create an
|
||||
instance of it. Instead, this service is used exclusively to bootstrap new
|
||||
isolated processes in which other services will run.
|
||||
|
||||
## Exposing Interfaces Between Content Processes
|
||||
|
||||
Apart from the standard Service Manager APIs, the Content layer defines a number
|
||||
of additional concepts for Content and its embedder to expose interfaces
|
||||
specifically between Content processes in various contexts.
|
||||
|
||||
### Exposing Browser Interfaces to Renderer Documents and Workers
|
||||
|
||||
Documents and workers are somewhat of a special case since interface access
|
||||
decisions often require browser-centric state that the Service Manager cannot
|
||||
know about, such as details of the current `BrowserContext`, the origin of the
|
||||
renderered content, installed extensions in the renderer, *etc.* For this
|
||||
reason, interface brokering decisions are increasingly being made by the
|
||||
browser.
|
||||
|
||||
There are two ways this is done: the Deprecated way and the New way.
|
||||
|
||||
#### The Deprecated Way: InterfaceProvider
|
||||
|
||||
This is built on the concept of **interface filters** and the
|
||||
**`InterfaceProvider`** interface. It is **deprecated** and new features should
|
||||
use [The New Way](#The-New-Way_Interface-Brokers) instead. This section only
|
||||
briefly covers practical usage in Chromium.
|
||||
|
||||
The `content_browser` manifest exposes capabilities on a few named interface
|
||||
filters, the main one being `"navigation:frame"`. There are others scoped to
|
||||
different worker contexts, *e.g.* `"navigation:service_worker"`.
|
||||
`RenderProcessHostImpl` or `RenderFrameHostImpl` sets up an `InterfaceProvider`
|
||||
for each known execution context in the corresponding renderer, filtered through
|
||||
the Service Manager according to one of the named filters.
|
||||
|
||||
The practical result of all this means the interface must be listed in the
|
||||
`content_browser` manifest under the
|
||||
`ExposeInterfaceFilterCapability_Deprecated("navigation:frame", "renderer", ...)`
|
||||
entry, and a corresponding interface request handler must be registered with the
|
||||
host's `registry_` in
|
||||
[`RenderFrameHostImpl::RegisterMojoInterfaces`](https://cs.chromium.org/chromium/src/content/browser/frame_host/render_frame_host_impl.cc?rcl=0a23c78c57ecb2405837155aa0a0def7b5ba9c22&l=3971)
|
||||
|
||||
Similarly for worker contexts, an interface must be exposed by the `"renderer"`
|
||||
capability on the corresponding interface filter
|
||||
(*e.g.*, `"navigation:shared_worker"`) and a request handler must be registered
|
||||
within
|
||||
[`RendererInterfaceBinders::InitializeParameterizedBinderRegistry`](https://cs.chromium.org/chromium/src/content/browser/renderer_interface_binders.cc?rcl=0a23c78c57ecb2405837155aa0a0def7b5ba9c22&l=116).
|
||||
|
||||
The best way to understand all of this after reading this section is to look at
|
||||
the linked code above and examine a few examples. They are fairly repetitive.
|
||||
For additional convenience, here is also a link to the `content_browser`
|
||||
[manifest](https://cs.chromium.org/chromium/src/content/public/app/content_browser_manifest.cc).
|
||||
|
||||
#### The New Way: Interface Brokers
|
||||
|
||||
*** aside
|
||||
In classic Google tradition, the New Way is not entirely ready yet. As of this
|
||||
writing, worker-scoped interfaces must still use the Old Way described above.
|
||||
***
|
||||
|
||||
Rather than the confusing spaghetti of interface filter logic, we now define an
|
||||
explicit mojom interface with a persistent connection between a renderer's
|
||||
frame object and the corresponding `RenderFrameHostImpl` in the browser process.
|
||||
This interface is called
|
||||
[`DocumentInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/frame/document_interface_broker.mojom?rcl=ea6921f717f21e9a72d321a15c4bf50d47d10310&l=11)
|
||||
and is fairly easy to work with: you simply add a new factory method to the
|
||||
interface definition:
|
||||
|
||||
``` cpp
|
||||
interface DocumentInterfaceBroker {
|
||||
...
|
||||
|
||||
GetGoatTeleporter(magic.mojom.GoatTeleporter& request);
|
||||
};
|
||||
```
|
||||
|
||||
and implement this new method on `RenderFrameHostImpl`, which is an
|
||||
implementation (**the** production implementation) of
|
||||
`DocumentInterfaceBroker`:
|
||||
|
||||
``` cpp
|
||||
void RenderFrameHostImpl::GetGoatTeleporter(
|
||||
magic::mojom::GoatTeleporterRequest request) {
|
||||
goat_teleporter_binding_.Bind(std::move(request));
|
||||
}
|
||||
```
|
||||
|
||||
### Exposing Browser Interfaces to Render Processes
|
||||
|
||||
Sometimes (albeit rarely) it's useful to expose a browser interface directly to
|
||||
a renderer process. This can be done as for any other interface exposed between
|
||||
two services. In this specific instance, the `content_browser` manifest exposes
|
||||
a capability named `"renderer"` which `content_renderer` requires. Any interface
|
||||
listed as part of that capability can be accessed by a `content_renderer`
|
||||
instance by using its own `Connector`. See below.
|
||||
|
||||
### Exposing Browser Interfaces to Content Child Processes
|
||||
|
||||
All Content child process types (renderer, GPU, and plugin) share a common API
|
||||
to interface with the Service Manager. Their Service Manager connection is
|
||||
initialized and maintained by `ChildThreadImpl` on process startup, and from
|
||||
the main thread, you can access the process's `Connector` as follows:
|
||||
|
||||
``` cpp
|
||||
auto* connector = content::ChildThread::Get()->GetConnector();
|
||||
|
||||
// For example...
|
||||
connector->BindInterface(content::mojom::kBrowserServiceName,
|
||||
std::move(some_request));
|
||||
```
|
||||
|
||||
### Exposing Content Child Process Interfaces to the Browser
|
||||
|
||||
Content child processes may also expose interfaces to the browser, though this
|
||||
is much less common and requires a fair bit of caution since the browser must be
|
||||
careful to only call `Connector.BindInterface` in these cases with an exact
|
||||
`service_manager::Identity` to avoid unexpected behavior.
|
||||
|
||||
Every child process provides a subclass of ChildThreadImpl, and this can be used
|
||||
to install a new `ConnectionFilter` on the process's Service Manager connection
|
||||
before starting to accept requests.
|
||||
|
||||
This behavior should really be considered deprecated, but for posterity, here is
|
||||
how the GPU process does it:
|
||||
|
||||
1. [Disable Service Manager connection auto-start](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=62)
|
||||
2. [Register a new ConnectionFilter impl to handle certain interface requests](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=255)
|
||||
3. [Start the Service Manager connection manually](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=257)
|
||||
|
||||
It's much more common instead for there to be some primordial interface
|
||||
connection established by the child process which can then be used to facilitate
|
||||
push communications from the browser, so please consider not duplicating this
|
||||
behavior.
|
||||
|
||||
## Additional Support
|
||||
|
||||
If this document was not helpful in some way, please post a message to your
|
||||
friendly
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
or
|
||||
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
|
||||
mailing list.
|
@ -1,157 +0,0 @@
|
||||
# Mojo For Chromium Developers
|
||||
|
||||
## Overview
|
||||
|
||||
This document contains the minimum amount of information needed for a developer
|
||||
to start using Mojo in Chromium. For more detailed documentation on the C++
|
||||
bindings, see [this link](/mojo/public/cpp/bindings/README.md).
|
||||
|
||||
## Terminology
|
||||
|
||||
A **message pipe** is a pair of **endpoints**. Each endpoint has a queue of
|
||||
incoming messages, and writing a message to one endpoint effectively enqueues
|
||||
that message on the other endpoint. Message pipes are thus bidirectional.
|
||||
|
||||
A **mojom** file describes **interfaces** which describe strongly typed message
|
||||
structures, similar to proto files.
|
||||
|
||||
Given a **mojom interface** and a **message pipe**, the two **endpoints**
|
||||
can be given the labels **InterfacePtr** and **Binding**. This now describes a
|
||||
strongly typed **message pipe** which transports messages described by the
|
||||
**mojom interface**. The **InterfacePtr** is the **endpoint** which "sends"
|
||||
messages, and the **Binding** "receives" messages. Note that the **message
|
||||
pipe** itself is still bidirectional, and it's possible for a message to have a
|
||||
response callback, which the **InterfacePtr** would receive.
|
||||
|
||||
Another way to think of this is that an **InterfacePtr** is capable of making
|
||||
remote calls on an implementation of the mojom interface associated with the
|
||||
**Binding**.
|
||||
|
||||
The **Binding** itself is just glue that wires the endpoint's message queue up
|
||||
to some implementation of the interface provided by the developer.
|
||||
|
||||
## Example
|
||||
|
||||
Let's apply this to Chrome. Let's say we want to send a "Ping" message from a
|
||||
Browser to a Renderer. First we need to define the mojom interface.
|
||||
|
||||
```
|
||||
module example.mojom;
|
||||
interface PingResponder {
|
||||
// Receives a "Ping" and responds with a random integer.
|
||||
Ping() => (int random);
|
||||
};
|
||||
```
|
||||
|
||||
Now let's make a MessagePipe.
|
||||
```cpp
|
||||
example::mojom::PingResponderPtr ping_responder;
|
||||
example::mojom::PingResponderRequest request = mojo::MakeRequest(&ping_responder);
|
||||
```
|
||||
|
||||
In this example, ```ping_responder``` is the **InterfacePtr**, and ```request```
|
||||
is an **InterfaceRequest**, which is a **Binding** precursor that will shortly
|
||||
be turned into a **Binding**. Now we can send our Ping message.
|
||||
|
||||
```cpp
|
||||
auto callback = base::Bind(&OnPong);
|
||||
ping_responder->Ping(callback);
|
||||
```
|
||||
|
||||
Important aside: If we want to receive the the response, we must keep the object
|
||||
```ping_responder``` alive. After all, it's just a wrapper around a **Message
|
||||
Pipe endpoint**, if it were to go away, there'd be nothing left to receive the
|
||||
response.
|
||||
|
||||
We're done! Of course, if everything were this easy, this document wouldn't need
|
||||
to exist. We've taken the hard problem of sending a message from the Browser to
|
||||
a Renderer, and transformed it into a problem where we just need to take the
|
||||
```request``` object, pass it to the Renderer, turn it into a **Binding**, and
|
||||
implement the interface.
|
||||
|
||||
In Chrome, processes host services, and the services themselves are connected to
|
||||
a Service Manager via **message pipes**. It's easy to pass ```request``` to the
|
||||
appropriate Renderer using the Service Manager, but this requires explicitly
|
||||
declaring our intentions via manifest files. For this example, we'll use the
|
||||
content_browser service [manifest
|
||||
file](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_browser_manifest.json)
|
||||
and the content_renderer service [manifest
|
||||
file](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_renderer_manifest.json).
|
||||
|
||||
```js
|
||||
content_renderer_manifest.json:
|
||||
...
|
||||
"interface_provider_specs": {
|
||||
"service_manager:connector": {
|
||||
"provides": {
|
||||
"cool_ping_feature": [
|
||||
"example::mojom::PingResponder"
|
||||
]
|
||||
},
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
```js
|
||||
content_browser_manifest.json:
|
||||
...
|
||||
"interface_provider_specs": {
|
||||
"service_manager:connector": {
|
||||
"requires": {
|
||||
"content_renderer": [ "cool_ping_feature" ],
|
||||
},
|
||||
},
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
These changes indicate that the content_renderer service provides the interface
|
||||
PingResponder, under the **capability** named "cool_ping_feature". And the
|
||||
content_browser services intends to use this feature.
|
||||
```content::BindInterface``` is a helper function that takes ```request``` and
|
||||
sends it to the renderer process via the Service Manager.
|
||||
|
||||
```cpp
|
||||
content::RenderProcessHost* host = GetRenderProcessHost();
|
||||
content::BindInterface(host, std::move(request));
|
||||
```
|
||||
|
||||
Putting this all together for the browser process:
|
||||
```cpp
|
||||
example::mojom::PingResponderPtr ping_responder; // Make sure to keep this alive! Otherwise the response will never be received.
|
||||
example::mojom::PingResponderRequest request = mojo::MakeRequest(&ping_responder);
|
||||
ping_responder->Ping(base::BindOnce(&OnPong));
|
||||
content::RenderProcessHost* host = GetRenderProcessHost();
|
||||
content::BindInterface(host, std::move(request));
|
||||
```
|
||||
|
||||
In the Renderer process, we need to write an implementation for PingResponder,
|
||||
and ensure that a **Binding** is created using the transported ```request```. In a
|
||||
standalone Mojo service, this would require us to implement
|
||||
```service_manager::Service::OnBindInterface()```. In Chrome, this is abstracted
|
||||
behind ```content::ConnectionFilters``` and
|
||||
```service_manager::BinderRegistry```. This is typically done in
|
||||
```RenderThreadImpl::Init```.
|
||||
|
||||
```cpp
|
||||
class PingResponderImpl : mojom::PingResponder {
|
||||
void BindToInterface(example::mojom::PingResponderRequest request) {
|
||||
binding_.reset(
|
||||
new mojo::Binding<mojom::MemlogClient>(this, std::move(request)));
|
||||
}
|
||||
void Ping(PingCallback callback) { std::move(callback).Run(4); }
|
||||
std::unique_ptr<mojo::Binding<mojom::PingResponder>> binding_;
|
||||
};
|
||||
|
||||
RenderThreadImpl::Init() {
|
||||
...
|
||||
this->ping_responder = std::make_unique<PingResponderImpl>();
|
||||
auto registry = base::MakeUnique<service_manager::BinderRegistry>();
|
||||
|
||||
// This makes the assumption that |this->ping_responder| will outlive |registry|.
|
||||
registry->AddInterface(base::Bind(&PingResponderImpl::BindToInterface), base::Unretained(this->ping_responder.get()));
|
||||
|
||||
GetServiceManagerConnection()->AddConnectionFilter(
|
||||
base::MakeUnique<SimpleConnectionFilter>(std::move(registry)));
|
||||
...
|
||||
```
|
374
docs/mojo_ipc_conversion.md
Normal file
374
docs/mojo_ipc_conversion.md
Normal file
@ -0,0 +1,374 @@
|
||||
# Converting Legacy IPC to Mojo
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
|
||||
A number of IPC messages sent (primarily between the browser and renderer
|
||||
processes) are still defined using Chrome's old IPC system in `//ipc`. This
|
||||
system uses
|
||||
[`base::Pickle`](https://cs.chromium.org/chromium/src/base/pickle.h?rcl=8b7842262ee1239b1f3ae20b9c851748ef0b9a8b&l=128)
|
||||
as the basis for message serialization and is supported by a number if `IPC_*`
|
||||
preprocessor macros defined in `//ipc` and used around the source tree.
|
||||
|
||||
There is an ongoing, distributed effort to get these messages converted to Mojo
|
||||
interface messages. Messages that still need to be converted are tracked in two
|
||||
spreadsheets:
|
||||
|
||||
- [Chrome IPC to Mojo migration](https://docs.google.com/spreadsheets/d/1pGWX_wxGdjAVtQOmlDDfhuIc3Pbwg9FtvFXAXYu7b7c/edit#gid=0) for non-web platform messages
|
||||
- [Mojoifying Platform Features](https://docs.google.com/spreadsheets/d/1VIINt17Dg2cJjPpoJ_HY3HI0uLpidql-1u8pBJtpbGk/edit#gid=1603373208) for web platform messages
|
||||
|
||||
This document is concerned primarily with rote conversion of legacy IPC messages
|
||||
to Mojo interface messages. If you are considering more holistic refactoring and
|
||||
better isolation of an entire subsystem of the browser, you may consider
|
||||
[servicifying](servicification.md) the feature instead of merely converting its
|
||||
IPCs.
|
||||
|
||||
See other [Mojo & Services](/docs/README.md#Mojo-Services) documentation
|
||||
for introductory guides, API references, and more.
|
||||
|
||||
## Legacy IPC Concepts
|
||||
|
||||
Each Content child process has a single **`IPC::Channel`** implementation going
|
||||
between it and the browser process, and this is used as the sole two-way FIFO
|
||||
to send legacy IPC messages between the processes.
|
||||
|
||||
There are two fundamental types of legacy IPC messages: **control** messages,
|
||||
defined via `IPC_MESSAGE_CONTROLn` macros (where `n` is some small integer) and
|
||||
**routed** messages defined via `IPC_MESSAGE_ROUTEDn` macros.
|
||||
|
||||
Control messages generally go between a browser-side process host (*e.g.*,
|
||||
`RenderProcessHost` or `GpuProcessHost`) and the child-side `ChildThreadImpl`
|
||||
subclass. All of these classes implement `IPC::Sender` and thus have a `Send`
|
||||
method for sending a control message to their remote counterpart, and they
|
||||
implement `IPC::Listener` to receive incoming control messages via
|
||||
`OnMessageReceived`.
|
||||
|
||||
Routed messages are relegated to **routes** which have arbitrary meaning
|
||||
determined by their use within a given process. For example, renderers use
|
||||
routes to isolate messages scoped to individual render frames, and so such
|
||||
routed messages will travel between a `RenderFrameHostImpl` and its
|
||||
corresponding `RenderFrameImpl`, both of which also implement `IPC::Sender` and
|
||||
`IPC::Listener`.
|
||||
|
||||
## Mojo Interfaces as Routes
|
||||
|
||||
Routed messages in the old IPC system always carry a **routing ID** to identify
|
||||
to the receiving endpoint which routed object (*e.g.* which `RenderFrameImpl`
|
||||
or `RenderViewImpl` or whatever) the message is targeting. Each endpoint is thus
|
||||
required to do some additional book-keeping to track what each routing ID means.
|
||||
|
||||
Mojo interfaces obviate the need for routing IDs, as new "routes" can be
|
||||
established by simply creating a new interface pipe and passing one endpoint to
|
||||
something which knows how to bind it.
|
||||
|
||||
When thinking about an IPC message conversion to Mojo, it's important to
|
||||
consider whether the message is a control message or a routed message, as this
|
||||
determines where you might find an existing Mojo interface to carry your
|
||||
message, or where you will want to add a new end-to-end Mojo interface for that
|
||||
purpose. This can mean the difference between a single per-process interface
|
||||
going between each `RenderProcessHostImpl` and its corresponding
|
||||
`RenderThreadImpl`, vs a per-frame interface going between each
|
||||
`RenderFrameHostImpl` and its corresponding `RenderFrameImpl`.
|
||||
|
||||
## Ordering Considerations
|
||||
|
||||
One **very important** consideration when doing IPC conversions is the relative
|
||||
ordering of IPC-driven operations. With the old IPC system, because every
|
||||
message between two processes is globally ordered, it is quite easy for parts
|
||||
of the system to (intentionally or often unintentionally) rely on strict
|
||||
ordering guarantees.
|
||||
|
||||
For example, imagine a `WebContentsObserver` in the browser processes observes
|
||||
a frame navigation and immediately sends an IPC message to the frame to
|
||||
configure some new behavior. The implementation may be inadvertently relying on
|
||||
this message arriving *before* some other tangentially related message sent to
|
||||
the same frame shortly after the same navigation event.
|
||||
|
||||
Mojo does not (and in fact cannot) make any strict ordering guarantees between
|
||||
separate message pipes, as message pipes may be freely moved across process
|
||||
boundaries and thus cannot necessarily share a common FIFO at all times.
|
||||
|
||||
If the two messages described above were moved to separate Mojo interfaces on
|
||||
separate message pipes, renderer behavior could break as the first message may
|
||||
arrive after the second message.
|
||||
|
||||
The best solution to this problem is to rethink the IPC surface and/or
|
||||
implementation on either side to eliminate ordering dependencies between two
|
||||
interfaces that otherwise seem to be logically distinct. Failing that, Mojo's
|
||||
solution to this problem is to support
|
||||
[**associated interfaces**](/mojo/public/tools/bindings/README.md#Associated-Interfaces).
|
||||
In a nutshell, these allow multiple distinct interfaces to be multiplexed over
|
||||
a shared message pipe.
|
||||
|
||||
## Channel-Associated Interfaces
|
||||
|
||||
The previous section mentions **associated interfaces** as a general-purpose
|
||||
solution for establishing a mutual FIFO between multiple logical Mojo interfaces
|
||||
by having them share a single message pipe.
|
||||
|
||||
In Chrome, the `IPC::Channel` which carries all legacy IPC messages between
|
||||
two processes is itself a Mojo message pipe. We provide a mechanism for
|
||||
associating arbitrary Mojo interfaces with this pipe, which means messages can
|
||||
be converted to Mojo while preserving strict FIFO with respect to other legacy
|
||||
IPC messages. Such interfaces are designated in Chrome parlance as
|
||||
**Channel-associated interfaces**.
|
||||
|
||||
*** aside
|
||||
**NOTE:** Channel-associated interface acquisition is not constrained by the
|
||||
Service Manager in any way, so security reviewers need to be careful to inspect
|
||||
new additions and uses of such interfaces.
|
||||
***
|
||||
|
||||
Usage of Channel-associated interfaces should be rare but is considered a
|
||||
reasonable intermediate solution for incremental IPC conversions where it would
|
||||
be too risky or noisy to convert a large IPC surface all at once, but it would
|
||||
also be impossible to split the IPC surface between legacy IPC and a dedicated
|
||||
Mojo interface pipe without introducing timing bugs.
|
||||
|
||||
At this point in Chrome's development, practical usage of Channel-associated
|
||||
interfaces is restricted to the `IPC::Channel` between the browser process and
|
||||
a renderer process, as this is the most complex IPC surface with the most
|
||||
implicit ordering dependencies. A few simple APIs exist to support this.
|
||||
|
||||
`RenderProcessHostImpl` owns an `IPC::Channel` to its corresponding
|
||||
`RenderThreadImpl` in the render process. This object has a
|
||||
`GetRemoteAssociatedInterfaces` method which can be used to pass arbitrary
|
||||
associated interface requests:
|
||||
|
||||
``` cpp
|
||||
magic::mojom::GoatTeleporterAssociatedPtr teleporter;
|
||||
channel_->GetRemoteAssociatedInterfaces()->GetInterface(&teleporter);
|
||||
|
||||
// These messages are all guaranteed to arrive in the same order they were sent.
|
||||
channel_->Send(new FooMsg_SomeLegacyIPC);
|
||||
teleporter->TeleportAllGoats();
|
||||
channel_->Send(new FooMsg_AnotherLegacyIPC);
|
||||
```
|
||||
|
||||
Likewise, `ChildThreadImpl` has an `IPC::Channel` that can be used in the same
|
||||
way to send such messages back to the browser.
|
||||
|
||||
To receive and bind incoming Channel-associated interface requests, the above
|
||||
objects also implement `IPC::Listener::OnAssociatedInterfaceRequest`.
|
||||
|
||||
For supplementation of routed messages, both `RenderFrameHostImpl` and
|
||||
`RenderFrameImpl` define a `GetRemoteAssociatedInterfaces` method which works
|
||||
like the one on `IPC::Channel`, and both objects also implement
|
||||
`IPC::Listener::OnAssociatedInterfaceRequest` for processing incoming associated
|
||||
interface requests specific to their own frame.
|
||||
|
||||
There are some example conversion CLs which use Channel-associated interfaces
|
||||
[here](https://codereview.chromium.org/2381493003) and
|
||||
[here](https://codereview.chromium.org/2400313002).
|
||||
|
||||
## Deciding How to Approach a Conversion
|
||||
|
||||
There are a few questions you should ask before embarking upon any IPC message
|
||||
conversion journey, and there are many potential approaches to consider. The
|
||||
right one depends on context.
|
||||
|
||||
Note that this section assumes the message is traveling between the browser
|
||||
process and a renderer process. Other cases are rare and developers may wish to
|
||||
consult
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
before proceeding with them. Otherwise, apply the following basic algorithm to
|
||||
decide how to proceed:
|
||||
|
||||
- General note: If the message is a reply to some other message (typically these
|
||||
take a "request ID" argument), see the note about message replies at the
|
||||
bottom of this section.
|
||||
- Consider whether or not the message makes sense as part of the IPC surface of
|
||||
a new or existing service somewhere in `//services` or `//chrome/services`,
|
||||
*etc.* This is less and less likely to be the case as time goes on, as many
|
||||
remaining IPC conversions are quite narrowly dealing with specific
|
||||
browser/renderer details rather than the browser's supporting subsystems. If
|
||||
defining a new service, you may wish to consult some of the other
|
||||
[Mojo & Services documentation](/docs/README.md#Mojo-Services) first.
|
||||
- If the message is an `IPC_MESSAGE_CONTROL` message:
|
||||
- If there are likely to be strict ordering requirements between this
|
||||
message and other legacy IPC or Channel-associated interface messages,
|
||||
consider using a new or existing
|
||||
[Channel-associated interface](#Channel-Associated-Interfaces) between
|
||||
`RenderProcessHostImpl` and `RenderThreadImpl`.
|
||||
- If the message is sent from a renderer to the browser:
|
||||
- If an existing interface is bound by `RenderProcessHostImpl` and
|
||||
requested through `RenderThread`'s Connector and seems to be a good
|
||||
fit for the message, add the equivalent Mojo message to that
|
||||
interface.
|
||||
- If no such interface exists, consider adding one for this message and
|
||||
any related messages.
|
||||
- If the message is sent from the browser to a renderer:
|
||||
- If an existing interface is bound by `RenderThreadImpl` and requested
|
||||
through a `BrowserContext` Connector referencing a specific
|
||||
`RenderProcessHost` [identity](https://cs.chromium.org/chromium/src/content/public/browser/render_process_host.h?rcl=1497b88b7d6400a2a5cced258df03d53800d7848&l=327),
|
||||
and the interface seems to be a good fit for the message, add the
|
||||
equivalent Mojo message to that interface.
|
||||
- If no such interface exists, consider adding one for this message and
|
||||
any related messages.
|
||||
- If the message is an `IPC_MESSAGE_ROUTED` message:
|
||||
- Determine what the routing endpoints are. If they are
|
||||
`RenderFrameHostImpl` and `RenderFrameImpl`:
|
||||
- If there are likely to be strict ordering requirements between this
|
||||
message and other legacy IPC or Channel-associated interface messages,
|
||||
consider using a new or existing
|
||||
[Channel-associated interface](#Channel-Associated-Interfaces) between
|
||||
`RenderFrameHostImpl` and `RenderFrameImpl`.
|
||||
- If the message is sent from a renderer to the browser:
|
||||
- If an existing interface is bound by `RenderFrameHostImpl` and
|
||||
acquired either via `RenderFrame::GetRemoteInterfaces` or
|
||||
`RenderFrame::GetDocumentInterfaceBroker` and the interface seems
|
||||
to be a good fit for this message, add the equivalent Mojo message
|
||||
to that interface.
|
||||
- If no such interface exists, consider adding one and exposing it
|
||||
via a new getter method on `DocumentInterfaceBroker`. See the
|
||||
[simple example](/docs/mojo_and_services.md#Example_Defining-a-New-Frame-Interface)
|
||||
earlier in this document.
|
||||
- If the message is sent from the browser to a renderer, consider
|
||||
adding a Mojo equivalent to the `content.mojom.Frame` interface
|
||||
defined
|
||||
[here](https://cs.chromium.org/chromium/src/content/common/frame.mojom?rcl=138b66744ee9ee853cbb0ae8437b71eaa1fafaa9&l=42).
|
||||
- If the routing endpoints are **not** frame objects (for example, they may
|
||||
be `RenderView`/`RenderViewHost` objects), this is a special case which
|
||||
does not yet have an easy conversion approach readily available. Contact
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/chromium-mojo)
|
||||
to propose or discuss options.
|
||||
|
||||
### Dealing With Replies
|
||||
|
||||
If the message is a **reply**, meaning it has a "request ID" which correlates it
|
||||
to a prior message in the opposite direction, consider converting the
|
||||
**request** message following the algorithm above. Unlike with legacy IPC, Mojo
|
||||
messages support replies as a first-class concept. So for example if you have:
|
||||
|
||||
``` cpp
|
||||
IPC_CONTROL_MESSAGE2(FooHostMsg_DoTheThing,
|
||||
int /* request_id */,
|
||||
std::string /* name */);
|
||||
IPC_CONTROL_MESSAGE2(FooMsg_DidTheThing,
|
||||
int /* request_id */,
|
||||
bool /* success */);
|
||||
```
|
||||
|
||||
You should consider defining an interface `Foo` which is bound in
|
||||
`RenderProcessHostImpl` and acquired from `RenderThreadImpl`, with the following
|
||||
mojom definition:
|
||||
|
||||
``` cpp
|
||||
interface Foo {
|
||||
DoTheThing(string name) => (bool success);
|
||||
};
|
||||
```
|
||||
|
||||
## Repurposing `IPC::ParamTraits` and `IPC_STRUCT*` Invocations
|
||||
|
||||
Occasionally it is useful to do partial IPC conversions, where you want to
|
||||
convert a message to a Mojo interface method but you don't want to necessarily
|
||||
convert every structure passed by the message. In this case, you can leverage
|
||||
Mojo's
|
||||
[type-mapping](https://chromium.googlesource.com/chromium/src/+/master/mojo/public/cpp/bindings/README.md#Type-Mapping)
|
||||
system to repurpose existing `IPC::ParamTraits`.
|
||||
|
||||
*** aside
|
||||
**NOTE**: Although in some cases `IPC::ParamTraits<T>` specializations are
|
||||
defined manually in library code, the `IPC_STRUCT*` macro helpers also define
|
||||
`IPC::ParamTraits<T>` specializations under the hood. All advice in this section
|
||||
pertains to both kinds of definitions.
|
||||
***
|
||||
|
||||
If a mojom struct is declared without a struct body and is tagged with
|
||||
`[Native]`, and a corresponding typemap is provided for the struct, the emitted
|
||||
C++ bindings will -- as if by magic -- replace the mojom type with the
|
||||
typemapped C++ type and will internally use the existing `IPC::ParamTraits<T>`
|
||||
specialization for that type in order to serialize and deserialize the struct.
|
||||
|
||||
For example, given the
|
||||
[`resource_messages.h`](https://cs.chromium.org/chromium/src/content/common/resource_messages.h?rcl=2e7a430d8d88222c04ab3ffb0a143fa85b3cec5b&l=215) header
|
||||
which defines an IPC mapping for `content::ResourceRequest`:
|
||||
|
||||
``` cpp
|
||||
IPC_STRUCT_TRAITS_BEGIN(content::ResourceRequest)
|
||||
IPC_STRUCT_TRAITS_MEMBER(method)
|
||||
IPC_STRUCT_TRAITS_MEMBER(url)
|
||||
// ...
|
||||
IPC_STRUCT_TRAITS_END()
|
||||
```
|
||||
|
||||
and the
|
||||
[`resource_request.h`](https://cs.chromium.org/chromium/src/content/common/resource_request.h?rcl=dce9e476a525e4ff0304787935dc1a8c38392ac8&l=32) header
|
||||
which actually defines the `content::ResourceRequest` type:
|
||||
|
||||
``` cpp
|
||||
namespace content {
|
||||
|
||||
struct CONTENT_EXPORT ResourceRequest {
|
||||
// ...
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
```
|
||||
|
||||
we can declare a corresponding "native" mojom struct:
|
||||
|
||||
``` cpp
|
||||
module content.mojom;
|
||||
|
||||
[Native]
|
||||
struct URLRequest;
|
||||
```
|
||||
|
||||
and add a typemap like
|
||||
[`url_request.typemap`](https://cs.chromium.org/chromium/src/content/common/url_request.typemap?rcl=4b5963fa744a706398f8f06a4cbbf70d7fa3213d)
|
||||
to define how to map between them:
|
||||
|
||||
``` python
|
||||
mojom = "//content/public/common/url_loader.mojom"
|
||||
public_headers = [ "//content/common/resource_request.h" ]
|
||||
traits_headers = [ "//content/common/resource_messages.h" ]
|
||||
...
|
||||
type_mappings = [ "content.mojom.URLRequest=content::ResourceRequest" ]
|
||||
```
|
||||
|
||||
Note specifically that public_headers includes the definition of the native C++
|
||||
type, and traits_headers includes the definition of the legacy IPC traits.
|
||||
|
||||
As a result of all this, other mojom files can now reference
|
||||
`content.mojom.URLRequest` as a type for method parameters and other struct
|
||||
fields, and the generated C++ bindings will represent those values exclusively
|
||||
as `content::ResourceRequest` objects.
|
||||
|
||||
This same basic approach can be used to leverage existing `IPC_ENUM_TRAITS` for
|
||||
invocations for `[Native]` mojom enum aliases.
|
||||
|
||||
*** aside
|
||||
**NOTE:** Use of `[Native]` mojom definitions is strictly limited to C++
|
||||
bindings. If a mojom message depends on such definitions, it cannot be sent or
|
||||
received by other language bindings. This feature also depends on continued
|
||||
support for legacy IPC serialization and all uses of it should therefore be
|
||||
treated as technical debt.
|
||||
***
|
||||
|
||||
## Typemaps For Content and Blink Types
|
||||
|
||||
Using typemapping for messages that go between Blink and content browser code
|
||||
can sometimes be tricky due to things like dependency cycles or confusion over
|
||||
the correct place for some definition
|
||||
to live. There are some example CLs provided here, but feel free to also contact
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
with specific details if you encounter trouble.
|
||||
|
||||
[This CL](https://codereview.chromium.org/2363533002) introduces a Mojom
|
||||
definition and typemap for `ui::WindowOpenDisposition` as a precursor to the
|
||||
IPC conversion below.
|
||||
|
||||
The [follow-up CL](https://codereview.chromium.org/2363573002) uses that
|
||||
definition along with several other new typemaps (including native typemaps as
|
||||
described above) to convert the relatively large `ViewHostMsg_CreateWindow`
|
||||
message to Mojo.
|
||||
|
||||
## Additional Support
|
||||
|
||||
If this document was not helpful in some way, please post a message to your
|
||||
friendly
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
mailing list.
|
@ -1,102 +1,197 @@
|
||||
# Servicification Strategies
|
||||
|
||||
This document captures strategies, hints, and best practices for solving typical
|
||||
challenges enountered when converting existing Chromium
|
||||
code to services. It is assumed that you have already read the high-level
|
||||
documentation on [what a service is](/services).
|
||||
|
||||
If you're looking for Mojo documentation, please see the [general
|
||||
Mojo documentation](/mojo) and/or the [documentation on converting Chrome IPC to
|
||||
Mojo](/ipc).
|
||||
|
||||
Note that throughout the below document we link to CLs to illustrate the
|
||||
strategies being made. Over the course of time code tends to shift, so it is
|
||||
likely that the code on trunk does not exactly match what it was at the time of
|
||||
the CLs. When necessary, use the CLs as a starting point for examining the
|
||||
current state of the codebase with respect to these issues (e.g., exactly where
|
||||
a service is embedded within the content layer).
|
||||
# Servicifying Chromium Features
|
||||
|
||||
[TOC]
|
||||
|
||||
## Questions to Answer When Getting Started
|
||||
## Overview
|
||||
|
||||
For the basic nuts and bolts of how to create a new service, see [the
|
||||
documentation on adding a new service](/services#Adding-a-new-service). This
|
||||
section gives questions that you should answer in order to shape the design of
|
||||
your service, as well as hints as to which answers make sense given your
|
||||
situation.
|
||||
Much to the dismay of Chromium developers, practicing linguists, and keyboard
|
||||
operators everywhere, the term **servicificificification** [sic] has been
|
||||
egregiously smuggled into the Chromium parlance.
|
||||
|
||||
### Is your service global or per-BrowserContext?
|
||||
The Service Manager can either:
|
||||
Lots of Chromium code is contained in reasonably well-isolated component
|
||||
libraries with some occasionally fuzzy boundaries and often a surprising number
|
||||
of gnarly runtime interdependencies among a complex graph of components. Y
|
||||
implements one of Z's delegate interfaces, while X implements one of Y's
|
||||
delegate interfaces, and now it's possible for some ridiculous bug to creep in
|
||||
where W calls into Z at the wrong time and causes a crash in X. Yikes.
|
||||
|
||||
- create one service instance per instance group or
|
||||
- field all connection requests for a given service via the same instance
|
||||
Servicification embodies the ongoing process of **servicifying** Chromium
|
||||
features and subsystems, or refactoring these collections of library code into
|
||||
services with well-defined public API boundaries and very strong runtime
|
||||
isolation via Mojo interfaces.
|
||||
|
||||
Which of these policies the Service Manager employs is determined by the
|
||||
contents of your service manifest: the former is the default, while the latter
|
||||
is selected by informing the Service Manager that your service has the
|
||||
"instance_sharing" option value set to "shared_across_instance_groups"
|
||||
([example](https://cs.chromium.org/chromium/src/services/device/manifest.json)).
|
||||
The primary goals are to improve maintainability and extensibility of the system
|
||||
over time, while also allowing for more flexible runtime configuration. For
|
||||
example, with the Network Service in place we can now run the entire network
|
||||
stack either inside or outside of the browser process with the flip of a
|
||||
command-line switch. Client code using the Network Service stays the same,
|
||||
independent of that switch.
|
||||
|
||||
Service manifests are described in more detail in this
|
||||
[document](https://chromium.googlesource.com/chromium/src/+/master/services/service_manager/service_manifests.md).
|
||||
This document focuses on helpful guidelines and patterns for servicifying parts
|
||||
of Chromium, taking into account some nuances in how Chromium models its core
|
||||
services as well as how it embeds and configures the Service Manager. Readers
|
||||
are strongly encouraged to first read some basic
|
||||
[Service Manager documentation](/services/service_manager/README.md), as it will
|
||||
likely make the contents of this document easier to digest.
|
||||
|
||||
In practice, there is one instance group per-BrowserContext, so the question
|
||||
becomes: Is your Service a global or keyed by BrowserContext? In considering
|
||||
this question, there is one obvious hint: If you are converting per-Profile
|
||||
classes (e.g., KeyedServices), then your service is almost certainly going to
|
||||
want an instance per BrowserContext. More generally, if you envision needing to
|
||||
use *any* state related to the profile (e.g., you need to store files in the
|
||||
user's home directory), then your service should have an instance
|
||||
per-BrowserContext.
|
||||
Also see general [Mojo & Services](/docs/README.md#Mojo-Services)
|
||||
documentation for other introductory guides, API references, *etc.*
|
||||
|
||||
Conversely, your service could be a good fit for being global if it is a utility
|
||||
that is unconcerned with the identity of the requesting client (e.g., the [data
|
||||
decoder service](/services/data_decoder), which simply decodes untrusted data in
|
||||
a separate process.
|
||||
## Setting Up The Service
|
||||
|
||||
### Will you embed your service in //content, //chrome, or neither?
|
||||
There are three big things you must decide when building and hooking up a shiny
|
||||
new service:
|
||||
|
||||
At the start (and potentially even long-term), your service will likely not
|
||||
actually run in its own process but will rather be embedded in the browser
|
||||
process. This is especially true in the common case where you are converting
|
||||
existing browser-process code.
|
||||
- Where should the service live in the tree?
|
||||
- Do you need an instance of your service per BrowserContext?
|
||||
- Can Content depend on your service, or must Content embedders like Chrome do
|
||||
so independently?
|
||||
|
||||
You then have a question: Where should it be embedded? The answer to this
|
||||
question hinges on the nature and location of the code that you are converting:
|
||||
This section aims to help you understand and answer those questions.
|
||||
|
||||
- //content is the obvious choice if you are converting existing //content code
|
||||
(e.g., the Device Service). Global services
|
||||
are embedded by [content::ServiceManagerContext](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?type=cs&q=CreateDeviceService),
|
||||
while per-BrowserContext services are naturally embedded by [content::BrowserContext](https://cs.chromium.org/chromium/src/content/browser/browser_context.cc?type=cs&q=CreateFileService).
|
||||
### Where in the Tree?
|
||||
|
||||
- If your service is converting existing //chrome code, then you will need
|
||||
to embed your service in //chrome rather than //content. Global services
|
||||
are embedded by [ChromeContentBrowserClient](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?type=cs&q=CreateMediaService),
|
||||
while per-Profile services are embedded by [ProfileImpl](https://cs.chromium.org/chromium/src/chrome/browser/profiles/profile_impl.cc?type=cs&q=CreateIdentityService).
|
||||
Based on the
|
||||
[service development guidelines](/services/README.md), any service which could
|
||||
be reasonably justified as a core system service in a hypothetical,
|
||||
well-designed operating system may belong in the top-level `//services`
|
||||
directory. If that sounds super hand-wavy and unclear, that's because it is!
|
||||
There isn't really a great universal policy here, so when in doubt, contact your
|
||||
favorite local
|
||||
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev)
|
||||
mailing list and start a friendly discussion.
|
||||
|
||||
- If you are looking to convert all or part of a component (i.e., a feature in
|
||||
//components) into a service, the question arises of whether your new service
|
||||
is worthy of being in //services (i.e., is it a foundational service?). If
|
||||
not, then it can be placed in //components/services. See this
|
||||
[document](https://docs.google.com/document/d/1Zati5ZohwjUM0vz5qj6sWg5r-_I0iisUoSoAMNdd7C8/edit#) for discussion of this point.
|
||||
Other common places where developers place services, and why:
|
||||
|
||||
### If your service is embedded in the browser process, what is its threading model?
|
||||
- `//components/services` for services which haven't yet made the cut for
|
||||
`//services` but which are either used by Content directly or by multiple
|
||||
Content embedders.
|
||||
- `//chrome/services` for services which are used exclusively within Chrome and
|
||||
not shared with other Content embedders.
|
||||
- `//chromeos/services` for services which are used on Chrome OS by more than
|
||||
just Chrome itself (for example, if the `ash` service must also connect to
|
||||
them for use in system UI).
|
||||
|
||||
If your service is embedded in the browser process, it will run on the IO thread
|
||||
by default. You can change that by specifying a task runner as part of the
|
||||
information for constructing your service. In particular, if the code that you
|
||||
are converting is UI-thread code, then you likely want your service running on
|
||||
the UI thread. Look at the changes to profile_impl.cc in [this
|
||||
CL](https://codereview.chromium.org/2753753007) to see an example of setting the
|
||||
task runner that a service should be run on as part of the factory for creating
|
||||
the service.
|
||||
### Inside Content or Not?
|
||||
|
||||
### What is your approach for incremental conversion?
|
||||
The next decision you need to make is whether or not Content will wire in your
|
||||
service directly -- that is, whether or not your service is necessary to support
|
||||
some subsystem Content makes available to either the web platform or to Content
|
||||
embedders like Chrome, Android WebView, Cast Shell, and various third-party
|
||||
applications.
|
||||
|
||||
For example, Content cannot function at all without the Network Service being
|
||||
available, because Content depends heavily on the Network Service to issue and
|
||||
process all of its network requests (imagine that, right?). As such, the
|
||||
Network Service is wired up to the Service Manager from within Content directly.
|
||||
In general, services which will be wired up in Content must live either in
|
||||
`//services` or `//components/services` but ideally the former.
|
||||
|
||||
Conversely there are a large number of services used only by Chrome today,
|
||||
such as the `unzip` service which safely performs sandboxed unpacking of
|
||||
compressed archive files on behalf of clients in the browser process. These
|
||||
can always be placed in `//chrome/services`.
|
||||
|
||||
### Per-BrowserContext or Not?
|
||||
|
||||
Now that you've decided on a source location for your service and you know
|
||||
whether it will be wired into Content or hooked up by Content embedder code, all
|
||||
that's left left is to decide whether or not you want an instance of your service
|
||||
per BrowserContext (*i.e.* per user profile in Chrome).
|
||||
|
||||
The alternative is for you to manage your own instance arity, either as a
|
||||
singleton service (quite common) or as a service which supports multiple
|
||||
instances that are *not* each intrinsically tied to a BrowserContext. Most
|
||||
services choose this path because BrowserContext coupling is typically
|
||||
unnecessary.
|
||||
|
||||
As a general rule, if you're porting a subsystem which today relies heavily
|
||||
on `BrowserContextKeyedService`, it's likely that you want your service
|
||||
instances to have a 1:1 correspondence with BrowserContext instances.
|
||||
|
||||
### Putting It All Together
|
||||
|
||||
Let's get down to brass tacks. You're a developer of action. You've made all the
|
||||
important choices you need to make and you've even built a small and extremely
|
||||
well-tested prototype service with the help of
|
||||
[this glorious guide](/services/service_manager/README.md#Services). Now you
|
||||
want to get it working in Chromium while suffering as little pain as possible.
|
||||
|
||||
You're not going to believe it, but this section was written *just for YOU*.
|
||||
|
||||
For services which are **are not** isolated per BrowserContext and which **can**
|
||||
be wired directly into Content:
|
||||
|
||||
- Include your service's manifest in the `content_packaged_services` manifest
|
||||
directly, similar to
|
||||
[these ones](https://cs.chromium.org/chromium/src/content/public/app/content_packaged_services_manifest.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=63).
|
||||
- If you want to run your service embedded in the browser process, follow the
|
||||
examples using `RegisterInProcessService`
|
||||
[here](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=589).
|
||||
- If you want to run your service out-of-process, update
|
||||
`out_of_process_services`
|
||||
[like so](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=642)
|
||||
and hook up your actual private `Service` implementation exactly like the
|
||||
many examples
|
||||
[here](https://cs.chromium.org/chromium/src/content/utility/utility_service_factory.cc?rcl=2bdcc80a55c72a26ffe9778681f98dc4b6a565c0&l=114).
|
||||
|
||||
For services which are **are** isolated per BrowserContext and which **can**
|
||||
be wired directly into Content:
|
||||
|
||||
- Include your service's manifest in the `content_browser` manifest directly,
|
||||
similar to
|
||||
[these ones](https://cs.chromium.org/chromium/src/content/public/app/content_browser_manifest.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=277).
|
||||
- If you want to run your service embedded in the browser process, follow the
|
||||
example
|
||||
[here](https://cs.chromium.org/chromium/src/content/browser/browser_context.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=250)
|
||||
- If you want to run your service out-of-process, you are doing something that
|
||||
hasn't been done yet and you will need to build a new thing.
|
||||
|
||||
For services which **are not** isolated per BrowserContext but which **can not**
|
||||
be wired directly into Content:
|
||||
|
||||
- Include your service's manifest in Chrome's `content_packaged_services`
|
||||
manifest overlay similar to
|
||||
[these ones](https://cs.chromium.org/chromium/src/chrome/app/chrome_packaged_service_manifests.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=135)
|
||||
- If you want to run your service embedded in the browser process, follow the
|
||||
examples in `ChromeContentBrowserClient::HandleServiceRequest`
|
||||
[here](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=3891)
|
||||
- If you want to run your service out-of-process, modify
|
||||
`ChromeContentBrowserClient::RegisterOutOfProcessServices` like the examples
|
||||
[here](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=3785)
|
||||
and hook up your `Service` implementation in
|
||||
`ChromeContentUtilityClient::HandleServiceRequest` like the ones
|
||||
[here](https://cs.chromium.org/chromium/src/chrome/utility/chrome_content_utility_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=237).
|
||||
|
||||
For services which **are** isolated per BrowserContext but which **can not** be
|
||||
wired directly into Content:
|
||||
|
||||
- Include your service's manifest in Chrome's `content_browser` manifest overlay
|
||||
similar to
|
||||
[these ones](https://cs.chromium.org/chromium/src/chrome/app/chrome_content_browser_overlay_manifest.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=247)
|
||||
- If you want to run your service embedded in the browser process, follow the
|
||||
examples in `ProfileImpl::HandleServiceRequest`
|
||||
[here](https://cs.chromium.org/chromium/src/chrome/browser/profiles/profile_impl.cc?rcl=350aea1a0242c2ea8610c9f755acee085c74ea7d&l=1263)
|
||||
- If you want to run your service out-of-process, you are doing something that
|
||||
hasn't been done yet and you will need to build a new thing.
|
||||
|
||||
*** aside
|
||||
The non-Content examples above are obviously specific to Chrome as the embedder,
|
||||
but Chrome's additions to supported services are all facilitated through the
|
||||
common `ContentBrowserClient` and `ContentUtilityClient` APIs that all embedders
|
||||
can implement. Mimicking what Chrome does should be sufficient for any embedder.
|
||||
***
|
||||
|
||||
## Incremental Servicification
|
||||
|
||||
For large Chromium features it is not feasible to convert an entire subsystem
|
||||
to a service all at once. As a result, it may be necessary for the subsystem
|
||||
to spend a considerable amount of time (weeks or months) split between the old
|
||||
implementation and your beautiful, sparkling new service implementation.
|
||||
|
||||
In creating your service, you likely have two goals:
|
||||
|
||||
- Making the service available to other services
|
||||
- Making the service available to its consumers
|
||||
- Making the service self-contained
|
||||
|
||||
Those two goals are not the same, and to some extent are at tension:
|
||||
@ -111,7 +206,7 @@ Those two goals are not the same, and to some extent are at tension:
|
||||
Whatever your goals, you will need to proceed incrementally if your project is
|
||||
at all non-trivial (as they basically all are given the nature of the effort).
|
||||
You should explicitly decide what your approach to incremental bringup and
|
||||
conversion will be. Here some approaches that have been taken for various
|
||||
conversion will be. Here are some approaches that have been taken for various
|
||||
services:
|
||||
|
||||
- Build out your service depending directly on existing code,
|
||||
@ -139,11 +234,11 @@ service available for new use cases sooner at the cost of leaving legacy code in
|
||||
place longer, while the last is most suitable when you want to be very exacting
|
||||
about doing the servicification cleanly as you go.
|
||||
|
||||
## Platform-Specific Issues
|
||||
## Platform-Specific Issues: Android
|
||||
|
||||
### Android
|
||||
As you servicify code running on Android, you might find that you need to port
|
||||
interfaces that are served in Java. Here is an [example CL](https://codereview.chromium.org/2643713002) that gives a basic
|
||||
interfaces that are served in Java. Here is an
|
||||
[example CL](https://codereview.chromium.org/2643713002) that gives a basic
|
||||
pattern to follow in doing this.
|
||||
|
||||
You also might need to register JNI in your service. That is simple to set
|
||||
@ -156,50 +251,60 @@ Finally, it is possible that your feature will have coupling to UI process state
|
||||
(e.g., the Activity) via Android system APIs. To handle this challenging
|
||||
issue, see the section on [Coupling to UI](#Coupling-to-UI).
|
||||
|
||||
### iOS
|
||||
## Platform-Specific Issues: iOS
|
||||
|
||||
*** aside
|
||||
**WARNING:** Some of this content is obsolete and needs to be updated. When in
|
||||
doubt, look approximately near the recommended bits of code and try to find
|
||||
relevant prior art.
|
||||
***
|
||||
|
||||
Services are supported on iOS, with the usage model in //ios/web being very
|
||||
close to the usage model in //content. More specifically:
|
||||
|
||||
* To embed a global service in the browser service, override
|
||||
[WebClient::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::Register&sq=package:chromium&l=136). For an
|
||||
example usage, see
|
||||
[WebClient::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::Register&sq=package:chromium&l=136). For an example usage, see
|
||||
[ShellWebClient](https://cs.chromium.org/chromium/src/ios/web/shell/shell_web_client.mm?q=ShellWebClient::RegisterS&sq=package:chromium&l=91)
|
||||
and the related integration test that [connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=89).
|
||||
and the related integration test that
|
||||
[connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=89).
|
||||
* To embed a per-BrowserState service, override
|
||||
[BrowserState::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/browser_state.h?q=BrowserState::RegisterServices&sq=package:chromium&l=89). For an
|
||||
example usage, see
|
||||
[ShellBrowserState](https://cs.chromium.org/chromium/src/ios/web/shell/shell_browser_state.mm?q=ShellBrowserState::RegisterServices&sq=package:chromium&l=48)
|
||||
and the related integration test that [connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=110).
|
||||
and the related integration test that
|
||||
[connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=110).
|
||||
* To register a per-frame Mojo interface, override
|
||||
[WebClient::BindInterfaceRequestFromMainFrame](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=148). For an
|
||||
example usage, see
|
||||
[WebClient::BindInterfaceRequestFromMainFrame](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=148).
|
||||
For an example usage, see
|
||||
[ShellWebClient](https://cs.chromium.org/chromium/src/ios/web/shell/shell_web_client.mm?type=cs&q=ShellWebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=115)
|
||||
and the related integration test that [connects to the interface](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=130). Note that this is the
|
||||
equivalent of [ContentBrowserClient::BindInterfaceRequestFromFrame()](https://cs.chromium.org/chromium/src/content/public/browser/content_browser_client.h?type=cs&q=ContentBrowserClient::BindInterfaceRequestFromFrame&sq=package:chromium&l=667), as on iOS all operation "in the content area" is implicitly
|
||||
operating in the context of the page's main frame.
|
||||
and the related integration test that
|
||||
[connects to the interface](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=130).
|
||||
Note that this is the equivalent of
|
||||
[ContentBrowserClient::BindInterfaceRequestFromFrame()](https://cs.chromium.org/chromium/src/content/public/browser/content_browser_client.h?type=cs&q=ContentBrowserClient::BindInterfaceRequestFromFrame&sq=package:chromium&l=667),
|
||||
as on iOS all operation "in the content area" is implicitly operating in the
|
||||
context of the page's main frame.
|
||||
|
||||
If you have a use case or need for services on iOS, contact
|
||||
blundell@chromium.org. For general information on the motivations and vision for supporting services on iOS, see the high-level [servicification design doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU/edit) (in particular, search for the mentions
|
||||
of iOS within the doc).
|
||||
blundell@chromium.org. For general information on the motivations and vision for
|
||||
supporting services on iOS, see the high-level
|
||||
[servicification design doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU/edit).
|
||||
In particular, search for the mentions of iOS within the doc.
|
||||
|
||||
## Client-Specific Issues
|
||||
|
||||
### Services and Blink
|
||||
Connecting to services directly from Blink is fully supported. [This
|
||||
CL](https://codereview.chromium.org/2698083007) gives a basic example of
|
||||
connecting to an arbitrary service by name from Blink (look at the change to
|
||||
SensorProviderProxy.cpp as a starting point).
|
||||
|
||||
Below, we go through strategies for some common challenges encountered when
|
||||
servicifying features that have Blink as a client.
|
||||
|
||||
#### Mocking Interface Impls in JS
|
||||
It is a common pattern in Blink's web tests to mock a remote Mojo interface
|
||||
in JS. [This CL](https://codereview.chromium.org/2643713002) illustrates the
|
||||
basic pattern for porting such mocking of an interface hosted by
|
||||
//content/browser to an interface hosted by an arbitrary service (see the
|
||||
changes to mock-battery-monitor.js).
|
||||
in JS so that native Blink code requests interfaces from the test JS rather
|
||||
than whatever would normally service them in the browser process.
|
||||
|
||||
The current way to set up that sort of thing looks like
|
||||
[this](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/battery-status/resources/mock-battery-monitor.js?rcl=be6e0001855f7f1cfc26205d0ff5a2b5b324fcbd&l=19).
|
||||
|
||||
*** aside
|
||||
**NOTE:** The above approach to mocking in JS no longer applies when using
|
||||
the new recommended `DocumentInterfaceBroker` approach to exposing interfaces
|
||||
to documents. New JS mocking support is in development for this.
|
||||
***
|
||||
|
||||
#### Feature Impls That Depend on Blink Headers
|
||||
In the course of servicifying a feature that has Blink as a client, you might
|
||||
@ -215,7 +320,9 @@ To meet this challenge, you have two options:
|
||||
|
||||
1. Move the code in question from C++ to mojom (e.g., if it is simple structs).
|
||||
2. Move the code into the service's C++ client library, being very explicit
|
||||
about its usage by Blink. See [this CL](https://codereview.chromium.org/2415083002) for a basic pattern to follow.
|
||||
about its usage by Blink. See
|
||||
[this CL](https://codereview.chromium.org/2415083002) for a basic pattern to
|
||||
follow.
|
||||
|
||||
#### Frame-Scoped Connections
|
||||
You must think carefully about the scoping of the connection being made
|
||||
@ -226,8 +333,8 @@ Blink has no frame-scoped connection to arbitrary services (by design, as
|
||||
arbitrary services have no knowledge of frames or even a notion of what a frame
|
||||
is).
|
||||
|
||||
After a [long
|
||||
discussion](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/CSnDUjthAuw),
|
||||
After a
|
||||
[long discussion](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/CSnDUjthAuw),
|
||||
the policy that we have adopted for this challenge is the following:
|
||||
|
||||
CURRENT
|
||||
@ -246,9 +353,6 @@ AFTER SERVICIFYING THE FEATURE IN QUESTION
|
||||
|
||||
Notably, from the renderer's POV essentially nothing changes here.
|
||||
|
||||
In the longer term, this will still be the basic model, only with "the browser"
|
||||
replaced by "the Navigation Service" or "the web permissions broker".
|
||||
|
||||
## Strategies for Challenges to Decoupling from //content
|
||||
|
||||
### Coupling to UI
|
||||
@ -268,16 +372,17 @@ what requires the coupling and to avoid the coupling creeping in scope.
|
||||
The basic strategy to support this coupling while still servicifying the feature
|
||||
in question is to inject a mechanism of mapping from an opaque "context ID" to
|
||||
the required context. The embedder (e.g., //content) maintains this map, and the
|
||||
service makes use of it. The embedder also serves as an intermediary: It
|
||||
service makes use of it. The embedder also serves as an intermediary: it
|
||||
provides a connection that is appropriately context-scoped to clients. When
|
||||
clients request the feature in question, the embedder forwards the request on
|
||||
along with the appropriate context ID. The service impl can then map that
|
||||
context ID back to the needed context on-demand using the mapping functionality
|
||||
injected into the service impl.
|
||||
|
||||
To make this more concrete, see [this CL](https://codereview.chromium.org/2734943003).
|
||||
To make this more concrete, see
|
||||
[this CL](https://codereview.chromium.org/2734943003).
|
||||
|
||||
### Shutdown of singletons
|
||||
### Shutdown of Singletons
|
||||
|
||||
You might find that your feature includes singletons that are shut down as part
|
||||
of //content's shutdown process. As part of decoupling the feature
|
||||
@ -293,29 +398,15 @@ either ported into your service or eliminated:
|
||||
to when the previous code was executing, and ensure that any differences
|
||||
introduced do not impact correctness.
|
||||
|
||||
See [this thread](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/Y9FKZf9n1ls) for more discussion of this issue.
|
||||
See
|
||||
[this thread](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/Y9FKZf9n1ls)
|
||||
for more discussion of this issue.
|
||||
|
||||
### Tests that muck with service internals
|
||||
It is often the case that browsertests reach directly into what will become part
|
||||
of the internal service implementation to either inject mock/fake state or to
|
||||
monitor private state.
|
||||
## Additional Support
|
||||
|
||||
This poses a challenge: As part of servicification, *no* code outside the
|
||||
service impl should depend on the service impl. Thus, these dependencies need to
|
||||
be removed. The question is how to do so while preserving testing coverage.
|
||||
|
||||
To answer this question, there are several different strategies. These
|
||||
strategies are not mutually-exclusive; they can and should be combined to
|
||||
preserve the full breadth of coverage.
|
||||
|
||||
- Blink client-side behavior can be tested via [web tests](https://codereview.chromium.org/2731953003)
|
||||
- To test service impl behavior, create [service tests](https://codereview.chromium.org/2774783003).
|
||||
- To preserve tests of end-to-end behavior (e.g., that when Blink makes a
|
||||
request via a Web API in JS, the relevant feature impl receives a connection
|
||||
request), we are planning on introducing the ability to register mock
|
||||
implementations with the Service Manager.
|
||||
|
||||
To emphasize one very important point: it is in general necessary to leave
|
||||
*some* test of end-to-end functionality, as otherwise it is too easy for bustage
|
||||
to slip in via e.g. changes to how services are registered. See [this thread](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/lJCKAElWz-E)
|
||||
for further discussion of this point.
|
||||
If this document was not helpful in some way, please post a message to your
|
||||
friendly local
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
or
|
||||
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
|
||||
mailing list.
|
||||
|
860
ipc/README.md
860
ipc/README.md
@ -1,860 +0,0 @@
|
||||
# Converting Legacy Chrome IPC To Mojo
|
||||
|
||||
Looking for [Mojo Documentation](/mojo)?
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
|
||||
The `//ipc` directory contains interfaces and implementation for Chrome's
|
||||
legacy IPC system, including `IPC::Channel` and various macros for defining
|
||||
messages and type serialization. For details on using this system please see the
|
||||
original
|
||||
[documentation](https://www.chromium.org/developers/design-documents/inter-process-communication).
|
||||
|
||||
Legacy IPC is **deprecated**, and Chrome developers are strongly discouraged
|
||||
from introducing new messages using this system. [Mojo](/mojo) is the correct
|
||||
IPC system to use moving forward. This document introduces developers to the
|
||||
various tools available to help with conversion of legacy IPC messages to Mojo.
|
||||
It assumes familiarity with [Mojom](/mojo/public/tools/bindings) syntax and
|
||||
general use of Mojo [C++ bindings](/mojo/public/cpp/bindings).
|
||||
|
||||
The existing messages that still need to be converted are tracked in two
|
||||
spreadsheets:
|
||||
|
||||
[Chrome IPC to Mojo migration](
|
||||
https://docs.google.com/spreadsheets/d/1pGWX_wxGdjAVtQOmlDDfhuIc3Pbwg9FtvFXAXYu7b7c/edit#gid=0) -
|
||||
for non-web platform messages
|
||||
|
||||
[Mojoifying Platform Features](
|
||||
https://docs.google.com/spreadsheets/d/1VIINt17Dg2cJjPpoJ_HY3HI0uLpidql-1u8pBJtpbGk/edit#gid=1603373208) -
|
||||
for web platform messages
|
||||
|
||||
Ownership of items is claimed by specifying a username in the 'Owner' field to
|
||||
prevent duplication of effort. If you are not very familiar with the messages
|
||||
you plan to convert, it might be worth asking the owner first.
|
||||
|
||||
In traditional Chrome IPC, we have One Big Pipe (the `IPC::Channel`) between
|
||||
each connected process. Sending an IPC from one process to another means knowing
|
||||
how to get a handle to the Channel interface (*e.g.,*
|
||||
[RenderProcessHost::GetChannel](
|
||||
https://cs.chromium.org/chromium/src/content/public/browser/render_process_host.h?rcl=722be98f7e4d7551710fb9cf30674750cdd0d857&l=196)
|
||||
when sending from the browser to a renderer process), and then having either an
|
||||
`IPC::MessageFilter` or some other appropriate `IPC::Listener` implementation
|
||||
installed in the right place on the other side of the channel.
|
||||
|
||||
Because of this arrangement, any message sent on a channel is sent in FIFO order
|
||||
with respect to all other messages on the channel. While this may be easier to
|
||||
reason about in general, it carries with it the unfortunate consequence that
|
||||
many unrelated messages in the system have an implicit, often unintended
|
||||
ordering dependency.
|
||||
|
||||
It's primarily for this reason that conversion to Mojo IPC can be more
|
||||
challenging than would otherwise be necessary, and that is why we have a number
|
||||
of different tools available to facilitate such conversions.
|
||||
|
||||
## Deciding What to Do
|
||||
|
||||
There are few questions you should ask yourself before embarking upon any IPC
|
||||
message conversion journey. Should this be part of a service? Does message
|
||||
ordering matter with respect to other parts of the system? What is the meaning
|
||||
of life?
|
||||
|
||||
### Moving Messages to Services
|
||||
|
||||
We have a small but growing number of services defined in
|
||||
[`//services`](https://cs.chromium.org/chromium/src/services/), each of which has
|
||||
some set of public interfaces defined in their `public/interfaces` subdirectory.
|
||||
In the limit, this is the preferred destination for any message conversions
|
||||
pertaining to foundational system services (more info at
|
||||
[https://www.chromium.org/servicification](https://www.chromium.org/servicification).)
|
||||
For other code it may make sense to introduce services elsewhere (*e.g.*, in
|
||||
`//chrome/services` or `//components/services`), or to simply
|
||||
avoid using services altogether for now and instead define some one-off Mojom
|
||||
interface alongside the old messages file.
|
||||
|
||||
If you need help deciding where a message should live, or if you feel it would
|
||||
be appropriate to introduce a new service to implement some feature or large set
|
||||
of messages, please post to
|
||||
[`services-dev@chromium.org`](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
|
||||
with questions, concerns, and/or a brief proposal or design doc describing
|
||||
the augmentation of an existing service or introduction of a new service.
|
||||
|
||||
See the [Using Services](#Using-Services) section below for details.
|
||||
|
||||
When converting messages that still require tight coupling to content or Chrome
|
||||
code or which require unchanged ordering with respect to one or more remaining
|
||||
legacy IPC messages, it is often not immediately feasible to move a message
|
||||
definition or handler implementation into a service.
|
||||
|
||||
### Moving Messages to Not-Services
|
||||
|
||||
While this isn't strictly possible because everything is a service now, we model
|
||||
all existing content processes as service instances and provide helpers to make
|
||||
interface exposure and consumption between them relatively easy.
|
||||
|
||||
See [Using Content's Connectors](#Using-Content_s-Connectors) for details on
|
||||
the recommended way to accomplish this.
|
||||
|
||||
See
|
||||
[Using Content's Interface Registries](#Using-Content_s-Interface-Registries)
|
||||
for details on the **deprecated** way to accomplish this.
|
||||
|
||||
Note that when converting messages to standalone Mojo interfaces, every
|
||||
interface connection operates 100% independently of each other. This means that
|
||||
ordering is only guaranteed over a single interface (ignoring
|
||||
[associated interfaces](/mojo/public/tools/bindings#Associated-Interfaces).)
|
||||
Consider this example:
|
||||
|
||||
``` cpp
|
||||
mojom::FrobinatorPtr frob1;
|
||||
RenderThread::Get()->GetConnector()->BindInterface(
|
||||
foo_service::mojom::kServiceName, &frob1);
|
||||
|
||||
mojom::FrobinatorPtr frob2;
|
||||
RenderThread::Get()->GetConnector()->BindInterface(
|
||||
foo_service::mojom::kServiceName, &frob2);
|
||||
|
||||
// These are ordered on |frob1|.
|
||||
frob1->Frobinate(1);
|
||||
frob1->Frobinate(2);
|
||||
|
||||
// These are ordered on |frob2|.
|
||||
frob2->Frobinate(1);
|
||||
frob2->Frobinate(2);
|
||||
|
||||
// It is entirely possible, however, that the renderer receives:
|
||||
//
|
||||
// [frob1]Frobinate(1)
|
||||
// [frob2]Frobinate(1)
|
||||
// [frob1]Frobinate(2)
|
||||
// [frob2]Frobinate(2)
|
||||
//
|
||||
// Because |frob1| and |frob2| guarantee no mutual ordering.
|
||||
```
|
||||
|
||||
Also note that neither interface is ordered with respect to legacy
|
||||
`IPC::Channel` messages. This can present significant problems when converting a
|
||||
single message or group of messages which must retain ordering with respect to
|
||||
others still on the Channel.
|
||||
|
||||
### When Ordering Matters
|
||||
|
||||
If ordering really matters with respect to other legacy messages in the system,
|
||||
as is often the case for *e.g.* frame and navigation-related messages, you
|
||||
almost certainly want to take advantage of
|
||||
[Channel-associated interfaces](#Using-Channel_associated-Interfaces) to
|
||||
eliminate any risk of introducing subtle behavioral changes.
|
||||
|
||||
Even if ordering only matters among a small set of messages which you intend to
|
||||
move entirely to Mojom, you may wish to move them one-by-one in separate CLs.
|
||||
In that case, it may make sense to use a Channel-associated interface during the
|
||||
transitional period. Once all relevant messages are fully relocated into a
|
||||
single Mojom interface, it's trivial to lift the interface away from Channel
|
||||
association and into a proper independent service connection.
|
||||
|
||||
## Using Services
|
||||
|
||||
Suppose you have some IPC messages for safely decoding a PNG image:
|
||||
|
||||
``` cpp
|
||||
IPC_MESSAGE_CONTROL2(UtilityMsg_DecodePNG,
|
||||
int32_t request_id,
|
||||
std::string /* png_data */);
|
||||
IPC_MESSAGE_CONTROL2(UtilityHostMsg_PNGDecoded,
|
||||
int32_t request_id,
|
||||
int32_t width, int32_t height,
|
||||
std::string /* rgba_data */);
|
||||
```
|
||||
|
||||
This seems like a perfect fit for an addition to the sandboxed `data_decoder`
|
||||
service. Your first order of business is to translate this into a suitable
|
||||
public interface definition within that service:
|
||||
|
||||
``` cpp
|
||||
// src/services/data_decoder/public/mojom/png_decoder.mojom
|
||||
module data_decoder.mojom;
|
||||
|
||||
interface PngDecoder {
|
||||
Decode(array<uint8> png_data)
|
||||
=> (int32 width, int32 height, array<uint32> rbga_data);
|
||||
};
|
||||
```
|
||||
|
||||
You will need to update the relevant BUILD.gn target with the newly added mojom
|
||||
file. See [this section](
|
||||
https://chromium.googlesource.com/chromium/src/+/master/mojo/public/cpp/bindings/README.md#getting-started)
|
||||
for details.
|
||||
|
||||
You'll also want to define the implementation within
|
||||
`//services/data_decoder`, plugging in some appropriate binder so the service
|
||||
knows how to bind incoming interface requests to your implementation:
|
||||
|
||||
``` cpp
|
||||
// src/services/data_decoder/png_decoder_impl.h
|
||||
class PngDecoderImpl : public mojom::PngDecoder {
|
||||
public:
|
||||
static void BindRequest(mojom::PngDecoderRequest request) { /* ... */ }
|
||||
|
||||
// mojom::PngDecoder:
|
||||
void Decode(const std::vector<uint8_t>& png_data,
|
||||
const DecodeCallback& callback) override { /* ... */ }
|
||||
// ...
|
||||
};
|
||||
|
||||
// src/services/data_decoder/data_decoder_service.cc
|
||||
// Not quite legitimate pseudocode...
|
||||
DataDecoderService::DataDecoderService() {
|
||||
// ...
|
||||
registry_.AddInterface(base::Bind(&PngDecoderImpl::BindRequest));
|
||||
}
|
||||
```
|
||||
|
||||
and finally you need to update the usage of the old IPC by probably deleting
|
||||
lots of ugly code which sets up a `UtilityProcessHost` and replacing it with
|
||||
something like:
|
||||
|
||||
``` cpp
|
||||
#include "services/data_decoder/public/mojom/png_decoder.mojom.h"
|
||||
...
|
||||
void OnDecodedPng(const std::vector<uint8_t>& rgba_data) { /* ... */ }
|
||||
|
||||
data_decoder::mojom::PngDecoderPtr png_decoder;
|
||||
connector->BindInterface(data_decoder::mojom::kServiceName,
|
||||
mojo::MakeRequest(&png_decoder));
|
||||
png_decoder->Decode(untrusted_png_data, base::Bind(&OnDecodedPng));
|
||||
```
|
||||
|
||||
Where to get a [`Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h)
|
||||
is an interesting question, and the answer ultimately depends on where your code
|
||||
is written. All service instances get a primordial `Connector` which can be
|
||||
cloned arbitrarily many times and passed around to different threads.
|
||||
|
||||
If you're writing service code the answer is trivial since each `Service`
|
||||
instance has direct access to a `Connector`. If you're writing code at or above
|
||||
the content layer, the answer is slightly more interesting and is explained in
|
||||
the [Using Content's Connectors](#Using-Content_s-Connectors) section below.
|
||||
|
||||
## Using Content's Connectors
|
||||
|
||||
As explained earlier in this document, all content processes are modeled as
|
||||
service instances today. This means that all content processes have at least
|
||||
one [`Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h)
|
||||
instance which can be used to bind interfaces exposed by other services.
|
||||
|
||||
We define [`content::ServiceManagerConnection`](https://cs.chromium.org/chromium/src/content/public/common/service_manager_connection.h?rcl=dd92156efac57169b45aeb0094111b8d94302b12&l=38)
|
||||
as a helper which fully encapsulates the service instance state within a given
|
||||
Content process. The main thread of the browser process can access the global
|
||||
instance by calling
|
||||
[`content::ServiceManager::GetForProcess()`](https://cs.chromium.org/chromium/src/content/public/common/service_manager_connection.h?rcl=dd92156efac57169b45aeb0094111b8d94302b12&l=56),
|
||||
and this object has a `GetConnector()` method which exposes the `Connector` for
|
||||
that process.
|
||||
|
||||
The main thread of any Content child process can use
|
||||
`content::ChildThread::GetServiceManagerConnection` or
|
||||
`content::ChildThread::GetConnector` directly.
|
||||
|
||||
For example, any interfaces registered in
|
||||
[`RenderProcessHostImpl::RegisterMojoInterfaces()`](https://cs.chromium.org/chromium/src/content/browser/renderer_host/render_process_host_impl.cc?rcl=dd92156efac57169b45aeb0094111b8d94302b12&l=1203)
|
||||
can be acquired by a renderer as follows:
|
||||
|
||||
``` cpp
|
||||
mojom::LoggerPtr logger;
|
||||
content::RenderThread::Get()->GetConnector()->BindInterface(
|
||||
content::mojom::kBrowserServiceName, &logger);
|
||||
logger->Log("Message to log here");
|
||||
```
|
||||
|
||||
Usually `logger` will be saved in a field at construction time, so the
|
||||
connection is only created once. There may be situations where you want to
|
||||
create one connection per request, e.g. a new instance of the Mojo
|
||||
implementation is created with some information about the request, and any
|
||||
responses for this request go straight to that instance.
|
||||
|
||||
### On Other Threads
|
||||
|
||||
`Connector` instances can be created and asynchronously associated with each
|
||||
other to maximize flexibility in when and how outgoing interface requests are
|
||||
initiated.
|
||||
|
||||
For example if a background (*e.g.,* worker) thread in a renderer process wants
|
||||
to make an outgoing service request, it can construct its own `Connector` --
|
||||
which may be used immediately and retained on that thread -- and asynchronously
|
||||
associate it with the main-thread `Connector` like so:
|
||||
|
||||
``` cpp
|
||||
class Logger {
|
||||
public:
|
||||
explicit Logger(scoped_refptr<base::TaskRunner> main_thread_task_runner) {
|
||||
service_manager::mojom::ConnectorRequest request;
|
||||
|
||||
// Of course we could also retain |connector| if we intend to use it again.
|
||||
auto connector = service_manager::Connector::Create(&request);
|
||||
// Replace service_name with the name of the service to bind on, e.g.
|
||||
// content::mojom::kBrowserServiceName.
|
||||
connector->BindInterface("service_name", &logger_);
|
||||
logger_->Log("Test Message.");
|
||||
|
||||
// Doesn't matter when this happens, as long as it happens eventually.
|
||||
main_thread_task_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(&Logger::BindConnectorOnMainThread,
|
||||
std::move(request)));
|
||||
}
|
||||
|
||||
private:
|
||||
static void BindConnectorOnMainThread(
|
||||
service_manager::mojom::ConnectorRequest request) {
|
||||
DCHECK(RenderThreadImpl::Get());
|
||||
RenderThreadImpl::Get()->GetConnector()->BindConnectorRequest(
|
||||
std::move(request));
|
||||
}
|
||||
|
||||
mojom::LoggerPtr logger_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Logger);
|
||||
};
|
||||
```
|
||||
|
||||
## Using Content's Interface Registries
|
||||
|
||||
**NOTE:** This section is here mainly for posterity and documentation of
|
||||
existing usage. Please use `Connector` instead of using an `InterfaceProvider`
|
||||
directly.
|
||||
|
||||
For convenience the Service Manager's
|
||||
[client library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/)
|
||||
exposes two useful types: `BinderRegistry` and `InterfaceProvider`. These
|
||||
objects generally exist as an intertwined pair with a `BinderRegistry` in
|
||||
one process and a corresponding `InterfaceProvider` in another process.
|
||||
|
||||
The `BinderRegistry` is essentially just a mapping from interface name
|
||||
to binder function:
|
||||
|
||||
``` cpp
|
||||
void BindFrobinator(mojom::FrobinatorRequest request) {
|
||||
mojo::MakeStrongBinding(std::make_unique<FrobinatorImpl>, std::move(request));
|
||||
}
|
||||
|
||||
// |registry| will hereby handle all incoming requests for "mojom::Frobinator"
|
||||
// using the above function, which binds the request pipe handle to a new
|
||||
// instance of |FrobinatorImpl|.
|
||||
registry->AddInterface(base::Bind(&BindFrobinator));
|
||||
```
|
||||
|
||||
while an `InterfaceProvider` exposes a means of requesting interfaces from a
|
||||
remote `BinderRegistry`:
|
||||
|
||||
``` cpp
|
||||
mojom::FrobinatorPtr frob;
|
||||
|
||||
// MakeRequest creates a new pipe, and GetInterface sends one end of it to
|
||||
// the remote InterfaceRegistry along with the "mojom::Frobinator" name. The
|
||||
// other end of the pipe is bound to |frob| which may immediately begin sending
|
||||
// messages.
|
||||
provider->GetInterface(mojo::MakeRequest(&frob));
|
||||
frob->DoTheFrobinator();
|
||||
```
|
||||
|
||||
`RenderFrameHostImpl`'s binder registry corresponds to the `InterfaceProvider`
|
||||
returned by `RenderFrameImpl::GetRemoteInterfaces()`, and `RenderFrameImpl`'s
|
||||
binder registry corresponds to the `InterfaceProvider` returned by
|
||||
`RenderFrameHostImpl::GetRemoteInterfaces()`.
|
||||
|
||||
**NOTE:** this mechanism is being replaced with explicit interface factory
|
||||
approach described in [this document](
|
||||
https://docs.google.com/document/d/1e0qqv3ZGQYskE4XhtuGrYJeThjYXu8xozl7zIlwjSD0).
|
||||
|
||||
As noted above, use of these registries is generally discouraged.
|
||||
|
||||
### Deciding Which Interface Registry to Use
|
||||
|
||||
Once you have an implementation of a Mojo interface, the next thing to decide is
|
||||
which registry and service to register it on.
|
||||
|
||||
For browser/renderer communication, you can register your Mojo interface
|
||||
implementation in either the Browser or Renderer process (whichever side the
|
||||
interface was implemented on). Usually, this involves calling `AddInterface()`
|
||||
on the correct registry, passing a method that takes the Mojo Request object
|
||||
(e.g. `sample::mojom::LoggerRequest`) and binding it (e.g.
|
||||
`mojo::MakeStrongBinding()`, `bindings_.AddBinding()`, etc). Then the class that
|
||||
needs this API can call `BindInterface()` on the connector for that process,
|
||||
e.g.
|
||||
`ChildThreadImpl::current()->GetConnector()->BindInterface(mojom::kBrowserServiceName, std::move(&mojo_interface_))`.
|
||||
|
||||
**NOTE:** `content::ServiceManagerConnection::GetForProcess()` must be called in
|
||||
the browser process on the main thread, and its connector can only be used on
|
||||
the main thread; but you can clone connectors and move the clones around to
|
||||
other threads. A `Connector` is only bound to the thread which first calls into
|
||||
it.
|
||||
|
||||
Depending on what resources you need access to, the main classes are:
|
||||
|
||||
| Renderer Class | Corresponding Browser Class | Explanation |
|
||||
|-----------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| `RenderFrame` | `RenderFrameHost` | A single frame. Use this for frame-to-frame messages. |
|
||||
| `RenderView` | `RenderViewHost` | A view (conceptually a 'tab'). You cannot send Mojo messages to a `RenderView` directly, since frames in a tab can be in multiple processes (and the classes are deprecated). Migrate these to `RenderFrame` instead, or see section [Migrating IPC calls to `RenderView` or `RenderViewHost`](#other-routed-messages-to-the-browser). |
|
||||
| `RenderProcess` | `RenderProcessHost` | A process, containing multiple frames (probably from the same origin, but not always). |
|
||||
|
||||
**NOTE:** Previously, classes that ended with `Host` were implemented on the
|
||||
browser side; the equivalent classes on the renderer side had the same name
|
||||
without the `Host` suffix. We have since deviated from this convention since
|
||||
Mojo interfaces are not intended to prescribe where their endpoints live, so
|
||||
future classes should omit such suffixes and just describe the interface they
|
||||
are providing.
|
||||
|
||||
Of course, any combination of the above is possible, e.g. `RenderProcessHost`
|
||||
can register a Mojo interface that can be called by a `RenderFrame` (this would
|
||||
be a way of the browser communicating with multiple frames at once).
|
||||
|
||||
Once you know which class you want the implementation to be registered in, find
|
||||
the corresponding `Impl` class (e.g. `RenderProcessImpl`). There should be a
|
||||
`RegisterMojoInterfaces()` method where you can add calls to `AddInterface`,
|
||||
e.g. For a strong binding:
|
||||
|
||||
```cpp
|
||||
registry->AddInterface(base::Bind(&Logger::Create, GetID()));
|
||||
```
|
||||
|
||||
Then in `Logger` we add a static `Create()` method that takes the
|
||||
`LoggerRequest` object:
|
||||
|
||||
```cpp
|
||||
// static
|
||||
void Logger::Create(int render_process_id,
|
||||
mojom::LoggerRequest request) {
|
||||
mojo::MakeStrongBinding(std::make_unique<Logger>(render_process_id),
|
||||
std::move(request));
|
||||
}
|
||||
```
|
||||
|
||||
For a `BindingSet`, we can store a `std::unique_ptr<Logger>` on the
|
||||
`RenderProcessHost` instead, e.g.:
|
||||
|
||||
```cpp
|
||||
// render_process_host_impl.h:
|
||||
std::unique_ptr<Logger> logger_;
|
||||
|
||||
// render_process_host_impl.cc:
|
||||
logger_ = std::make_unique<Logger>(GetID());
|
||||
registry->AddInterface(base::Bind(&Logger::BindRequest,
|
||||
base::Unretained(logger_.get())));
|
||||
```
|
||||
|
||||
Then in `Logger` we define the `BindRequest` method:
|
||||
|
||||
```h
|
||||
class Logger : public sample::mojom::Logger {
|
||||
public:
|
||||
explicit Logger(int render_process_id);
|
||||
~Logger() override;
|
||||
|
||||
void BindRequest(mojom::LoggerRequest request);
|
||||
|
||||
// sample::mojom::Logger:
|
||||
void Log(const std::string& message) override;
|
||||
void GetTail(GetTailCallback callback) override;
|
||||
|
||||
private:
|
||||
mojo::BindingSet<sample::mojom::Logger> bindings_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Logger);
|
||||
};
|
||||
```
|
||||
|
||||
```cpp
|
||||
void Logger::BindRequest(mojom::LoggerRequest request) {
|
||||
bindings_.AddBinding(this, std::move(request));
|
||||
}
|
||||
```
|
||||
|
||||
As you can see above, `MakeStrongBinding()` is used to lazily create an
|
||||
interface implementation when a connection is created. The lifetime of this
|
||||
implementation spans the lifetime of the connection. This is done to not
|
||||
unnecessarily create instances that might not be used.
|
||||
|
||||
Because strong binding lifetime is not tracked or easily observable, any
|
||||
implementation bound with a strong binding must be very careful to avoid
|
||||
dependencies on things which it might outlive. As a general rule, if your
|
||||
implementation depends on any state outside of itself, please consider avoiding
|
||||
strong bindings in favor of explicit object ownership.
|
||||
|
||||
Alternatively, the relevant registry owner class can own the implementation
|
||||
instance and dispatch the connection requests that the implementation will
|
||||
bind.
|
||||
|
||||
#### Setting up Capabilities
|
||||
|
||||
Once you've registered your interface, you need to add capabilities (resolved at
|
||||
runtime) to the corresponding capabilities manifest json file.
|
||||
|
||||
The service manifest files (which contain the capability spec) are located in
|
||||
[/content/public/app/mojo/](/content/public/app/mojo/). As a general rule, the
|
||||
file you want to edit is the service which *provides* the interface (the side
|
||||
which instantiates the implementation), and the part of the file you want to add
|
||||
the name of the interface to is the service which *calls* the interface (i.e.
|
||||
the side containing `LoggerPtr`).
|
||||
|
||||
You can usually just run your Mojo code and look at the error messages. The
|
||||
errors look like:
|
||||
|
||||
```sh
|
||||
[ERROR:service_manager.cc(158)] Connection InterfaceProviderSpec prevented
|
||||
service: content_renderer from binding interface: content.mojom.Logger
|
||||
exposed by: content_browser
|
||||
```
|
||||
|
||||
This means something in the renderer process (called "content_renderer") was
|
||||
trying to bind to `content.mojom.Logger` in the browser process (called
|
||||
"content_browser"). To add a capability for this, we need to find the json file
|
||||
with the capabilities for "content_browser", and add our new interface with name
|
||||
`content.mojom.Logger` to the "renderer" section.
|
||||
|
||||
In this example, the capabilities for "content_browser" are implemented in
|
||||
[content_browser_manifest.json](/content/public/app/mojo/content_browser_manifest.json).
|
||||
It should look like:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "content_browser",
|
||||
"display_name": "Content (browser process)",
|
||||
"interface_provider_specs": {
|
||||
"service_manager:connector": {
|
||||
"provides": {
|
||||
// ...
|
||||
"renderer": [
|
||||
//...
|
||||
```
|
||||
|
||||
To add permission for `content.mojom.Logger`, add the string
|
||||
`"content.mojom.Logger"` to the "renderer" list.
|
||||
|
||||
Similarly, if the error was:
|
||||
|
||||
```sh
|
||||
[ERROR:service_manager.cc(158)] Connection InterfaceProviderSpec prevented
|
||||
service: content_browser from binding interface: content.mojom.Logger exposed
|
||||
by: content_renderer
|
||||
```
|
||||
|
||||
We would want the
|
||||
`interface_provider_specs.service_manager:connector.provides.browser` section in
|
||||
[content_renderer_manifest.json](/content/public/app/mojo/content_renderer_manifest.json)
|
||||
(which defines the capabilities for `content_renderer`).
|
||||
|
||||
TODO: Add more details on permission manifests here
|
||||
|
||||
## Using Channel-associated Interfaces
|
||||
|
||||
**NOTE**: Channel-associated interfaces are an interim solution to make the
|
||||
transition to Mojo IPC easier in Chrome. You should not design new features
|
||||
which rely on this system. The ballpark date of deletion for `IPC::Channel` is
|
||||
projected to be somewhere around mid-2019, and obviously Channel-associated
|
||||
interfaces can't live beyond that point.
|
||||
|
||||
Mojo has support for the concept of
|
||||
[associated interfaces](/mojo/public/tools/bindings#Associated-Interfaces).
|
||||
One interface is "associated" with another when it's a logically separate
|
||||
interface but it shares an underlying message pipe, causing both interfaces to
|
||||
maintain mutual FIFO message ordering. For example:
|
||||
|
||||
``` cpp
|
||||
// db.mojom
|
||||
module db.mojom;
|
||||
|
||||
interface Table {
|
||||
void AddRow(string data);
|
||||
};
|
||||
|
||||
interface Database {
|
||||
QuerySize() => (uint64 size);
|
||||
AddTable(associated Table& table)
|
||||
};
|
||||
|
||||
// db_client.cc
|
||||
db::mojom::DatabasePtr db = /* ... get it from somewhere... */
|
||||
db::mojom::TableAssociatedPtr new_table;
|
||||
db->AddTable(mojo::MakeRequest(&new_table));
|
||||
new_table->AddRow("my hovercraft is full of eels");
|
||||
db->QuerySize(base::Bind([](uint64_t size) { /* ... */ }));
|
||||
```
|
||||
|
||||
In the above code, the `AddTable` message will always arrive before the `AddRow`
|
||||
message, which itself will always arrive before the `QuerySize` message. If the
|
||||
`Table` interface were not associated with the `Database` pipe, it would be
|
||||
possible for the `QuerySize` message to be received before `AddRow`,
|
||||
potentially leading to unintended behavior.
|
||||
|
||||
The legacy `IPC::Channel` used everywhere today is in fact just another Mojo
|
||||
interface, and developers have the ability to associate other arbitrary Mojo
|
||||
interfaces with any given Channel. This means that you can define a set of Mojo
|
||||
messages to convert old IPC messages, and implement them in a way which
|
||||
perfectly preserves current message ordering.
|
||||
|
||||
There are many different facilities in place for taking advantage of
|
||||
Channel-associated interfaces, and the right one for your use case depends on
|
||||
how the legacy IPC message is used today. The subsections below cover various
|
||||
interesting scenarios.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
The most primitive way to use Channel-associated interfaces is by working
|
||||
directly with `IPC::Channel` (IO thread) or more commonly `IPC::ChannelProxy`
|
||||
(main thread). There are a handful of interesting interface methods here.
|
||||
|
||||
**On the IO thread** (*e.g.,* typically when working with process hosts that
|
||||
aren't for render processes), the interesting methods are as follows:
|
||||
|
||||
[`IPC::Channel::GetAssociatedInterfaceSupport`](https://cs.chromium.org/chromium/src/ipc/ipc_channel.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=235)
|
||||
returns an object for working with interfaces associated with the `Channel`.
|
||||
This is never null.
|
||||
|
||||
[`IPC::Channel::AssociatedInterfaceSupport::AddAssociatedInterface<T>`](
|
||||
https://cs.chromium.org/chromium/src/ipc/ipc_channel.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=115)
|
||||
allows you to add a binding function to handle all incoming requests for a
|
||||
specific type of associated interface. Callbacks added here are called on the IO
|
||||
thread any time a corresponding interface request arrives on the `Channel.` If
|
||||
no callback is registered for an incoming interface request, the request falls
|
||||
through to the Channel's `Listener` via
|
||||
[`IPC::Listener::OnAssociatedInterfaceRequest`](https://cs.chromium.org/chromium/src/ipc/ipc_listener.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=40).
|
||||
|
||||
[`IPC::Channel::AssociatedInterfaceSupport::GetRemoteAssociatedInterface<T>`](
|
||||
https://cs.chromium.org/chromium/src/ipc/ipc_channel.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=124)
|
||||
requests a Channel-associated interface from the remote endpoint of the channel.
|
||||
|
||||
**On the main thread**, typically when working with `RenderProcessHost`, basic
|
||||
usage involves calls to
|
||||
[`IPC::ChannelProxy::GetRemoteAssociatedInterface<T>`](
|
||||
https://cs.chromium.org/chromium/src/ipc/ipc_channel_proxy.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=196)
|
||||
when making outgoing interface requests, or some implementation of
|
||||
[`IPC::Listener::OnAssociatedInterfaceRequest`](https://cs.chromium.org/chromium/src/ipc/ipc_listener.h?rcl=a896ff44a395a50ab18f5120f20b7eb5a9550247&l=40)
|
||||
when handling incoming ones.
|
||||
|
||||
TODO - Add docs for using AssociatedInterfaceRegistry where possible.
|
||||
|
||||
### BrowserMessageFilter
|
||||
|
||||
[BrowserMessageFilter](https://cs.chromium.org/chromium/src/content/public/browser/browser_message_filter.h?rcl=805f2ca5aa0902f56885ea3c8c0a42cb80d84522&l=37)
|
||||
is a popular helper for listening to incoming legacy IPC messages on the browser
|
||||
process IO thread and (typically) handling them there.
|
||||
|
||||
A common and totally reasonable tactic for converting a group of messages on an
|
||||
existing `BrowserMessageFilter` is to define a similiarly named Mojom interface
|
||||
in an inner `mojom` namespace (*e.g.,* a `content::FooMessageFilter` would have
|
||||
a corresponding `content::mojom::FooMessageFilter` interface), and have the
|
||||
`BrowserMessageFilter` implementation also implement
|
||||
`BrowserAssociatedInterface<T>` (optionally overriding methods such as
|
||||
`BrowserMessageFilter::OnDestruct()` if it needs to be deleted on a certain
|
||||
thread).
|
||||
|
||||
Real code is probably the most useful explanation, so here are some example
|
||||
conversion CLs which demonstrate practical `BrowserAssociatedInterface` usage.
|
||||
|
||||
[FrameHostMsg_SetCookie](https://codereview.chromium.org/2167513003) - This
|
||||
CL introduces a `content::mojom::RenderFrameMessageFilter` interface
|
||||
corresponding to the existing `content::RenderFrameMessageFilter` implementation
|
||||
of `BrowserMessageFilter`. Of particular interest is the fact that it only
|
||||
converts **one** of the messages on that filter. This is fine because ordering
|
||||
among the messages -- Mojom or otherwise -- is unchanged.
|
||||
|
||||
[FrameHostMsg_GetCookie](https://codereview.chromium.org/2202723005) - A small
|
||||
follow-up to the above CL, this converts another message on the same filter. It
|
||||
is common to convert a large group of messages one-by-one in separate CLs. Also
|
||||
note that this message, unlike the one above on the same interface, is
|
||||
synchronous.
|
||||
|
||||
[ViewHostMsg_GenerateRoutingID](https://codereview.chromium.org/2351333002) -
|
||||
Another small CL to introduce a new `BrowserAssociatedInterface`.
|
||||
|
||||
### Routed Per-Frame Messages To the Browser
|
||||
|
||||
Many legacy IPC messages are "routed" -- they carry a routing ID parameter which
|
||||
is interpreted by the channel endpoint and used to pass a received message on to
|
||||
some other more specific handler.
|
||||
|
||||
Messages received by the browser with a frame routing ID for example are routed
|
||||
to the RenderFrameHost's owning [`WebContents`](https://cs.chromium.org/chromium/src/content/browser/web_contents/web_contents_impl.h?rcl=a12e52d81346dd23d8284763d44c4ee657f11cea&l=447)
|
||||
with the corresponding `RenderFrameHostImpl` as additional context.
|
||||
|
||||
[This CL](https://codereview.chromium.org/2310583002) introduces usage of
|
||||
[`WebContentsFrameBindingSet<T>`](https://cs.chromium.org/chromium/src/content/public/browser/web_contents_binding_set.h?rcl=d364139fee76154a1d9fa8875ad0cbb5ccb523c3&l=112), which helps establish
|
||||
per-frame bindings for Channel-associated interfaces. Some hidden magic is done
|
||||
to make it so that interface requests from a remote
|
||||
[`RenderFrame AssociatedInterfaceProvider`](https://cs.chromium.org/chromium/src/content/public/renderer/render_frame.h?rcl=d364139fee76154a1d9fa8875ad0cbb5ccb523c3&l=170)
|
||||
are routed to the appropriate `WebContentsFrameBindingSet`, typically installed
|
||||
(as in this CL) by a `WebContentsObserver`.
|
||||
|
||||
When a message is received by an interface implementation using a
|
||||
`WebContentsFrameBindingSet`, that object's `dispatch_context()` can be used
|
||||
to retrieve the `RenderFrameHostImpl` targeted by the message. See the above CL
|
||||
for additional clarity.
|
||||
|
||||
**NOTE:** When the ordering of messages doesn't matter, the `InterfaceProvider`
|
||||
of the relevant `RenderFrameHost` should be used to replace the routing ID. It
|
||||
can be obtained by calling `RenderFrame::GetRemoteInterfaces()`.
|
||||
|
||||
### Other Routed Messages To the Browser
|
||||
|
||||
Other routing IDs are used when targeting either specific `RenderViewHost` or
|
||||
`RenderWidgetHost` instances. We don't currently have any facilities in place
|
||||
to assist with these conversions. Because render views are essentially a
|
||||
deprecated concept, messages targeting "view" routes should not be converted
|
||||
as-is, but should instead be moved to target either widgets or frames
|
||||
accordingly.
|
||||
|
||||
Facilities to assist in conversion of widget-routed messages may be added in the
|
||||
future. Who knows, maybe you'll be the brave developer to add them (and to then
|
||||
update this documentation, of course!) If you decide this is exactly what you
|
||||
need but are nervous about the prospect of writing it yourself, please send a
|
||||
friendly message to [chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
explaining the use case so we can help you get things done.
|
||||
|
||||
### Messages to a Renderer
|
||||
|
||||
[This CL](https://codereview.chromium.org/2381493003) converts `ViewMsg_New`
|
||||
to a Mojo interface, by virtue of the fact that
|
||||
`IPC::ChannelProxy::GetRemoteAssociatedInterface` from the browser process
|
||||
results in an associated interface request arriving at
|
||||
`ChildThreadImpl::OnAssociatedInterfaceRequest` in the corresponding child
|
||||
process.
|
||||
|
||||
Similar message conversions are done by [this CL](https://codereview.chromium.org/2400313002).
|
||||
|
||||
Note that we do not currently have any helpers for converting routed messages
|
||||
from browser to renderer. Please confer with
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
if such a use case is blocking your work.
|
||||
|
||||
## Miscellany
|
||||
|
||||
### Using Legacy IPC Traits
|
||||
|
||||
In some circumstances there may be a C++ enum, struct, or class that you want
|
||||
to use in a Mojom via [type mapping](/mojo/public/cpp/bindings#Type-Mapping),
|
||||
and that type may already have `IPC::ParamTraits` defined (possibly via
|
||||
`IPC_STRUCT_TRAITS*` macros) for legacy IPC.
|
||||
|
||||
If this is the case and the Mojom which uses the type will definitely only be
|
||||
called from and implemented in C++ code, *and* you have sufficient reason to
|
||||
avoid moving or duplicating the type definition in Mojom, you can take advantage
|
||||
of the existing `ParamTraits`.
|
||||
|
||||
#### The [Native] Attribute
|
||||
In order to do this you must declare a placeholder type in Mojom somewhere, like
|
||||
so:
|
||||
|
||||
```
|
||||
module foo.mojom;
|
||||
|
||||
[Native]
|
||||
enum WindowType;
|
||||
|
||||
[Native]
|
||||
struct MyGiganticStructure;
|
||||
```
|
||||
|
||||
The rest of your Mojom will use this typename when referring to the type, but
|
||||
the wire format used is defined entirely by `IPC::ParamTraits<T>` for whatever
|
||||
`T` to which you typemap the Mojom type. For example if you typemap
|
||||
`foo::mojom::MyGiganticStructure` to `foo::MyGiganticStructure`, your typemap
|
||||
must point to some header which defines
|
||||
`IPC::ParamTraits<foo::MyGiganticStructure>`.
|
||||
|
||||
There are several examples of this traits implementation in common IPC traits
|
||||
defined [here](https://code.google.com/p/chromium/codesearch#chromium/src/ipc/ipc_message_utils.h).
|
||||
|
||||
#### A Practical Example
|
||||
|
||||
Given the [`resource_messages.h`](https://cs.chromium.org/chromium/src/content/common/resource_messages.h?rcl=2e7a430d8d88222c04ab3ffb0a143fa85b3cec5b&l=215)
|
||||
header with the following definition:
|
||||
|
||||
``` cpp
|
||||
IPC_STRUCT_TRAITS_BEGIN(content::ResourceRequest)
|
||||
IPC_STRUCT_TRAITS_MEMBER(method)
|
||||
IPC_STRUCT_TRAITS_MEMBER(url)
|
||||
// ...
|
||||
IPC_STRUCT_TRAITS_END()
|
||||
```
|
||||
|
||||
and the [`resource_request.h`](https://cs.chromium.org/chromium/src/content/common/resource_request.h?rcl=dce9e476a525e4ff0304787935dc1a8c38392ac8&l=32)
|
||||
header with the definition for `content::ResourceRequest`:
|
||||
|
||||
``` cpp
|
||||
namespace content {
|
||||
|
||||
struct CONTENT_EXPORT ResourceRequest {
|
||||
// ...
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
```
|
||||
|
||||
we can declare a corresponding Mojom type:
|
||||
|
||||
```
|
||||
module content.mojom;
|
||||
|
||||
[Native]
|
||||
struct URLRequest;
|
||||
```
|
||||
|
||||
and add a typemap like [url_request.typemap](https://cs.chromium.org/chromium/src/content/common/url_request.typemap)
|
||||
to define the mapping:
|
||||
|
||||
```
|
||||
mojom = "//content/public/common/url_loader.mojom"
|
||||
public_headers = [ "//content/common/resource_request.h" ]
|
||||
traits_headers = [ "//content/common/resource_messages.h" ]
|
||||
...
|
||||
type_mappings = [ "content.mojom.URLRequest=content::ResourceRequest" ]
|
||||
```
|
||||
|
||||
Note specifically that `public_headers` includes the definition of the native
|
||||
C++ type, and `traits_headers` includes the definition of the legacy IPC traits.
|
||||
|
||||
Finally, note that this same approach can be used to leverage existing
|
||||
`IPC_ENUM_TRAITS` for `[Native]` Mojom enum aliases.
|
||||
|
||||
### Typemaps With Content and Blink Types
|
||||
|
||||
Using typemapping for messages that go between Blink and content browser code
|
||||
can sometimes be tricky due to things like dependency cycles or confusion over
|
||||
the correct place for some definition to live. There are some example
|
||||
CLs provided here, but feel free to also contact
|
||||
[chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo)
|
||||
with specific details if you encounter trouble.
|
||||
|
||||
[This CL](https://codereview.chromium.org/2363533002) introduces a Mojom
|
||||
definition and typemap for
|
||||
`ui::WindowOpenDisposition` as a precursor to the IPC conversion below.
|
||||
|
||||
The [follow-up CL](https://codereview.chromium.org/2363573002) uses that
|
||||
definition along with several other new typemaps (including native typemaps as
|
||||
described above in [Using Legacy IPC Traits](#Using-Legacy-IPC-Traits)) to
|
||||
convert the relatively large `ViewHostMsg_CreateWindow` message to Mojo.
|
||||
|
||||
### Utility Process Messages
|
||||
|
||||
Given that there are no interesting ordering dependencies among disparate IPC
|
||||
messages to and from utility processes, and because the utility process is
|
||||
already sort of a mixed bag of unrelated IPCs, the correct way to convert
|
||||
utility process IPCs to Mojo is to move them into services.
|
||||
|
||||
We already have support for running services out-of-process (with or without a
|
||||
sandbox), and many utility process operations already have a suitable service
|
||||
home they could be moved to. For example, the `data_decoder` service in
|
||||
[`//services/data_decoder`](https://cs.chromium.org/chromium/src/services/data_decoder/)
|
||||
is a good place to stick utility process IPCs that do decoding of relatively
|
||||
complex and untrusted data, of which (at the time of this writing) there are
|
||||
quite a few.
|
||||
|
||||
When in doubt, contact
|
||||
[`services-dev@chromium.org`](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
|
||||
with ideas, questions, suggestions, etc.
|
||||
|
||||
### Additional Documentation
|
||||
|
||||
[Chrome IPC to Mojo IPC Cheat Sheet](https://www.chromium.org/developers/design-documents/mojo/chrome-ipc-to-mojo-ipc-cheat-sheet)
|
||||
: A slightly dated but still valuable document covering some details
|
||||
regarding the conceptual mapping between legacy IPC and Mojo.
|
||||
|
||||
[Additional example CLs](https://docs.google.com/document/d/1GCi08AVMV96cD-tI8kW3xfRir3aNMb5U2yJFU3iAFSU)
|
132
mojo/README.md
132
mojo/README.md
@ -4,18 +4,21 @@
|
||||
|
||||
## Getting Started With Mojo
|
||||
|
||||
To get started using Mojo in applications which already support it (such as
|
||||
Chrome), the fastest path forward will be to look at the bindings documentation
|
||||
for your language of choice ([**C++**](#C_Bindings),
|
||||
[**JavaScript**](#JavaScript-Bindings), or [**Java**](#Java-Bindings)) as well
|
||||
as the documentation for the
|
||||
[**Mojom IDL and bindings generator**](/mojo/public/tools/bindings/README.md).
|
||||
To get started using Mojo in Chromium, the fastest path forward will likely be
|
||||
to read the Mojo sections of the
|
||||
[Intro to Mojo & Services](/docs/mojo_and_services.md) guide.
|
||||
|
||||
If you're looking for information on creating and/or connecting to services, see
|
||||
the top-level [Services documentation](/services/README.md).
|
||||
For more detailed reference material on the most commonly used features of Mojo,
|
||||
head directly to the [bindings](#Bindings-APIs) documentation for your language
|
||||
of choice or the more general
|
||||
[mojom Interface Definition Language (IDL)](/mojo/public/tools/bindings/README.md)
|
||||
documentation.
|
||||
|
||||
For specific details regarding the conversion of old things to new things, check
|
||||
out [Converting Legacy Chrome IPC To Mojo](/ipc/README.md).
|
||||
If you're looking for information on creating and/or connecting to services,
|
||||
you're in the wrong place! Mojo does not deal with services, it only facilitates
|
||||
interface definition, message passing, and other lower-level IPC primitives.
|
||||
Instead, you should take a look at some of the other available
|
||||
[Mojo & Services](/docs/README.md#Mojo-Services) documentation.
|
||||
|
||||
## System Overview
|
||||
|
||||
@ -25,9 +28,10 @@ library with code generation for multiple target languages to facilitate
|
||||
convenient message passing across arbitrary inter- and intra-process boundaries.
|
||||
|
||||
The documentation here is segmented according to the different libraries
|
||||
comprising Mojo. The basic hierarchy of features is as follows:
|
||||
comprising Mojo. Mojo is divided into cleanly-separated layers with the basic
|
||||
hierarchy of subcomponents as follows:
|
||||
|
||||

|
||||

|
||||
|
||||
## Mojo Core
|
||||
In order to use any of the more interesting high-level support libraries like
|
||||
@ -44,6 +48,9 @@ Mojo support within each process by calling `mojo::core::Init()`. See
|
||||
|
||||
This is a reasonable option when you can guarantee that all interconnected
|
||||
process binaries are linking against precisely the same revision of Mojo Core.
|
||||
This includes Chromium itself as well as any developer tools and test
|
||||
executables built within the tree.
|
||||
|
||||
To support other scenarios, use dynamic linking.
|
||||
|
||||
### Dynamic Linking
|
||||
@ -51,7 +58,7 @@ On some platforms, it's also possible for applications to rely on a
|
||||
dynamically-linked Mojo Core library (`libmojo_core.so` or `mojo_core.dll`)
|
||||
instead of statically linking against Mojo Core.
|
||||
|
||||
In order to take advantage of this mechanism, the corresponding library must be
|
||||
In order to take advantage of this mechanism, the library's binary must be
|
||||
present in either:
|
||||
|
||||
- The working directory of the application
|
||||
@ -60,84 +67,65 @@ present in either:
|
||||
|
||||
Instead of calling `mojo::core::Init()` as embedders do, an application using
|
||||
dynamic Mojo Core instead calls `MojoInitialize()` from the C System API. This
|
||||
call will attempt to locate (see above) and load a Mojo Core library to support
|
||||
subsequent Mojo API usage within the process.
|
||||
call will attempt to locate (see above) and load the Mojo Core library to
|
||||
support subsequent Mojo API usage within the process.
|
||||
|
||||
Note that the Mojo Core shared library presents a stable, forward-compatible C
|
||||
ABI which can support all current and future versions of the higher-level,
|
||||
public (and not binary-stable) System and Bindings APIs.
|
||||
Note that the Mojo Core shared library presents a **stable C ABI** designed with
|
||||
both forward- and backward-compatibility in mind. Thus old applications will
|
||||
work with new versions of the shared library, and new applications can work
|
||||
with old versions of the shared library (modulo any dependency on newer
|
||||
features, whose absence can be gracefully detected at runtime).
|
||||
|
||||
## C System API
|
||||
Once Mojo is initialized within a process, the public
|
||||
[**C System API**](/mojo/public/c/system/README.md) is usable on any thread for
|
||||
the remainder of the process's lifetime. This is a lightweight API with a
|
||||
relatively small, stable, forward-compatible ABI, comprising the total public
|
||||
API surface of the Mojo Core library.
|
||||
the remainder of the process's lifetime. This encapsulates Mojo Core's stable
|
||||
ABI and comprises the total public API surface of the Mojo Core library.
|
||||
|
||||
This API is rarely used directly, but it is the foundation upon which all
|
||||
higher-level Mojo APIs are built. It exposes the fundamental capabilities to
|
||||
create and interact Mojo primitives like **message pipes**, **data pipes**, and
|
||||
**shared buffers**, as well as APIs to help bootstrap connections among
|
||||
processes.
|
||||
The C System library's only dependency (apart from the system libc and e.g.
|
||||
pthreads) is Mojo Core itself. As such, it's possible build a fully-featured
|
||||
multiprocess system using only Mojo Core and its exposed C API. It exposes the
|
||||
fundamental cross-platform capabilities to create and manipulate Mojo primitives
|
||||
like **message pipes**, **data pipes**, and **shared buffers**, as well as APIs
|
||||
to help bootstrap connections among processes.
|
||||
|
||||
Despite this, it's rare for applications to use the C API directly. Instead this
|
||||
API acts as a stable foundation upon which several higher-level and more
|
||||
ergonomic Mojo libraries are built.
|
||||
|
||||
## Platform Support API
|
||||
Mojo provides a small collection of abstractions around platform-specific IPC
|
||||
primitives to facilitate bootstrapping Mojo IPC between two processes. See the
|
||||
[Platform API](/mojo/public/cpp/platform/README.md) documentation for details.
|
||||
|
||||
## High-Level System APIs
|
||||
## Higher-Level System APIs
|
||||
There is a relatively small, higher-level system API for each supported
|
||||
language, built upon the low-level C API. Like the C API, direct usage of these
|
||||
system APIs is rare compared to the bindings APIs, but it is sometimes desirable
|
||||
or necessary.
|
||||
|
||||
### C++
|
||||
The [**C++ System API**](/mojo/public/cpp/system/README.md) provides a layer of
|
||||
C++ helper classes and functions to make safe System API usage easier:
|
||||
strongly-typed handle scopers, synchronous waiting operations, system handle
|
||||
wrapping and unwrapping helpers, common handle operations, and utilities for
|
||||
more easily watching handle state changes.
|
||||
These APIs provide wrappers around low-level [system API](#C-System-API)
|
||||
concepts, presenting interfaces that are more idiomatic for the target language:
|
||||
|
||||
### JavaScript
|
||||
The [**JavaScript System API**](/third_party/blink/renderer/core/mojo/README.md)
|
||||
exposes the Mojo primitives to JavaScript, covering all basic functionality of the
|
||||
low-level C API.
|
||||
- [**C++ System API**](/mojo/public/cpp/system/README.md)
|
||||
- [**JavaScript System API**](/third_party/blink/renderer/core/mojo/README.md)
|
||||
- [**Java System API**](/mojo/public/java/system/README.md)
|
||||
|
||||
### Java
|
||||
The [**Java System API**](/mojo/public/java/system/README.md) provides helper
|
||||
classes for working with Mojo primitives, covering all basic functionality of
|
||||
the low-level C API.
|
||||
## Bindings APIs
|
||||
The [**mojom Interface Definition Language (IDL)**](/mojo/public/tools/bindings/README.md)
|
||||
is used to generate interface bindings for various languages to send and receive
|
||||
mojom interface messages using Mojo message pipes. The generated code is
|
||||
supported by a language-specific bindings API:
|
||||
|
||||
## High-Level Bindings APIs
|
||||
Typically developers do not use raw message pipe I/O directly, but instead
|
||||
define some set of interfaces which are used to generate code that resembles
|
||||
an idiomatic method-calling interface in the target language of choice. This is
|
||||
the bindings layer.
|
||||
- [**C++ Bindings API**](/mojo/public/cpp/bindings/README.md)
|
||||
- [**JavaScript Bindings API**](/mojo/public/js/README.md)
|
||||
- [**Java Bindings API**](/mojo/public/java/bindings/README.md)
|
||||
|
||||
### Mojom IDL and Bindings Generator
|
||||
Interfaces are defined using the
|
||||
[**Mojom IDL**](/mojo/public/tools/bindings/README.md), which can be fed to the
|
||||
[**bindings generator**](/mojo/public/tools/bindings/README.md) to generate code
|
||||
in various supported languages. Generated code manages serialization and
|
||||
deserialization of messages between interface clients and implementations,
|
||||
simplifying the code -- and ultimately hiding the message pipe -- on either side
|
||||
of an interface connection.
|
||||
|
||||
### C++ Bindings
|
||||
By far the most commonly used API defined by Mojo, the
|
||||
[**C++ Bindings API**](/mojo/public/cpp/bindings/README.md) exposes a robust set
|
||||
of features for interacting with message pipes via generated C++ bindings code,
|
||||
including support for sets of related bindings endpoints, associated interfaces,
|
||||
nested sync IPC, versioning, bad-message reporting, arbitrary message filter
|
||||
injection, and convenient test facilities.
|
||||
|
||||
### JavaScript Bindings
|
||||
The [**JavaScript Bindings API**](/mojo/public/js/README.md) provides helper
|
||||
classes for working with JavaScript code emitted by the bindings generator.
|
||||
|
||||
### Java Bindings
|
||||
The [**Java Bindings API**](/mojo/public/java/bindings/README.md) provides
|
||||
helper classes for working with Java code emitted by the bindings generator.
|
||||
Note that the C++ bindings see the broadest usage in Chromium and are thus
|
||||
naturally the most feature-rich, including support for things like
|
||||
[associated interfaces](/mojo/public/cpp/bindings/README.md#Associated-Interfaces),
|
||||
[synchronous calls](/mojo/public/cpp/bindings/README.md#Synchronous-Calls), and
|
||||
[type-mapping](/mojo/public/cpp/bindings/README.md#Type-Mapping).
|
||||
|
||||
## FAQ
|
||||
|
||||
@ -162,7 +150,9 @@ Compared to the old IPC in Chrome, making a Mojo call is about 1/3 faster and us
|
||||
|
||||
### Can I use in-process message pipes?
|
||||
Yes, and message pipe usage is identical regardless of whether the pipe actually
|
||||
crosses a process boundary -- in fact this detail is intentionally obscured.
|
||||
crosses a process boundary -- in fact the location of the other end of a pipe is
|
||||
intentionally obscured, in part for the sake of efficiency, and in part to
|
||||
discourage tight coupling of application logic to such details.
|
||||
|
||||
Message pipes which don't cross a process boundary are efficient: sent messages
|
||||
are never copied, and a write on one end will synchronously modify the message
|
||||
|
@ -43,8 +43,8 @@ user-provided notification handlers may be invoked at any time on arbitrary
|
||||
threads in the process. It is entirely up to the API user to take appropriate
|
||||
measures to synchronize operations against other application state.
|
||||
|
||||
The higher level [system](/mojo/README.md#High-Level-System-APIs) and
|
||||
[bindings](/mojo/README.md#High-Level-Bindings-APIs) APIs provide helpers to
|
||||
The higher-level [system](/mojo/README.md#Higher_Level-System-APIs) and
|
||||
[bindings](/mojo/README.md#Bindings-APIs) APIs provide helpers to
|
||||
simplify Mojo usage in this regard, at the expense of some flexibility.
|
||||
|
||||
## Result Codes
|
||||
|
@ -17,7 +17,7 @@ snippets. For a detailed API references please consult the headers in
|
||||
[//mojo/public/cpp/bindings](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/README.md).
|
||||
|
||||
For a simplified guide targeted at Chromium developers, see [this
|
||||
link](/docs/mojo_guide.md).
|
||||
link](/docs/mojo_and_services.md).
|
||||
|
||||
## Getting Started
|
||||
|
||||
@ -153,7 +153,7 @@ routed to some implementation which will **bind** it. The `InterfaceRequest<T>`
|
||||
doesn't actually *do* anything other than hold onto a pipe endpoint and carry
|
||||
useful compile-time type information.
|
||||
|
||||

|
||||

|
||||
|
||||
So how do we create a strongly-typed message pipe?
|
||||
|
||||
@ -211,7 +211,7 @@ logger->Log("Hello!");
|
||||
|
||||
This actually writes a `Log` message to the pipe.
|
||||
|
||||

|
||||

|
||||
|
||||
But as mentioned above, `InterfaceRequest` *doesn't actually do anything*, so
|
||||
that message will just sit on the pipe forever. We need a way to read messages
|
||||
@ -278,7 +278,7 @@ motion by the above line of code:
|
||||
3. The `Log` message is read and deserialized, causing the `Binding` to invoke
|
||||
the `Logger::Log` implementation on its bound `LoggerImpl`.
|
||||
|
||||

|
||||

|
||||
|
||||
As a result, our implementation will eventually log the client's `"Hello!"`
|
||||
message via `LOG(ERROR)`.
|
||||
@ -1261,8 +1261,7 @@ sync method call? It continues to process incoming sync request messages
|
||||
messages and sync response messages that don’t match the ongoing sync
|
||||
call.
|
||||
|
||||

|
||||

|
||||
|
||||
Please note that sync response messages that don’t match the ongoing
|
||||
sync call cannot re-enter. That is because they correspond to sync calls
|
||||
@ -1274,8 +1273,7 @@ while the stack unwinds.
|
||||
Please note that the re-entrancy behavior doesn’t prevent deadlocks
|
||||
involving async calls. You need to avoid call sequences such as:
|
||||
|
||||

|
||||

|
||||
|
||||
### Read more
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
# Mojom IDL and Bindings Generator
|
||||
# Mojom Interface Definition Language (IDL)
|
||||
This document is a subset of the [Mojo documentation](/mojo/README.md).
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
|
||||
Mojom is the IDL for Mojo bindings interfaces. Given a `.mojom` file, the
|
||||
Mojom is the IDL for Mojo interfaces. Given a `.mojom` file, the
|
||||
[bindings
|
||||
generator](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/)
|
||||
outputs bindings for all supported languages: **C++**, **JavaScript**, and
|
||||
generator](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/) can
|
||||
output bindings for any supported language: **C++**, **JavaScript**, or
|
||||
**Java**.
|
||||
|
||||
For a trivial example consider the following hypothetical Mojom file we write to
|
||||
`//services/widget/public/interfaces/frobinator.mojom`:
|
||||
`//services/widget/public/mojom/frobinator.mojom`:
|
||||
|
||||
```
|
||||
module widget.mojom;
|
||||
@ -25,15 +25,15 @@ interface Frobinator {
|
||||
This defines a single [interface](#Interfaces) named `Frobinator` in a
|
||||
[module](#Modules) named `widget.mojom` (and thus fully qualified in Mojom as
|
||||
`widget.mojom.Frobinator`.) Note that many interfaces and/or other types of
|
||||
definitions may be included in a single Mojom file.
|
||||
definitions (structs, enums, *etc.*) may be included in a single Mojom file.
|
||||
|
||||
If we add a corresponding GN target to
|
||||
`//services/widget/public/interfaces/BUILD.gn`:
|
||||
`//services/widget/public/mojom/BUILD.gn`:
|
||||
|
||||
```
|
||||
import("mojo/public/tools/bindings/mojom.gni")
|
||||
|
||||
mojom("interfaces") {
|
||||
mojom("mojom") {
|
||||
sources = [
|
||||
"frobinator.mojom",
|
||||
]
|
||||
@ -43,22 +43,32 @@ mojom("interfaces") {
|
||||
and then build this target:
|
||||
|
||||
```
|
||||
ninja -C out/r services/widget/public/interfaces
|
||||
ninja -C out/r services/widget/public/mojom
|
||||
```
|
||||
|
||||
we'll find several generated sources in our output directory:
|
||||
|
||||
```
|
||||
out/r/gen/services/widget/public/interfaces/frobinator.mojom.cc
|
||||
out/r/gen/services/widget/public/interfaces/frobinator.mojom.h
|
||||
out/r/gen/services/widget/public/interfaces/frobinator.mojom.js
|
||||
out/r/gen/services/widget/public/interfaces/frobinator.mojom.srcjar
|
||||
...
|
||||
out/r/gen/services/widget/public/mojom/frobinator.mojom.cc
|
||||
out/r/gen/services/widget/public/mojom/frobinator.mojom.h
|
||||
out/r/gen/services/widget/public/mojom/frobinator.mojom-shared.h
|
||||
etc...
|
||||
```
|
||||
|
||||
Each of these generated source modules includes a set of definitions
|
||||
representing the Mojom contents within the target language. For more details
|
||||
regarding the generated outputs please see
|
||||
representing the Mojom contents in C++. You can also build or depend on suffixed
|
||||
target names to get bindings for other languages. For example,
|
||||
|
||||
```
|
||||
ninja -C out/r services/widget/public/mojom:mojom_js
|
||||
ninja -C out/r services/widget/public/mojom:mojom_java
|
||||
```
|
||||
|
||||
would generate JavaScript and Java bindings respectively, in the same generated
|
||||
output directory.
|
||||
|
||||
For more details regarding the generated
|
||||
outputs please see
|
||||
[documentation for individual target languages](#Generated-Code-For-Target-Languages).
|
||||
|
||||
## Mojom Syntax
|
||||
@ -136,7 +146,7 @@ If your Mojom references definitions from other Mojom files, you must **import**
|
||||
those files. Import syntax is as follows:
|
||||
|
||||
```
|
||||
import "services/widget/public/interfaces/frobinator.mojom";
|
||||
import "services/widget/public/mojom/frobinator.mojom";
|
||||
```
|
||||
|
||||
Import paths are always relative to the top-level directory.
|
||||
@ -493,7 +503,7 @@ relative ordering guarantees among them. Associated interfaces are useful when
|
||||
one interface needs to guarantee strict FIFO ordering with respect to one or
|
||||
more other interfaces, as they allow interfaces to share a single pipe.
|
||||
|
||||
Currenly associated interfaces are only supported in generated C++ bindings.
|
||||
Currently associated interfaces are only supported in generated C++ bindings.
|
||||
See the documentation for
|
||||
[C++ Associated Interfaces](/mojo/public/cpp/bindings/README.md#Associated-Interfaces).
|
||||
|
||||
|
@ -1,84 +1,256 @@
|
||||
# Chrome Foundation Services
|
||||
# Service Development Guidelines
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
|
||||
This directory contains Chrome Foundation Services. If you think of Chrome as a
|
||||
"portable OS," Chrome Foundation Services can be thought of as that OS'
|
||||
foundational "system services" layer.
|
||||
If you're looking for general documentation on the Service Manager, what a
|
||||
"service" is, and how to build one, see the
|
||||
[Service Manager & Services](/services/service_manager/README.md)
|
||||
documentation instead of this document.
|
||||
|
||||
Roughly each subdirectory here corresponds to a service that:
|
||||
The top-level `//services` directory contains the sources, public Mojo interface
|
||||
definitions, and public client libraries for a number of essential services,
|
||||
designated as **Chrome Foundation Services**. If you think of Chrome as a
|
||||
"portable OS," Chrome Foundation Services can be thought of as the core system
|
||||
services of that OS.
|
||||
|
||||
* is a client of `//services/service_manager` with its own unique Identity.
|
||||
* could logically run a standalone process for security/performance isolation
|
||||
benefits depending on the constraints of the host OS.
|
||||
Each subdirectory here corresponds to a service that:
|
||||
|
||||
## API Standards
|
||||
- implements
|
||||
[`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h?rcl=ebec02bcca6e327b2e01855ce43ab1bec0aeef27&l=21)
|
||||
and is thus a client of the Service Manager
|
||||
- generally focuses on a subset of functionality or features which are
|
||||
thematically or functionally related in a way that makes sense given the name
|
||||
of the service
|
||||
- could logically run in an isolated process for security or performance
|
||||
isolation, depending on the constraints of the host OS
|
||||
|
||||
As illustrated above, the individual services in //services are intended for
|
||||
graceful reusability across a broad variety of use cases. To enable this goal,
|
||||
we have rigorous [standards](/services/api_standards.md) on services'
|
||||
public APIs. Before doing significant work in //services (and especially before
|
||||
becoming an owner of a service), please internalize these standards -- you are
|
||||
responsible for upholding them.
|
||||
*** aside
|
||||
Note that there are other parts of the tree which aggregate
|
||||
slightly-less-than-foundational service definitions, such as services specific
|
||||
to the Chrome browser, defined in `//chrome/services`. The motivations, advice,
|
||||
and standards discussed in this document apply to all service definitions in the
|
||||
Chromium tree.
|
||||
***
|
||||
|
||||
## Service Directory Structure
|
||||
The `//services/service_manager` directory contains the implementation and
|
||||
public APIs of the Service Manager itself, including an embedder API with which
|
||||
software applications (such as Chrome) can embed the Service Manager to manage
|
||||
their own multiprocess service architecture.
|
||||
|
||||
Individual services are structured like so:
|
||||
One of the main motivations for expressing Chromium as a collection of services
|
||||
is long-term maintainability and code health. Because service API boundaries are
|
||||
strictly limited to Mojo interfaces, state owned and managed by each service is
|
||||
strongly isolated from other components in the system.
|
||||
|
||||
```
|
||||
//services/foo/ <-- Implementation code, may have subdirs.
|
||||
/public/
|
||||
/cpp/ <-- C++ client libraries (optional)
|
||||
/mojom/ <-- Mojom interfaces
|
||||
Another key motivation is general modularity and reusability: in the past there
|
||||
have been a number of missed opportunities for potential new features or
|
||||
Chromium-based products due to the browser's generally monolothic and inflexible
|
||||
system design. With the Service Manager & services providing scaffolding for
|
||||
system components, it becomes progressively easier to build out newer use cases
|
||||
with *e.g.* a smaller resource footprint, or a different process model, or even
|
||||
a more granular binary distribution.
|
||||
|
||||
## Service Standards
|
||||
|
||||
As outlined above, individual services are intended for graceful reusability
|
||||
across a broad variety of use cases. To enable this goal, we have rigorous
|
||||
standards on services' structure and public API design. Before doing significant
|
||||
work in `//services` (or other places where services are defined), please
|
||||
internalize these standards. All Chromium developers are responsible for
|
||||
upholding them!
|
||||
|
||||
### Public Service APIs
|
||||
|
||||
In creating and maintaining a service's public API, please respect the following
|
||||
principles:
|
||||
|
||||
- The purpose of a service should be readily apparent.
|
||||
- The supported client use cases of the service should be easy for a new
|
||||
consumer to understand.
|
||||
- The service should use idioms and design patterns consistent with other
|
||||
services.
|
||||
- From the service's public API documentation and tests, it should be feasible
|
||||
to develop a new implementation of the service which satisfies existing
|
||||
clients and doesn't require mimicking internal implementation details of the
|
||||
existing service.
|
||||
- Perhaps most important of all, a service's public API should be designed with
|
||||
multiple hypothetical clients in mind, *not* focused on supporting only a
|
||||
single narrow use known at development time. **Always be thinking about the
|
||||
future!**
|
||||
|
||||
If you're working on a new service and have concerns or doubts about API design,
|
||||
please post to
|
||||
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev)
|
||||
and ask for help. The list is generally quite responsive, and it's loaded with
|
||||
people who have done a lot of work on services.
|
||||
|
||||
### Service API Design Tips
|
||||
|
||||
#### Using Interface Factories to Establish Context
|
||||
|
||||
One common pitfall when designing service APIs is to write something like:
|
||||
|
||||
``` cpp
|
||||
interface GoatTeleporter {
|
||||
// Sets the client interface pipe for this teleporter. Must be called before
|
||||
// other interface methods.
|
||||
SetClient(GoatTeleporterClient client);
|
||||
|
||||
TeleportGoat(string name);
|
||||
};
|
||||
|
||||
interface GoatTeleporterClient {
|
||||
TeleporterReady();
|
||||
};
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
The problem with this approach is that a client may easily fail to call
|
||||
`SetClient` before calling `TeleportGoat`. When such ordering requirements are
|
||||
necessary, the service can benefit clients by designing an API that is harder
|
||||
to fail at. For example:
|
||||
|
||||
Code within `//services` may only depend on each other via each other's
|
||||
`/public/` directories, *i.e.* implementation code may not be shared directly.
|
||||
``` cpp
|
||||
interface GoatTeleporterFactory {
|
||||
GetGoatTeleporter(GoatTeleporter& request, GoatTeleporterClient client);
|
||||
};
|
||||
|
||||
Service code should also take care to tightly limit the dependencies on static
|
||||
libraries from outside of `//services`. Dependencies to large platform
|
||||
layers like `//content`, `//chrome` or `//third_party/WebKit` must be avoided.
|
||||
interface GoatTeleporter {
|
||||
TeleportGoat(string name);
|
||||
};
|
||||
```
|
||||
|
||||
## Physical Packaging
|
||||
Instead of exposing `GoatTeleporter` directly to other services, the service can
|
||||
expose `GoatTeleporterFactory` instead. Now it's impossible for a client to
|
||||
acquire a functioning `GoatTeleporter` pipe without also providing a
|
||||
corresponding client pipe to complement it.
|
||||
|
||||
Note that while it may be possible to build a discrete physical package (DSO)
|
||||
for each service, products consuming these services may package them
|
||||
differently, e.g. by combining them into a single package.
|
||||
### Service & Interface Naming
|
||||
|
||||
## Additional Documentation
|
||||
Just some basic tips for service and interface naming:
|
||||
|
||||
[High-level Design Doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU)
|
||||
- Strive to give your service a name that makes it immediately obvious what the
|
||||
service is for (*e.g.*, `"network"`, `"metrics"`) rather than a meaningless
|
||||
codename like `"cromulator_3000"`.
|
||||
- Avoid the usage of `"Service"` in interface names. While the term "service"
|
||||
is overloaded in Chromium and certainly has plenty of valid interpretations,
|
||||
in the context of Service Manager services it has a very specific meaning
|
||||
and should not be overloaded further if possible. An interface which exposes a
|
||||
control API for a goat teleporter can just be called `GoatTeleporter`, not
|
||||
`GoatTeleporterService`.
|
||||
|
||||
[Servicification Homepage](https://sites.google.com/a/chromium.org/dev/servicification)
|
||||
- Strive to avoid conceptual layering violations in naming and documentation --
|
||||
*e.g.*, avoid referencing Blink or Content concepts like "renderers" or
|
||||
"frame hosts".
|
||||
|
||||
[Servicification Strategies](/docs/servicification.md)
|
||||
- Use the names `FooClient` and `FooObserver` consistently in interfaces. If
|
||||
there is an expected 1:1 correspondence between a Foo and its client interface
|
||||
counterpart, that counterpart should most likely be called `FooClient`. If
|
||||
there is expected to be 1-to-many correspondence between a Foo and its
|
||||
counterpart clients, the client interface may be better named `FooObserver`.
|
||||
|
||||
## Relationship To Other Top-Level Directories
|
||||
### Service Directory & Dependency Structure
|
||||
|
||||
Services can be thought of as integrators of library code from across the
|
||||
Chromium repository, most commonly `//base` and `//mojo` (obviously) but for
|
||||
each service also `//components`, `//ui`, *etc.* in accordance with the
|
||||
functionality they provide.
|
||||
Services typically follow a canonical directory structure:
|
||||
|
||||
Not everything in `//components` is automatically a service in its own right.
|
||||
Think of `//components` as sort of like a `//lib`. Individual `//components` can
|
||||
define, implement and use Mojom interfaces, but only `//services` have unique
|
||||
identities with the Service Manager and so only `//services` make it possible
|
||||
for Mojom interfaces to be acquired.
|
||||
```
|
||||
//services/service_name/ # Private implementation
|
||||
public/
|
||||
mojom/ # Mojom interfaces
|
||||
cpp/ # C++ client libraries (optional)
|
||||
java/ # Java client libararies (optional, rare)
|
||||
js/ # JS client libraries (optional, rare)
|
||||
```
|
||||
|
||||
## Adding a new service
|
||||
As a general rule, **nothing below `/public` can depend on the private service
|
||||
implementation** (*i.e.* things above `/public`). Enforcing this principle makes
|
||||
it much easier to keep the service's state well-isolated from the rest of the
|
||||
system.
|
||||
|
||||
See the [Service Manager documentation](/services/service_manager) for more
|
||||
details regarding how to define a service and expose or consume interfaces to
|
||||
and from other services.
|
||||
Generally the language-specific client libraries are built against only the
|
||||
public mojom API of the service (and usually few other common dependencies like
|
||||
`//base` and `//mojo`), and they bootstrap connections to those interfaces by
|
||||
using public Service Manager APIs like
|
||||
[`Connector`](/services/service_manager/README.md#Connectors).
|
||||
|
||||
Please start a thread on [services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
|
||||
if you want to introduce a new service.
|
||||
Even in the private service implementation, services should not depend on very
|
||||
large components like Content, Chrome, or Blink.
|
||||
|
||||
If you are servicifying existing Chromium code: Please first read the
|
||||
[servicification strategies documentation](/docs/servicification.md), which
|
||||
contains information that will hopefully make your task easier.
|
||||
*** aside
|
||||
NOTE: Exceptions to the above rule are made in rare cases where Blink or V8 is
|
||||
actually required as part of the service implementation. For example
|
||||
`"data_decoder"` uses Blink implementation to decode common image formats, and
|
||||
`"proxy_resolver"` uses V8 to execute proxy autoconfig scripts.
|
||||
***
|
||||
|
||||
### Service Documentation
|
||||
|
||||
- Every service should have a top-level `README.md` that explains the purpose and
|
||||
supported usage models of the service.
|
||||
|
||||
- Every public interface should be documented within its Mojom file at both the
|
||||
interface level and indivudal message level.
|
||||
|
||||
- Interface documentation should be complete enough to serve as test
|
||||
specifications. If the method returns information of a user's accounts, what
|
||||
should happen if the user is not signed in? If the method makes a request for
|
||||
an access token, what happens if a client makes a second method call before
|
||||
the first one has completed? If the method returns a nullable object, under
|
||||
which conditions will it be null?
|
||||
|
||||
- Avoid writing interface documentation which is unnecessarily prescriptive
|
||||
about implementation details. Keep in mind that these are **interface**
|
||||
definitions, not implementations thereof.
|
||||
|
||||
- Avoid writing documentation which is tailored to a specific client.
|
||||
|
||||
### Service Testing
|
||||
|
||||
- Try to cover service implementation details with unit tests tied as closely
|
||||
as possible to the private implementation object or method being tested,
|
||||
rather than exercising implementation details through public API surface.
|
||||
|
||||
- For integration tests, try to have tests cover as much of the public API
|
||||
surface as possible while mocking out as little of the underlying service as
|
||||
possible.
|
||||
|
||||
- Treat the public API tests as "conformance tests" which clearly demonstrate
|
||||
what expectations and guarantees are supposed to be upheld by *any*
|
||||
implementation of the service's APIs.
|
||||
|
||||
- Take advantage of the
|
||||
[test support library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/test/)
|
||||
provided by the Service Manager. In particular, `TestConnectorFactory` is
|
||||
useful for driving public API tests with the service running inside the test
|
||||
process, and `TestServiceManager` makes it possible to easily cover
|
||||
out-of-process testing scenarios while faking out as little of the system as
|
||||
possible.
|
||||
|
||||
## Adding a New Service
|
||||
|
||||
See the [Service Manager documentation](/services/service_manager/README.md) for
|
||||
more details regarding how to define a service and expose or consume interfaces
|
||||
to and from it, as well as how to make the service available to an application's
|
||||
runtime environment.
|
||||
|
||||
Please start a thread on
|
||||
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
|
||||
if you want to propose the introduction of a new service.
|
||||
|
||||
If you are servicifying an existing Chromium feature, please check out
|
||||
[Servicifying Chromium Features](/docs/servicification.md).
|
||||
|
||||
## Other Docs
|
||||
|
||||
Here are some other external documents that aren't quite fully captured by any
|
||||
documents in the Chromium tree. Beware of obsolete information:
|
||||
|
||||
- [High-level Design Doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU)
|
||||
- [Servicification Homepage](https://sites.google.com/a/chromium.org/dev/servicification)
|
||||
|
||||
## Additional Support
|
||||
|
||||
You can always post to
|
||||
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev)
|
||||
with questions or concerns about anything related to service development.
|
||||
|
@ -1,116 +0,0 @@
|
||||
# API Standards for Foundation Services
|
||||
|
||||
In creating and maintaining the public-facing structure of a foundation service,
|
||||
you should hold yourself to several first-order goals:
|
||||
|
||||
* The purpose of the service should be readily apparent.
|
||||
* The supported usage models of the service should be easy for a new
|
||||
consumer to understand.
|
||||
* The service should be consistent with other foundation services.
|
||||
* From the API documentation and tests, it should be feasible
|
||||
to develop a distinct implementation of the service (i.e., without having to
|
||||
delve into the internals of the current implementation).
|
||||
|
||||
Below we outline concrete standards that aid in achieving the above goals.
|
||||
|
||||
## Naming
|
||||
|
||||
* Strive to give your service a name that makes it immediately obvious what the
|
||||
service is for ("network", "metrics").
|
||||
|
||||
* Avoid the usage of "Service" in interface names. While the term "Service" is
|
||||
overloaded in Chromium, in the context of //services it has a very specific
|
||||
meaning and should not be overloaded.
|
||||
|
||||
* Strive to avoid conceptual layering violations in naming -- e.g., references
|
||||
to Blink or //content.
|
||||
|
||||
* Use the names "FooClient" and "FooObserver" consistently in interfaces. If
|
||||
there is an expected 1:1 correspondence between Foo and its counterpart, that
|
||||
counterpart should be called FooClient. If there is an expected 1:many
|
||||
correspondence between Foo and its counterparts, those counterparts should be
|
||||
called FooObservers.
|
||||
|
||||
## Documentation
|
||||
|
||||
* Every service should have a top-level README.md that explains the purpose and
|
||||
supported usage models of the service.
|
||||
|
||||
* Every public interface should be documented at the interface (class) level and
|
||||
at the method level.
|
||||
|
||||
* Interface documentation should be complete enough to serve as test
|
||||
specifications. If the method returns information of a user's accounts, what
|
||||
happens if the user is not signed in? If the method makes a request for an
|
||||
access token, what happens if a client makes a second method call before a
|
||||
first one has completed? If the method returns a nullable object, under which
|
||||
conditions will it be null?
|
||||
|
||||
* Strive to avoid your documentation being too specific to a given client.
|
||||
|
||||
## API Shape
|
||||
|
||||
* Strive to avoid molding your API shape too specifically to the needs of a
|
||||
given client. Most foundational services should make sense even in a
|
||||
Chrome-less system environment. A good test is: Would your service's APIs seem
|
||||
sensible to users who don't have knowledge of Chrome's specific needs?
|
||||
|
||||
* If a given interface Foo requires "construction parameters" (e.g., the client
|
||||
must give it a FooClient before calling any methods), provide a FooProvider
|
||||
interface with a GetFoo() method that takes in the relevant construction
|
||||
parameters. This approach eliminates the possibility of a badly-written (or
|
||||
malevolent) client calling methods on a partially-constructed Foo. To be
|
||||
concrete:
|
||||
|
||||
````
|
||||
// NO: Client will have access to partially-constructed Foo.
|
||||
interface Foo {
|
||||
SetClient(FooClient client);
|
||||
...
|
||||
};
|
||||
|
||||
// YES: Foo will be completely constructed before client has access.
|
||||
interface FooProvider {
|
||||
GetFoo(Foo& request, FooClient client);
|
||||
};
|
||||
interface Foo { ... };
|
||||
````
|
||||
|
||||
* In the absence of specific guidance, strive for consistency with surrounding
|
||||
interfaces and with interfaces in other services.
|
||||
|
||||
## Testing
|
||||
|
||||
* Use service tests to test the public interfaces exposed by your service.
|
||||
|
||||
* Every public method should be covered by at least one service test. Strive
|
||||
to have your tests enforce your documentation (corollary: if you can enforce
|
||||
your documentation without any tests, improve your documentation :).
|
||||
|
||||
* Think of these tests as a form of "compliance tests": They should be written
|
||||
in such a way that engineers with a distinct implementation of your
|
||||
APIs should trivially be able to run your tests against their implementation.
|
||||
Notably, try to avoid relying on implementation details of the service in its
|
||||
tests.
|
||||
|
||||
* Related to the above, aim for a high degree of coverage with these tests. If a
|
||||
reimplementation passes your tests, you should have a high degree of
|
||||
confidence that it will be usable by your consumers.
|
||||
|
||||
## Appendix: Responsibility for Upholding These Standards
|
||||
|
||||
The responsibility for holding these standards is shared across
|
||||
//services/OWNERS, individual service OWNERS, and services developers:
|
||||
|
||||
* //services/OWNERS own the standards themselves and are responsible for
|
||||
ensuring that quality and consistency are maintained across //services.
|
||||
* Individual service OWNERS are responsible for ensuring that their service
|
||||
adheres to these standards.
|
||||
* Service developers are responsible for ensuring that their CLs adhere to
|
||||
these standards (and thus making life easier for the OWNERS that must review
|
||||
these CLs :).
|
||||
|
||||
We expect that these standards will evolve over time. If you encounter a tricky
|
||||
situation not covered here, please send an email to services-dev@. Similarly, if
|
||||
you see inconsistency or violations of the standards, please file a bug and CC
|
||||
relevant OWNERS (i.e., of the service in question and/or //services/OWNERS).
|
File diff suppressed because it is too large
Load Diff
@ -1,315 +0,0 @@
|
||||
# Service Manifests
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
|
||||
Manifest files are used to configure security properties and
|
||||
permissions for services, such as listing allowed sets of interfaces or
|
||||
specifying a sandbox type. The files use JSON format and are usually
|
||||
placed in the same directory as the service source files, but the path
|
||||
is configurable in the BUILD.gn file for the service
|
||||
(see [README.md](README.md#build-targets) for an example).
|
||||
|
||||
## Terminology
|
||||
|
||||
The Service Manager is responsible for starting new service instances on-demand,
|
||||
and a given service may have any number of concurrently running instances.
|
||||
The Service Manager disambiguates service instances by their unique identity.
|
||||
A service's **identity** is represented by the 4-tuple of its service name,
|
||||
instance group, instance ID, and a globally unique ID:
|
||||
|
||||
### Service name
|
||||
|
||||
A free-form -- typically short -- string identifying the the specific service
|
||||
being run in the instance.
|
||||
|
||||
### Instance Group
|
||||
|
||||
Service instances started by the Service Manager are organized into
|
||||
*instance groups*. Typically a service instance can only connect to other
|
||||
service instances in its own group. Exceptions are made for targeted services
|
||||
which are designed to be accessible cross-group (such as singleton services), as
|
||||
well as for connecting services which are sufficiently privileged to connect to
|
||||
any service instance in the system.
|
||||
|
||||
Instance group is represented by a `base::Token` in `service_manager::Identity`.
|
||||
When instance group is omitted from an Identity passed with an interface request
|
||||
to the Service Manager, the Service Manager assumes that the request should be
|
||||
routed to a service instance in the requestor's same instance group.
|
||||
|
||||
In Chrome, every `BrowserContext` has a randomly generated instance group ID
|
||||
associated with it, and this is used to isolate the service instances which run
|
||||
on behalf of specific profiles, including renderers.
|
||||
|
||||
### Instance ID
|
||||
|
||||
Another `base::Token` in `service_manager::Identity`, used to disambiguate
|
||||
multiple instances of a service for the same user. Every instance has an
|
||||
instance ID, but it typically takes on the default value of a zero-token.
|
||||
|
||||
### Globally Unique ID
|
||||
|
||||
Every service instance started by the Service Manager is assigned a randomly
|
||||
generated `base::Token` value designated as the instance's
|
||||
**globally unique ID**. This value is always unique to a single instance across
|
||||
time and space.
|
||||
|
||||
### Connections
|
||||
|
||||
Every service instance has a Connector API it can use to issue requests to the
|
||||
Service Manager. One such request is `BindInterface`, which allows the service
|
||||
to bind an interface within another service instance.
|
||||
|
||||
When binding an interface, the **source identity** refers to the service
|
||||
initiating the bind request, and the **target identity** refers to the
|
||||
destination service instance. Based on both the source and target identities,
|
||||
the Service Manager may choose to start a new service instance, reuse an
|
||||
existing instance as the destination for the bind request or deny the request.
|
||||
|
||||
### Interface provider
|
||||
|
||||
InterfaceProvider is a Mojo
|
||||
[interface](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/interface_provider.mojom)
|
||||
for providing an implementation of an interface by name. It is implemented by
|
||||
different hosts (for frames and workers) in the browser, and the manifest
|
||||
allows to specify different sets of capabilities exposed by these hosts.
|
||||
|
||||
## File structure
|
||||
|
||||
### name (string)
|
||||
|
||||
A unique identifier that is used to refer to the service.
|
||||
|
||||
### display\_name (string)
|
||||
|
||||
A human-readable name which can have any descriptive value. Not user-visible.
|
||||
|
||||
### sandbox\_type (string)
|
||||
|
||||
An optional field that provides access to several types of sandboxes.
|
||||
Inside a sandbox, by default the service is essentially restricted to CPU and
|
||||
memory access only. Common values are:
|
||||
|
||||
* `utility` (default) - also allows full access to one configurable directory;
|
||||
* `none` - no sandboxing is applied;
|
||||
* `none_and_elevated` - under Windows, no sandboxing is applied and privileges
|
||||
are elevated.
|
||||
|
||||
If the service cannot run with a sandbox type of utility, elevated, or none,
|
||||
please reach out to the security team.
|
||||
|
||||
### options (dictionary)
|
||||
|
||||
This field can be used to specify values for any of the following options:
|
||||
|
||||
#### instance\_sharing (string)
|
||||
|
||||
Determines which parameters result in the creation of a new service
|
||||
instance on an incoming service start/interface bind request.
|
||||
|
||||
Possible values:
|
||||
|
||||
##### none (default)
|
||||
|
||||
When one service is connecting to another, checks are performed to either find
|
||||
an existing instance that matches the target identity or create a new one if
|
||||
no match is found.
|
||||
|
||||
By default, all four identity components (service name, instance group, instance
|
||||
ID, and globally unique ID) are used to find a match.
|
||||
|
||||
See
|
||||
[advice](https://chromium.googlesource.com/chromium/src/+/master/docs/servicification.md#is-your-service-global-or-per_browsercontext)
|
||||
on using this option.
|
||||
|
||||
Example:
|
||||
[identity](https://cs.chromium.org/chromium/src/services/identity/manifest.json)
|
||||
|
||||
##### shared\_across\_instance\_groups
|
||||
|
||||
In this case, the instance group parameter is ignored when looking for a
|
||||
matching target instance, so an existing instance can be reused regardless of
|
||||
which instance group the connecting service belongs to.
|
||||
|
||||
Example:
|
||||
[data_decoder](https://cs.chromium.org/chromium/src/services/data_decoder/manifest.json)
|
||||
|
||||
##### singleton
|
||||
|
||||
In this case, both instance group and instance ID parameters are ignored when
|
||||
an interface request is targeting the service. Only one service instance is
|
||||
created, and all interface requests targeting the service will be routed to that
|
||||
instance.
|
||||
|
||||
Example:
|
||||
[download_manager](https://cs.chromium.org/chromium/src/chrome/browser/android/download/manifest.json)
|
||||
|
||||
#### can\_connect\_to\_instances\_in\_any\_group (bool)
|
||||
|
||||
This option allows a service to make outgoing requests with a target instance
|
||||
group other than its own.
|
||||
|
||||
**Note:** this privilege must only be granted to services that are trusted
|
||||
at the same level as the browser process itself.
|
||||
|
||||
Example:
|
||||
[content_browser](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_browser_manifest.json)
|
||||
|
||||
The browser process manages all `BrowserContexts`, so it needs this privilege
|
||||
to act on behalf of different users.
|
||||
|
||||
#### can\_connect\_to\_other\_services\_with\_any\_instance\_name (bool)
|
||||
|
||||
This option allows a service to specify an instance ID that is
|
||||
different from the service name when connecting.
|
||||
|
||||
**Note:** this privilege must only be granted to services that are trusted
|
||||
at the same level as the browser process itself.
|
||||
|
||||
Example:
|
||||
[chrome_browser](https://cs.chromium.org/chromium/src/chrome/app/chrome_manifest.json)
|
||||
|
||||
Code in chrome_browser calls an XML parsing library function, which generates a
|
||||
random instance ID to
|
||||
[isolate unrelated decode operations](https://cs.chromium.org/chromium/src/services/data_decoder/public/cpp/safe_xml_parser.cc?l=50).
|
||||
|
||||
#### can\_create\_other\_service\_instances (bool)
|
||||
|
||||
This option allows a service to register arbitrary new service instances it
|
||||
creates on its own.
|
||||
|
||||
**Note:** this privilege must only be granted to services that are trusted
|
||||
at least at the same level as the Service Manager itself.
|
||||
|
||||
Example:
|
||||
[content_browser](https://cs.chromium.org/chromium/src/content/public/app/mojo/content_browser_manifest.json)
|
||||
|
||||
The browser manages render processes, and thus needs this privilege to manage
|
||||
the content_renderer instances on behalf of the service manager.
|
||||
|
||||
### interface\_provider\_specs (dictionary)
|
||||
|
||||
The interface provider spec is a dictionary keyed by interface provider
|
||||
name, with each value representing the capability spec for that
|
||||
provider.
|
||||
Each capability spec defines an optional "provides" key and an optional
|
||||
"requires" key.
|
||||
|
||||
Every interface provider spec (often exclusively) contains one standard
|
||||
capability spec named “service_manager:connector”. This is the
|
||||
capability spec enforced when inter-service connections are made from a
|
||||
service's `Connector` interface.
|
||||
|
||||
Some other examples of capability specs are things like "navigation:frame",
|
||||
which enforces capability specs for interfaces retrieved through a
|
||||
frame's `InterfaceProvider`.
|
||||
|
||||
See [README.md](README.md#service-manifests) for some examples.
|
||||
|
||||
**Note:** Since multiple interface provider support makes the manifest files
|
||||
harder to understand, there is a plan to simplify this section
|
||||
(see https://crbug.com/718652 for more details).
|
||||
|
||||
#### provides (dictionary)
|
||||
|
||||
This optional section specifies a set of capabilities provided by the service.
|
||||
A capability is a set of accessible interfaces.
|
||||
|
||||
For example, suppose we have the following capabilities:
|
||||
|
||||
* useful_capability
|
||||
* useful\_interface\_1
|
||||
* useful\_interface\_2
|
||||
* better\_capability
|
||||
* better\_interface
|
||||
|
||||
The `provides` section would be:
|
||||
``` json
|
||||
"provides": {
|
||||
"useful_capability": [
|
||||
"useful_interface_1",
|
||||
"useful_interface_2" ],
|
||||
"better_capability": [
|
||||
"better_interface" ],
|
||||
}
|
||||
```
|
||||
|
||||
#### requires (dictionary)
|
||||
|
||||
This optional section is also a dictionary, keyed by remote service
|
||||
names (the service name must match the "name" value in the remote service's
|
||||
manifest). Each value is a list of capabilities required by this service
|
||||
from the listed remote service. This section does not name interfaces,
|
||||
only capabilities.
|
||||
|
||||
For example, if our capability requires service "some_capability" from
|
||||
service "another_service", the "requires" section will look like this:
|
||||
|
||||
``` json
|
||||
"requires": {
|
||||
"another_service": [ "some_capability" ]
|
||||
```
|
||||
|
||||
An asterisk is a wildcard which means that any listed capabilities are
|
||||
required of any service that provides them. For example:
|
||||
|
||||
``` json
|
||||
"requires": {
|
||||
"*": [ "some_capability" ]
|
||||
```
|
||||
|
||||
In the above example, this service can access any interface provided as part
|
||||
of a capability named "some_capability" in any service on the system.
|
||||
|
||||
While generally discouraged, there are use cases for wildcards.
|
||||
Consider building something like a UI shell with a launcher that wants to
|
||||
tell any service "please launch with some default UI". The services providing
|
||||
a "launch" capability would be expected to include access to an
|
||||
"`app_launcher.mojom.Launcher`" interface as part of that capability, with an
|
||||
implementation that does something useful like open some default UI for the
|
||||
service:
|
||||
|
||||
``` json
|
||||
"provides": {
|
||||
"launch": [ "app_launcher.mojom.Launcher" ]
|
||||
}
|
||||
```
|
||||
|
||||
Then our app launcher service would expect to be able to bind
|
||||
`app_launcher.mojom.Launcher` in any service that provides that capability:
|
||||
|
||||
|
||||
``` json
|
||||
"requires": {
|
||||
"*" : [ "launch" ]
|
||||
}
|
||||
```
|
||||
|
||||
### required\_files (dictionary)
|
||||
|
||||
Allows the (sandboxed) service to specify a list of platform-specific files it
|
||||
needs to access from disk while running. Each file is keyed by an arbitrary
|
||||
name chosen by the service, and references a file path relative to the Service
|
||||
Manager embedder (e.g. the Chrome binary) at runtime.
|
||||
|
||||
Files specified here will be opened by the Service Manager before launching a
|
||||
new instance of this service, and their opened file descriptors will be passed
|
||||
to the new sandboxed process. The file descriptors may be accessed via
|
||||
`base::FileDescriptorStore` using the corresponding key string from the
|
||||
manifest.
|
||||
|
||||
**Note:** This is currently only supported on Android and Linux-based desktop
|
||||
builds.
|
||||
|
||||
#### path (string)
|
||||
|
||||
Path to the file relative to the executable location at runtime.
|
||||
|
||||
#### platform (string)
|
||||
|
||||
The platform this file is required on.
|
||||
Possible values:
|
||||
|
||||
* `linux`
|
||||
* `android`
|
Reference in New Issue
Block a user