| # 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 bounaries. |
| |
| 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_guide.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/interfaces/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/interfaces/BUILD.gn`: |
| |
| ``` |
| import("//mojo/public/tools/bindings/mojom.gni") |
| |
| mojom("interfaces") { |
| sources = [ |
| "db.mojom", |
| ] |
| } |
| ``` |
| |
| Ensure that any target that needs this interface depends on it, e.g. with a line like: |
| |
| ``` |
| deps += [ '//services/db/public/interfaces' ] |
| ``` |
| |
| If we then build this target: |
| |
| ``` |
| ninja -C out/r services/db/public/interfaces |
| ``` |
| |
| 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/interfaces/db.mojom.cc |
| out/gen/services/db/public/interfaces/db.mojom.h |
| ``` |
| |
| You can include the above generated header in your sources in order to use the |
| definitions therein: |
| |
| ``` cpp |
| #include "services/business/public/interfaces/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 `logging.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; |
| }; |
| |
| using LoggerPtr = mojo::InterfacePtr<Logger>; |
| using LoggerRequest = mojo::InterfaceRequest<Logger>; |
| |
| } // namespace mojom |
| } // namespace sample |
| ``` |
| |
| Makes sense. Let's take a closer look at those type aliases at the end. |
| |
| ### InterfacePtr and InterfaceRequest |
| |
| You will notice the type aliases for `LoggerPtr` and |
| `LoggerRequest` are using two of the most fundamental template types in the C++ |
| bindings library: **`InterfacePtr<T>`** and **`InterfaceRequest<T>`**. |
| |
| In the world of Mojo bindings libraries these are effectively strongly-typed |
| message pipe endpoints. If an `InterfacePtr<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. |
| |
| An `InterfaceRequest<T>` is essentially just a typed container to hold the other |
| end of an `InterfacePtr<T>`'s pipe -- the receiving end -- until it can be |
| routed to some implementation which will **bind** it. The `InterfaceRequest<T>` |
| doesn't actually *do* anything other than hold onto a pipe endpoint and carry |
| useful compile-time type information. |
| |
|  |
| |
| So how do we create a strongly-typed message pipe? |
| |
| ### 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; |
| sample::mojom::LoggerPtr logger( |
| sample::mojom::LoggerPtrInfo(std::move(pipe.handle0), 0)); |
| sample::mojom::LoggerRequest request(std::move(pipe.handle1)); |
| ``` |
| |
| That's pretty verbose, but the C++ Bindings library provides a more convenient |
| way to accomplish the same thing. [interface_request.h](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/interface_request.h) |
| defines a `MakeRequest` function: |
| |
| ``` cpp |
| sample::mojom::LoggerPtr logger; |
| auto request = mojo::MakeRequest(&logger); |
| ``` |
| |
| This second snippet is equivalent to the first one. |
| |
| *** note |
| **NOTE:** In the first example above you may notice usage of the `LoggerPtrInfo` |
| type, which is a generated alias for `mojo::InterfacePtrInfo<Logger>`. This is |
| similar to an `InterfaceRequest<T>` in that it merely holds onto a pipe handle |
| and cannot actually read or write messages on the pipe. Both this type and |
| `InterfaceRequest<T>` are safe to move freely from sequence to sequence, whereas |
| a bound `InterfacePtr<T>` is bound to a single sequence. |
| |
| An `InterfacePtr<T>` may be unbound by calling its `PassInterface()` method, |
| which returns a new `InterfacePtrInfo<T>`. Conversely, an `InterfacePtr<T>` may |
| bind (and thus take ownership of) an `InterfacePtrInfo<T>` so that interface |
| calls can be made on the pipe. |
| |
| The sequence-bound nature of `InterfacePtr<T>` is necessary to support safe |
| dispatch of its [message responses](#Receiving-Responses) and |
| [connection error notifications](#Connection-Errors). |
| *** |
| |
| Once the `LoggerPtr` 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, `InterfaceRequest` *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 interface request**. |
| |
| ### Binding an Interface Request |
| |
| 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 the aptly |
| named `mojo::Binding<T>`. A `mojo::Binding<T>` bridges an implementation of `T` |
| with a single bound message pipe endpoint (via a `mojo::InterfaceRequest<T>`), |
| which it continuously watches for readability. |
| |
| Any time the bound pipe becomes readable, the `Binding` 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::Binding`. This is |
| a common pattern, since a bound implementation must outlive any `mojo::Binding` |
| which binds it. |
| |
| ``` cpp |
| #include "base/logging.h" |
| #include "base/macros.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 an InterfaceRequest in the constructor. |
| |
| explicit LoggerImpl(sample::mojom::LoggerRequest request) |
| : binding_(this, std::move(request)) {} |
| ~Logger() override {} |
| |
| // sample::mojom::Logger: |
| void Log(const std::string& message) override { |
| LOG(ERROR) << "[Logger] " << message; |
| } |
| |
| private: |
| mojo::Binding<sample::mojom::Logger> binding_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LoggerImpl); |
| }; |
| ``` |
| |
| Now we can construct a `LoggerImpl` over our pending `LoggerRequest`, and the |
| previously queued `Log` message will be dispatched ASAP on the `LoggerImpl`'s |
| sequence: |
| |
| ``` cpp |
| LoggerImpl impl(std::move(request)); |
| ``` |
| |
| 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 `LoggerRequest` along |
| to the `Binding`. |
| 2. The `Binding` takes ownership of the `LoggerRequest`'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 `Binding` to invoke |
| the `Logger::Log` implementation on its bound `LoggerImpl`. |
| |
|  |
| |
| As a result, our implementation will eventually log the client's `"Hello!"` |
| message via `LOG(ERROR)`. |
| |
| *** note |
| **NOTE:** Messages will only be read and dispatched from a pipe as long as the |
| object which binds it (*i.e.* the `mojo::Binding` 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. 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 an InterfaceRequest in the constructor. |
| |
| explicit LoggerImpl(sample::mojom::LoggerRequest request) |
| : binding_(this, std::move(request)) {} |
| ~Logger() override {} |
| |
| // 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::Binding<sample::mojom::Logger> binding_; |
| std::vector<std::string> lines_; |
| |
| DISALLOW_COPY_AND_ASSIGN(LoggerImpl); |
| }; |
| ``` |
| |
| 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 disconnecition 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::InterfacePtr<T>` and it is destroyed. |
| |
| Regardless of the underlying cause, when a connection error is encountered on |
| a binding endpoint, that endpoint's **connection error handler** (if set) is |
| invoked. This handler is a simple `base::Closure` 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::Binding<T>`, |
| `mojo::InterfacePtr<T>`, *etc.*) support setting their connection error handler |
| via a `set_connection_error_handler` method. |
| |
| We can set up another end-to-end `Logger` example to demonstrate error handler |
| invocation: |
| |
| ``` cpp |
| sample::mojom::LoggerPtr logger; |
| LoggerImpl impl(mojo::MakeRequest(&logger)); |
| impl.set_connection_error_handler(base::BindOnce([] { LOG(ERROR) << "Bye."; })); |
| 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 |
| `"Bye."`. Like all other bindings callbacks, a connection error handler will |
| **never** be invoked once its corresponding binding object has been destroyed. |
| |
| In fact, suppose instead that `LoggerImpl` had set up the following error |
| handler within its constructor: |
| |
| ``` cpp |
| LoggerImpl::LoggerImpl(sample::mojom::LoggerRequest request) |
| : binding_(this, std::move(request)) { |
| binding_.set_connection_error_handler( |
| base::BindOnce(&LoggerImpl::OnError, base::Unretained(this))); |
| } |
| |
| void LoggerImpl::OnError() { |
| LOG(ERROR) << "Client disconnected! Purging log lines."; |
| lines_.clear(); |
| } |
| ``` |
| |
| The use of `base::Unretained` is *safe* because the error handler will never be |
| invoked beyond the lifetime of `binding_`, and `this` owns `binding_`. |
| |
| ### A Note About Endpoint Lifetime and Callbacks |
| Once a `mojo::InterfacePtr<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::Binding<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 `InterfacePtr<T>` is held: |
| 1. The consumer owns the `InterfacePtr<T>`: `set_connection_error_handler` |
| should be used. |
| 2. The consumer doesn't own the `InterfacePtr<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 `InterfacePtr<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 InterfacePtr<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 |
| void GoBindALogger(sample::mojom::LoggerRequest request) { |
| LoggerImpl impl(std::move(request)); |
| base::RunLoop loop; |
| impl.set_connection_error_handler(loop.QuitClosure()); |
| loop.Run(); |
| } |
| |
| void LogSomething() { |
| sample::mojom::LoggerPtr logger; |
| bg_thread->task_runner()->PostTask( |
| FROM_HERE, base::BindOnce(&GoBindALogger, mojo::MakeRequest(&logger))); |
| 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 genereated 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 `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; |
| float32 float_value; |
| string string_value; |
| }; |
| |
| interface Dictionary { |
| AddValue(string key, Value value); |
| }; |
| ``` |
| |
| This generates a the following C++ interface: |
| |
| ```cpp |
| class Value { |
| public: |
| virtual ~Value() {} |
| |
| virtual void AddValue(const std::string& key, ValuePtr value) = 0; |
| }; |
| ``` |
| |
| And we can use it like so: |
| |
| ```cpp |
| ValuePtr value = Value::New(); |
| value->set_int_value(42); |
| CHECK(value->is_int_value()); |
| CHECK_EQ(value->which(), Value::Tag::INT_VALUE); |
| |
| value->set_float_value(42); |
| CHECK(value->is_float_value()); |
| CHECK_EQ(value->which(), Value::Tag::FLOAT_VALUE); |
| |
| value->set_string_value("bananas"); |
| CHECK(value->is_string_value()); |
| CHECK_EQ(value->which(), Value::Tag::STRING_VALUE); |
| ``` |
| |
| 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::New(); |
| value->set_int_value(42); |
| LOG(INFO) << "Value is " << value->string_value(); // DCHECK! |
| ``` |
| |
| ### Sending Interfaces Over Interfaces |
| |
| We know how to create interface pipes and use their Ptr and Request 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 Interface Requests |
| |
| Consider a new example Mojom in `//sample/db.mojom`: |
| |
| ``` cpp |
| module db.mojom; |
| |
| interface Table { |
| void AddRow(int32 key, string data); |
| }; |
| |
| interface Database { |
| AddTable(Table& table); |
| }; |
| ``` |
| |
| As noted in the |
| [Mojom IDL documentation](/mojo/public/tools/bindings/README.md#Primitive-Types), |
| the `Table&` syntax denotes a `Table` interface request. This corresponds |
| precisely to the `InterfaceRequest<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; |
| } |
| |
| using TablePtr = mojo::InterfacePtr<Table>; |
| using TableRequest = mojo::InterfaceRequest<Table>; |
| |
| class Database { |
| public: |
| virtual ~Database() {} |
| |
| virtual void AddTable(TableRequest table); |
| }; |
| |
| using DatabasePtr = mojo::InterfacePtr<Database>; |
| using DatabaseRequest = mojo::InterfaceRequest<Database>; |
| |
| } // 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(db::mojom::TableRequest request) |
| : binding_(this, std::move(request)) {} |
| ~TableImpl() override {} |
| |
| // db::mojom::Table: |
| void AddRow(int32_t key, const std::string& data) override { |
| rows_.insert({key, data}); |
| } |
| |
| private: |
| mojo::Binding<db::mojom::Table> binding_; |
| std::map<int32_t, std::string> rows_; |
| }; |
| |
| class DatabaseImpl : public db::mojom::Database { |
| public: |
| explicit DatabaseImpl(db::mojom::DatabaseRequest request) |
| : binding_(this, std::move(request)) {} |
| ~DatabaseImpl() override {} |
| |
| // db::mojom::Database: |
| void AddTable(db::mojom::TableRequest table) { |
| tables_.emplace_back(std::make_unique<TableImpl>(std::move(table))); |
| } |
| |
| private: |
| mojo::Binding<db::mojom::Database> binding_; |
| std::vector<std::unique_ptr<TableImpl>> tables_; |
| }; |
| ``` |
| |
| Pretty straightforward. The `Table&` Mojom paramter to `AddTable` translates to |
| a C++ `db::mojom::TableRequest`, aliased from |
| `mojo::InterfaceRequest<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 `TableRequest`. |
| |
| Let's see how this can be used. |
| |
| ``` cpp |
| db::mojom::DatabasePtr database; |
| DatabaseImpl db_impl(mojo::MakeRequest(&database)); |
| |
| db::mojom::TablePtr table1, table2; |
| database->AddTable(mojo::MakeRequest(&table1)); |
| database->AddTable(mojo::MakeRequest(&table2)); |
| |
| table1->AddRow(1, "hiiiiiiii"); |
| table2->AddRow(2, "heyyyyyy"); |
| ``` |
| |
| Notice that we can again start using the new `Table` pipes immediately, even |
| while their `TableRequest` endpoints are still in transit. |
| |
| #### Sending InterfacePtrs |
| |
| Of course we can also send `InterfacePtr`s: |
| |
| ``` cpp |
| interface TableListener { |
| OnRowAdded(int32 key, string data); |
| }; |
| |
| interface Table { |
| AddRow(int32 key, string data); |
| |
| AddListener(TableListener listener); |
| }; |
| ``` |
| |
| This would generate a `Table::AddListener` signature like so: |
| |
| ``` cpp |
| virtual void AddListener(TableListenerPtr listener) = 0; |
| ``` |
| |
| and this could be used like so: |
| |
| ``` cpp |
| db::mojom::TableListenerPtr listener; |
| TableListenerImpl impl(mojo::MakeRequest(&listener)); |
| table->AddListener(std::move(listener)); |
| ``` |
| |
| ## Other Interface Binding Types |
| |
| The [Interfaces](#Interfaces) section above covers basic usage of the most |
| common bindings object types: `InterfacePtr`, `InterfaceRequest`, and `Binding`. |
| 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. |
| |
| ### Strong Bindings |
| |
| A **strong binding** exists as a standalone object which owns its interface |
| implementation and automatically cleans itself up when its bound interface |
| endpoint detects an error. The |
| [**`MakeStrongBinding`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/strong_binding.h) |
| function is used to create such a binding. |
| . |
| |
| ``` 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 Binding object! |
| }; |
| |
| db::mojom::LoggerPtr logger; |
| mojo::MakeStrongBinding(std::make_unique<LoggerImpl>(), |
| mojo::MakeRequest(&logger)); |
| |
| 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. |
| |
| ### Binding Sets |
| |
| Sometimes it's useful to share a single implementation instance with multiple |
| clients. [**`BindingSet`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/binding_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 `BindingSet` to bind multiple `Logger` requests to a single |
| implementation instance: |
| |
| ``` cpp |
| class LogManager : public system::mojom::LoggerProvider, |
| public system::mojom::Logger { |
| public: |
| explicit LogManager(system::mojom::LoggerProviderRequest request) |
| : provider_binding_(this, std::move(request)) {} |
| ~LogManager() {} |
| |
| // system::mojom::LoggerProvider: |
| void GetLogger(LoggerRequest request) override { |
| logger_bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| // system::mojom::Logger: |
| void Log(const std::string& message) override { |
| LOG(ERROR) << "[Logger] " << message; |
| } |
| |
| private: |
| mojo::Binding<system::mojom::LoggerProvider> provider_binding_; |
| mojo::BindingSet<system::mojom::Logger> logger_bindings_; |
| }; |
| |
| ``` |
| |
| |
| ### InterfacePtr Sets |
| |
| Similar to the `BindingSet` above, sometimes it's useful to maintain a set of |
| `InterfacePtr`s for *e.g.* a set of clients observing some event. |
| [**`InterfacePtrSet`**](https://cs.chromium.org/chromium/src/mojo/public/cpp/bindings/interface_ptr_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(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}); |
| listeners_.ForEach([key, &data](db::mojom::TableListener* listener) { |
| listener->OnRowAdded(key, data); |
| }); |
| } |
| |
| void AddListener(db::mojom::TableListenerPtr listener) { |
| listeners_.AddPtr(std::move(listener)); |
| } |
| |
| private: |
| mojo::InterfacePtrSet<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 bindings to access a single message pipe from |
| multiple sequences. |
| |
| ### Mojom |
| |
| A new keyword `associated` is introduced for interface pointer/request |
| fields. For example: |
| |
| ``` cpp |
| interface Bar {}; |
| |
| struct Qux { |
| associated Bar bar3; |
| }; |
| |
| interface Foo { |
| // Uses associated interface pointer. |
| SetBar(associated Bar bar1); |
| // Uses associated interface request. |
| GetBar(associated Bar& bar2); |
| // Passes a struct with associated interface pointer. |
| PassQux(Qux qux); |
| // Uses associated interface pointer in callback. |
| AsyncGetBar() => (associated Bar bar4); |
| }; |
| ``` |
| |
| It means the interface impl/client will communicate using the same |
| message pipe over which the associated interface pointer/request is |
| passed. |
| |
| ### Using associated interfaces in C++ |
| |
| When generating C++ bindings, the associated interface pointer of `Bar` is |
| mapped to `BarAssociatedPtrInfo` (which is an alias of |
| `mojo::AssociatedInterfacePtrInfo<Bar>`); associated interface request to |
| `BarAssociatedRequest` (which is an alias of |
| `mojo::AssociatedInterfaceRequest<Bar>`). |
| |
| ``` cpp |
| // In mojom: |
| interface Foo { |
| ... |
| SetBar(associated Bar bar1); |
| GetBar(associated Bar& bar2); |
| ... |
| }; |
| |
| // In C++: |
| class Foo { |
| ... |
| virtual void SetBar(BarAssociatedPtrInfo bar1) = 0; |
| virtual void GetBar(BarAssociatedRequest bar2) = 0; |
| ... |
| }; |
| ``` |
| |
| #### Passing associated interface requests |
| |
| Assume you have already got an `InterfacePtr<Foo> foo_ptr`, and you would like |
| to call `GetBar()` on it. You can do: |
| |
| ``` cpp |
| BarAssociatedPtrInfo bar_ptr_info; |
| BarAssociatedRequest bar_request = MakeRequest(&bar_ptr_info); |
| foo_ptr->GetBar(std::move(bar_request)); |
| |
| // BarAssociatedPtr is an alias of AssociatedInterfacePtr<Bar>. |
| BarAssociatedPtr bar_ptr; |
| bar_ptr.Bind(std::move(bar_ptr_info)); |
| bar_ptr->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_request` or `bar_ptr_info`) 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_ptr->DoSomething()` before passing |
| `bar_request`. 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 `AssociatedBinding<Bar>` before processing any subsequent |
| messages. If `bar_request` is in a subsequent message, message dispatching gets |
| into a deadlock. On the other hand, as soon as `bar_request` is sent, `bar_ptr` |
| is usable. There is no need to wait until `bar_request` is bound to an |
| implementation at the remote side. |
| |
| A `MakeRequest` overload which takes an `AssociatedInterfacePtr` pointer |
| (instead of an `AssociatedInterfacePtrInfo` pointer) is provided to make the |
| code a little shorter. The following code achieves the same purpose: |
| |
| ``` cpp |
| BarAssociatedPtr bar_ptr; |
| foo_ptr->GetBar(MakeRequest(&bar_ptr)); |
| bar_ptr->DoSomething(); |
| ``` |
| |
| The implementation of `Foo` looks like this: |
| |
| ``` cpp |
| class FooImpl : public Foo { |
| ... |
| void GetBar(BarAssociatedRequest bar2) override { |
| bar_binding_.Bind(std::move(bar2)); |
| ... |
| } |
| ... |
| |
| Binding<Foo> foo_binding_; |
| AssociatedBinding<Bar> bar_binding_; |
| }; |
| ``` |
| |
| In this example, `bar_binding_`'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 `AssociatedBinding<Bar>` there. |
| |
| When the underlying message pipe is disconnected (e.g., `foo_ptr` or |
| `foo_binding_` is destroyed), all associated interface endpoints (e.g., |
| `bar_ptr` and `bar_binding_`) will receive a connection error. |
| |
| #### Passing associated interface pointers |
| |
| Similarly, assume you have already got an `InterfacePtr<Foo> foo_ptr`, and you |
| would like to call `SetBar()` on it. You can do: |
| |
| ``` cpp |
| AssociatedBind<Bar> bar_binding(some_bar_impl); |
| BarAssociatedPtrInfo bar_ptr_info; |
| BarAssociatedRequest bar_request = MakeRequest(&bar_ptr_info); |
| foo_ptr->SetBar(std::move(bar_ptr_info)); |
| bar_binding.Bind(std::move(bar_request)); |
| ``` |
| |
| The following code achieves the same purpose: |
| |
| ``` cpp |
| AssociatedBind<Bar> bar_binding(some_bar_impl); |
| BarAssociatedPtrInfo bar_ptr_info; |
| bar_binding.Bind(&bar_ptr_info); |
| foo_ptr->SetBar(std::move(bar_ptr_info)); |
| ``` |
| |
| ### Performance considerations |
| |
| When using associated interfaces on different sequences than the master sequence |
| (where the master 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 master interface incur an extra sequence hop during dispatch. |
| |
| Therefore, performance-wise associated interfaces are better suited for |
| scenarios where message receiving happens on the master sequence. |
| |
| ### Testing |
| |
| Associated interfaces need to be associated with a master interface before |
| they can be used. This means one end of the associated interface must be sent |
| over one end of the master interface, or over one end of another associated |
| interface which itself already has a master interface. |
| |
| If you want to test an associated interface endpoint without first |
| associating it, you can use `mojo::MakeIsolatedRequest()`. 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 |
| |
| See [this document](https://www.chromium.org/developers/design-documents/mojo/synchronous-calls) |
| |
| TODO: Move the above doc into the repository markdown docs. |
| |
| ## 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! |
| |
| ### Global Configuration |
| |
| While this feature is quite powerful, it introduces some unavoidable complexity |
| into build system. This stems from the fact that type-mapping is an inherently |
| viral concept: if `gfx::mojom::Rect` is mapped to `gfx::Rect` anywhere, the |
| mapping needs to apply *everywhere*. |
| |
| For this reason we have a few global typemap configurations defined in |
| [chromium_bindings_configuration.gni](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/chromium_bindings_configuration.gni) |
| and |
| [blink_bindings_configuration.gni](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/blink_bindings_configuration.gni). These configure the two supported [variants](#Variants) of Mojom generated |
| bindings in the repository. Read more on this in the sections that follow. |
| |
| For now, let's take a look at how to express the mapping from `gfx::mojom::Rect` |
| to `gfx::Rect`. |
| |
| ### 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 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`). |
| |
| There are other methods a `StructTraits` specialization may define to satisfy |
| some less common requirements. See |
| [Advanced StructTraits Usage](#Advanced-StructTraits-Usage) for details. |
| |
| 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_struct_traits.h`: |
| |
| ``` cpp |
| #include "mojo/public/cpp/bindings/struct_traits.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/mojo/geometry.mojom.h" |
| |
| namespace mojo { |
| |
| template <> |
| class StructTraits<gfx::mojom::RectDataView, 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 |
| ``` |
| |
| And in `//ui/gfx/geometry/mojo/geometry_struct_traits.cc`: |
| |
| ``` cpp |
| #include "ui/gfx/geometry/mojo/geometry_struct_traits.h" |
| |
| namespace mojo { |
| |
| // static |
| template <> |
| 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. |
| |
| ### Enabling a New Type Mapping |
| |
| We've defined the `StructTraits` necessary, but we still need to teach the |
| bindings generator (and hence the build system) about the mapping. To do this we |
| must create a **typemap** file, which uses familiar GN syntax to describe the |
| new type mapping. |
| |
| Let's place this `geometry.typemap` file alongside our Mojom file: |
| |
| ``` |
| mojom = "//ui/gfx/geometry/mojo/geometry.mojom" |
| public_headers = [ "//ui/gfx/geometry/rect.h" ] |
| traits_headers = [ "//ui/gfx/geometry/mojo/geometry_struct_traits.h" ] |
| sources = [ |
| "//ui/gfx/geometry/mojo/geometry_struct_traits.cc", |
| "//ui/gfx/geometry/mojo/geometry_struct_traits.h", |
| ] |
| public_deps = [ "//ui/gfx/geometry" ] |
| type_mappings = [ |
| "gfx.mojom.Rect=gfx::Rect", |
| ] |
| ``` |
| |
| Let's look at each of the variables above: |
| |
| * `mojom`: Specifies the `mojom` file to which the typemap applies. Many |
| typemaps may apply to the same `mojom` file, but any given typemap may only |
| apply to a single `mojom` file. |
| * `public_headers`: Additional headers required by any code which would depend |
| on the Mojom definition of `gfx.mojom.Rect` now that the typemap is applied. |
| Any headers required for the native target type definition should be listed |
| here. |
| * `traits_headers`: Headers which contain the relevant `StructTraits` |
| specialization(s) for any type mappings described by this file. |
| * `sources`: Any implementation sources and headers needed for the |
| `StructTraits` definition. These sources are compiled directly into the |
| generated C++ bindings target for a `mojom` file applying this typemap. |
| * `public_deps`: Target dependencies exposed by the `public_headers` and |
| `traits_headers`. |
| * `deps`: Target dependencies exposed by `sources` but not already covered by |
| `public_deps`. |
| * `type_mappings`: A list of type mappings to be applied for this typemap. The |
| strings in this list are of the format `"MojomType=CppType"`, where |
| `MojomType` must be a fully qualified Mojom typename and `CppType` must be a |
| fully qualified C++ typename. Additional attributes may be specified in square |
| brackets following the `CppType`: |
| * `move_only`: The `CppType` is move-only and should be passed by value |
| in any generated method signatures. Note that `move_only` is transitive, |
| so containers of `MojomType` will translate to containers of `CppType` |
| also passed by value. |
| * `copyable_pass_by_value`: Forces values of type `CppType` to be passed by |
| value without moving them. Unlike `move_only`, this is not transitive. |
| * `nullable_is_same_type`: By default a non-nullable `MojomType` will be |
| mapped to `CppType` while a nullable `MojomType?` will be mapped to |
| `base::Optional<CppType>`. If this attribute is set, the `base::Optional` |
| wrapper is omitted for nullable `MojomType?` values, but the |
| `StructTraits` definition for this type mapping must define additional |
| `IsNull` and `SetToNull` methods. See |
| [Specializing Nullability](#Specializing-Nullability) below. |
| * `force_serialize`: The typemap is incompatible with lazy serialization |
| (e.g. consider a typemap to a `base::StringPiece`, where retaining a |
| copy is unsafe). Any messages carrying the type will be forced down the |
| eager serailization path. |
| |
| |
| Now that we have the typemap file we need to add it to a local list of typemaps |
| that can be added to the global configuration. We create a new |
| `//ui/gfx/typemaps.gni` file with the following contents: |
| |
| ``` |
| typemaps = [ |
| "//ui/gfx/geometry/mojo/geometry.typemap", |
| ] |
| ``` |
| |
| And finally we can reference this file in the global default (Chromium) bindings |
| configuration by adding it to `_typemap_imports` in |
| [chromium_bindings_configuration.gni](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/chromium_bindings_configuration.gni): |
| |
| ``` |
| _typemap_imports = [ |
| ..., |
| "//ui/gfx/typemaps.gni", |
| ..., |
| ] |
| ``` |
| |
| ### 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<shared_buffer>` | `mojo::ScopedSharedBufferHandle` |
| | `FooInterface` | `FooInterfacePtr` |
| | `FooInterface&` | `FooInterfaceRequest` |
| | `associated FooInterface` | `FooAssociatedInterfacePtr` |
| | `associated FooInterface&` | `FooAssociatedInterfaceRequest` |
| | `string` | Value or reference to any type `T` that has a `mojo::StringTraits` specialization defined. By default this includes `std::string`, `base::StringPiece`, and `WTF::String` (Blink). |
| | `array<T>` | Value or reference to any type `T` that has a `mojo::ArrayTraits` specialization defined. By default this includes `std::vector<T>`, `mojo::CArray<T>`, and `WTF::Vector<T>` (Blink). |
| | `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>`, and `WTF::HashMap<T>` (Blink). |
| | `FooEnum` | Value of any type that has an appropriate `EnumTraits` specialization defined. By default this inlcudes 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. |
| |
| ### 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 `FooInterface&`) 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 `WTF::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 WTF::String& data) override { |
| // ... |
| } |
| }; |
| ``` |
| |
| In addition to using different C++ types for builtin strings, arrays, and maps, |
| the global typemap configuration for default and "blink" variants are completely |
| separate. To add a typemap for the Blink configuration, you can modify |
| [blink_bindings_configuration.gni](https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/blink_bindings_configuration.gni). |
| |
| All variants share some definitions which are unaffected by differences in the |
| type mapping configuration (enums, for example). 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. |
| |
| Finally, note that for `mojom` GN targets, there is implicitly a corresponding |
| `mojom_{variant}` target defined for any supported bindings configuration. So |
| for example if you've defined in `//sample/BUILD.gn`: |
| |
| ``` |
| import("mojo/public/tools/bindings/mojom.gni") |
| |
| mojom("interfaces") { |
| sources = [ |
| "db.mojom", |
| ] |
| } |
| ``` |
| |
| Code in Blink which wishes to use the generated Blink-variant definitions must |
| depend on `"//sample:interfaces_blink"`. |
| |
| ## Versioning Considerations |
| |
| For general documentation of versioning in the Mojom IDL see |
| [Versioning](/mojo/public/tools/bindings/README.md#Versiwoning). |
| |
| This section briefly discusses some C++-specific considerations relevant to |
| versioned Mojom types. |
| |
| ### Querying Interface Versions |
| |
| `InterfacePtr` defines the following methods to query or assert remote interface |
| version: |
| |
| ```cpp |
| void QueryVersion(const base::Callback<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 `InterfacePtr` 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 |
| |
| For convenience, every extensible enum has a generated helper function to |
| determine whether a received enum value is known by the implementation's current |
| version of the enum definition. For example: |
| |
| ```cpp |
| [Extensible] |
| enum Department { |
| SALES, |
| DEV, |
| RESEARCH, |
| }; |
| ``` |
| |
| generates the function in the same namespace as the generated C++ enum type: |
| |
| ```cpp |
| inline bool IsKnownEnumValue(Department value); |
| ``` |
| |
| ### Using Mojo Bindings in Chrome |
| |
| See [Converting Legacy Chrome IPC To Mojo](/ipc/README.md). |
| |
| ### Additional Documentation |
| |
| [Calling Mojo From Blink](https://www.chromium.org/developers/design-documents/mojo/calling-mojo-from-blink) |
| : A brief overview of what it looks like to use Mojom C++ bindings from |
| within Blink code. |