Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame^] | 1 | # Intro to Mojo & Services |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | ## Overview |
| 6 | |
| 7 | This document contains the minimum amount of information needed for a developer |
| 8 | to start using Mojo effectively in Chromium, with example Mojo interface usage, |
| 9 | service definition and hookup, and a brief overview of the Content layer's core |
| 10 | services. |
| 11 | |
| 12 | See other [Mojo & Services](/docs/README.md#Mojo-Services) documentation |
| 13 | for introductory guides, API references, and more. |
| 14 | |
| 15 | ## Mojo Terminology |
| 16 | |
| 17 | A **message pipe** is a pair of **endpoints**. Each endpoint has a queue of |
| 18 | incoming messages, and writing a message at one endpoint effectively enqueues |
| 19 | that message on the other (**peer**) endpoint. Message pipes are thus |
| 20 | bidirectional. |
| 21 | |
| 22 | A **mojom** file describes **interfaces**, which are strongly-typed collections |
| 23 | of **messages**. Each interface message is roughly analogous to a single proto |
| 24 | message, for developers who are familiar with Google protobufs. |
| 25 | |
| 26 | Given a mojom interface and a message pipe, one of the endpoints |
| 27 | can be designated as an **InterfacePtr** and is used to *send* messages described by |
| 28 | the interface. The other endpoint can be designated as a **Binding** and is used |
| 29 | to *receive* interface messages. |
| 30 | |
| 31 | *** aside |
| 32 | NOTE: The above generalization is a bit oversimplified. Remember that the |
| 33 | message pipe is still bidirectional, and it's possible for a mojom message to |
| 34 | expect a reply. Replies are sent from the Binding endpoint and received by the |
| 35 | InterfacePtr endpoint. |
| 36 | *** |
| 37 | |
| 38 | The Binding endpoint must be associated with (*i.e.* **bound** to) an |
| 39 | **implementation** of its mojom interface in order to process received messages. |
| 40 | A received message is dispatched as a scheduled task invoking the corresponding |
| 41 | interface method on the implementation object. |
| 42 | |
| 43 | Another way to think about all this is simply that **an InterfacePtr makes |
| 44 | calls on a remote implementation of its interface associated with a |
| 45 | corresponding remote Binding.** |
| 46 | |
| 47 | ## Example: Defining a New Frame Interface |
| 48 | |
| 49 | Let's apply this to Chrome. Suppose we want to send a "Ping" message from a |
| 50 | render frame to its corresponding `RenderFrameHostImpl` instance in the browser |
| 51 | process. We need to define a nice mojom interface for this purpose, create a |
| 52 | pipe to use that interface, and then plumb one end of the pipe to the right |
| 53 | place so the sent messages can be received and processed there. This section |
| 54 | goes through that process in detail. |
| 55 | |
| 56 | ### Defining the Interface |
| 57 | |
| 58 | The first step involves creating a new `.mojom` file with an interface |
| 59 | definition, like so: |
| 60 | |
| 61 | ``` cpp |
| 62 | // src/example/public/mojom/ping_responder.mojom |
| 63 | module example.mojom; |
| 64 | |
| 65 | interface PingResponder { |
| 66 | // Receives a "Ping" and responds with a random integer. |
| 67 | Ping() => (int random); |
| 68 | }; |
| 69 | ``` |
| 70 | |
| 71 | This should have a corresponding build rule to generate C++ bindings for the |
| 72 | definition here: |
| 73 | |
| 74 | ``` python |
| 75 | # src/example/public/mojom/BUILD.gn |
| 76 | import "mojo/public/tools/bindings/mojom.gni" |
| 77 | mojom("mojom") { |
| 78 | sources = [ "ping_responder.mojom" ] |
| 79 | } |
| 80 | ``` |
| 81 | |
| 82 | ### Creating the Pipe |
| 83 | |
| 84 | Now let's create a message pipe to use this interface. |
| 85 | |
| 86 | *** aside |
| 87 | As a general rule and as a matter of convenience when |
| 88 | using Mojo, the *client* of an interface (*i.e.* the InterfacePtr side) is |
| 89 | typically the party who creates a new pipe. This is convenient because the |
| 90 | InterfacePtr may be used to start sending messages immediately without waiting |
| 91 | for the InterfaceRequest endpoint to be transferred or bound anywhere. |
| 92 | *** |
| 93 | |
| 94 | This code would be placed somewhere in the renderer: |
| 95 | |
| 96 | ```cpp |
| 97 | example::mojom::PingResponderPtr ping_responder; |
| 98 | example::mojom::PingResponderRequest request = |
| 99 | mojo::MakeRequest(&ping_responder); |
| 100 | ``` |
| 101 | |
| 102 | In this example, ```ping_responder``` is the InterfacePtr, and ```request``` |
| 103 | is an InterfaceRequest, which is a Binding precursor that will eventually |
| 104 | be turned into a Binding. `mojo::MakeRequest` is the most common way to create |
| 105 | a message pipe: it yields both endpoints as strongly-typed objects, with the |
| 106 | `InterfacePtr` as an output argument and the `InterfaceRequest` as the return |
| 107 | value. |
| 108 | |
| 109 | *** aside |
| 110 | NOTE: Every mojom interface `T` generates corresponding C++ type aliases |
| 111 | `TPtr = InterfacePtr<T>` and `TRequest = InterfaceRequest<T>`. Chromium code |
| 112 | almost exclusively uses these aliases instead of writing out the more verbose |
| 113 | templated name. |
| 114 | |
| 115 | Also note that an InterfaceRequest doesn't actually **do** anything. It is an |
| 116 | inert holder of a single message pipe endpoint. It exists only to make its |
| 117 | endpoint more strongly-typed at compile-time, indicating that the endpoint |
| 118 | expects to be bound by a Binding of the same interface type. |
| 119 | *** |
| 120 | |
| 121 | ### Sending a Message |
| 122 | |
| 123 | Finally, we can call the `Ping()` method on our InterfacePtr to send a message: |
| 124 | |
| 125 | ```cpp |
| 126 | ping_responder->Ping(base::BindOnce(&OnPong)); |
| 127 | ``` |
| 128 | |
| 129 | *** aside |
| 130 | **IMPORTANT:** If we want to receive the the response, we must keep the |
| 131 | `ping_responder` object alive until `OnPong` is invoked. After all, |
| 132 | `ping_responder` *owns* its message pipe endpoint. If it's destroyed then so is |
| 133 | the endpoint, and there will be nothing to receive the response message. |
| 134 | *** |
| 135 | |
| 136 | We're almost done! Of course, if everything were this easy, this document |
| 137 | wouldn't need to exist. We've taken the hard problem of sending a message from |
| 138 | a renderer process to the browser process, and transformed it into a problem |
| 139 | where we just need to take the `request` object from above and pass it to the |
| 140 | browser process somehow where it can be turned into a Binding that dispatches |
| 141 | its received messages. |
| 142 | |
| 143 | ### Sending an InterfaceRequest to the Browser |
| 144 | |
| 145 | It's worth noting that InterfaceRequests (and message pipe endpoints in general) |
| 146 | are just another type of object that can be freely sent over mojom messages. |
| 147 | The most common way to get an InterfaceRequest somewhere is to pass it as a |
| 148 | method argument on some other already-connected interface. |
| 149 | |
| 150 | One such interface which we always have connected between a renderer's |
| 151 | `RenderFrameImpl` and its corresponding `RenderFrameHostImpl` in the browser |
| 152 | is |
| 153 | [`DocumentInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/frame/document_interface_broker.mojom). |
| 154 | We can update this definition to add support for our new PingResponder |
| 155 | interface: |
| 156 | |
| 157 | ``` cpp |
| 158 | interface DocumentInterfaceBroker { |
| 159 | ... |
| 160 | |
| 161 | GetPingResponder(PingResponder& responder); |
| 162 | } |
| 163 | ``` |
| 164 | |
| 165 | The `&` syntax is not a reference! In mojom it denotes an InterfaceRequest. |
| 166 | Specifically in this case, the `GetPingResponder` takes a single |
| 167 | `PingResponderRequest` argument. If the `&` were omitted, this would instead |
| 168 | take a `PingResponderPtr`. |
| 169 | |
| 170 | Now the renderer can call this method with the `request` object it created |
| 171 | earlier via `mojo::MakeRequest`: |
| 172 | |
| 173 | ``` cpp |
| 174 | RenderFrame* my_frame = GetMyFrame(); |
| 175 | my_frame->GetDocumentInterfaceBroker()->GetPingResponder(std::move(request)); |
| 176 | ``` |
| 177 | |
| 178 | This will transfer the PingResponderRequest endpoint to the browser process |
| 179 | where it will be received by the corresponding `DocumentInterfaceBroker` |
| 180 | implementation. More on that below. |
| 181 | |
| 182 | ### Implementing the Interface |
| 183 | |
| 184 | Finally, we need a browser-side implementation of our `PingResponder` interface |
| 185 | as well as an implementation of the new |
| 186 | `DocumentInterfaceBroker.GetPingResponder` message. Let's implement |
| 187 | `PingResponder` first: |
| 188 | |
| 189 | ```cpp |
| 190 | #include "example/public/mojom/ping_responder.mojom.h" |
| 191 | |
| 192 | class PingResponderImpl : example::mojom::PingResponder { |
| 193 | public: |
| 194 | explicit PingResponderImpl(example::mojom::PingResponderRequest request) |
| 195 | : binding_(this, std::move(request)) {} |
| 196 | |
| 197 | // example::mojom::PingResponder: |
| 198 | void Ping(PingCallback callback) override { |
| 199 | // Respond with a random 4, chosen by fair dice roll. |
| 200 | std::move(callback).Run(4); |
| 201 | } |
| 202 | |
| 203 | private: |
| 204 | mojo::Binding<example::mojom::PingResponder> binding_; |
| 205 | |
| 206 | DISALLOW_COPY_AND_ASSIGN(PingResponderImpl); |
| 207 | }; |
| 208 | ``` |
| 209 | |
| 210 | And conveniently `RenderFrameHostImpl` implements `DocumentInterfaceBroker`, and |
| 211 | any calls made on the object returned by |
| 212 | `RenderFrameImpl::GetDocumentInterfaceBroker()' will be routed directly to the |
| 213 | `RenderFrameHostImpl`. So the only thing left to do is update |
| 214 | `RenderFrameHostImpl` to implement `GetPingResponder`. If you forget to do this |
| 215 | the compiler will complain anyway, because generated mojom interface methods are |
| 216 | pure virtual methods in C++. |
| 217 | |
| 218 | ``` cpp |
| 219 | // render_frame_host_impl.h |
| 220 | class RenderFrameHostImpl |
| 221 | ... |
| 222 | void GetPingResponder(example::mojom::PingResponderRequest request) override; |
| 223 | ... |
| 224 | private: |
| 225 | ... |
| 226 | std::unique_ptr<PingResponderImpl> ping_responder_; |
| 227 | ... |
| 228 | }; |
| 229 | |
| 230 | // render_frame_host_impl.cc |
| 231 | void RenderFrameHostImpl::GetPingResponder( |
| 232 | example::mojom::PingResponderRequest request) { |
| 233 | ping_responder_ = std::make_unique<PingResponderImpl>(std::move(request)); |
| 234 | } |
| 235 | ``` |
| 236 | |
| 237 | And we're done. This setup is sufficient to plumb a new interface connection |
| 238 | between a renderer frame and its browser-side host object! |
| 239 | |
| 240 | Assuming we kept our `ping_responder` object alive in the renderer long enough, |
| 241 | we would eventually see its `OnPong` callback invoked with the totally random |
| 242 | value of `4`, as defined by the browser-side implementation above. |
| 243 | |
| 244 | ## Services Overview & Terminology |
| 245 | The previous section only scratches the surface of how Mojo IPC is used in |
| 246 | Chromium. While renderer-to-browser messaging is simple and possibly the most |
| 247 | prevalent usage by sheer code volume, we are incrementally decomposing the |
| 248 | codebase into a set of services with a bit more granularity than the traditional |
| 249 | Content browser/renderer/gpu/utility process split. |
| 250 | |
| 251 | A **service** is a self-contained library of code which implements one or more |
| 252 | related features or behaviors and whose interaction with outside code is done |
| 253 | *exclusively* through Mojo interface connections facilitated by the **Service |
| 254 | Manager.** |
| 255 | |
| 256 | The **Service Manager** is a component which can run in a dedicated process |
| 257 | or embedded within another process. Only one Service Manager exists globally |
| 258 | across the system, and in Chromium the browser process runs an embedded Service |
| 259 | Manager instance immediately on startup. The Service Manager spawns |
| 260 | **service instances** on-demand, and it routes each interface request from a |
| 261 | service instance to some destination instance of the Service Manager's choosing. |
| 262 | |
| 263 | Each service instance implements the |
| 264 | [**`Service`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h) |
| 265 | interface to receive incoming interface requests brokered by the Service |
| 266 | Manager, and each service instance has a |
| 267 | [**`Connector`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h) |
| 268 | it can use to issue interface requests to other services via the |
| 269 | Service Manager. |
| 270 | |
| 271 | Every service has a **manifest** which declares some static metadata about the |
| 272 | service. This metadata is used by the Service Manager for various purposes, |
| 273 | including as a declaration of what interfaces are exposed to other services in |
| 274 | the system. This eases the security review process. |
| 275 | |
| 276 | Inside its manifest every service declares its **service name**, used to |
| 277 | identify instances of the service in the most general sense. Names are free-form |
| 278 | and usually short strings which must be globally unique. Some services defined |
| 279 | in Chromium today include `"device"`, `"identity"`, and `"network"` services. |
| 280 | |
| 281 | For more complete and in-depth coverage of the concepts covered here and other |
| 282 | related APIs, see the |
| 283 | [Service Manager documentation](/services/service_manager/README.md). |
| 284 | |
| 285 | ## Example: Building a Simple Out-of-Process Service |
| 286 | |
| 287 | There are multiple steps required to get a new service up and running in |
| 288 | Chromium. You must: |
| 289 | |
| 290 | - Define the `Service` implementation |
| 291 | - Define the service's manifest |
| 292 | - Tell Chromium's Service Manager about the manifest |
| 293 | - Tell Chromium how to instantiate the `Service` implementation when it's needed |
| 294 | |
| 295 | This section walks through these steps with some brief explanations. For more |
| 296 | thorough documentation of the concepts and APIs used herein, see the |
| 297 | [Service Manager](/services/service_manager/README.md) and |
| 298 | [Mojo](/mojo/README.md) documentation. |
| 299 | |
| 300 | ### Defining the Service |
| 301 | |
| 302 | Typically service definitions are placed in a `services` directory, either at |
| 303 | the top level of the tree or within some subdirectory. In this example, we'll |
| 304 | define a new service for use by Chrome specifically, so we'll define it within |
| 305 | `//chrome/services`. |
| 306 | |
| 307 | We can create the following files. First some mojoms: |
| 308 | |
| 309 | ``` cpp |
| 310 | // src/chrome/services/math/public/mojom/constants.mojom |
| 311 | module math.mojom; |
| 312 | |
| 313 | // These are not used by the implementation directly, but will be used in |
| 314 | // following sections. |
| 315 | const string kServiceName = "math"; |
| 316 | const string kArithmeticCapability = "arithmetic"; |
| 317 | ``` |
| 318 | |
| 319 | ``` cpp |
| 320 | // src/chrome/services/math/public/mojom/divider.mojom |
| 321 | module math.mojom; |
| 322 | |
| 323 | interface Divider { |
| 324 | Divide(int32 dividend, int32 divisor) => (int32 quotient); |
| 325 | }; |
| 326 | ``` |
| 327 | |
| 328 | ``` python |
| 329 | # src/chrome/services/math/public/mojom/BUILD.gn |
| 330 | import "mojo/public/tools/bindings/mojom.gni" |
| 331 | |
| 332 | mojom("mojom") { |
| 333 | sources = [ |
| 334 | "constants.mojom", |
| 335 | "divider.mojom", |
| 336 | ] |
| 337 | } |
| 338 | ``` |
| 339 | |
| 340 | Then the actual `Service` implementation: |
| 341 | |
| 342 | ``` cpp |
| 343 | // src/chrome/services/math/math_service.h |
| 344 | #include "services/service_manager/public/cpp/service.h" |
| 345 | |
| 346 | #include "base/macros.h" |
| 347 | #include "chrome/services/math/public/mojom/divider.mojom.h" |
| 348 | |
| 349 | namespace math { |
| 350 | |
| 351 | class MathService : public service_manager::Service, |
| 352 | public mojom::Divider { |
| 353 | public: |
| 354 | explicit MathService(service_manager::mojom::ServiceRequest request); |
| 355 | ~MathService() override; |
| 356 | |
| 357 | private: |
| 358 | // service_manager::Service: |
| 359 | void OnBindInterface(const service_manager::BindSourceInfo& source, |
| 360 | const std::string& interface_name, |
| 361 | mojo::ScopedMessagePipeHandle interface_pipe) override; |
| 362 | |
| 363 | // mojom::Divider: |
| 364 | void Divide(int32_t dividend, |
| 365 | int32_t divisor, |
| 366 | DivideCallback callback) override; |
| 367 | |
| 368 | service_manager::ServiceBinding service_binding_; |
| 369 | |
| 370 | // You could also use a Binding. We use BindingSet to conveniently allow |
| 371 | // multiple clients to bind to the same instance of this class. See Mojo |
| 372 | // C++ Bindings documentation for more information. |
| 373 | mojo::BindingSet<mojom::Divider> divider_bindings_; |
| 374 | |
| 375 | DISALLOW_COPY_AND_ASSIGN(MathService); |
| 376 | }; |
| 377 | |
| 378 | } // namespace math |
| 379 | ``` |
| 380 | |
| 381 | ``` cpp |
| 382 | // src/chrome/services/math/math_service.cc |
| 383 | #include "chrome/services/math/math_service.h" |
| 384 | |
| 385 | namespace math { |
| 386 | |
| 387 | MathService::MathService(service_manager::ServiceRequest request) |
| 388 | : service_binding_(this, std::move(request)) {} |
| 389 | |
| 390 | MathService::~MathService() = default; |
| 391 | |
| 392 | void MathService::OnBindInterface( |
| 393 | const service_manager::BindSourceInfo& source, |
| 394 | const std::string& interface_name, |
| 395 | mojo::ScopedMessagePipeHandle interface_pipe) { |
| 396 | // Note that services typically use a service_manager::BinderRegistry if they |
| 397 | // plan on handling many different interface request types. |
| 398 | if (interface_name == mojom::Divider::Name_) { |
| 399 | divider_bindings_.AddBinding( |
| 400 | this, mojom::DividerRequest(std::move(interface_pipe))); |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | void MathService::Divide(int32_t dividend, |
| 405 | int32_t divisor, |
| 406 | DivideCallback callback) { |
| 407 | // Respond with the quotient! |
| 408 | callback.Run(dividend / divisor); |
| 409 | } |
| 410 | |
| 411 | } // namespace math |
| 412 | ``` |
| 413 | |
| 414 | ``` python |
| 415 | # src/chrome/services/math/BUILD.gn |
| 416 | |
| 417 | source_set("math") { |
| 418 | sources = [ |
| 419 | "math.cc", |
| 420 | "math.h", |
| 421 | ] |
| 422 | |
| 423 | deps = [ |
| 424 | "//base", |
| 425 | "//chrome/services/math/public/mojom", |
| 426 | "//services/service_manager/public/cpp", |
| 427 | ] |
| 428 | } |
| 429 | ``` |
| 430 | |
| 431 | Now we have a fully defined `math` service implementation, including a nice |
| 432 | little `Divider` interface for clients to play with. Next we need to define the |
| 433 | service's manifest to declare how the service can be used. |
| 434 | |
| 435 | ### Defining the Manifest |
| 436 | Manifests are defined as |
| 437 | [`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h) |
| 438 | objects, typically built using a |
| 439 | [`ManifestBuilder`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h). As a general rule, services should define their manifest |
| 440 | in a dedicated `source_set` or `component` target under their `public/cpp` |
| 441 | subdirectory (typically referred to as the service's **C++ client library**). |
| 442 | |
| 443 | We can create the following files for this purpose: |
| 444 | |
| 445 | ``` cpp |
| 446 | // src/chrome/services/math/public/cpp/manifest.h |
| 447 | #include "services/service_manager/public/cpp/manifest.h" |
| 448 | |
| 449 | namespace math { |
| 450 | |
| 451 | const service_manager::Manifest& GetManifest(); |
| 452 | |
| 453 | } // namespace math |
| 454 | ``` |
| 455 | |
| 456 | ``` cpp |
| 457 | // src/chrome/services/math/public/cpp/manifest.cc |
| 458 | #include "chrome/services/math/public/cpp/manifest.h" |
| 459 | |
| 460 | #include "base/no_destructor.h" |
| 461 | #include "chrome/services/math/public/mojom/constants.mojom.h" |
| 462 | #include "chrome/services/math/public/mojom/divider.mojom.h" |
| 463 | #include "services/service_manager/public/cpp/manifest_builder.h" |
| 464 | |
| 465 | namespace math { |
| 466 | |
| 467 | const service_manager::Manifest& GetManifest() { |
| 468 | static base::NoDestructor<service_manager::Manifest> manifest{ |
| 469 | service_manager::ManifestBuilder() |
| 470 | .WithServiceName(mojom::kServiceName) |
| 471 | .ExposeCapability( |
| 472 | mojom::kArithmeticCapability, |
| 473 | service_manager::Manifest::InterfaceList<mojom::Divider>()) |
| 474 | .Build()}; |
| 475 | return *manifest |
| 476 | } |
| 477 | |
| 478 | } // namespace math |
| 479 | ``` |
| 480 | |
| 481 | We also need to define a build target for our manifest sources: |
| 482 | |
| 483 | ``` python |
| 484 | # src/chrome/services/math/public/cpp/BUILD.gn |
| 485 | |
| 486 | source_set("manifest") { |
| 487 | sources = [ |
| 488 | "manifest.cc", |
| 489 | "manifest.h", |
| 490 | ] |
| 491 | |
| 492 | deps = [ |
| 493 | "//base", |
| 494 | "//chrome/services/math/public/mojom", |
| 495 | "//services/service_manager/public/cpp", |
| 496 | ] |
| 497 | } |
| 498 | ``` |
| 499 | |
| 500 | The above `Manifest` definition declares that the service is named `math` and |
| 501 | that it **exposes** a single **capability** named `arithmetic` which allows |
| 502 | access to the `Divider` interface. |
| 503 | |
| 504 | Another service may **require** this capability from its own manifest in order |
| 505 | for the Service Manager to grant it access to a `Divider`. We'll see this a |
| 506 | few sections below. First, let's get the manifest and service implementation |
| 507 | registered with Chromium's Service Manager. |
| 508 | |
| 509 | ### Registering the Manifest |
| 510 | |
| 511 | For the most common out-of-process service cases, we register service manifests |
| 512 | by **packaging** them in Chrome. This can be done by augmenting the value |
| 513 | returned by |
| 514 | [`GetChromePackagedServiceManifests`](https://cs.chromium.org/chromium/src/chrome/app/chrome_packaged_service_manifests.cc?rcl=af43cabf3c01e28be437becb972a7eae44fd54e8&l=133). |
| 515 | |
| 516 | We can add our manifest there: |
| 517 | |
| 518 | ``` cpp |
| 519 | // Deep within src/chrome/app/chrome_packaged_service_manifests.cc... |
| 520 | const std::vector<service_manager::Manifest> |
| 521 | GetChromePackagedServiceManifests() { |
| 522 | ... |
| 523 | math::GetManifest(), |
| 524 | ... |
| 525 | ``` |
| 526 | |
| 527 | And don't forget to add a GN dependency from |
| 528 | `//chrome/app:packaged_service_manifests` onto |
| 529 | `//chrome/services/math/public/cpp:manifest`! |
| 530 | |
| 531 | We're almost done with service setup. The last step is to teach Chromium (and |
| 532 | thus the Service Manager) how to launch an instance of our beautiful `math` |
| 533 | service. |
| 534 | |
| 535 | ### Hooking Up the Service Implementation |
| 536 | |
| 537 | There are two parts to this for an out-of-process Chrome service. |
| 538 | |
| 539 | First, we need |
| 540 | to inform the embedded Service Manager that this service is an out-of-process |
| 541 | service. The goofiness of this part is a product of some legacy issues and it |
| 542 | should be eliminated soon, but for now it just means teaching the Service |
| 543 | Manager how to *label* the process it creates for this service (e.g. how the process will |
| 544 | appear in the system task manager). We modify |
| 545 | [`ChromeContentBrowserClient::RegisterOutOfProcessServices`](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=960886a7febcc2acccea7f797d3d5e03a344a12c&l=3766) |
| 546 | for this: |
| 547 | |
| 548 | ``` cpp |
| 549 | void ChromeContentBrowserClient::RegisterOutOfProcessServices( |
| 550 | OutOfProcessServicesMap* services) { |
| 551 | ... |
| 552 | |
| 553 | (*services)[math::mojom::kServiceName] = |
| 554 | base::BindRepeating([]() -> base::string16 { |
| 555 | return "Math Service"; |
| 556 | }); |
| 557 | |
| 558 | ... |
| 559 | } |
| 560 | ``` |
| 561 | |
| 562 | And finally, since nearly all out-of-process services run in a "utility" process |
| 563 | today, we need to add a dependency on our actual `Service` implementation to |
| 564 | Chrome's service spawning code within the utility process. |
| 565 | |
| 566 | For this step we just modify |
| 567 | [`ChromeContentUtilityClient::MaybeCreateMainThreadService`](https://cs.chromium.org/chromium/src/chrome/utility/chrome_content_utility_client.cc?rcl=7226adebd6e8d077d673a82acf1aab0790627178&l=261) |
| 568 | by adding a block of code as follows: |
| 569 | |
| 570 | ``` cpp |
| 571 | void ChromeContentUtilityClient::MaybeCreateMainThreadService( |
| 572 | const std::string& service_name, |
| 573 | service_manager::mojom::ServiceRequest request) { |
| 574 | ... |
| 575 | |
| 576 | if (service_name == math::mojom::kServiceName) |
| 577 | return std::make_unique<math::MathService>(std::move(request)); |
| 578 | |
| 579 | ... |
| 580 | } |
| 581 | ``` |
| 582 | |
| 583 | And we're done! |
| 584 | |
| 585 | As one nice follow-up step, let's use our math service from the browser. |
| 586 | |
| 587 | ### Using the Service |
| 588 | |
| 589 | We can grant the browser process access to our `Divider` interface by |
| 590 | **requiring** the `math` service's `arithmetic` capability within the |
| 591 | `content_browser` service manifest. |
| 592 | |
| 593 | *** aside |
| 594 | NOTE: See the following section for an elaboration on what `content_browser` is. |
| 595 | For the sake of this example, it's magic. |
| 596 | *** |
| 597 | |
| 598 | For Chrome-specific features such as our glorious new `math` service, we can |
| 599 | amend the `content_browser` manifest by modifying |
| 600 | [GetChromeContentBrowserOverlayManifest](https://cs.chromium.org/chromium/src/chrome/app/chrome_content_browser_overlay_manifest.cc?rcl=38db90321e8e3627b2f3165cdb051fa8d668af48&l=100) |
| 601 | as follows: |
| 602 | |
| 603 | ``` cpp |
| 604 | // src/chrome/app/chrome_content_browser_overlay_manifest.cc |
| 605 | |
| 606 | ... |
| 607 | const service_manager::Manifest& GetChromeContentBrowserOverlayManifest() { |
| 608 | ... |
| 609 | .RequireCapability(math::mojom::kServiceName, |
| 610 | math::mojom::kArithmeticCapability) |
| 611 | ... |
| 612 | } |
| 613 | ``` |
| 614 | |
| 615 | Finally, we can use the global `content_browser` instance's `Connector` to send |
| 616 | an interface request to our service. This is accessible from the main thread of |
| 617 | the browser process. Somewhere in `src/chrome/browser`, we can write: |
| 618 | |
| 619 | ``` cpp |
| 620 | // This gives us the global content_browser's Connector |
| 621 | service_manager::Connector* connector = |
| 622 | content::ServiceManagerConnection::GetForProcess()->GetConnector(); |
| 623 | |
| 624 | // Recall from the earlier Mojo section that mojo::MakeRequest creates a new |
| 625 | // message pipe for our interface. Connector passes the request endpoint to |
| 626 | // the Service Manager along with the name of our target service, "math". |
| 627 | math::mojom::DividerPtr divider; |
| 628 | connector->BindInterface(math::mojom::kServiceName, |
| 629 | mojo::MakeRequest(÷r)); |
| 630 | |
| 631 | // As a client, we do not have to wait for any acknowledgement or confirmation |
| 632 | // of a connection. We can start queueing messages immediately and they will be |
| 633 | // delivered as soon as the service is up and running. |
| 634 | divider->Divide( |
| 635 | 42, 6, base::BindOnce([](int32_t quotient) { LOG(INFO) << quotient; })); |
| 636 | ``` |
| 637 | |
| 638 | This should successfully spawn a new process to run the `math` service if it's |
| 639 | not already running, then ask it to do a division, and ultimately log the result |
| 640 | after it's sent back to the browser process. |
| 641 | |
| 642 | Finally it's worth reiterating that every service instance in the system has |
| 643 | its own `Connector` and there's no reason we have to limit ourselves to |
| 644 | `content_browser` as the client, as long as the appropriate manifest declares |
| 645 | that it requires our `arithmetic` capability. |
| 646 | |
| 647 | If we did not update the `content_browser` manifest overlay as we did in this |
| 648 | example, the `Divide` call would never reach the `math` service (in fact the |
| 649 | service wouldn't even be started) and instead we'd get an error message (or in |
| 650 | developer builds, an assertion failure) informing us that the Service Manager |
| 651 | blocked the `BindInterface` call. |
| 652 | |
| 653 | ## Content-Layer Services Overview |
| 654 | |
| 655 | Apart from very early initialization steps in the browser process, every bit of |
| 656 | logic in Chromium today is effectively running as part of one service instance |
| 657 | or another. |
| 658 | |
| 659 | Although we continue to migrate parts of the browser's privileged |
| 660 | functionality to more granular services defined below the Content layer, the |
| 661 | main services defined in Chromium today continue to model the Content layer's |
| 662 | classical multiprocess architecture which defines a handful of |
| 663 | **process types**: browser, renderer, gpu, utility, and plugin processes. For |
| 664 | each of these process types, we now define corresponding services. |
| 665 | |
| 666 | Manifest definitions for all of the following services can be found in |
| 667 | `//content/public/app`. |
| 668 | |
| 669 | ### The Browser Service |
| 670 | |
| 671 | `content_browser` is defined to encapsulate general-purpose browser process |
| 672 | code. There are multiple instances of this service, all running within the |
| 673 | singular browser process. There is one shared global instance as well an |
| 674 | additional instance for each `BrowserContext` (*i.e.* per Chrome profile). |
| 675 | |
| 676 | The global instance exists primarily so that arbitrary browser process code can |
| 677 | reach various system services conveniently via a global `Connector` instance |
| 678 | on the main thread. |
| 679 | |
| 680 | Each instance associated with a `BrowserContext` is placed in an isolated |
| 681 | instance group specific to that `BrowserContext`. This limits the service |
| 682 | instances with which its `Connector` can make contact. These instances are |
| 683 | used primarily to facilitate the spawning of other isolated per-profile service |
| 684 | instances, such as renderers and plugins. |
| 685 | |
| 686 | ### The Renderer Service |
| 687 | |
| 688 | A `content_renderer` instance is spawned in its own sandboxed process for every |
| 689 | site-isolated instance of Blink we require. Instances are placed in the same |
| 690 | instance group as the renderer's corresponding `BrowserContext`, *i.e.* the |
| 691 | profile which navigated to the site being rendered. |
| 692 | |
| 693 | Most interfaces used by `content_renderer` are not brokered through the Service |
| 694 | Manager but instead are brokered through dedicated interfaces implemented by |
| 695 | `content_browser`, with which each renderer maintains persistent connections. |
| 696 | |
| 697 | ### The GPU Service |
| 698 | |
| 699 | Only a single instance of `content_gpu` exists at a time and it always runs in |
| 700 | its own isolated, sandboxed process. This service hosts the code in content/gpu |
| 701 | and whatever else Content's embedder adds to that for GPU support. |
| 702 | |
| 703 | ### The Plugin Service |
| 704 | |
| 705 | `content_plugin` hosts a plugin in an isolated process. Similarly to |
| 706 | `content_renderer` instances, each instance of `content_plugin` belongs to |
| 707 | an instance group associated with a specific `BrowserContext`, and in general |
| 708 | plugins get most of their functionality by talking directly to `content_browser` |
| 709 | rather than brokering interface requests through the Service Manager. |
| 710 | |
| 711 | ### The Utility Service |
| 712 | |
| 713 | `content_utility` exists only nominally today, as there is no remaining API |
| 714 | surface within Content which would allow a caller to explicitly create an |
| 715 | instance of it. Instead, this service is used exclusively to bootstrap new |
| 716 | isolated processes in which other services will run. |
| 717 | |
| 718 | ## Exposing Interfaces Between Content Processes |
| 719 | |
| 720 | Apart from the standard Service Manager APIs, the Content layer defines a number |
| 721 | of additional concepts for Content and its embedder to expose interfaces |
| 722 | specifically between Content processes in various contexts. |
| 723 | |
| 724 | ### Exposing Browser Interfaces to Renderer Documents and Workers |
| 725 | |
| 726 | Documents and workers are somewhat of a special case since interface access |
| 727 | decisions often require browser-centric state that the Service Manager cannot |
| 728 | know about, such as details of the current `BrowserContext`, the origin of the |
| 729 | renderered content, installed extensions in the renderer, *etc.* For this |
| 730 | reason, interface brokering decisions are increasingly being made by the |
| 731 | browser. |
| 732 | |
| 733 | There are two ways this is done: the Deprecated way and the New way. |
| 734 | |
| 735 | #### The Deprecated Way: InterfaceProvider |
| 736 | |
| 737 | This is built on the concept of **interface filters** and the |
| 738 | **`InterfaceProvider`** interface. It is **deprecated** and new features should |
| 739 | use [The New Way](#The-New-Way_Interface-Brokers) instead. This section only |
| 740 | briefly covers practical usage in Chromium. |
| 741 | |
| 742 | The `content_browser` manifest exposes capabilities on a few named interface |
| 743 | filters, the main one being `"navigation:frame"`. There are others scoped to |
| 744 | different worker contexts, *e.g.* `"navigation:service_worker"`. |
| 745 | `RenderProcessHostImpl` or `RenderFrameHostImpl` sets up an `InterfaceProvider` |
| 746 | for each known execution context in the corresponding renderer, filtered through |
| 747 | the Service Manager according to one of the named filters. |
| 748 | |
| 749 | The practical result of all this means the interface must be listed in the |
| 750 | `content_browser` manifest under the |
| 751 | `ExposeInterfaceFilterCapability_Deprecated("navigation:frame", "renderer", ...)` |
| 752 | entry, and a corresponding interface request handler must be registered with the |
| 753 | host's `registry_` in |
| 754 | [`RenderFrameHostImpl::RegisterMojoInterfaces`](https://cs.chromium.org/chromium/src/content/browser/frame_host/render_frame_host_impl.cc?rcl=0a23c78c57ecb2405837155aa0a0def7b5ba9c22&l=3971) |
| 755 | |
| 756 | Similarly for worker contexts, an interface must be exposed by the `"renderer"` |
| 757 | capability on the corresponding interface filter |
| 758 | (*e.g.*, `"navigation:shared_worker"`) and a request handler must be registered |
| 759 | within |
| 760 | [`RendererInterfaceBinders::InitializeParameterizedBinderRegistry`](https://cs.chromium.org/chromium/src/content/browser/renderer_interface_binders.cc?rcl=0a23c78c57ecb2405837155aa0a0def7b5ba9c22&l=116). |
| 761 | |
| 762 | The best way to understand all of this after reading this section is to look at |
| 763 | the linked code above and examine a few examples. They are fairly repetitive. |
| 764 | For additional convenience, here is also a link to the `content_browser` |
| 765 | [manifest](https://cs.chromium.org/chromium/src/content/public/app/content_browser_manifest.cc). |
| 766 | |
| 767 | #### The New Way: Interface Brokers |
| 768 | |
| 769 | *** aside |
| 770 | In classic Google tradition, the New Way is not entirely ready yet. As of this |
| 771 | writing, worker-scoped interfaces must still use the Old Way described above. |
| 772 | *** |
| 773 | |
| 774 | Rather than the confusing spaghetti of interface filter logic, we now define an |
| 775 | explicit mojom interface with a persistent connection between a renderer's |
| 776 | frame object and the corresponding `RenderFrameHostImpl` in the browser process. |
| 777 | This interface is called |
| 778 | [`DocumentInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/frame/document_interface_broker.mojom?rcl=ea6921f717f21e9a72d321a15c4bf50d47d10310&l=11) |
| 779 | and is fairly easy to work with: you simply add a new factory method to the |
| 780 | interface definition: |
| 781 | |
| 782 | ``` cpp |
| 783 | interface DocumentInterfaceBroker { |
| 784 | ... |
| 785 | |
| 786 | GetGoatTeleporter(magic.mojom.GoatTeleporter& request); |
| 787 | }; |
| 788 | ``` |
| 789 | |
| 790 | and implement this new method on `RenderFrameHostImpl`, which is an |
| 791 | implementation (**the** production implementation) of |
| 792 | `DocumentInterfaceBroker`: |
| 793 | |
| 794 | ``` cpp |
| 795 | void RenderFrameHostImpl::GetGoatTeleporter( |
| 796 | magic::mojom::GoatTeleporterRequest request) { |
| 797 | goat_teleporter_binding_.Bind(std::move(request)); |
| 798 | } |
| 799 | ``` |
| 800 | |
| 801 | ### Exposing Browser Interfaces to Render Processes |
| 802 | |
| 803 | Sometimes (albeit rarely) it's useful to expose a browser interface directly to |
| 804 | a renderer process. This can be done as for any other interface exposed between |
| 805 | two services. In this specific instance, the `content_browser` manifest exposes |
| 806 | a capability named `"renderer"` which `content_renderer` requires. Any interface |
| 807 | listed as part of that capability can be accessed by a `content_renderer` |
| 808 | instance by using its own `Connector`. See below. |
| 809 | |
| 810 | ### Exposing Browser Interfaces to Content Child Processes |
| 811 | |
| 812 | All Content child process types (renderer, GPU, and plugin) share a common API |
| 813 | to interface with the Service Manager. Their Service Manager connection is |
| 814 | initialized and maintained by `ChildThreadImpl` on process startup, and from |
| 815 | the main thread, you can access the process's `Connector` as follows: |
| 816 | |
| 817 | ``` cpp |
| 818 | auto* connector = content::ChildThread::Get()->GetConnector(); |
| 819 | |
| 820 | // For example... |
| 821 | connector->BindInterface(content::mojom::kBrowserServiceName, |
| 822 | std::move(some_request)); |
| 823 | ``` |
| 824 | |
| 825 | ### Exposing Content Child Process Interfaces to the Browser |
| 826 | |
| 827 | Content child processes may also expose interfaces to the browser, though this |
| 828 | is much less common and requires a fair bit of caution since the browser must be |
| 829 | careful to only call `Connector.BindInterface` in these cases with an exact |
| 830 | `service_manager::Identity` to avoid unexpected behavior. |
| 831 | |
| 832 | Every child process provides a subclass of ChildThreadImpl, and this can be used |
| 833 | to install a new `ConnectionFilter` on the process's Service Manager connection |
| 834 | before starting to accept requests. |
| 835 | |
| 836 | This behavior should really be considered deprecated, but for posterity, here is |
| 837 | how the GPU process does it: |
| 838 | |
| 839 | 1. [Disable Service Manager connection auto-start](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=62) |
| 840 | 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) |
| 841 | 3. [Start the Service Manager connection manually](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=257) |
| 842 | |
| 843 | It's much more common instead for there to be some primordial interface |
| 844 | connection established by the child process which can then be used to facilitate |
| 845 | push communications from the browser, so please consider not duplicating this |
| 846 | behavior. |
| 847 | |
| 848 | ## Additional Support |
| 849 | |
| 850 | If this document was not helpful in some way, please post a message to your |
| 851 | friendly |
| 852 | [chromium-mojo@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) |
| 853 | or |
| 854 | [services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) |
| 855 | mailing list. |