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