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. Docker学习总结(12)——非常详细的 Docker 学习笔记

    一.Docker 简介 Docker 两个主要部件: Docker: 开源的容器虚拟化平台 Docker Hub: 用于分享.管理 Docker 容器的 Docker SaaS 平台 -- Docke ...

  2. OpenStack-API开发

    介绍两种OpenStack-API(Java版)--jcoulds && openstack4j Jclouds 1.介绍 jclouds -该API提供云计算环境的可移植抽象层以及云 ...

  3. HDU 4704 Sum Fermat定律

    Problem Description   Sample Input 2   Sample Output 2 Hint 1. For N = 2, S(1) = S(2) = 1. 2. The in ...

  4. android mvp高速开发框架介绍(dileber使用之小工具使用)

    android mvp框架:dileber(https://github.com/dileber/dileber.git) 继续为大家介绍android mvp开源框架 dileber 官方交流qq群 ...

  5. 从头认识Spring-1.16 SpEl对集合的操作(1)-建立集合以及訪问集合的元素,以&lt;util:list/&gt;为例

    这一章节我们来讨论一下怎样建立集合以及訪问集合的元素? 1.建立集合? (1)domain 蛋糕类: package com.raylee.my_new_spring.my_new_spring.ch ...

  6. 软件project之软件设计

    英雄是随着历史的时代产生的.软工也不例外.软件project这一门学科主要是为了解决当代软件危机而诞生的, 学习软件project的视频过后,最终让我揭开了它的神奇面纱,让我对软工设计有了一个初步的认 ...

  7. 递归神经网络——就是解决AST这样的问题

    原文:https://zybuluo.com/hanbingtao/note/626300 有时候把句子看做是词的序列是不够的,比如下面这句话『两个外语学院的学生』: 上图显示了这句话的两个不同的语法 ...

  8. Android项目实战(四十四):浅谈Postman (网络请求调试插件)

    前言: Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件.    在项目开发中,可以依赖此工具模拟API测试. 使用详解: 各种情况Api的模拟请求的Postman使用方 ...

  9. day01-Python介绍,安装,idea

    一. python 简介 Python,读作['paɪθɑn],翻译成汉语是蟒蛇的意思,Python 的 logo 也是两条缠绕在一起的蟒蛇的样子,然而 Python 语言和蟒蛇实际上并没有一毛钱关系 ...

  10. 第一章、zabbix安装

    前言: 注意:本文不涉及性能测试.性能优化中的监控,所有文字的出发点都是日常运维监控. 在开始之前,我们还是先统一下认识:要监控一个对象,需要掌握哪些东西呢? 监控对象的理解:要监控的对象你是否了解呢 ...