For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎

Mojo Migration Guide

Summary

We’re migrating Chrome’s IPCs to Mojo, our new IPC system. See the chromium-dev post for the motivation behind this project.

How do I migrate my IPC to Mojo?

Don’t panic

If you get stuck reach out to chromium-mojo@ and we’ll help. :)

Read the Mojo documentation

The Mojo documentation and a getting started guide can be found on the Mojo documentation page. There’s also a Chrome IPC To Mojo IPC Cheat Sheet which is particularly useful for this conversion.

Claim your message(s)

We track the messages that still need to be converted in two spreadsheets:
Please put your username next to the one(s) you’re converting to prevent duplication of effort. If you’re not very familiar with the messages you plan to convert, it might be worth asking the owner first.

Convert your Chrome IPCs to Mojo

Converting from Chrome IPC to Mojo is mostly straightforward, but still requires some thought. Here we outline the basic approach. See the Chrome IPC To Mojo IPC Cheat Sheet for more details. See the "Common scenarios" section below for commonly occurring scenarios.
 
The below examples snippets were taken from https://codereview.chromium.org/2024363002, where you can find a complete example. The code below removes some details from that CL for clearer presentation.

Convert the message definition

The old Chrome IPC system uses macros to generate IPC “stubs”. In Mojo we use an IDL. For example, if you previously had this old-style IPC message defined in mime_registry_messages.h:
 
 
IPC_SYNC_MESSAGE_CONTROL1_1(MimeRegistryMsg_GetMimeTypeFromExtension,   
                            base::FilePath::StringType /* extension */, 
                            std::string /* mime_type */)
 
You need to replace that with this .mojom file:
 
 
module blink.mojom;
 
 
interface MimeRegistry {
  [Sync]
  GetMimeTypeFromExtension(string extension) => (string mime_type);
};
 
Note that Mojo can group several messages together into an interface, which helps bring some structure to Chrome’s many IPCs.

Whitelist the IPC

We don't want every process to be able to talk to every other process so we maintain a whitelist of which interfaces each process exports. You will need to add your interface to the right whitelist. If you don't you'll find that e.g. browser tests fail with an error saying that the connection to the interface was disallowed.
 
There are several of these files e.g. chrome/browser/chrome_content_utility_manifest_overlay.json lists the interfaces exported by the utility process to the browser process. You will have to find the corresponding JSON file your process.

Fix build files and includes

Add a build target for your new .mojom file:
 
 
import("//mojo/public/tools/bindings/mojom.gni")
mojom("mime_registry_mojom") {
  sources = [
    "platform/mime_registry.mojom",
  ]
}
 
If your new .mojom file imports any other .mojom files, add their targets as public_deps above.
 
Include the generated Mojo header in your C++ file instead of the old IPC header:
 
#include "third_party/WebKit/public/platform/mime_registry.mojom.h"

Register the interface implementation

Interface implementations (the "server" side of the interface) need to be registered before they can be called by a client. The registration happens in interface registries. Such registries can be accessed in the various processes (e.g. one is passed to RenderProcessHostImpl::RegisterMojoInterfaces, in which you can register your service).
 
Here’s now you get the InterfaceRegistry to register the interface implementation on, depending on where the message is sent from and if the old Chrome IPC message was routed or not:
 
   Renderer-initiated  Browser-initiated
 Routed  RenderFrameHost::GetInterfaceRegistry()  RenderFrame::GetInterfaceRegistry()
 Control  RenderProcessHostImpl::RegisterMojoInterfaces()  RenderThreadImpl::Init()
Example:
 
 
GetInterfaceRegistry()->AddInterface(
    base::Bind(&MimeRegistryImpl::Create),
    BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
 
(In the above example we want messages to be handled on a particular thread, so we pass an optional argument to indicate that.)
 
As you can see above, the interface implementation is created lazily when a connection is created through the call to MimeRegistryImpl::Create. This is done to not unnecessarily create instances that might not be used. The ownership of the created instances is often tied to the lifetime of the connection, using a StrongBinding:
 
 
 
// static
void MimeRegistryImpl::Create(blink::mojom::MimeRegistryRequest request) {
  mojo::MakeStrongBinding(
      base::MakeUnique<MimeRegistryImpl>(),
      std::move(request));
}
 
(MakeStrongBinding was introduced after the CL this example is based on, as a simplification.)
 

Call the interface

Calling the interface requires creating a connection (which can be reused for multiple calls) and an interface proxy to use to call the implementation (the server):
 
 
// Only once:
blink::mojom::MimeRegistryPtr mime_registry;
RenderThread::Get()->GetRemoteInterfaces()->GetInterface(
    mojo::GetProxy(&mime_registry));
 
// Every time:
std::string mime_type;
if (!mime_registry->GetMimeTypeFromExtension(
    base::UTF16ToUTF8(base::string16(file_extension)), &mime_type)) {
  return string();  // Error handling.
}
 
Here’s how you get the InterfaceProvider to lookup the interface implementation from, depending on where the message is sent from and if the old Chrome IPC message was routed or not:
 
   Browser-initiated  Renderer-initiated
 Routed  RenderFrameHost::GetRemoteInterfaces()  RenderFrame::GetRemoteInterfaces()
 Control  RenderProcessHost::GetRemoteInterfaces()  RenderThread::GetRemoteInterfaces()

Two approaches to migrating your IPC

Longer term we'd like Chrome to consist of more loosely connected services. This means that we'd like to avoid unnecessary ordering constraints between Mojo interfaces. This is why Mojo messages aren't ordered between interfaces (unless you use associated interfaces) by default.
 
Because today there are many such ordering dependencies, you have two options when converting IPC to Mojo:
  • For things that could be easily made a standalone service (e.g. a JSON parser) use the above means of registering your interface.
  • For things that can't be easily converted into standalone services, use the helpers described in "Converting BrowserMessageFilters" and "Converting WebContentsObservers" below. These provide stronger (i.e. the same as before) ordering guarantees for messages and lifetime guarantees for the service implementations.

Common scenarios

Converting BrowserMessageFilters

Example CL: https://codereview.chromium.org/2167513003

Browser message filters are quite straightforward to convert:

  1. Have your message filter inherit from BrowserAssociatedInterface. You typically can just keep the old implementation with some minor tweaks e.g. you need to remove the On prefix from the message handler method names.
  2. Optionally override methods such as OnDestruct if you e.g. need your implementation to be deleted on a certain thread.

Converting WebContentsObservers

 
Use the GetAssociatedInterfaceRegistry and GetRemoteAssociatedInterfaces helpers to register your service and connect your client. Your messages will be ordered w.r.t. all old Chrome IPCs, WebContentsObservermethods, and other interfaces using these helpers.

Lifetime issues

In the typical case we use StrongBinding for the server side. StrongBinding takes ownership of the interface implementation and will delete it when the connection to the client is closed. This means that the implementation will be deleted quite "late" i.e. some time before the message loop is. This in turn means that some care must be taken when referring to other objects in the implementation's destructor, as they might have been deleted.

These issues can typically be avoided using the helpers described above.

Handling "late" messages

Messages can in principle arrive at any time before the connection is closed. In particular messages could arrive e.g. after the render process host has been deleted. This means that any state referred to in the interface method implementations must be either

  • owned
  • referred to through some form of weak pointer (so we can detect if it still exists), or
  • or looked up on each use e.g. using the process ID (which is not unlike using a weak pointer).

These issues can typically be avoided using the helpers described above.

Mocking in tests

Unlike Chrome IPC, Mojo IPCs can currently only be mocked on the server side, meaning that the test will actually send a message and your mock will make sure that it arrived. This introduces a few extra steps in your test.

First you need a mock of the interface. Your mock is probably be similar to your old Chome IPC test. Example:

class MockPageLoadMetrics : public mojom::PageLoadMetrics {
 public:
  MOCK_METHOD2(TimingUpdated,
               void(const PageLoadTiming&, const PageLoadMetadata&));
};
 
Second, to put it all together, you create a client connection connected to your mock:
 
 
class PageTimingMetricsSenderTest : public testing::Test {
 public:
  PageTimingMetricsSenderTest() : binding_(&mock_page_load_metrics_) {}
 
 protected:
  void SetUp() override {
    binding_.Bind(mojo::GetProxy(&page_load_metrics_));
  }
 
  base::MessageLoop loop_;  // 1
  mojom::PageLoadMetricsPtr page_load_metrics_;  // 2
  MockPageLoadMetrics mock_page_load_metrics_;  // 3
  mojo::Binding<mojom::PageLoadMetrics> binding_;
};
  1. You will not actually use the loop_ variable, but one need to exist and this declaration causes a global message loop to be created.
  2. This is the client side, which you will pass to the class under of test (which will need to e.g. have a test-only constructor that allows it to be injected).
  3. This is your mock (aka the server side).
Third, after a method that causes a message to be sent is called, we need to manually step the message loop to actually deliver the message (i.e. to your server-side mock):
 
 
TEST_F(PageTimingMetricsSenderTest, MyTest) {
  MyClient client(std::move(page_load_metrics_));
  client.SomeMethod();  // Calls TimingUpdated internally.  
  base::RunLoop().RunUntilIdle();  // Actually deliver message(s).
  EXPECT_CALL(mock_page_load_metrics_, TimingUpdated(_, _));
}

Replacing request/response messages pairs

A set of request/response messages

 
IPC_MESSAGE_ROUTED0(FooHostMsg_Frobinate)
IPC_MESSAGE_ROUTED0(FooMsg_DidFrobinate)
IPC_MESSAGE_ROUTED1(FooMsg_FrobinateError, std::string /* error */)
 
should be replaced by a method with a return value (with an optional error)
 
 
Interface Foo {
  Frobinate() => (bool success, string? error);
};
 
This doesn’t work if the reply isn’t always sent (in which case you need two interfaces, similar to the current Chrome IPC situation).

Replacing routing IDs

Chrome IPC uses routing IDs to dispatch messages specific to a particular RenderFrame(Host). When converting to Mojo, a whole connection may be specific to a particular frame. It is the responsibility of interface implementation to retain this knowledge. Example:

 
GetInterfaceRegistry->AddInterface(
    base::Bind(&CreateUsbDeviceManager, render_frame_host));
 
When sending messages, instead of passing the routing ID in the message, use the InterfaceProvider on the RenderFrame(Host) corresponding to the routing ID.

Dealing with message ordering

Mojo doesn’t provide a FIFO guarantee between messages sent on different message pipes. If you need cross-interface message ordering either

  • use the associated interface feature or
  • use the GetAssociatedInterfaceRegistry and GetRemoteAssociatedInterfaces helpers mentioned earlier.

Dealing with circular build dependencies

With Mojo’s typemap feature, which lets you have Mojo automatically communicate in terms of your own existing C++ types, there are situations where you might end up with a circular build dependency. Here’s an example:
 
 
Here we have a mojom target and the content component involved in a cycle. The mojom typemaps some type defined in your_type.h and thus depends on content. Content contains code using the mojom and thus depends on it, hence the cycle.
 
The answer here is to pick one component that will link the mojom symbols and then re-export the symbols, for use in another component. Example (CL):
 
mojom("mojo_bindings") {
  # ...
 
  # The chromium variant must be linked with content and use the same export
  # settings in component build because of the WebBluetoothDeviceId typemap
  # inside content.
  export_class_attribute = "CONTENT_EXPORT"
  export_define = "CONTENT_IMPLEMENTATION=1"
  export_header = "content/common/content_export.h"
  # Similarly, the blink variant must be linked with the platform component
  # since it uses types from it in its typemaps.
  export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
  export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
  export_header_blink = "third_party/WebKit/public/platform/WebCommon.h"
}

Example CLs

Some additional example CLs are listed in this document.
 
 
 

[Chromium文档转载,第001章] Mojo Migration Guide的更多相关文章

  1. [Chromium文档转载,第003章]Proposal: Mojo Synchronous Methods

    Proposal: Mojo Synchronous Methods yzshen@chromium.org 02/02/2016 Overview Currently there are quite ...

  2. [Chromium文档转载,第002章]Mojo C++ Bindings API

    Mojo C++ Bindings API This document is a subset of the Mojo documentation. Contents Overview Getting ...

  3. [Chromium文档转载,第006章]Chrome IPC To Mojo IPC Cheat Sheet

    For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎ Chrome IPC To Mojo IPC Cheat Sheet 目录 1 O ...

  4. [Chromium文档转载,第005章]Calling Mojo from Blink

    For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎ Calling Mojo from Blink Variants Let's as ...

  5. [Chromium文档转载,第004章]Mojo Synchronous Calls

    For Developers‎ > ‎Design Documents‎ > ‎Mojo‎ > ‎ Synchronous Calls Think carefully before ...

  6. [Chromium文档转载,第007章]JNI on Chromium for Android

    Overview JNI (Java Native Interface) is the mechanism that enables Java code to call native function ...

  7. 用R创建Word和PowerPoint文档--转载

    https://www.jianshu.com/p/7df62865c3ed Rapp --简书 Microsoft的Office软件在办公软件领域占有绝对的主导地位,几乎每个职场人士都必须掌握Wor ...

  8. java实现支付宝接口--文档..转载

    //实现java支付宝很简单,只要从支付宝官方下载   http://help.alipay.com/support/index_sh.htm下载程序,配置一下参数就OK了:   1.先到http:/ ...

  9. iOS开发主要参考文档(转载)

    Objective-C,语言的系统详细资料.这是做iOS开发的前题与基础.https://developer.apple.com/library/ios/#documentation/Cocoa/Co ...

随机推荐

  1. mayan 游戏 search

    纯搜索,,,模拟,,还不算太难,,就是细节略繁琐 首先因为题目要求保证字典序,所以显然把右边的块换到左边不如把左边的块换到右边优, 所以可以进行不小规模的剪枝,之后显然交换两块相同的色块没有意义,至此 ...

  2. 在jqueryEasyUI界面将时间以日期加时分秒的格式显示

    问题描写叙述: oracle 10G中用户表有一个字段是日期型.数据格式为yyyy-MM-dd HH:mm:ss,前端显示时仅仅能显示成yyyy-MM-dd 后面的 HH:mm:ss不显示. 经过一番 ...

  3. [USACO09JAN] 气象测量/气象牛The Baric Bovine 解题报告(DP)

    题目链接:https://www.luogu.org/problemnew/show/P2933 Description 为了研究农场的气候,Betsy帮助农夫John做了N(1 <= N &l ...

  4. es6 --- 功能

    标记的模板文字 1.模板文字!确实很棒.我们不再会这样做…. const concatenatedString = "I am the " + number + "per ...

  5. BZOJ 3110 线段树套线段树

    思路: 外围一个权值线段树 里面是个区间线段树 搞一个标记永久化 //By SiriusRen #include <cstdio> #include <cstring> #in ...

  6. POJ 2449 第k短路 Dijkstra+A*

    这道题我拖了半年,,,终于写出来了 思路: 先反向建边 从终点做一次最短路 ->这是估价函数h(x) 再正常建边,从起点搜一遍 (priority_queue(h(x)+g(x))) g(x)是 ...

  7. ZooKeeper Recipes and Solutions

    原文地址:http://zookeeper.apache.org/doc/current/recipes.html 参考:https://zookeeper.apache.org/doc/trunk/ ...

  8. SparkSQL基础

    * SparkSQL基础 起源: 1.在三四年前,Hive可以说是SQL on Hadoop的唯一选择,负责将SQL编译成可扩展的MapReduce作业.鉴于Hive的性能以及与Spark的兼容,Sh ...

  9. Android Gallery和ImageSwitcher同步自动(滚动)播放图片库

    本文主要内容是如何让Gallery和ImageSwitcher控件能够同步自动播放图片集 ,看起来较难,然而,实现的方法非常简单, 请跟我慢慢来.总的来说,本文要实现的效果如下图:(截图效果不怎么好) ...

  10. RadioButton的drawableTop图片文字不居中

    在安卓应用的开发中,一般普通应用用到最多的就是底部放一个RadioGroup实现切换的布局,今天在实现的时候,却出现了底部RadiButton的drawableTop图片及文字无法居中的情况,经过对比 ...