For Developers‎ > ‎Design Documents‎ > ‎


From the chromium-dev post introducing src/mojo:

This is an effort to extract a common platform out of Chrome's renderer and plugin processes that can support multiple types of sandboxed content, such as HTML, Pepper, or NaCl.

That sounds like a fairly lofty goal. What it really boils down to is isolating the components of our multi-process architecture that are concerned with process management, sandboxing and IPC. Much of this work is / will be straightforward extraction of code that is currently part of src/content/ into a narrower submodule.

In part with this effort, we are re-thinking IPC. Chrome IPC has served us well, but we see some ways in which it could be improved:
  • The IPC system should make connections between services and clients more of a first-class concept. We have a lot of code that is effectively just doing that in an ad-hoc fashion for different services.
  • Related to the above, the IPC system should make it easy to move services to different threads or processes with minimal disruption to clients.
  • We should have a way to machine-generate C++ bindings for IPC. Imagine if making your code callable from another process was as simple as implementing an interface. That could help with code clarity as well as enable easier/better unit and fuzz testing.
  • The IPC system should make it easier to setup and pass around byte streams, shared memory and files. There is a bit of a barrier to using these things correctly in the current setup, which often results in people choosing less efficient methods of passing around data.
  • The IPC system should use a messaging format that is optionally capable of being used in cases where we want to talk between end-points built from different versions of the code base. This would open up the possibility of breaking Chrome up into components that are versioned separately. (Note: We already do this to a certain extent by leveraging Pepper, but growing Pepper for these use cases is costly if the APIs are not general purpose.)
  • Related to the above, we should have a binary pickling format that is more resilient to version skew. See the mess that is content/common/ for an example of how base::Pickle is not at all the right choice for archiving data structures.
The above is just a taste of some of the benefits to be had. More details as they emerge :-)


The Mojo system API provides a small suite of low-level IPC primitives: message pipes, data pipes, and shared buffers. On top of this API we’ve built higher-level bindings APIs to simplify messaging for consumers writing C++, Java, or JavaScript code.

This document focuses primarily on using C++ bindings, which is the most common usage encountered by Chromium developers.

Message Pipes

A message pipe is a lightweight primitive for reliable bidirectional transfer of relatively small packets of data between two endpoints. Mojo primitives, represented by their handles, can also be sent over a message pipe.

Because we bootstrap a primordial message pipe between the browser process and each child process, this in turn means that you can create a new pipe and ultimately send either end to any process, and the two ends will still be able to talk to each other seamlessly and exclusively.

While message pipes can carry unstructured data, they are almost always used in conjunction with generated bindings.

Data Pipes

When large amounts of data need to be sent, a data pipe should be used. It's unidirectional and is internally implemented using shared memory. Unlike a simple shared memory buffer however, data pipes provide some rudimentary signalling support.

Shared Buffer

This is standard shared memory for sharing large amounts of data between processes.



Mojom is the IDL for Mojo interfaces. Given a .mojom file, the bindings generator outputs bindings for all three of the currently supported languages.

For example:
// src/components/frob/public/interfaces/frobinator.mojom
module frob.mojom;

interface Frobinator {

would generate the following outputs:

The generated code hides away all the details of serializing and deserializing messages on either end of a pipe.

The C++ header (frobinator.mojom.h) defines an abstract class for each mojom interface specified. Namespaces are derived from the module name.

NOTE: Chromium convention for component foo’s module name is foo.mojom. This means all mojom-generated C++ typenames for component foo will live in the foo::mojom namespace to avoid collisions with non-generated typenames.

In this example the generated frob::mojom::Frobinator has a single pure virtual function:
namespace frob {

class Frobinator {
  virtual void Frobinate() = 0;

}  // namespace frob

To create a Frobinator service, one simply implements foo::Frobinator and provides a means of binding pipes to it.


The Mojo bindings are what connect a class implementing an interface to a message pipe.

Let’s look at some sample code:
// src/components/frob/

#include "components/frob/public/interfaces/frobinator.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"

namespace frob {

class FrobinatorImpl : public mojom::Frobinator {
  FrobinatorImpl(mojom::FrobinatorRequest request)
      : binding_(this, std::move(request)) {}
  ~FrobinatorImpl() override {}

  // mojom::Frobinator:
  void Frobinate() override { DLOG(INFO) << "I can't stop frobinating!"; }

  mojo::Binding<mojom::Frobinator> binding_;

}  // namespace frob

The first thing to note is that mojo::Binding<T> binds one end of a message pipe to an implementation of a service. This means it watches that end of the pipe for incoming messages; it knows how to decode messages for interface T, and it dispatches them to methods on the bound T implementation.

mojom::FrobinatorRequest is a generated type alias for mojo::InterfaceRequest<mojom::Frobinator> and is essentially semantic sugar for a strongly-typed message pipe endpoint. A common way to create new message pipes is via the MakeRequest call defined in interface_request.h:
frob::mojom::FrobinatorPtr proxy;
mojom::FrobinatorRequest request = mojo::MakeRequest(&proxy);

This creates a new message pipe with one end owned by proxy and the other end owned by request. It has the nice property of attaching common type information to each end of the pipe.

Note that InterfaceRequest<T> doesn’t actually do anything. It just scopes a pipe endpoint and associates it with an interface type at compile time. As such, other typed service binding primitives such as mojo::Binding<T> take these objects as input when they need an endpoint to bind to.

mojom::FrobinatorPtr is a generated type alias for mojo::InterfacePtr<mojom::Frobinator>. An InterfacePtr<T> scopes a message pipe endpoint as well, but it also internally implements every method on T by serializing a corresponding message and writing it to the pipe.

Hence we can put this together to talk to a FrobinatorImpl over a pipe:
frob::mojom::FrobinatorPtr frobinator;
frob::FrobinatorImpl impl(MakeRequest(&frobinator));

// Tada!

Behind the scenes this serializes a message corresponding to the Frobinate request and writes it to one end of the pipe. Eventually (and incidentally, very soon after), impl’s internal mojo::Binding will decode this message and dispatch a call to impl.Frobinate().

NOTE: In this example the service and client are in the same process, and this works just fine. If they were in different processes (see the example below in Exposing Services in Chromium), the call to Frobinate() would look exactly the same!

This doc might be helpful to understand the behavior of mojo.

Responding to Requests

A common idiom in Chromium IPC is to keep track of IPC requests with some kind of opaque identifier (i.e. an integer request ID) so that you can later respond to a specific request using some nominally related message in the other direction.

This is baked into mojom interface definitions. We can extend our Frobinator service like so:
module frob.mojom;

interface Frobinator {
  GetFrobinationLevels() => (int min, int max);

and update our implementation:
class FrobinatorImpl : public mojom::Frobinator {
  // ...

  // mojom::Frobinator:
  void Frobinate() override { /* ... */ }
  void GetFrobinationLevels(const GetFrobinationLevelsCallback& callback) {
    callback.Run(1, 42);

When the service implementation runs callback, the response arguments are serialized and sent back over the pipe. The proxy on the other end knows how to read this response and will in turn dispatch it to a callback on that end:
void ShowLevels(int min, int max) {
  DLOG(INFO) << "Frobinator min=" << min << " max=" << max;

// ...

  mojom::FrobinatorPtr frobinator;
  FrobinatorImpl impl(MakeRequest(&frobinator));


This does what you’d expect.

Note: The service implementation shouldn't silently drop the callback without running it, unless it closes the message pipe (usually by destroying/closing the Binding object). Otherwise, it will cause DCHECK in debug build, and lead to message pipe disconnection in debug/release build.

Data Pipe

First, create a data pipe. Note that error handling has been removed for simplicity.
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
mojo::MojoCreateDataPipeOptions options = {


mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle));

To send data:
std::string data = "Hello world.";
uint32_t data_size = static_cast<uint32_t>(data.size() + 1);

The receiver:
uint32_t data_size = 0;
mojo::MojoHandleSignalsState state;
mojo::MojoWait(consumer_handle.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
               MOJO_DEADLINE_INDEFINITE, &state));
CHECK_EQ(MOJO_HANDLE_SIGNAL_READABLE, state.satisfied_signals);

mojo::ReadDataRaw(consumer_handle.get(), nullptr, &data_size,

CHECK_NE(0, static_cast<int>(data_size));

char data[64];

CHECK_LT(static_cast<int>(data_size), 64);
mojo::ReadDataRaw(consumer_handle.get(), data, &data_size,

Data pipe has a number of other features, see the header for more information. This includes two-phase reads and writes to avoid copies and the ability to read data without consuming it. See the header for details.

Shared Buffer

Simple example:

// Create a 1 KiB shared buffer.

mojo::SharedBuffer shared_buffer(1024);

mojo::ScopedSharedBufferHandle shared_buffer_handle =


// Map the entire buffer.

mojo::ScopedSharedBufferMapping mapping = shared_buffer_handle->Map(1024);


// Write to the buffer.

reinterpret_cast<char*>(mapping.get())[1023] = 'a';

// Unmap.


// Create a read-only handle to the shared buffer.

mojo::ScopedSharedBufferHandle read_only_shared_buffer_handle =




// Map just the last byte of the buffer.

mojo::ScopedSharedBufferMapping partial_mapping =

   read_only_shared_buffer_handle->MapAtOffset(1, 1023);


// Closing the handle does not affect mappings mapped from it.



// Check that what we wrote before is still there.

DCHECK_EQ(reinterpret_cast<char*>(partial_mapping.get())[0], 'a');

// Automatically unmap when |partial_mapping| falls out of scope.

Further Documentation

Questions, Discussion, etc.

A good place to find highly concentrated doses of people who know and care about Mojo in Chromium would be the  mailing list.