| Index: mojo/public/cpp/bindings/README.md
|
| diff --git a/mojo/public/cpp/bindings/README.md b/mojo/public/cpp/bindings/README.md
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b37267a338f5a2337c3752fef1f25d1096226c5b
|
| --- /dev/null
|
| +++ b/mojo/public/cpp/bindings/README.md
|
| @@ -0,0 +1,1231 @@
|
| +#  Mojo C++ Bindings API
|
| +This document is a subset of the [Mojo documentation](/mojo).
|
| +
|
| +[TOC]
|
| +
|
| +## Overview
|
| +The Mojo C++ Bindings API leverages the
|
| +[C++ System API](/mojo/public/cpp/system) 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),
|
| +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/).
|
| +
|
| +## 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",
|
| + ]
|
| +}
|
| +```
|
| +
|
| +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/business/public/interfaces/factory.mojom.cc
|
| +out/gen/services/business/public/interfaces/factory.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 binding each end:
|
| +
|
| +``` cpp
|
| +#include "sample/logger.mojom.h"
|
| +
|
| +mojo::MessagePipe pipe;
|
| +sample::mojom::LoggerPtr logger;
|
| +sample::mojom::LoggerRequest request;
|
| +
|
| +logger.Bind(sample::mojom::LoggerPtrInfo(std::move(pipe.handle0), 0u));
|
| +request.Bind(std::move(pipe.handle1));
|
| +```
|
| +
|
| +That's pretty verbose, but the C++ Bindings library provides more convenient
|
| +ways 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;
|
| +sample::mojom::LoggerRequest request = mojo::MakeRequest(&logger);
|
| +```
|
| +
|
| +and the `InterfaceRequest<T>` constructor can also take an explicit
|
| +`InterfacePtr<T>*` output argument:
|
| +
|
| +``` cpp
|
| +sample::mojom::LoggerPtr logger;
|
| +sample::mojom::LoggerRequest request(&logger);
|
| +```
|
| +
|
| +Both of these last two snippets are 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 thread to thread, whereas a
|
| +bound `InterfacePtr<T>` is bound to a single thread.
|
| +
|
| +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 thread-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
|
| +thread:
|
| +
|
| +``` 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::Callback<void(const std::string& message)>;
|
| +
|
| + virtual void GetTail(const 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(const GetTailCallback& callback) override {
|
| + 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::Bind(&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 there are no remaining messages available on a pipe and the remote end has
|
| +been closed, a connection error will be triggered on the local end. Connection
|
| +errors may also be triggered by automatic forced local pipe closure due to
|
| +*e.g.* a validation error when processing a received message.
|
| +
|
| +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::Bind([] { 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::Bind(&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 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.
|
| +
|
| +### Sending Interfaces Over Interfaces
|
| +
|
| +Now 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#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(base::MakeUnique<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.chromim.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(base::MakeUnique<DatabaseImpl>(),
|
| + mojo::MakeRequest(&logger));
|
| +
|
| +logger->Log("NOM NOM NOM MESSAGES");
|
| +```
|
| +
|
| +Now as long as `logger` remains open somewhere in the system, the bound
|
| +`DatabaseImpl` 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
|
| +
|
| +See [this document](https://www.chromium.org/developers/design-documents/mojo/associated-interfaces).
|
| +
|
| +TODO: Move the above doc into the repository markdown docs.
|
| +
|
| +## 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.com/chromium/src/mojo/public/tools/bindings/chromium_bindings_configuration.gni)
|
| +and
|
| +[blink_bindings_configuration.gni](https://cs.chromium.com/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" ]
|
| +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 private implementation sources needed for the `StructTraits`
|
| + definition.
|
| +* `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.
|
| +
|
| +
|
| +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.com/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.
|
| +
|
| +### 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 you have on some occasions 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#Versioning).
|
| +
|
| +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);
|
| +```
|
| +
|
| +### 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.
|
|
|