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. light oj 1094 Farthest Nodes in a Tree(树的直径模板)

    1094 - Farthest Nodes in a Tree problem=1094" style="color:rgb(79,107,114)"> probl ...

  2. 智课雅思短语---四、Exploit to the full one’s favorableconditions and avoid unfavorable ones

    智课雅思短语---四.Exploit to the full one’s favorableconditions and avoid unfavorable ones 一.总结 一句话总结:扬长避短 ...

  3. lucene LZ4 会将doc存储在一个chunk里进行Lz4压缩 ES的_source便如此

    默认情况下,Elasticsearch 用 JSON 字符串来表示文档主体保存在 _source 字段中.像其他保存的字段一样,_source 字段也会在写入硬盘前压缩.The _source is ...

  4. 35.angularJS的ng-repeat指令

    转自:https://www.cnblogs.com/best/tag/Angular/ 1. <html> <head> <meta charset="utf ...

  5. linux中的raid

    参考文档 http://www.cnblogs.com/ivictor/p/6099807.html 制作raid5 http://blog.51cto.com/11134648/2103384 RA ...

  6. Perceptron Learning Algorithm(python实现)

    一.概论 对于给定的n维(两种类型)数据(训练集),找出一个n-1维的面,能够"尽可能"地按照数据类型分开.通过这个面,我们可以通过这个面对测试数据进行预测. 例如对于二维数据,要 ...

  7. codeforces 401 B Sereja and Contests【贪心】

    题意:给出最后的时间n,div 1必须和div2一起举行,并且div2的时间总是比div1大1 给出Sereja(只能参加div2)参加过的k场比赛的时间,问他最少错过了多少场div2,最多错过了多少 ...

  8. 逻辑学总结x

    逻辑学是研究事实联系: 肯定.否定: 条件 结论: 联系  规则: 的学问.

  9. NetHogs---按进程或程序实时统计网络带宽使用率。

    NetHogs是一个开源的命令行工具(类似于Linux的top命令),用来按进程或程序实时统计网络带宽使用率. 来自NetHogs项目网站: NetHogs是一个小型的net top工具,不像大多数工 ...

  10. 【ICM Technex 2018 and Codeforces Round #463 (Div. 1 + Div. 2, combined) C】 Permutation Cycle

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] p[i] = p[p[i]]一直进行下去 在1..n的排列下肯定会回到原位置的. 即最后会形成若干个环. g[i]显然等于那个环的大 ...