| # Mojo C++ Bindings API |
| This document is a subset of the [Mojo documentation](/mojo/README.md). |
| |
| [TOC] |
| |
| ## Overview |
| The Mojo C++ Bindings API leverages the |
| [C++ System API](/mojo/public/cpp/system/README.md) to provide a more natural |
| set of primitives for communicating over Mojo message pipes. Combined with |
| generated code from the |
| [Mojom IDL and bindings generator](/mojo/public/tools/bindings/README.md), users |
| can easily connect interface clients and implementations across arbitrary intra- |
| and inter-process boundaries. |
| |
| This document provides a detailed guide to bindings API usage with example code |
| snippets. For a detailed API references please consult the headers in |
| [//mojo/public/cpp/bindings](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/README.md). |
| |
| For a simplified guide targeted at Chromium developers, see [this |
| link](/docs/mojo_and_services.md). |
| |
| ## Getting Started |
| |
| When a Mojom IDL file is processed by the bindings generator, C++ code is |
| emitted in a series of `.h` and `.cc` files with names based on the input |
| `.mojom` file. Suppose we create the following Mojom file at |
| `//services/db/public/mojom/db.mojom`: |
| |
| ``` |
| module db.mojom; |
| |
| interface Table { |
| AddRow(int32 key, string data); |
| }; |
| |
| interface Database { |
| CreateTable(Table& table); |
| }; |
| ``` |
| |
| And a GN target to generate the bindings in |
| `//services/db/public/mojom/BUILD.gn`: |
| |
| ``` |
| import("//mojo/public/tools/bindings/mojom.gni") |
| |
| mojom("mojom") { |
| sources = [ |
| "db.mojom", |
| ] |
| } |
| ``` |
| |
| Ensure that any target that needs this interface depends on it, e.g. with a line like: |
| |
| ``` |
| deps += [ '//services/db/public/mojom' ] |
| ``` |
| |
| If we then build this target: |
| |
| ``` |
| ninja -C out/r services/db/public/mojom |
| ``` |
| |
| This will produce several generated source files, some of which are relevant to |
| C++ bindings. Two of these files are: |
| |
| ``` |
| out/gen/services/db/public/mojom/db.mojom.cc |
| out/gen/services/db/public/mojom/db.mojom.h |
| ``` |
| |
| You can include the above generated header in your sources in order to use the |
| definitions therein: |
| |
| ``` cpp |
| #include <string_view> |
| |
| #include "services/business/public/mojom/factory.mojom.h" |
| |
| class TableImpl : public db::mojom::Table { |
| // ... |
| }; |
| ``` |
| |
| This document covers the different kinds of definitions generated by Mojom IDL |
| for C++ consumers and how they can effectively be used to communicate across |
| message pipes. |
| |
| *** note |
| **NOTE:** Using C++ bindings from within Blink code is typically subject to |
| special constraints which require the use of a different generated header. |
| For details, see [Blink Type Mapping](#Blink-Type-Mapping). |
| *** |
| |
| ## Interfaces |
| |
| Mojom IDL interfaces are translated to corresponding C++ (pure virtual) class |
| interface definitions in the generated header, consisting of a single generated |
| method signature for each request message on the interface. Internally there is |
| also generated code for serialization and deserialization of messages, but this |
| detail is hidden from bindings consumers. |
| |
| ### Basic Usage |
| |
| Let's consider a new `//sample/logger.mojom` to define a simple logging |
| interface which clients can use to log simple string messages: |
| |
| ``` cpp |
| module sample.mojom; |
| |
| interface Logger { |
| Log(string message); |
| }; |
| ``` |
| |
| Running this through the bindings generator will produce a `logger.mojom.h` |
| with the following definitions (modulo unimportant details): |
| |
| ``` cpp |
| namespace sample { |
| namespace mojom { |
| |
| class Logger { |
| virtual ~Logger() {} |
| |
| virtual void Log(const std::string& message) = 0; |
| }; |
| |
| } // namespace mojom |
| } // namespace sample |
| ``` |
| |
| ### Remote and PendingReceiver |
| |
| In the world of Mojo bindings libraries these are effectively strongly-typed |
| message pipe endpoints. If a `Remote<T>` is bound to a message pipe |
| endpoint, it can be dereferenced to make calls on an opaque `T` interface. These |
| calls immediately serialize their arguments (using generated code) and write a |
| corresponding message to the pipe. |
| |
| A `PendingReceiver<T>` is essentially just a typed container to hold the other |
| end of a `Remote<T>`'s pipe -- the receiving end -- until it can be |
| routed to some implementation which will **bind** it. The `PendingReceiver<T>` |
| doesn't actually *do* anything other than hold onto a pipe endpoint and carry |
| useful compile-time type information. |
| |
|  |
| |
| So how do we create a strongly-typed message pipe? |
| |
| ### Creating Interface Pipes |
| |
| One way to do this is by manually creating a pipe and wrapping each end with a |
| strongly-typed object: |
| |
| ``` cpp |
| #include "sample/logger.mojom.h" |
| |
| mojo::MessagePipe pipe; |
| mojo::Remote<sample::mojom::Logger> logger( |
| mojo::PendingRemote<sample::mojom::Logger>(std::move(pipe.handle0), 0)); |
| mojo::PendingReceiver<sample::mojom::Logger> receiver(std::move(pipe.handle1)); |
| ``` |
| |
| That's pretty verbose, but the C++ Bindings library provides a more convenient |
| way to accomplish the same thing. [remote.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/remote.h) |
| defines a `BindNewPipeAndPassReceiver` method: |
| |
| ``` cpp |
| mojo::Remote<sample::mojom::Logger> logger; |
| auto receiver = logger.BindNewPipeAndPassReceiver(); |
| ``` |
| |
| This second snippet is equivalent to the first one. |
| |
| *** note |
| **NOTE:** In the first example above you may notice usage of the |
| `mojo::PendingRemote<Logger>`. This is similar to a `PendingReceiver<T>` |
| in that it merely holds onto a pipe handle and cannot actually read or |
| write messages on the pipe. Both this type and `PendingReceiver<T>` are safe |
| to move freely from sequence to sequence, whereas a bound `Remote<T>` is bound |
| to a single sequence. |
| |
| A `Remote<T>` may be unbound by calling its `Unbind()` method, |
| which returns a new `PendingRemote<T>`. Conversely, an `Remote<T>` may |
| bind (and thus take ownership of) an `PendingRemote<T>` so that interface |
| calls can be made on the pipe. |
| |
| The sequence-bound nature of `Remote<T>` is necessary to support safe |
| dispatch of its [message responses](#Receiving-Responses) and |
| [connection error notifications](#Connection-Errors). |
| *** |
| |
| Once the `PendingRemote<Logger>` is bound we can immediately begin calling `Logger` |
| interface methods on it, which will immediately write messages into the pipe. |
| These messages will stay queued on the receiving end of the pipe until someone |
| binds to it and starts reading them. |
| |
| ``` cpp |
| logger->Log("Hello!"); |
| ``` |
| |
| This actually writes a `Log` message to the pipe. |
| |
|  |
| |
| But as mentioned above, `PendingReceiver` *doesn't actually do anything*, so |
| that message will just sit on the pipe forever. We need a way to read messages |
| off the other end of the pipe and dispatch them. We have to |
| **bind the pending receiver**. |
| |
| ### Binding a Pending Receiver |
| |
| There are many different helper classes in the bindings library for binding the |
| receiving end of a message pipe. The most primitive among them is `mojo::Receiver<T>`. |
| A `mojo::Receiver<T>` bridges an implementation of `T` |
| with a single bound message pipe endpoint (via a `mojo::PendingReceiver<T>`), |
| which it continuously watches for readability. |
| |
| Any time the bound pipe becomes readable, the `Receiver` will schedule a task to |
| read, deserialize (using generated code), and dispatch all available messages to |
| the bound `T` implementation. Below is a sample implementation of the `Logger` |
| interface. Notice that the implementation itself owns a `mojo::Receiver`. This is |
| a common pattern, since a bound implementation must outlive any `mojo::Receiver` |
| which binds it. |
| |
| ``` cpp |
| #include "base/logging.h" |
| #include "sample/logger.mojom.h" |
| |
| class LoggerImpl : public sample::mojom::Logger { |
| public: |
| // NOTE: A common pattern for interface implementations which have one |
| // instance per client is to take a PendingReceiver in the constructor. |
| |
| explicit LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| Logger(const Logger&) = delete; |
| Logger& operator=(const Logger&) = delete; |
| ~Logger() override {} |
| |
| // sample::mojom::Logger: |
| void Log(const std::string& message) override { |
| LOG(ERROR) << "[Logger] " << message; |
| } |
| |
| private: |
| mojo::Receiver<sample::mojom::Logger> receiver_; |
| }; |
| ``` |
| |
| Now we can construct a `LoggerImpl` over our `PendingReceiver<Logger>`, and the |
| previously queued `Log` message will be dispatched ASAP on the `LoggerImpl`'s |
| sequence: |
| |
| ``` cpp |
| LoggerImpl impl(std::move(receiver)); |
| ``` |
| |
| If `LoggerImpl` is in another process, see |
| [Sending Interfaces Over Interfaces](#sending-interfaces-over-interfaces). |
| |
| The diagram below illustrates the following sequence of events, all set in |
| motion by the above line of code: |
| |
| 1. The `LoggerImpl` constructor is called, passing the `PendingReceiver<Logger>` along |
| to the `Receiver`. |
| 2. The `Receiver` takes ownership of the `PendingReceiver<Logger>`'s pipe endpoint and |
| begins watching it for readability. The pipe is readable immediately, so a |
| task is scheduled to read the pending `Log` message from the pipe ASAP. |
| 3. The `Log` message is read and deserialized, causing the `Receiver` to invoke |
| the `Logger::Log` implementation on its bound `LoggerImpl`. |
| |
|  |
| |
| As a result, our implementation will eventually log the client's `"Hello!"` |
| message via `LOG(ERROR)`. |
| |
| *** note |
| **NOTE:** Messages will only be read and dispatched from a pipe as long as the |
| object which binds it (*i.e.* the `mojo::Receiver` in the above example) remains |
| alive. |
| *** |
| |
| ### Receiving Responses |
| |
| Some Mojom interface methods expect a response. Suppose we modify our `Logger` |
| interface so that the last logged line can be queried like so: |
| |
| ``` cpp |
| module sample.mojom; |
| |
| interface Logger { |
| Log(string message); |
| GetTail() => (string message); |
| }; |
| ``` |
| |
| The generated C++ interface will now look like: |
| |
| ``` cpp |
| namespace sample { |
| namespace mojom { |
| |
| class Logger { |
| public: |
| virtual ~Logger() {} |
| |
| virtual void Log(const std::string& message) = 0; |
| |
| using GetTailCallback = base::OnceCallback<void(const std::string& message)>; |
| |
| virtual void GetTail(GetTailCallback callback) = 0; |
| } |
| |
| } // namespace mojom |
| } // namespace sample |
| ``` |
| |
| As before, both clients and implementations of this interface use the same |
| signature for the `GetTail` method: implementations use the `callback` argument |
| to *respond* to the request, while clients pass a `callback` argument to |
| asynchronously `receive` the response. The parameter `GetTailCallback` passed to |
| the implementation of `GetTail` is sequence-affine. It must be invoked on the |
| same sequence that `GetTail` is called on. A client's `callback` runs on the |
| same sequence on which they invoked `GetTail` (the sequence to which their |
| `logger` is bound). Here's an updated implementation: |
| |
| ```cpp |
| class LoggerImpl : public sample::mojom::Logger { |
| public: |
| // NOTE: A common pattern for interface implementations which have one |
| // instance per client is to take a PendingReceiver in the constructor. |
| |
| explicit LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| ~Logger() override {} |
| Logger(const Logger&) = delete; |
| Logger& operator=(const Logger&) = delete; |
| |
| // sample::mojom::Logger: |
| void Log(const std::string& message) override { |
| LOG(ERROR) << "[Logger] " << message; |
| lines_.push_back(message); |
| } |
| |
| void GetTail(GetTailCallback callback) override { |
| std::move(callback).Run(lines_.back()); |
| } |
| |
| private: |
| mojo::Receiver<sample::mojom::Logger> receiver_; |
| std::vector<std::string> lines_; |
| }; |
| ``` |
| |
| And an updated client call: |
| |
| ``` cpp |
| void OnGetTail(const std::string& message) { |
| LOG(ERROR) << "Tail was: " << message; |
| } |
| |
| logger->GetTail(base::BindOnce(&OnGetTail)); |
| ``` |
| |
| Behind the scenes, the implementation-side callback is actually serializing the |
| response arguments and writing them onto the pipe for delivery back to the |
| client. Meanwhile the client-side callback is invoked by some internal logic |
| which watches the pipe for an incoming response message, reads and deserializes |
| it once it arrives, and then invokes the callback with the deserialized |
| parameters. |
| |
| ### Connection Errors |
| |
| If a pipe is disconnected, both endpoints will be able to observe the connection |
| error (unless the disconnection is caused by closing/destroying an endpoint, in |
| which case that endpoint won't get such a notification). If there are remaining |
| incoming messages for an endpoint on disconnection, the connection error won't |
| be triggered until the messages are drained. |
| |
| Pipe disconnection may be caused by: |
| * Mojo system-level causes: process terminated, resource exhausted, etc. |
| * The bindings close the pipe due to a validation error when processing a |
| received message. |
| * The peer endpoint is closed. For example, the remote side is a bound |
| `mojo::Remote<T>` and it is destroyed. |
| |
| Regardless of the underlying cause, when a connection error is encountered on |
| a receiver endpoint, that endpoint's **disconnect handler** (if set) is |
| invoked. This handler is a simple `base::OnceClosure` and may only be invoked |
| *once* as long as the endpoint is bound to the same pipe. Typically clients and |
| implementations use this handler to do some kind of cleanup or -- particuarly if |
| the error was unexpected -- create a new pipe and attempt to establish a new |
| connection with it. |
| |
| All message pipe-binding C++ objects (*e.g.*, `mojo::Receiver<T>`, |
| `mojo::Remote<T>`, *etc.*) support setting their disconnect handler |
| via a `set_disconnect_handler` method. |
| |
| We can set up another end-to-end `Logger` example to demonstrate disconnect handler |
| invocation. Suppose that `LoggerImpl` had set up the following disconnect |
| handler within its constructor: |
| |
| ``` cpp |
| LoggerImpl::LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> receiver) |
| : receiver_(this, std::move(receiver)) { |
| receiver_.set_disconnect_handler( |
| base::BindOnce(&LoggerImpl::OnError, base::Unretained(this))); |
| } |
| |
| void LoggerImpl::OnError() { |
| LOG(ERROR) << "Client disconnected! Purging log lines."; |
| lines_.clear(); |
| } |
| |
| mojo::Remote<sample::mojom::Logger> logger; |
| LoggerImpl impl(logger.BindNewPipeAndPassReceiver()); |
| logger->Log("OK cool"); |
| logger.reset(); // Closes the client end. |
| |
| ``` |
| |
| As long as `impl` stays alive here, it will eventually receive the `Log` message |
| followed immediately by an invocation of the bound callback which outputs |
| `"Client disconnected! Purging log lines."`. Like all other receiver callbacks, a disconnect handler will |
| **never** be invoked once its corresponding receiver object has been destroyed. |
| |
| The use of `base::Unretained` is *safe* because the error handler will never be |
| invoked beyond the lifetime of `receiver_`, and `this` owns `receiver_`. |
| |
| ### A Note About Endpoint Lifetime and Callbacks |
| Once a `mojo::Remote<T>` is destroyed, it is guaranteed that pending |
| callbacks as well as the connection error handler (if registered) won't be |
| called. |
| |
| Once a `mojo::Receiver<T>` is destroyed, it is guaranteed that no more method |
| calls are dispatched to the implementation and the connection error handler (if |
| registered) won't be called. |
| |
| ### Best practices for dealing with process crashes and callbacks |
| A common situation when calling mojo interface methods that take a callback is |
| that the caller wants to know if the other endpoint is torn down (e.g. because |
| of a crash). In that case, the consumer usually wants to know if the response |
| callback won't be run. There are different solutions for this problem, depending |
| on how the `Remote<T>` is held: |
| 1. The consumer owns the `Remote<T>`: `set_disconnect_handler` |
| should be used. |
| 2. The consumer doesn't own the `Remote<T>`: there are two helpers |
| depending on the behavior that the caller wants. If the caller wants to |
| ensure that an error handler is run, then |
| [**`mojo::WrapCallbackWithDropHandler`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/callback_helpers.h?l=46) |
| should be used. If the caller wants the callback to always be run, then |
| [**`mojo::WrapCallbackWithDefaultInvokeIfNotRun`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/callback_helpers.h?l=40) |
| helper should be used. With both of these helpers, usual callback care should |
| be followed to ensure that the callbacks don't run after the consumer is |
| destructed (e.g. because the owner of the `Remote<T>` outlives the |
| consumer). This includes using |
| [**`base::WeakPtr`**](https://cs.chromium.org/chromium/src/base/memory/weak_ptr.h?l=5) |
| or |
| [**`base::RefCounted`**](https://cs.chromium.org/chromium/src/base/memory/ref_counted.h?l=246). |
| It should also be noted that with these helpers, the callbacks could be run |
| synchronously while the Remote<T> is reset or destroyed. |
| |
| ### A Note About Ordering |
| |
| As mentioned in the previous section, closing one end of a pipe will eventually |
| trigger a connection error on the other end. However it's important to note that |
| this event is itself ordered with respect to any other event (*e.g.* writing a |
| message) on the pipe. |
| |
| This means that it's safe to write something contrived like: |
| |
| ``` cpp |
| LoggerImpl::LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> receiver, |
| base::OnceClosure disconnect_handler) |
| : receiver_(this, std::move(receiver)) { |
| receiver_.set_disconnect_handler(std::move(disconnect_handler)); |
| } |
| |
| void GoBindALogger(mojo::PendingReceiver<sample::mojom::Logger> receiver) { |
| base::RunLoop loop; |
| LoggerImpl impl(std::move(receiver), loop.QuitClosure()); |
| loop.Run(); |
| } |
| |
| void LogSomething() { |
| mojo::Remote<sample::mojom::Logger> logger; |
| bg_thread->task_runner()->PostTask( |
| FROM_HERE, base::BindOnce(&GoBindALogger, logger.BindNewPipeAndPassReceiver())); |
| logger->Log("OK Computer"); |
| } |
| ``` |
| |
| When `logger` goes out of scope it immediately closes its end of the message |
| pipe, but the impl-side won't notice this until it receives the sent `Log` |
| message. Thus the `impl` above will first log our message and *then* see a |
| connection error and break out of the run loop. |
| |
| ## Types |
| |
| ### Enums |
| |
| [Mojom enums](/mojo/public/tools/bindings/README.md#Enumeration-Types) translate |
| directly to equivalent strongly-typed C++11 enum classes with `int32_t` as the |
| underlying type. The typename and value names are identical between Mojom and |
| C++. Mojo also always defines a special enumerator `kMaxValue` that shares the |
| value of the highest enumerator: this makes it easy to record Mojo enums in |
| histograms and interoperate with legacy IPC. |
| |
| For example, consider the following Mojom definition: |
| |
| ```cpp |
| module business.mojom; |
| |
| enum Department { |
| kEngineering, |
| kMarketing, |
| kSales, |
| }; |
| ``` |
| |
| This translates to the following C++ definition: |
| |
| ```cpp |
| namespace business { |
| namespace mojom { |
| |
| enum class Department : int32_t { |
| kEngineering, |
| kMarketing, |
| kSales, |
| kMaxValue = kSales, |
| }; |
| |
| } // namespace mojom |
| } // namespace business |
| ``` |
| |
| ### Structs |
| |
| [Mojom structs](mojo/public/tools/bindings/README.md#Structs) can be used to |
| define logical groupings of fields into a new composite type. Every Mojom struct |
| elicits the generation of an identically named, representative C++ class, with |
| identically named public fields of corresponding C++ types, and several helpful |
| public methods. |
| |
| For example, consider the following Mojom struct: |
| |
| ```cpp |
| module business.mojom; |
| |
| struct Employee { |
| int64 id; |
| string username; |
| Department department; |
| }; |
| ``` |
| |
| This would generate a C++ class like so: |
| |
| ```cpp |
| namespace business { |
| namespace mojom { |
| |
| class Employee; |
| |
| using EmployeePtr = mojo::StructPtr<Employee>; |
| |
| class Employee { |
| public: |
| // Default constructor - applies default values, potentially ones specified |
| // explicitly within the Mojom. |
| Employee(); |
| |
| // Value constructor - an explicit argument for every field in the struct, in |
| // lexical Mojom definition order. |
| Employee(int64_t id, const std::string& username, Department department); |
| |
| // Creates a new copy of this struct value |
| EmployeePtr Clone(); |
| |
| // Tests for equality with another struct value of the same type. |
| bool Equals(const Employee& other); |
| |
| // Equivalent public fields with names identical to the Mojom. |
| int64_t id; |
| std::string username; |
| Department department; |
| }; |
| |
| } // namespace mojom |
| } // namespace business |
| ``` |
| |
| Note when used as a message parameter or as a field within another Mojom struct, |
| a `struct` type is wrapped by the move-only `mojo::StructPtr` helper, which is |
| roughly equivalent to a `std::unique_ptr` with some additional utility methods. |
| This allows struct values to be nullable and struct types to be potentially |
| self-referential. |
| |
| Every generated struct class has a static `New()` method which returns a new |
| `mojo::StructPtr<T>` wrapping a new instance of the class constructed by |
| forwarding the arguments from `New`. For example: |
| |
| ```cpp |
| mojom::EmployeePtr e1 = mojom::Employee::New(); |
| e1->id = 42; |
| e1->username = "mojo"; |
| e1->department = mojom::Department::kEngineering; |
| ``` |
| |
| is equivalent to |
| |
| ```cpp |
| auto e1 = mojom::Employee::New(42, "mojo", mojom::Department::kEngineering); |
| ``` |
| |
| Now if we define an interface like: |
| |
| ```cpp |
| interface EmployeeManager { |
| AddEmployee(Employee e); |
| }; |
| ``` |
| |
| We'll get this C++ interface to implement: |
| |
| ```cpp |
| class EmployeeManager { |
| public: |
| virtual ~EmployeManager() {} |
| |
| virtual void AddEmployee(EmployeePtr e) = 0; |
| }; |
| ``` |
| |
| And we can send this message from C++ code as follows: |
| |
| ```cpp |
| mojom::EmployeManagerPtr manager = ...; |
| manager->AddEmployee( |
| Employee::New(42, "mojo", mojom::Department::kEngineering)); |
| |
| // or |
| auto e = Employee::New(42, "mojo", mojom::Department::kEngineering); |
| manager->AddEmployee(std::move(e)); |
| ``` |
| |
| ### Unions |
| |
| Similarly to [structs](#Structs), tagged unions generate an identically named, |
| representative C++ class which is typically wrapped in a `mojo::StructPtr<T>`. |
| |
| Unlike structs, all generated union fields are private and must be retrieved and |
| manipulated using accessors. A field `foo` is accessible by `get_foo()` and |
| settable by `set_foo()`. There is also a boolean `is_foo()` for each field which |
| indicates whether the union is currently taking on the value of field `foo` in |
| exclusion to all other union fields. |
| |
| Finally, every generated union class also has a nested `Tag` enum class which |
| enumerates all of the named union fields. A Mojom union value's current type can |
| be determined by calling the `which()` method which returns a `Tag`. |
| |
| For example, consider the following Mojom definitions: |
| |
| ```cpp |
| union Value { |
| int64 int_value; |
| float float_value; |
| string string_value; |
| }; |
| |
| interface Dictionary { |
| AddValue(string key, Value value); |
| }; |
| ``` |
| |
| This generates the following C++ interface: |
| |
| ```cpp |
| class Value { |
| public: |
| ~Value() {} |
| }; |
| |
| class Dictionary { |
| public: |
| virtual ~Dictionary() {} |
| |
| virtual void AddValue(const std::string& key, ValuePtr value) = 0; |
| }; |
| ``` |
| |
| And we can use it like so: |
| |
| ```cpp |
| ValuePtr value = Value::NewIntValue(42); |
| CHECK(value->is_int_value()); |
| CHECK_EQ(value->which(), Value::Tag::kIntValue); |
| |
| value->set_float_value(42); |
| CHECK(value->is_float_value()); |
| CHECK_EQ(value->which(), Value::Tag::kFloatValue); |
| |
| value->set_string_value("bananas"); |
| CHECK(value->is_string_value()); |
| CHECK_EQ(value->which(), Value::Tag::kStringValue); |
| ``` |
| |
| Finally, note that if a union value is not currently occupied by a given field, |
| attempts to access that field will DCHECK: |
| |
| ```cpp |
| ValuePtr value = Value::NewIntValue(42); |
| LOG(INFO) << "Value is " << value->get_string_value(); // DCHECK! |
| ``` |
| |
| ### Result |
| |
| Methods may use a special `result<T,E>` type to express that a method may either |
| return a message of type T on success and a message of type E on failure. This |
| type maps to `base::expected` in C++. |
| |
| For example, consider the following Mojom method: |
| |
| ```mojom |
| module foo.mojom; |
| |
| struct Success { |
| int64 elapsed_ms; |
| }; |
| |
| struct Failure { |
| string reason; |
| }; |
| |
| interface Iface { |
| DoSomething() => result<Success, Failure>; |
| }; |
| ``` |
| |
| This would generate a C++ interface like so: |
| |
| ```cpp |
| namespace foo::mojom { |
| |
| class Iface { |
| virtual ~IFace() {} |
| |
| virtual void DoSomething(DoSomethingCallback callback) = 0; |
| }; |
| |
| } // namespace foo::mojom |
| ``` |
| |
| `DoSomethingCallback` takes a base::expected as the single parameter. If the |
| API invocation was successful, the callback can be invoked with `base::ok` |
| along with the success type. If the API invocation was unsuccessful, the |
| callback can be invoked with `base::unexpected` along with the error type. |
| For example: |
| |
| ```cpp |
| namespace foo { |
| |
| class IfaceImpl : public mojom::Iface { |
| DoSomething(DoSomethingCallback callback) override { |
| if (success) { |
| auto success = mojom::Success::New(); |
| success->elapsed_ms = 9001; |
| callback.Run(base::ok(std::move(success))); |
| } else { |
| auto failure = mojom::Failure::New(); |
| failure->reason = "too hard!"; |
| callback.Run(base::unexpected(std::move(failure))); |
| } |
| } |
| }; |
| |
| } // namespace foo |
| ``` |
| |
| ### Features |
| |
| Mojom `feature` generates a `base::Feature` with the given `name` and |
| `default_state` (`true` => `ENABLED_BY_DEFAULT`). The feature can be accessed |
| and tested in C++ using the mapped name even if it is not used to mark any |
| [interfaces](#runtimefeature-on-interfaces) or |
| [methods](#runtimefeature-on-methods). |
| |
| ```mojom |
| module experiment.mojom; |
| |
| // Introduce a new runtime feature flag. |
| feature kUseElevator { |
| const string name = "UseElevator"; |
| const bool default_state = false; |
| }; |
| ``` |
| |
| ```cpp |
| #include "base/feature_list.h" |
| #include "experiment.mojom-features.h" |
| |
| if (base::FeatureList::IsEnabled(experiment::mojom::kUseElevator)) { |
| LOG(INFO) << "Going up...."; |
| } |
| ``` |
| |
| ```sh |
| ./chrome --enable-features=UseElevator |
| # Going up.... |
| ``` |
| |
| ### Sending Interfaces Over Interfaces |
| |
| We know how to create interface pipes and use their Remote and PendingReceiver endpoints |
| in some interesting ways. This still doesn't add up to interesting IPC! The |
| bread and butter of Mojo IPC is the ability to transfer interface endpoints |
| across other interfaces, so let's take a look at how to accomplish that. |
| |
| #### Sending Pending Receivers |
| |
| Consider a new example Mojom in `//sample/db.mojom`: |
| |
| ``` cpp |
| module db.mojom; |
| |
| interface Table { |
| void AddRow(int32 key, string data); |
| }; |
| |
| interface Database { |
| AddTable(pending_receiver<Table> table); |
| }; |
| ``` |
| |
| As noted in the |
| [Mojom IDL documentation](/mojo/public/tools/bindings/README.md#Primitive-Types), |
| the `pending_receiver<Table>` syntax corresponds |
| precisely to the `PendingReceiver<T>` type discussed in the sections above, and |
| in fact the generated code for these interfaces is approximately: |
| |
| ``` cpp |
| namespace db { |
| namespace mojom { |
| |
| class Table { |
| public: |
| virtual ~Table() {} |
| |
| virtual void AddRow(int32_t key, const std::string& data) = 0; |
| } |
| |
| class Database { |
| public: |
| virtual ~Database() {} |
| |
| virtual void AddTable(mojo::PendingReceiver<Table> table); |
| }; |
| |
| } // namespace mojom |
| } // namespace db |
| ``` |
| |
| We can put this all together now with an implementation of `Table` and |
| `Database`: |
| |
| ``` cpp |
| #include "sample/db.mojom.h" |
| |
| class TableImpl : public db::mojom:Table { |
| public: |
| explicit TableImpl(mojo::PendingReceiver<db::mojom::Table> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| ~TableImpl() override {} |
| |
| // db::mojom::Table: |
| void AddRow(int32_t key, const std::string& data) override { |
| rows_.insert({key, data}); |
| } |
| |
| private: |
| mojo::Receiver<db::mojom::Table> receiver_; |
| std::map<int32_t, std::string> rows_; |
| }; |
| |
| class DatabaseImpl : public db::mojom::Database { |
| public: |
| explicit DatabaseImpl(mojo::PendingReceiver<db::mojom::Database> receiver) |
| : receiver_(this, std::move(receiver)) {} |
| ~DatabaseImpl() override {} |
| |
| // db::mojom::Database: |
| void AddTable(mojo::PendingReceiver<db::mojom::Table> table) { |
| tables_.emplace_back(std::make_unique<TableImpl>(std::move(table))); |
| } |
| |
| private: |
| mojo::Receiver<db::mojom::Database> receiver_; |
| std::vector<std::unique_ptr<TableImpl>> tables_; |
| }; |
| ``` |
| |
| Pretty straightforward. The `pending_receiver<Table>` Mojom parameter to `AddTable` translates to |
| a C++ `mojo::PendingReceiver<db::mojom::Table>`, which we know is just a |
| strongly-typed message pipe handle. When `DatabaseImpl` gets an `AddTable` call, |
| it constructs a new `TableImpl` and binds it to the received `mojo::PendingReceiver<db::mojom::Table>`. |
| |
| Let's see how this can be used. |
| |
| ``` cpp |
| mojo::Remote<db::mojom::Database> database; |
| DatabaseImpl db_impl(database.BindNewPipeAndPassReceiver()); |
| |
| mojo::Remote<db::mojom::Table> table1, table2; |
| database->AddTable(table1.BindNewPipeAndPassReceiver()); |
| database->AddTable(table2.BindNewPipeAndPassReceiver()); |
| |
| table1->AddRow(1, "hiiiiiiii"); |
| table2->AddRow(2, "heyyyyyy"); |
| ``` |
| |
| Notice that we can again start using the new `Table` pipes immediately, even |
| while their `mojo::PendingReceiver<db::mojom::Table>` endpoints are still in transit. |
| |
| #### Sending Remotes |
| |
| Of course we can also send `Remote`s: |
| |
| ``` cpp |
| interface TableListener { |
| OnRowAdded(int32 key, string data); |
| }; |
| |
| interface Table { |
| AddRow(int32 key, string data); |
| |
| AddListener(pending_remote<TableListener> listener); |
| }; |
| ``` |
| |
| This would generate a `Table::AddListener` signature like so: |
| |
| ``` cpp |
| virtual void AddListener(mojo::PendingRemote<TableListener> listener) = 0; |
| ``` |
| |
| and this could be used like so: |
| |
| ``` cpp |
| mojo::PendingRemote<db::mojom::TableListener> listener; |
| TableListenerImpl impl(listener.InitWithNewPipeAndPassReceiver()); |
| table->AddListener(std::move(listener)); |
| ``` |
| |
| ### RuntimeFeature on interfaces |
| |
| If an interface is marked with a `RuntimeFeature` attribute, and the associated |
| feature is disabled, then it is not possible to bind the interface to a |
| receiver, and not possible to create a remote to call methods on. Attempts to |
| bind remotes or receivers will result in the underlying pipe being `reset()`. |
| `SelfOwnedReceivers` will not be created. A compromised process can override |
| these checks and might falsely request a disabled interface but a trustworthy |
| process will not bind a concrete endpoint to interact with the disabled |
| interface. |
| |
| Note that it remains possible to create and transport generic wrapper |
| objects to disabled interfaces - security decisions should be made based on a |
| test of the generated feature - or the bound state of a Remote or Receiver. |
| |
| ```mojom |
| // Feature controls runtime availability of interface. |
| [RuntimeFeature=kUseElevator] |
| interface DefaultDenied { |
| GetInt() => (int32 ret); |
| }; |
| |
| interface PassesInterfaces { |
| BindPendingRemoteDisabled(pending_remote<DefaultDenied> iface); |
| BindPendingReceiverDisabled(pending_receiver<DefaultDenied> iface); |
| }; |
| ``` |
| |
| ```C++ |
| void BindPendingRemoteDisabled( |
| mojo::PendingRemote<mojom::DefaultDenied> iface) override { |
| mojo::Remote<mojom::DefaultDenied> denied_remote; |
| // Remote will not bind: |
| denied_remote.Bind(std::move(iface)); |
| ASSERT_FALSE(denied_remote); |
| } |
| void BindPendingReceiverDisabled( |
| mojo::PendingReceiver<mojom::DefaultDenied> iface) override { |
| std::unique_ptr<DefaultDeniedImpl> denied_impl; |
| // Object can still be created: |
| denied_impl = std::make_unique<DefaultDeniedImpl>(std::move(iface)); |
| // But its internal receiver_ will not bind or receive remote calls. |
| ASSERT_FALSE(denied_impl->receiver().is_bound()); |
| } |
| ``` |
| |
| ### RuntimeFeature on methods |
| |
| If a method is marked with a `RuntimeFeature` attribute it is not possible to |
| call that method on a remote (attempting to do so will result in a CHECK()), |
| and receivers will reject incoming messages at the validation stage, causing |
| their linked remote to become disconnected. |
| |
| ```mojom |
| // Feature controls runtime availability of interface. |
| interface NormalInterface { |
| [RuntimeFeature=related.module.mojom.kFeature] |
| GetInt() => (int32 ret); |
| }; |
| ``` |
| |
| ```C++ |
| mojo::Remote<mojom::NormalInterface> remote; |
| remote->GetInt(); // CHECKs if kFeature is not enabled. |
| ``` |
| |
| ## Other Interface Binding Types |
| |
| The [Interfaces](#Interfaces) section above covers basic usage of the most |
| common bindings object types: `Remote`, `PendingReceiver`, and `Receiver`. |
| While these types are probably the most commonly used in practice, there are |
| several other ways of binding both client- and implementation-side interface |
| pipes. |
| |
| ### Self-owned Receivers |
| |
| A **self-owned receiver** exists as a standalone object which owns its interface |
| implementation and automatically cleans itself up when its bound interface |
| endpoint detects an error. The |
| [**`MakeSelfOwnedReceiver`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/self_owned_receiver.h) |
| function is used to create such a receiver. |
| . |
| |
| ``` cpp |
| class LoggerImpl : public sample::mojom::Logger { |
| public: |
| LoggerImpl() {} |
| ~LoggerImpl() override {} |
| |
| // sample::mojom::Logger: |
| void Log(const std::string& message) override { |
| LOG(ERROR) << "[Logger] " << message; |
| } |
| |
| private: |
| // NOTE: This doesn't own any Receiver object! |
| }; |
| |
| mojo::Remote<db::mojom::Logger> logger; |
| mojo::MakeSelfOwnedReceiver(std::make_unique<LoggerImpl>(), |
| logger.BindNewPipeAndPassReceiver()); |
| |
| logger->Log("NOM NOM NOM MESSAGES"); |
| ``` |
| |
| Now as long as `logger` remains open somewhere in the system, the bound |
| `LoggerImpl` on the other end will remain alive. |
| |
| ### Receiver Sets |
| |
| Sometimes it's useful to share a single implementation instance with multiple |
| clients. [**`ReceiverSet`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/receiver_set.h) |
| makes this easy. Consider the Mojom: |
| |
| ``` cpp |
| module system.mojom; |
| |
| interface Logger { |
| Log(string message); |
| }; |
| |
| interface LoggerProvider { |
| GetLogger(Logger& logger); |
| }; |
| ``` |
| |
| We can use `ReceiverSet` to bind multiple `Logger` pending receivers to a single |
| implementation instance: |
| |
| ``` cpp |
| class LogManager : public system::mojom::LoggerProvider, |
| public system::mojom::Logger { |
| public: |
| explicit LogManager(mojo::PendingReceiver<system::mojom::LoggerProvider> receiver) |
| : provider_receiver_(this, std::move(receiver)) {} |
| ~LogManager() {} |
| |
| // system::mojom::LoggerProvider: |
| void GetLogger(mojo::PendingReceiver<Logger> receiver) override { |
| logger_receivers_.Add(this, std::move(receiver)); |
| } |
| |
| // system::mojom::Logger: |
| void Log(const std::string& message) override { |
| LOG(ERROR) << "[Logger] " << message; |
| } |
| |
| private: |
| mojo::Receiver<system::mojom::LoggerProvider> provider_receiver_; |
| mojo::ReceiverSet<system::mojom::Logger> logger_receivers_; |
| }; |
| |
| ``` |
| |
| |
| ### Remote Sets |
| |
| Similar to the `ReceiverSet` above, sometimes it's useful to maintain a set of |
| `Remote`s for *e.g.* a set of clients observing some event. |
| [**`RemoteSet`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/remote_set.h) |
| is here to help. Take the Mojom: |
| |
| ``` cpp |
| module db.mojom; |
| |
| interface TableListener { |
| OnRowAdded(int32 key, string data); |
| }; |
| |
| interface Table { |
| AddRow(int32 key, string data); |
| AddListener(pending_remote<TableListener> listener); |
| }; |
| ``` |
| |
| An implementation of `Table` might look something like like this: |
| |
| ``` cpp |
| class TableImpl : public db::mojom::Table { |
| public: |
| TableImpl() {} |
| ~TableImpl() override {} |
| |
| // db::mojom::Table: |
| void AddRow(int32_t key, const std::string& data) override { |
| rows_.insert({key, data}); |
| for (auto& listener : listeners_) { |
| listener->OnRowAdded(key, data); |
| } |
| } |
| |
| void AddListener(mojo::PendingRemote<db::mojom::TableListener> listener) { |
| listeners_.Add(std::move(listener)); |
| } |
| |
| private: |
| mojo::RemoteSet<db::mojom::Table> listeners_; |
| std::map<int32_t, std::string> rows_; |
| }; |
| ``` |
| |
| ## Associated Interfaces |
| |
| Associated interfaces are interfaces which: |
| |
| * enable running multiple interfaces over a single message pipe while |
| preserving message ordering. |
| * make it possible for the receiver to access a single message pipe from |
| multiple sequences. |
| |
| ### Mojom |
| |
| New types `pending_associated_remote` and `pending_associated_receiver` are |
| introduced for remote/receiver fields. For example: |
| |
| ``` cpp |
| interface Bar {}; |
| |
| struct Qux { |
| pending_associated_remote<Bar> bar; |
| }; |
| |
| interface Foo { |
| // Uses associated remote. |
| PassBarRemote(pending_associated_remote<Bar> bar); |
| // Uses associated receiver. |
| PassBarReceiver(pending_associated_receiver<Bar> bar); |
| // Passes a struct with associated interface pointer. |
| PassQux(Qux qux); |
| // Uses associated interface pointer in callback. |
| AsyncGetBar() => (pending_associated_remote<Bar> bar); |
| }; |
| ``` |
| |
| In each case the interface impl/client will communicate using the same message |
| pipe over which the associated remote/receiver is passed. |
| |
| ### Using associated interfaces in C++ |
| |
| When generating C++ bindings, the pending_associated_remote of `Bar` is |
| mapped to `mojo::PendingAssociatedRemote<Bar>`; pending_associated_receiver to |
| `mojo::PendingAssociatedReceiver<Bar>`. |
| |
| ``` cpp |
| // In mojom: |
| interface Foo { |
| ... |
| PassBarRemote(pending_associated_remote<Bar> bar); |
| PassBarReceiver(pending_associated_receiver<Bar> bar); |
| ... |
| }; |
| |
| // In C++: |
| class Foo { |
| ... |
| virtual void PassBarRemote(mojo::PendingAssociatedRemote<Bar> bar) = 0; |
| virtual void PassBarReceiver(mojo::PendingAssociatedReceiver<Bar> bar) = 0; |
| ... |
| }; |
| ``` |
| |
| #### Passing pending associated receivers |
| |
| Assume you already have a `Remote<Foo> foo`, and you would like to call |
| `PassBarReceiver()` on it. You can do: |
| |
| ``` cpp |
| mojo::PendingAssociatedRemote<Bar> pending_bar; |
| mojo::PendingAssociatedReceiver<Bar> bar_receiver = pending_bar.InitWithNewEndpointAndPassReceiver(); |
| foo->PassBarReceiver(std::move(bar_receiver)); |
| |
| mojo::AssociatedRemote<Bar> bar; |
| bar.Bind(std::move(pending_bar)); |
| bar->DoSomething(); |
| ``` |
| |
| First, the code creates an associated interface of type `Bar`. It looks very |
| similar to what you would do to setup a non-associated interface. An |
| important difference is that one of the two associated endpoints (either |
| `bar_receiver` or `pending_bar`) must be sent over another interface. That is |
| how the interface is associated with an existing message pipe. |
| |
| It should be noted that you cannot call `bar->DoSomething()` before passing |
| `bar_receiver`. This is required by the FIFO-ness guarantee: at the receiver |
| side, when the message of `DoSomething` call arrives, we want to dispatch it to |
| the corresponding `AssociatedReceiver<Bar>` before processing any subsequent |
| messages. If `bar_receiver` is in a subsequent message, message dispatching gets |
| into a deadlock. On the other hand, as soon as `bar_receiver` is sent, `bar` |
| is usable. There is no need to wait until `bar_receiver` is bound to an |
| implementation at the remote side. |
| |
| `AssociatedRemote` provides a `BindNewEndpointAndPassReceiver` method |
| to make the code a little shorter. The following code achieves the same purpose: |
| |
| ``` cpp |
| mojo::AssociatedRemote<Bar> bar; |
| foo->PassBarReceiver(bar.BindNewEndpointAndPassReceiver()); |
| bar->DoSomething(); |
| ``` |
| |
| The implementation of `Foo` looks like this: |
| |
| ``` cpp |
| class FooImpl : public Foo { |
| ... |
| void PassBarReceiver(mojo::PendingAssociatedReceiver<Bar> bar) override { |
| bar_receiver_.Bind(std::move(bar)); |
| ... |
| } |
| ... |
| |
| Receiver<Foo> foo_receiver_; |
| AssociatedReceiver<Bar> bar_receiver_; |
| }; |
| ``` |
| |
| In this example, `bar_receiver_`'s lifespan is tied to that of `FooImpl`. But you |
| don't have to do that. You can, for example, pass `bar2` to another sequence to |
| bind to an `AssociatedReceiver<Bar>` there. |
| |
| When the underlying message pipe is disconnected (e.g., `foo` or |
| `foo_receiver_` is destroyed), all associated interface endpoints (e.g., |
| `bar` and `bar_receiver_`) will receive a disconnect error. |
| |
| #### Passing associated remotes |
| |
| Similarly, assume you have already got an `Remote<Foo> foo`, and you |
| would like to call `PassBarRemote()` on it. You can do: |
| |
| ``` cpp |
| mojo::AssociatedReceiver<Bar> bar_receiver(some_bar_impl); |
| mojo::PendingAssociatedRemote<Bar> bar; |
| mojo::PendingAssociatedReceiver<Bar> bar_pending_receiver = bar.InitWithNewEndpointAndPassReceiver(); |
| foo->PassBarRemote(std::move(bar)); |
| bar_receiver.Bind(std::move(bar_pending_receiver)); |
| ``` |
| |
| The following code achieves the same purpose: |
| |
| ``` cpp |
| mojo::AssociatedReceiver<Bar> bar_receiver(some_bar_impl); |
| mojo::PendingAssociatedRemote<Bar> bar; |
| bar_receiver.Bind(bar.InitWithNewPipeAndPassReceiver()); |
| foo->PassBarRemote(std::move(bar)); |
| ``` |
| |
| ### Performance considerations |
| |
| When using associated interfaces on different sequences than the primary |
| sequence (where the primary interface lives): |
| |
| * Sending messages: send happens directly on the calling sequence. So there |
| isn't sequence hopping. |
| * Receiving messages: associated interfaces bound on a different sequence from |
| the primary interface incur an extra sequence hop during dispatch. |
| |
| Therefore, performance-wise associated interfaces are better suited for |
| scenarios where message receiving happens on the primary sequence. |
| |
| ### Testing |
| |
| Associated interfaces need to be associated with a primary interface before |
| they can be used. This means one end of the associated interface must be sent |
| over one end of the primary interface, or over one end of another associated |
| interface which itself already has a primary interface. |
| |
| If you want to test an associated interface endpoint without first |
| associating it, you can use `AssociatedRemote::BindNewEndpointAndPassDedicatedReceiver`. |
| This will create working associated interface endpoints which are not actually |
| associated with anything else. |
| |
| ### Read more |
| |
| * [Design: Mojo Associated Interfaces](https://docs.google.com/document/d/1nq3J_HbS-gvVfIoEhcVyxm1uY-9G_7lhD-4Kyxb1WIY/edit) |
| |
| ## Synchronous Calls |
| |
| ### Think carefully before you decide to use sync calls |
| |
| Although sync calls are convenient, you should avoid them whenever they |
| are not absolutely necessary: |
| |
| * Sync calls hurt parallelism and therefore hurt performance. |
| * Re-entrancy changes message order and produces call stacks that you |
| probably never think about while you are coding. It has always been a |
| huge pain. |
| * Sync calls may lead to deadlocks. |
| * Sync web apis are [strongly discouraged](https://www.w3.org/TR/design-principles/#async-by-default). |
| * The `[Sync]` annotation does not affect the bindings for the service side |
| and therefore does **not** guard against re-entrancy, especially when the |
| client is untrusted (e.g. the renderer process). |
| |
| ### Mojom changes |
| |
| A new attribute `[Sync]` (or `[Sync=true]`) is introduced for methods. |
| For example: |
| |
| ``` cpp |
| interface Foo { |
| [Sync] |
| SomeSyncCall() => (Bar result); |
| }; |
| ``` |
| |
| It indicates that when `SomeSyncCall()` is called, the control flow of |
| the calling thread is blocked until the response is received. |
| |
| It is not allowed to use this attribute with functions that don’t have |
| responses. If you just need to wait until the service side finishes |
| processing the call, you can use an empty response parameter list: |
| |
| ``` cpp |
| [Sync] |
| SomeSyncCallWithNoResult() => (); |
| ``` |
| |
| ### Generated bindings (C++) |
| |
| The generated C++ interface of the Foo interface above is: |
| |
| ``` cpp |
| class Foo { |
| public: |
| // The service side implements this signature. The client side can |
| // also use this signature if it wants to call the method asynchronously. |
| virtual void SomeSyncCall(SomeSyncCallCallback callback) = 0; |
| |
| // The client side uses this signature to call the method synchronously. |
| virtual bool SomeSyncCall(BarPtr* result); |
| }; |
| ``` |
| |
| As you can see, the client side and the service side use different |
| signatures. At the client side, response is mapped to output parameters |
| and the boolean return value indicates whether the operation is |
| successful. (Returning false usually means a connection error has |
| occurred.) |
| |
| At the service side, a signature with callback is used. The reason is |
| that in some cases the implementation may need to do some asynchronous |
| work which the sync method’s result depends on. |
| |
| *** note |
| **NOTE:** you can also use the signature with callback at the client side to |
| call the method asynchronously. |
| *** |
| |
| ### Re-entrancy |
| |
| What happens on the calling thread while waiting for the response of a |
| sync method call? It continues to process incoming sync request messages |
| (i.e., sync method calls); block other messages, including async |
| messages and sync response messages that don’t match the ongoing sync |
| call. |
| |
|  |
| |
| Please note that sync response messages that don’t match the ongoing |
| sync call cannot re-enter. That is because they correspond to sync calls |
| down in the call stack. Therefore, they need to be queued and processed |
| while the stack unwinds. |
| |
| ### Avoid deadlocks |
| |
| Please note that the re-entrancy behavior doesn’t prevent deadlocks |
| involving async calls. You need to avoid call sequences such as: |
| |
|  |
| |
| ### Read more |
| |
| * [Design Proposal: Mojo Sync Methods]( |
| https://docs.google.com/document/d/1dixzFzZQW8e3ldjdM8Adbo8klXDDE4pVekwo5aLgUsE) |
| |
| ## Type Mapping |
| |
| In many instances you might prefer that your generated C++ bindings use a more |
| natural type to represent certain Mojom types in your interface methods. For one |
| example consider a Mojom struct such as the `Rect` below: |
| |
| ``` cpp |
| module gfx.mojom; |
| |
| struct Rect { |
| int32 x; |
| int32 y; |
| int32 width; |
| int32 height; |
| }; |
| |
| interface Canvas { |
| void FillRect(Rect rect); |
| }; |
| ``` |
| |
| The `Canvas` Mojom interface would normally generate a C++ interface like: |
| |
| ``` cpp |
| class Canvas { |
| public: |
| virtual void FillRect(RectPtr rect) = 0; |
| }; |
| ``` |
| |
| However, the Chromium tree already defines a native |
| [`gfx::Rect`](https://cs.chromium.org/chromium/src/ui/gfx/geometry/rect.h) which |
| is equivalent in meaning but which also has useful helper methods. Instead of |
| manually converting between a `gfx::Rect` and the Mojom-generated `RectPtr` at |
| every message boundary, wouldn't it be nice if the Mojom bindings generator |
| could instead generate: |
| |
| ``` cpp |
| class Canvas { |
| public: |
| virtual void FillRect(const gfx::Rect& rect) = 0; |
| } |
| ``` |
| |
| The correct answer is, "Yes! That would be nice!" And fortunately, it can! |
| |
| ### Defining `StructTraits` |
| |
| In order to teach generated bindings code how to serialize an arbitrary native |
| type `T` as an arbitrary Mojom type `mojom::U`, we need to define an appropriate |
| specialization of the |
| [`mojo::StructTraits`](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/struct_traits.h) |
| template. |
| |
| A valid specialization of `StructTraits` MUST define the following static |
| methods: |
| |
| * A single static accessor for every field of the Mojom struct, with the exact |
| same name as the struct field. These accessors must all take a (preferably |
| const) ref to an object of the native type, and must return a value compatible |
| with the Mojom struct field's type. This is used to safely and consistently |
| extract data from the native type during message serialization without |
| incurring extra copying costs. |
| |
| * A single static `Read` method which initializes an instance of the the native |
| type given a serialized representation of the Mojom struct. The `Read` method |
| must return a `bool` to indicate whether the incoming data is accepted |
| (`true`) or rejected (`false`). |
| |
| In order to define the mapping for `gfx::Rect`, we want the following |
| `StructTraits` specialization, which we'll define in |
| `//ui/gfx/geometry/mojo/geometry_mojom_traits.h`. |
| |
| *** note |
| **NOTE**: whereas in other usages of the mojom type in C++ we might directly |
| use the mojom type `gfx::mojom::Rect`, our `StructTraits` template must use the |
| [DataView](#using-generated-dataview-types) version (e.g. |
| `gfx::mojom::RectDataView`), which exposes a direct view of the serialized Mojom |
| structure within an incoming message's contents which we need in order to |
| perform the serialization. |
| *** |
| |
| <pre><code> |
| #include "mojo/public/cpp/bindings/mojom_traits.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/mojo/geometry.mojom.h" |
| |
| namespace mojo { |
| |
| template <> |
| class StructTraits<gfx::mojom::Rect<b>DataView</b>, gfx::Rect> { |
| public: |
| static int32_t x(const gfx::Rect& r) { return r.x(); } |
| static int32_t y(const gfx::Rect& r) { return r.y(); } |
| static int32_t width(const gfx::Rect& r) { return r.width(); } |
| static int32_t height(const gfx::Rect& r) { return r.height(); } |
| |
| static bool Read(gfx::mojom::RectDataView data, gfx::Rect* out_rect); |
| }; |
| |
| } // namespace mojo |
| </code></pre> |
| |
| And in `//ui/gfx/geometry/mojo/geometry_mojom_traits.cc`: |
| |
| ``` cpp |
| #include "ui/gfx/geometry/mojo/geometry_mojom_traits.h" |
| |
| namespace mojo { |
| |
| // static |
| bool StructTraits<gfx::mojom::RectDataView, gfx::Rect>::Read( |
| gfx::mojom::RectDataView data, |
| gfx::Rect* out_rect) { |
| if (data.width() < 0 || data.height() < 0) { |
| return false; |
| } |
| |
| out_rect->SetRect(data.x(), data.y(), data.width(), data.height()); |
| return true; |
| } |
| |
| } // namespace mojo |
| ``` |
| |
| Note that the `Read()` method returns `false` if either the incoming `width` or |
| `height` fields are negative. This acts as a validation step during |
| deserialization: if a client sends a `gfx::Rect` with a negative width or |
| height, its message will be rejected and the pipe will be closed. In this way, |
| type mapping can serve to enable custom validation logic in addition to making |
| callsites and interface implemention more convenient. |
| |
| When the struct fields have non-primitive types, e.g. string or array, |
| returning a read-only view of the data in the accessor is recommended to |
| avoid copying. It is safe because the input object is guaranteed to |
| outlive the usage of the result returned by the accessor method. |
| |
| The following example uses `std::string_view` to return a view of the GURL's |
| data (`//url/mojom/url_gurl_mojom_traits.h`): |
| |
| ``` cpp |
| |
| #include "url/gurl.h" |
| #include "url/mojom/url.mojom.h" |
| #include "url/url_constants.h" |
| |
| namespace mojo { |
| |
| template <> |
| struct StructTraits<url::mojom::UrlDataView, GURL> { |
| static std::string_view url(const GURL& r) { |
| if (r.possibly_invalid_spec().length() > url::kMaxURLChars || |
| !r.is_valid()) { |
| return std::string_view(); |
| } |
| return r.possibly_invalid_spec(); |
| } |
| }; |
| |
| } // namespace mojo |
| ``` |
| |
| ### Defining `EnumTraits` |
| |
| Similar to `StructTraits`, you can specialize the |
| [`mojo::EnumTraits`](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/enum_traits.h) |
| to handle conversion between a Mojom enum and a native enum |
| |
| In general, it's better to just use the Mojom enum directly. However, in some circumstances this |
| is impractical: perhaps the native enum is from a third-party library, or the value must be used |
| by code that is not aware of Mojo (such as Cronet). |
| |
| A specialization typically uses simple `switch` statements to convert between the two enums: |
| |
| ```cpp |
| #include "mojo/public/cpp/bindings/enum_traits.h" |
| |
| template <> |
| struct EnumTraits<mojom::MyEnum, MyEnum> { |
| static mojom::MyEnum ToMojom(MyEnum input); |
| static bool FromMojom(mojom::MyEnum input, MyEnum* output); |
| }; |
| ``` |
| |
| ```cpp |
| #include "mojo/public/cpp/bindings/enum_traits.h" |
| |
| // static |
| mojom::MyEnum |
| EnumTraits<mojom::MyEnum, MyEnum>::ToMojom(MyEnum input) { |
| switch (input) { |
| case MyEnum::CUSTOM_VALUE_0: |
| return mojom::MyEnum::VALUE_0; |
| case MyEnum::CUSTOM_VALUE_1: |
| return mojom::MyEnum::VALUE_1; |
| }; |
| |
| NOTREACHED(); |
| } |
| |
| // static |
| bool EnumTraits<mojom::MyEnum, MyEnum>::FromMojom(mojom::MyEnum input, MyEnum* output) { |
| switch (input) { |
| case mojom::MyEnum::VALUE_0: |
| *output = MyEnum::CUSTOM_VALUE_0; |
| return true; |
| case mojom::MyEnum::VALUE_1: |
| *output = MyEnum::CUSTOM_VALUE_1; |
| return true; |
| }; |
| |
| return false; |
| } |
| ``` |
| |
| ### Enabling a New Type Mapping |
| |
| We've defined the `StructTraits` or `EnumTraits` necessary, but we still need to teach the |
| bindings generator (and hence the build system) about the mapping. To do this we |
| must add some more information to our `mojom` target in GN: |
| |
| ``` |
| # Without a typemap |
| mojom("mojom") { |
| sources = [ |
| "rect.mojom", |
| ] |
| } |
| |
| # With a typemap. |
| mojom("mojom") { |
| sources = [ |
| "rect.mojom", |
| ] |
| |
| cpp_typemaps = [ |
| { |
| # NOTE: A single typemap entry can list multiple individual type mappings. |
| # Each mapping assumes the same values for |traits_headers| etc below. |
| # |
| # To typemap a type with separate |traits_headers| etc, add a separate |
| # entry to |cpp_typemaps|. |
| types = [ |
| { |
| mojom = "gfx.mojom.Rect" |
| cpp = "::gfx::Rect" |
| }, |
| { |
| mojom = "mojom.MyEnum" |
| cpp = "::MyEnum" |
| }, |
| ] |
| traits_headers = [ "//ui/gfx/geometry/mojo/geometry_mojom_traits.h" ] |
| traits_sources = [ "//ui/gfx/geometry/mojo/geometry_mojom_traits.cc" ] |
| traits_public_deps = [ "//ui/gfx/geometry" ] |
| }, |
| ] |
| } |
| ``` |
| |
| See typemap documentation in |
| [mojom.gni](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/mojom.gni) |
| for details on the above definition and other supported parameters. |
| |
| With this extra configuration present, any mojom references to `gfx.mojom.Rect` |
| (e.g. for method parameters or struct fields) will be `gfx::Rect` references in |
| generated C++ code. |
| |
| For the Blink variant of bindings, add to the `blink_cpp_typemaps` list instead. |
| |
| ### Type Mapping Without `traits_sources` |
| |
| Using `traits_sources` in a typemap configuration means that the listed sources |
| will be baked directly into the corresponding `mojom` target's own sources. This |
| can be problematic if you want to use the same typemap for both Blink and |
| non-Blink bindings. |
| |
| For such cases, it is recommended that you define a separate `component` target |
| for your typemap traits, and reference this in the `traits_public_deps` of the |
| typemap: |
| |
| ``` |
| mojom("mojom") { |
| sources = [ |
| "rect.mojom", |
| ] |
| |
| cpp_typemaps = [ |
| { |
| types = [ |
| { |
| mojom = "gfx.mojom.Rect" |
| cpp = "::gfx::Rect" |
| }, |
| ] |
| traits_headers = [ "//ui/gfx/geometry/mojo/geometry_mojom_traits.h" ] |
| traits_public_deps = [ ":geometry_mojom_traits" ] |
| }, |
| ] |
| } |
| |
| component("geometry_mojom_traits") { |
| sources = [ |
| "//ui/gfx/geometry/mojo/geometry_mojom_traits.cc", |
| "//ui/gfx/geometry/mojo/geometry_mojom_traits.h", |
| ] |
| |
| # The header of course needs corresponding COMPONENT_EXPORT() tags. |
| defines = [ "IS_GEOMETRY_MOJOM_TRAITS_IMPL" ] |
| } |
| ``` |
| |
| ### StructTraits Reference |
| |
| Each of a `StructTraits` specialization's static getter methods -- one per |
| struct field -- must return a type which can be used as a data source for the |
| field during serialization. This is a quick reference mapping Mojom field type |
| to valid getter return types: |
| |
| | Mojom Field Type | C++ Getter Return Type | |
| |------------------------------|------------------------| |
| | `bool` | `bool` |
| | `int8` | `int8_t` |
| | `uint8` | `uint8_t` |
| | `int16` | `int16_t` |
| | `uint16` | `uint16_t` |
| | `int32` | `int32_t` |
| | `uint32` | `uint32_t` |
| | `int64` | `int64_t` |
| | `uint64` | `uint64_t` |
| | `float` | `float` |
| | `double` | `double` |
| | `handle` | `mojo::ScopedHandle` |
| | `handle<message_pipe>` | `mojo::ScopedMessagePipeHandle` |
| | `handle<data_pipe_consumer>` | `mojo::ScopedDataPipeConsumerHandle` |
| | `handle<data_pipe_producer>` | `mojo::ScopedDataPipeProducerHandle` |
| | `handle<platform>` | `mojo::PlatformHandle` |
| | `handle<shared_buffer>` | `mojo::ScopedSharedBufferHandle` |
| | `pending_remote<Foo>` | `mojo::PendingRemote<Foo>` |
| | `pending_receiver<Foo>` | `mojo::PendingReceiver<Foo>` |
| | `pending_associated_remote<Foo>` | `mojo::PendingAssociatedRemote<Foo>` |
| | `pending_associated_receiver<Foo>` | `mojo::PendingAssociatedReceiver<Foo>` |
| | `string` | Value or reference to any type `T` that has a `mojo::StringTraits` specialization defined. By default this includes `std::string`, `std::string_view`, and `blink::String` (Blink). |
| | `array<T>` | Value or reference to any type `T` that has a `mojo::ArrayTraits` specialization defined. By default this includes `std::array<T, N>`, `std::vector<T>`, `blink::Vector<T>` (Blink), etc. |
| | `array<T, N>` | Similar to the above, but the length of the data must be always the same as `N`. |
| | `map<K, V>` | Value or reference to any type `T` that has a `mojo::MapTraits` specialization defined. By default this includes `std::map<T>`, `mojo::unordered_map<T>`, `blink::HashMap<T>` (Blink), etc. |
| | `FooEnum` | Value of any type that has an appropriate `EnumTraits` specialization defined. By default this includes only the generated `FooEnum` type. |
| | `FooStruct` | Value or reference to any type that has an appropriate `StructTraits` specialization defined. By default this includes only the generated `FooStructPtr` type. |
| | `FooUnion` | Value of reference to any type that has an appropriate `UnionTraits` specialization defined. By default this includes only the generated `FooUnionPtr` type. |
| | `Foo?` | `std::optional<CppType>`, where `CppType` is the value type defined by the appropriate traits class specialization (e.g. `StructTraits`, `mojo::MapTraits`, etc.). This may be customized by the [typemapping](#Enabling-a-New-Type-Mapping). |
| |
| ### Using Generated DataView Types |
| |
| Static `Read` methods on `StructTraits` specializations get a generated |
| `FooDataView` argument (such as the `RectDataView` in the example above) which |
| exposes a direct view of the serialized Mojom structure within an incoming |
| message's contents. In order to make this as easy to work with as possible, the |
| generated `FooDataView` types have a generated method corresponding to every |
| struct field: |
| |
| * For POD field types (*e.g.* bools, floats, integers) these are simple accessor |
| methods with names identical to the field name. Hence in the `Rect` example we |
| can access things like `data.x()` and `data.width()`. The return types |
| correspond exactly to the mappings listed in the table above, under |
| [StructTraits Reference](#StructTraits-Reference). |
| |
| * For handle and interface types (*e.g* `handle` or `pending_remote<Foo>`) these |
| are named `TakeFieldName` (for a field named `field_name`) and they return an |
| appropriate move-only handle type by value. The return types correspond |
| exactly to the mappings listed in the table above, under |
| [StructTraits Reference](#StructTraits-Reference). |
| |
| * For all other field types (*e.g.*, enums, strings, arrays, maps, structs) |
| these are named `ReadFieldName` (for a field named `field_name`) and they |
| return a `bool` (to indicate success or failure in reading). On success they |
| fill their output argument with the deserialized field value. The output |
| argument may be a pointer to any type with an appropriate `StructTraits` |
| specialization defined, as mentioned in the table above, under |
| [StructTraits Reference](#StructTraits-Reference). |
| |
| An example would be useful here. Suppose we introduced a new Mojom struct: |
| |
| ``` cpp |
| struct RectPair { |
| Rect left; |
| Rect right; |
| }; |
| ``` |
| |
| and a corresponding C++ type: |
| |
| ``` cpp |
| class RectPair { |
| public: |
| RectPair() {} |
| |
| const gfx::Rect& left() const { return left_; } |
| const gfx::Rect& right() const { return right_; } |
| |
| void Set(const gfx::Rect& left, const gfx::Rect& right) { |
| left_ = left; |
| right_ = right; |
| } |
| |
| // ... some other stuff |
| |
| private: |
| gfx::Rect left_; |
| gfx::Rect right_; |
| }; |
| ``` |
| |
| Our traits to map `gfx::mojom::RectPair` to `gfx::RectPair` might look like |
| this: |
| |
| ``` cpp |
| namespace mojo { |
| |
| template <> |
| class StructTraits |
| public: |
| static const gfx::Rect& left(const gfx::RectPair& pair) { |
| return pair.left(); |
| } |
| |
| static const gfx::Rect& right(const gfx::RectPair& pair) { |
| return pair.right(); |
| } |
| |
| static bool Read(gfx::mojom::RectPairDataView data, gfx::RectPair* out_pair) { |
| gfx::Rect left, right; |
| if (!data.ReadLeft(&left) || !data.ReadRight(&right)) |
| return false; |
| out_pair->Set(left, right); |
| return true; |
| } |
| } // namespace mojo |
| ``` |
| |
| Generated `ReadFoo` methods always convert `multi_word_field_name` fields to |
| `ReadMultiWordFieldName` methods. |
| |
| <a name="Blink-Type-Mapping"></a> |
| ### Variants |
| |
| By now you may have noticed that additional C++ sources are generated when a |
| Mojom is processed. These exist due to type mapping, and the source files we |
| refer to throughout this docuemnt (namely `foo.mojom.cc` and `foo.mojom.h`) are |
| really only one **variant** (the *default* or *chromium* variant) of the C++ |
| bindings for a given Mojom file. |
| |
| The only other variant currently defined in the tree is the *blink* variant, |
| which produces a few additional files: |
| |
| ``` |
| out/gen/sample/db.mojom-blink.cc |
| out/gen/sample/db.mojom-blink.h |
| ``` |
| |
| These files mirror the definitions in the default variant but with different |
| C++ types in place of certain builtin field and parameter types. For example, |
| Mojom strings are represented by `blink::String` instead of `std::string`. To |
| avoid symbol collisions, the variant's symbols are nested in an extra inner |
| namespace, so Blink consumer of the interface might write something like: |
| |
| ``` |
| #include "sample/db.mojom-blink.h" |
| |
| class TableImpl : public db::mojom::blink::Table { |
| public: |
| void AddRow(int32_t key, const blink::String& data) override { |
| // ... |
| } |
| }; |
| ``` |
| |
| In addition to using different C++ types for builtin strings, arrays, and maps, |
| the custom typemaps applied to Blink bindings are managed separately from |
| regular bindings. |
| |
| `mojom` targets support a `blink_cpp_typemaps` parameter in addition to the |
| regular `cpp_typemaps`. This lists the typemaps to apply to Blink bindings. |
| |
| To depend specifically on generated Blink bindings, reference |
| `${target_name}_blink`. So for example, with the definition: |
| |
| ``` |
| # In //foo/mojom |
| mojom("mojom") { |
| sources = [ |
| "db.mojom", |
| ] |
| } |
| ``` |
| |
| C++ sources can depend on the Blink bindings by depending on |
| `"//foo/mojom:mojom_blink"`. |
| |
| Finally note that both bindings variants share some common definitions which are |
| unaffected by differences in the type-mapping configuration (like enums, and |
| structures describing serialized object formats). These definitions are |
| generated in *shared* sources: |
| |
| ``` |
| out/gen/sample/db.mojom-shared.cc |
| out/gen/sample/db.mojom-shared.h |
| out/gen/sample/db.mojom-shared-internal.h |
| ``` |
| |
| Including either variant's header (`db.mojom.h` or `db.mojom-blink.h`) |
| implicitly includes the shared header, but may wish to include *only* the shared |
| header in some instances. |
| |
| C++ sources can depend on shared sources only, by referencing the |
| `"${target_name}_shared"` target, e.g. `"//foo/mojom:mojom_shared"` in the |
| example above. |
| |
| For converting between Blink and non-Blink variants, please see |
| `//third_party/blink/public/platform/cross_variant_mojo_util.h`. |
| |
| Blink strings deserve a special mention, since `blink::String` can store either |
| Latin-1 or UTF-16, and converts to UTF-8 as needed. Since Mojo strings are |
| supposed to be UTF-8, converting a `blink::String` to a mojo string will convert |
| it to UTF-8. When converting a Mojo string back to a blink::String, the string |
| is re-encoded from UTF-8 back into UTF-16. Invalid UTF-16 is tolerated |
| throughout and converted to invalid UTF-8, so if your blink::String may contain |
| invalid UTF-16, don't represent it on the wire with a mojo string - use a mojo |
| ByteString instead. |
| |
| ## Versioning Considerations |
| |
| For general documentation of versioning in the Mojom IDL see |
| [Versioning](/mojo/public/tools/bindings/README.md#Versioning). |
| |
| This section briefly discusses some C++-specific considerations relevant to |
| versioned Mojom types. |
| |
| ### Querying Interface Versions |
| |
| `Remote` defines the following methods to query or assert remote interface |
| version: |
| |
| ```cpp |
| void QueryVersion(base::OnceCallback<void(uint32_t)> callback); |
| ``` |
| |
| This queries the remote endpoint for the version number of its binding. When a |
| response is received `callback` is invoked with the remote version number. Note |
| that this value is cached by the `Remote` instance to avoid redundant |
| queries. |
| |
| ```cpp |
| void RequireVersion(uint32_t version); |
| ``` |
| |
| Informs the remote endpoint that a minimum version of `version` is required by |
| the client. If the remote endpoint cannot support that version, it will close |
| its end of the pipe immediately, preventing any other requests from being |
| received. |
| |
| ### Versioned Enums |
| |
| All extensible enums should have one enumerator value designated as the default |
| using the `[Default]` attribute. When Mojo deserializes an enum value that is |
| not defined in the current version of the enum definition, that value will be |
| transparently mapped to the `[Default]` enumerator value. Implementations can |
| use the presence of this enumerator value to correctly handle version skew. |
| |
| ```cpp |
| [Extensible] |
| enum Department { |
| [Default] kUnknown, |
| kSales, |
| kDev, |
| kResearch, |
| }; |
| ``` |
| |
| *** note |
| **NOTE**: The `[Default]` enumerator value is distinct from the automatically |
| populated enum value used when a non-nullable enum field is not defined in an |
| older client's versioned struct definition |
| ([the enumerator value corresponding to `0`](/mojo/public/tools/bindings/README.md#ensuring-backward-compatible-behavior)). |
| *** |
| |
| ### Using Mojo Bindings in Chrome |
| |
| See [Converting Legacy Chrome IPC To Mojo](/docs/mojo_ipc_conversion.md). |
| |
| ### Additional Documentation |
| |
| [Calling Mojo From Blink](/docs/mojo_ipc_conversion.md#Blink_Specific-Advice): |
| A brief overview of what it looks like to use Mojom C++ bindings from |
| within Blink code. |