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. |