Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(324)

Unified Diff: mojo/public/cpp/bindings/README.md

Issue 2783223004: Adds lots of Mojo documentation (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 Graphic](https://goo.gl/6CdlbH) 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.
+
+![Diagram illustrating InterfacePtr and InterfaceRequest on either end of a message pipe](https://docs.google.com/drawings/d/17d5gvErbQ6DthEBMS7I1WhCh9bz0n12pvNjydzuRfTI/pub?w=600&h=100)
+
+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.
+
+![Diagram illustrating a message traveling on a pipe from LoggerPtr to LoggerRequest](https://docs.google.com/a/google.com/drawings/d/1jWEc6jJIP2ed77Gg4JJ3EVC7hvnwcImNqQJywFwpT8g/pub?w=648&h=123)
+
+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`.
+
+![Diagram illustrating the progression of binding a request, reading a pending message, and dispatching it](https://docs.google.com/drawings/d/1c73-PegT4lmjfHoxhWrHTQXRvzxgb0wdeBa35WBwZ3Q/pub?w=550&h=500)
+
+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.
« ipc/README.md ('K') | « mojo/public/cpp/README.md ('k') | mojo/public/cpp/system/README.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698