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 |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [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 |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [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 | |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [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 | |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [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 |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [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 |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [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 |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [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 |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 97 | // src/third_party/blink/example/public/ping_responder.h |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 98 | mojo::Remote<example::mojom::PingResponder> ping_responder; |
| 99 | mojo::PendingReceiver<example::mojom::PingResponder> receiver = |
| 100 | ping_responder.BindNewPipeAndPassReceiver(); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 101 | ``` |
| 102 | |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 103 | In this example, ```ping_responder``` is the `Remote`, and ```receiver``` |
| 104 | is a `PendingReceiver`, which is a `Receiver` precursor that will eventually |
| 105 | be turned into a `Receiver`. `BindNewPipeAndPassReceiver` is the most common way to create |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 106 | a message pipe: it yields the `PendingReceiver` as the return |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 107 | value. |
| 108 | |
| 109 | *** aside |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 110 | NOTE: A `PendingReceiver` doesn't actually **do** anything. It is an |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 111 | inert holder of a single message pipe endpoint. It exists only to make its |
| 112 | endpoint more strongly-typed at compile-time, indicating that the endpoint |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 113 | expects to be bound by a `Receiver` of the same interface type. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 114 | *** |
| 115 | |
| 116 | ### Sending a Message |
| 117 | |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 118 | 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] | 119 | |
| 120 | ```cpp |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 121 | // src/third_party/blink/example/public/ping_responder.h |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 122 | ping_responder->Ping(base::BindOnce(&OnPong)); |
| 123 | ``` |
| 124 | |
| 125 | *** aside |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 126 | **IMPORTANT:** If we want to receive the response, we must keep the |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 127 | `ping_responder` object alive until `OnPong` is invoked. After all, |
| 128 | `ping_responder` *owns* its message pipe endpoint. If it's destroyed then so is |
| 129 | the endpoint, and there will be nothing to receive the response message. |
| 130 | *** |
| 131 | |
| 132 | We're almost done! Of course, if everything were this easy, this document |
| 133 | wouldn't need to exist. We've taken the hard problem of sending a message from |
| 134 | a renderer process to the browser process, and transformed it into a problem |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 135 | where we just need to take the `receiver` object from above and pass it to the |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 136 | browser process somehow where it can be turned into a `Receiver` that dispatches |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 137 | its received messages. |
| 138 | |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 139 | ### Sending a `PendingReceiver` to the Browser |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 140 | |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 141 | It's worth noting that `PendingReceiver`s (and message pipe endpoints in general) |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 142 | are just another type of object that can be freely sent over mojom messages. |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 143 | 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] | 144 | method argument on some other already-connected interface. |
| 145 | |
| 146 | One such interface which we always have connected between a renderer's |
| 147 | `RenderFrameImpl` and its corresponding `RenderFrameHostImpl` in the browser |
| 148 | is |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 149 | [`BrowserInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/browser_interface_broker.mojom). |
| 150 | This interface is a factory for acquiring other interfaces. Its `GetInterface` |
| 151 | method takes a `GenericPendingReceiver`, which allows passing arbitrary |
| 152 | interface receivers. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 153 | |
| 154 | ``` cpp |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 155 | interface BrowserInterfaceBroker { |
| 156 | GetInterface(mojo_base.mojom.GenericPendingReceiver receiver); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 157 | } |
| 158 | ``` |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 159 | Since `GenericPendingReceiver` can be implicitly constructed from any specific |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 160 | `PendingReceiver`, it can call this method with the `receiver` object it created |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 161 | earlier via `BindNewPipeAndPassReceiver`: |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 162 | |
| 163 | ``` cpp |
| 164 | RenderFrame* my_frame = GetMyFrame(); |
Oksana Zhuravlova | d4f1f5c | 2019-11-14 05:57:11 | [diff] [blame] | 165 | my_frame->GetBrowserInterfaceBroker().GetInterface(std::move(receiver)); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 166 | ``` |
| 167 | |
Darwin Huang | b4bd245 | 2019-10-08 22:56:04 | [diff] [blame] | 168 | This will transfer the `PendingReceiver` endpoint to the browser process |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 169 | where it will be received by the corresponding `BrowserInterfaceBroker` |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 170 | implementation. More on that below. |
| 171 | |
| 172 | ### Implementing the Interface |
| 173 | |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 174 | Finally, we need a browser-side implementation of our `PingResponder` interface. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 175 | |
| 176 | ```cpp |
| 177 | #include "example/public/mojom/ping_responder.mojom.h" |
| 178 | |
| 179 | class PingResponderImpl : example::mojom::PingResponder { |
| 180 | public: |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 181 | explicit PingResponderImpl(mojo::PendingReceiver<example::mojom::PingResponder> receiver) |
| 182 | : receiver_(this, std::move(receiver)) {} |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 183 | |
| 184 | // example::mojom::PingResponder: |
| 185 | void Ping(PingCallback callback) override { |
| 186 | // Respond with a random 4, chosen by fair dice roll. |
| 187 | std::move(callback).Run(4); |
| 188 | } |
| 189 | |
| 190 | private: |
Charlie Hu | d4c0fe8 | 2019-10-08 19:48:13 | [diff] [blame] | 191 | mojo::Receiver<example::mojom::PingResponder> receiver_; |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 192 | |
| 193 | DISALLOW_COPY_AND_ASSIGN(PingResponderImpl); |
| 194 | }; |
| 195 | ``` |
| 196 | |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 197 | `RenderFrameHostImpl` owns an implementation of `BrowserInterfaceBroker`. |
| 198 | When this implementation receives a `GetInterface` method call, it calls |
| 199 | the handler previously registered for this specific interface. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 200 | |
| 201 | ``` cpp |
| 202 | // render_frame_host_impl.h |
| 203 | class RenderFrameHostImpl |
| 204 | ... |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 205 | void GetPingResponder(mojo::PendingReceiver<example::mojom::PingResponder> receiver); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 206 | ... |
| 207 | private: |
| 208 | ... |
| 209 | std::unique_ptr<PingResponderImpl> ping_responder_; |
| 210 | ... |
| 211 | }; |
| 212 | |
| 213 | // render_frame_host_impl.cc |
| 214 | void RenderFrameHostImpl::GetPingResponder( |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 215 | mojo::PendingReceiver<example::mojom::PingResponder> receiver) { |
| 216 | ping_responder_ = std::make_unique<PingResponderImpl>(std::move(receiver)); |
| 217 | } |
| 218 | |
| 219 | // browser_interface_binders.cc |
| 220 | void PopulateFrameBinders(RenderFrameHostImpl* host, |
| 221 | service_manager::BinderMap* map) { |
| 222 | ... |
| 223 | // Register the handler for PingResponder. |
| 224 | map->Add<example::mojom::PingResponder>(base::BindRepeating( |
| 225 | &RenderFrameHostImpl::GetPingResponder, base::Unretained(host))); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 226 | } |
| 227 | ``` |
| 228 | |
| 229 | And we're done. This setup is sufficient to plumb a new interface connection |
| 230 | between a renderer frame and its browser-side host object! |
| 231 | |
| 232 | Assuming we kept our `ping_responder` object alive in the renderer long enough, |
| 233 | we would eventually see its `OnPong` callback invoked with the totally random |
| 234 | value of `4`, as defined by the browser-side implementation above. |
| 235 | |
| 236 | ## Services Overview & Terminology |
| 237 | The previous section only scratches the surface of how Mojo IPC is used in |
| 238 | Chromium. While renderer-to-browser messaging is simple and possibly the most |
| 239 | prevalent usage by sheer code volume, we are incrementally decomposing the |
| 240 | codebase into a set of services with a bit more granularity than the traditional |
| 241 | Content browser/renderer/gpu/utility process split. |
| 242 | |
| 243 | A **service** is a self-contained library of code which implements one or more |
| 244 | related features or behaviors and whose interaction with outside code is done |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 245 | *exclusively* through Mojo interface connections, typically brokered by the |
| 246 | browser process. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 247 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 248 | Each service defines and implements a main Mojo interface which can be used |
| 249 | by the browser to manage an instance of the service. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 250 | |
| 251 | ## Example: Building a Simple Out-of-Process Service |
| 252 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 253 | There are multiple steps typically involved to get a new service up and running |
| 254 | in Chromium: |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 255 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 256 | - Define the main service interface and implementation |
| 257 | - Hook up the implementation in out-of-process code |
| 258 | - Write some browser logic to launch a service process |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 259 | |
| 260 | This section walks through these steps with some brief explanations. For more |
| 261 | thorough documentation of the concepts and APIs used herein, see the |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 262 | [Mojo](/mojo/README.md) documentation. |
| 263 | |
| 264 | ### Defining the Service |
| 265 | |
| 266 | Typically service definitions are placed in a `services` directory, either at |
| 267 | the top level of the tree or within some subdirectory. In this example, we'll |
| 268 | define a new service for use by Chrome specifically, so we'll define it within |
| 269 | `//chrome/services`. |
| 270 | |
| 271 | We can create the following files. First some mojoms: |
| 272 | |
| 273 | ``` cpp |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 274 | // src/chrome/services/math/public/mojom/math_service.mojom |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 275 | module math.mojom; |
| 276 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 277 | interface MathService { |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 278 | Divide(int32 dividend, int32 divisor) => (int32 quotient); |
| 279 | }; |
| 280 | ``` |
| 281 | |
| 282 | ``` python |
| 283 | # src/chrome/services/math/public/mojom/BUILD.gn |
Ken Rockot | a0cb6cf9 | 2019-03-26 16:40:42 | [diff] [blame] | 284 | import("//mojo/public/tools/bindings/mojom.gni") |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 285 | |
| 286 | mojom("mojom") { |
| 287 | sources = [ |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 288 | "math_service.mojom", |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 289 | ] |
| 290 | } |
| 291 | ``` |
| 292 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 293 | Then the actual `MathService` implementation: |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 294 | |
| 295 | ``` cpp |
| 296 | // src/chrome/services/math/math_service.h |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 297 | #include "base/macros.h" |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 298 | #include "chrome/services/math/public/mojom/math_service.mojom.h" |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 299 | |
| 300 | namespace math { |
| 301 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 302 | class MathService : public mojom::MathService { |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 303 | public: |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 304 | explicit MathService(mojo::PendingReceiver<mojom::MathService> receiver); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 305 | ~MathService() override; |
| 306 | |
| 307 | private: |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 308 | // mojom::MathService: |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 309 | void Divide(int32_t dividend, |
| 310 | int32_t divisor, |
| 311 | DivideCallback callback) override; |
| 312 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 313 | mojo::Receiver<mojom::MathService> receiver_; |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 314 | |
| 315 | DISALLOW_COPY_AND_ASSIGN(MathService); |
| 316 | }; |
| 317 | |
| 318 | } // namespace math |
| 319 | ``` |
| 320 | |
| 321 | ``` cpp |
| 322 | // src/chrome/services/math/math_service.cc |
| 323 | #include "chrome/services/math/math_service.h" |
| 324 | |
| 325 | namespace math { |
| 326 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 327 | MathService::MathService(mojo::PendingReceiver<mojom::MathService> receiver) |
| 328 | : receiver_(this, std::move(receiver)) {} |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 329 | |
| 330 | MathService::~MathService() = default; |
| 331 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 332 | void MathService::Divide(int32_t dividend, |
| 333 | int32_t divisor, |
| 334 | DivideCallback callback) { |
| 335 | // Respond with the quotient! |
Oksana Zhuravlova | 0941c08d | 2019-05-03 20:46:33 | [diff] [blame] | 336 | std::move(callback).Run(dividend / divisor); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | } // namespace math |
| 340 | ``` |
| 341 | |
| 342 | ``` python |
| 343 | # src/chrome/services/math/BUILD.gn |
| 344 | |
| 345 | source_set("math") { |
| 346 | sources = [ |
| 347 | "math.cc", |
| 348 | "math.h", |
| 349 | ] |
| 350 | |
| 351 | deps = [ |
| 352 | "//base", |
| 353 | "//chrome/services/math/public/mojom", |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 354 | ] |
| 355 | } |
| 356 | ``` |
| 357 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 358 | Now we have a fully defined `MathService` implementation that we can make |
| 359 | available in- or out-of-process. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 360 | |
| 361 | ### Hooking Up the Service Implementation |
| 362 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 363 | For an out-of-process Chrome service, we simply register a factory function |
| 364 | in [`//chrome/utility/services.cc`](https://cs.chromium.org/chromium/src/chrome/utility/services.cc). |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 365 | |
| 366 | ``` cpp |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 367 | auto RunMathService(mojo::PendingReceiver<math::mojom::MathService> receiver) { |
| 368 | return std::make_unique<math::MathService>(std::move(receiver)); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 369 | } |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 370 | |
| 371 | mojo::ServiceFactory* GetMainThreadServiceFactory() { |
| 372 | // Existing factories... |
| 373 | static base::NoDestructor<mojo::ServiceFactory> factory { |
| 374 | RunFilePatcher, |
| 375 | RunUnzipper, |
| 376 | |
| 377 | // We add our own factory to this list |
| 378 | RunMathService, |
| 379 | //... |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 380 | ``` |
| 381 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 382 | With this done, it is now possible for the browser process to launch new |
| 383 | out-of-process instances of MathService. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 384 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 385 | ### Launching the Service |
| 386 | |
| 387 | If you're running your service in-process, there's really nothing interesting |
| 388 | left to do. You can instantiate the service implementation just like any other |
| 389 | object, yet you can also talk to it via a Mojo Remote as if it were |
| 390 | out-of-process. |
| 391 | |
| 392 | To launch an out-of-process service instance after the hookup performed in the |
| 393 | previous section, use Content's |
| 394 | [`ServiceProcessHost`](https://cs.chromium.org/chromium/src/content/public/browser/service_process_host.h?rcl=e7a1f6c9a24f3151c875598174a05167fb12c5d5&l=47) |
| 395 | API: |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 396 | |
| 397 | ``` cpp |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 398 | mojo::Remote<math::mojom::MathService> math_service = |
| 399 | content::ServiceProcessHost::Launch<math::mojom::MathService>( |
| 400 | content::ServiceProcessHost::LaunchOptions() |
| 401 | .WithSandboxType(content::SandboxType::kUtility) |
| 402 | .WithDisplayName("Math!") |
| 403 | .Pass()); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 404 | ``` |
| 405 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 406 | Except in the case of crashes, the launched process will live as long as |
| 407 | `math_service` lives. As a corollary, you can force the process to be torn |
| 408 | down by destroying (or resetting) `math_service`. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 409 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 410 | We can now perform an out-of-process division: |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 411 | |
| 412 | ``` cpp |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 413 | // NOTE: As a client, we do not have to wait for any acknowledgement or |
| 414 | // confirmation of a connection. We can start queueing messages immediately and |
| 415 | // they will be delivered as soon as the service is up and running. |
| 416 | math_service->Divide( |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 417 | 42, 6, base::BindOnce([](int32_t quotient) { LOG(INFO) << quotient; })); |
| 418 | ``` |
Oksana Zhuravlova | 0941c08d | 2019-05-03 20:46:33 | [diff] [blame] | 419 | *** aside |
Mario Sanchez Prada | 7dead3e | 2019-12-20 18:46:38 | [diff] [blame] | 420 | NOTE: To ensure the execution of the response callback, the |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 421 | `mojo::Remote<math::mojom::MathService>` object must be kept alive (see |
Oksana Zhuravlova | 0941c08d | 2019-05-03 20:46:33 | [diff] [blame] | 422 | [this section](/mojo/public/cpp/bindings/README.md#A-Note-About-Endpoint-Lifetime-and-Callbacks) |
| 423 | and [this note from an earlier section](#sending-a-message)). |
| 424 | *** |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 425 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 426 | ## Content-Layer Services Overview |
| 427 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame^] | 428 | ### Interface Brokers |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 429 | |
Oksana Zhuravlova | ee1afd1 | 2020-02-15 00:47:27 | [diff] [blame] | 430 | We define an explicit mojom interface with a persistent connection |
| 431 | between a renderer's frame object and the corresponding |
| 432 | `RenderFrameHostImpl` in the browser process. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 433 | This interface is called |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 434 | [`BrowserInterfaceBroker`](https://cs.chromium.org/chromium/src/third_party/blink/public/mojom/browser_interface_broker.mojom?rcl=09aa5ae71649974cae8ad4f889d7cd093637ccdb&l=11) |
| 435 | 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] | 436 | |
| 437 | ``` cpp |
| 438 | void RenderFrameHostImpl::GetGoatTeleporter( |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 439 | mojo::PendingReceiver<magic::mojom::GoatTeleporter> receiver) { |
| 440 | goat_teleporter_receiver_.Bind(std::move(receiver)); |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 441 | } |
| 442 | ``` |
| 443 | |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 444 | and register this method in `PopulateFrameBinders` function in `browser_interface_binders.cc`, |
| 445 | which maps specific interfaces to their handlers in respective hosts: |
| 446 | |
| 447 | ``` cpp |
| 448 | // //content/browser/browser_interface_binders.cc |
| 449 | void PopulateFrameBinders(RenderFrameHostImpl* host, |
| 450 | service_manager::BinderMap* map) { |
| 451 | ... |
| 452 | map->Add<magic::mojom::GoatTeleporter>(base::BindRepeating( |
| 453 | &RenderFrameHostImpl::GetGoatTeleporter, base::Unretained(host))); |
| 454 | } |
| 455 | ``` |
| 456 | |
Oksana Zhuravlova | ee1afd1 | 2020-02-15 00:47:27 | [diff] [blame] | 457 | TODO: add information about workers and embedders. |
Oksana Zhuravlova | 9f3b8ef | 2019-08-26 20:27:40 | [diff] [blame] | 458 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 459 | ## Additional Support |
| 460 | |
| 461 | If this document was not helpful in some way, please post a message to your |
| 462 | friendly |
| 463 | [[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) |
| 464 | or |
| 465 | [[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) |
| 466 | mailing list. |