Movatterモバイル変換


[0]ホーム

URL:


project logoChromium Docs

Converting Legacy IPC to Mojo

Contents

Overview

A number of IPC messages sent (primarily between the browser and renderer processes) are still defined using Chrome's old IPC system in//ipc. This system usesbase::Pickle as the basis for message serialization and is supported by a number ifIPC_* preprocessor macros defined in//ipc and used around the source tree.

There is an ongoing, distributed effort to get these messages converted to Mojo interface messages. Messages that still need to be converted are tracked in two spreadsheets:

This document is concerned primarily with rote conversion of legacy IPC messages to Mojo interface messages. If you are considering more holistic refactoring and better isolation of an entire subsystem of the browser, you may considerservicifying the feature instead of merely converting its IPCs.

See otherMojo & Services documentation for introductory guides, API references, and more.

Legacy IPC Concepts

Each Content child process has a singleIPC::Channel implementation going between it and the browser process, and this is used as the sole two-way FIFO to send legacy IPC messages between the processes.

There are two fundamental types of legacy IPC messages:control messages, defined viaIPC_MESSAGE_CONTROLn macros (wheren is some small integer) androuted messages defined viaIPC_MESSAGE_ROUTEDn macros.

Control messages generally go between a browser-side process host (e.g.,RenderProcessHost orGpuProcessHost) and the child-sideChildThreadImpl subclass. All of these classes implementIPC::Sender and thus have aSend method for sending a control message to their remote counterpart, and they implementIPC::Listener to receive incoming control messages viaOnMessageReceived.

Routed messages are relegated toroutes which have arbitrary meaning determined by their use within a given process. For example, renderers use routes to isolate messages scoped to individual render frames, and so such routed messages will travel between aRenderFrameHostImpl and its correspondingRenderFrameImpl, both of which also implementIPC::Sender andIPC::Listener.

Mojo Interfaces as Routes

Routed messages in the old IPC system always carry arouting ID to identify to the receiving endpoint which routed object (e.g. whichRenderFrameImpl orRenderViewImpl or whatever) the message is targeting. Each endpoint is thus required to do some additional book-keeping to track what each routing ID means.

Mojo interfaces obviate the need for routing IDs, as new “routes” can be established by simply creating a new interface pipe and passing one endpoint to something which knows how to bind it.

When thinking about an IPC message conversion to Mojo, it's important to consider whether the message is a control message or a routed message, as this determines where you might find an existing Mojo interface to carry your message, or where you will want to add a new end-to-end Mojo interface for that purpose. This can mean the difference between a single per-process interface going between eachRenderProcessHostImpl and its correspondingRenderThreadImpl, vs a per-frame interface going between eachRenderFrameHostImpl and its correspondingRenderFrameImpl.

Ordering Considerations

Onevery important consideration when doing IPC conversions is the relative ordering of IPC-driven operations. With the old IPC system, because every message between two processes is globally ordered, it is quite easy for parts of the system to (intentionally or often unintentionally) rely on strict ordering guarantees.

For example, imagine aWebContentsObserver in the browser processes observes a frame navigation and immediately sends an IPC message to the frame to configure some new behavior. The implementation may be inadvertently relying on this message arrivingbefore some other tangentially related message sent to the same frame shortly after the same navigation event.

While Mojo guarantees strict ordering within each message pipe, Mojo does not (and in fact cannot) make any strict ordering guarantees between separate message pipes, as message pipes may be freely moved across process boundaries and thus cannot necessarily share a common FIFO at all times.

If the two messages described above were moved to separate Mojo interfaces on separate message pipes, renderer behavior could break as the first message may arrive after the second message.

The best solution to this problem is to rethink the IPC surface and/or implementation on either side to eliminate ordering dependencies between two interfaces that otherwise seem to be logically distinct. Failing that, Mojo's solution to this problem is to supportassociated interfaces. In a nutshell, these allow multiple distinct interfaces to be multiplexed over a shared message pipe.

Channel-Associated Interfaces

The previous section mentionsassociated interfaces as a general-purpose solution for establishing a mutual FIFO between multiple logical Mojo interfaces by having them share a single message pipe.

In Chrome, theIPC::Channel which carries all legacy IPC messages between two processes is itself a Mojo message pipe (implicitly). We provide a mechanism for associating arbitrary Mojo interfaces with this pipe, which means messages can be converted to Mojo while preserving strict FIFO with respect to other legacy IPC messages. Such interfaces are designated in Chrome parlance asChannel-associated interfaces.

Usage of Channel-associated interfaces should be rare but is considered a reasonable intermediate solution for incremental IPC conversions where it would be too risky or noisy to convert a large IPC surface all at once, but it would also be impossible to split the IPC surface between legacy IPC and a dedicated Mojo interface pipe without introducing timing bugs.

At this point in Chrome's development, practical usage of Channel-associated interfaces is restricted to theIPC::Channel between the browser process and a renderer process, as this is the most complex IPC surface with the most implicit ordering dependencies. A few simple APIs exist to support this.

RenderProcessHostImpl owns anIPC::Channel (throughIPC::ChannelProxy) to its correspondingRenderFrameImpl in the render process. This object has aGetRemoteAssociatedInterfaces method which can be used to pass arbitrary associated interface requests:

mojo::PendingAssociatedRemote<magic::mojom::GoatTeleporter> teleporter;render_frame->GetRemoteAssociatedInterfaces()->GetInterface(&teleporter);// These messages are all guaranteed to arrive in the same order they were sent.channel_->Send(newFooMsg_SomeLegacyIPC);teleporter->TeleportAllGoats();channel_->Send(newFooMsg_AnotherLegacyIPC);

Likewise,RenderFrameHostImpl defines aGetRemoteAssociatedInterfaces method.

To receive and bind incoming Channel-associated interface requests, the above objects also implementIPC::Listener::OnAssociatedInterfaceRequest.

There are some example conversion CLs which use Channel-associated interfaceshere andhere.

Deciding How to Approach a Conversion

There are a few questions you should ask before embarking upon any IPC message conversion journey, and there are many potential approaches to consider. The right one depends on context.

Note that this section assumes the message is traveling between the browser process and a renderer process. Other cases are rare and developers may wish to consultchromium-mojo@chromium.org before proceeding with them. Otherwise, apply the following basic algorithm to decide how to proceed:

  • General note: If the message is a reply to some other message (typically these take a “request ID” argument), see the note about message replies at the bottom of this section.
  • Consider whether or not the message makes sense as part of the IPC surface of a new or existing service somewhere in//services or//chrome/services,etc. This is less and less likely to be the case as time goes on, as many remaining IPC conversions are quite narrowly dealing with specific browser/renderer details rather than the browser's supporting subsystems. If defining a new service, you may wish to consult some of the otherMojo & Services documentation first.
  • If the message is anIPC_MESSAGE_CONTROL message:
    • If there are likely to be strict ordering requirements between this message and other legacy IPC or Channel-associated interface messages, consider using a new or existingChannel-associated interface betweenRenderProcessHostImpl andRenderThreadImpl.
    • If the message is sent from a renderer to the browser:
      • If an existing interface is bound byRenderProcessHostImpl and requested throughRenderThread's Connector and seems to be a good fit for the message, add the equivalent Mojo message to that interface.
      • If no such interface exists, consider adding one for this message and any related messages.
    • If the message is sent from the browser to a renderer:
      • If an existing interface is bound byRenderThreadImpl and requested through aBrowserContext Connector referencing a specificRenderProcessHostidentity, and the interface seems to be a good fit for the message, add the equivalent Mojo message to that interface.
      • If no such interface exists, consider adding one for this message and any related messages.
  • If the message is anIPC_MESSAGE_ROUTED message:
    • Determine what the routing endpoints are. If they areRenderFrameHostImpl andRenderFrameImpl:
      • If there are likely to be strict ordering requirements between this message and other legacy IPC or Channel-associated interface messages, consider using a new or existingChannel-associated interface betweenRenderFrameHostImpl andRenderFrameImpl.
      • If the message is sent from a renderer to the browser:
        • If an existing interface is bound byRenderFrameHostImpl and acquired viaRenderFrame::GetBrowserInterfaceBroker and the interface seems to be a good fit for this message, add the equivalent Mojo message to that interface.
        • If no such interface exists, consider adding one and registering it withRenderFrameHostImpl'sBrowserInterfaceBroker. See thesimple example in the “Intro to Mojo & Services” document.
      • If the message is sent from the browser to a renderer, consider adding a Mojo equivalent to thecontent.mojom.Frame interface definedhere.
    • If the routing endpoints arenot frame objects (for example, they may beRenderView/RenderViewHost objects), this is a special case which does not yet have an easy conversion approach readily available. Contactchromium-mojo@chromium.org to propose or discuss options.
NOTE: If you are converting a sync IPC, see the section onSynchronous Calls in the Mojo documentation.

Dealing With Replies

If the message is areply, meaning it has a “request ID” which correlates it to a prior message in the opposite direction, consider converting therequest message following the algorithm above. Unlike with legacy IPC, Mojo messages support replies as a first-class concept. So for example if you have:

IPC_CONTROL_MESSAGE2(FooHostMsg_DoTheThing,int/* request_id */,                     std::string/* name */);IPC_CONTROL_MESSAGE2(FooMsg_DidTheThing,int/* request_id */,bool/* success */);

You should consider defining an interfaceFoo which is bound inRenderProcessHostImpl and acquired fromRenderThreadImpl, with the following mojom definition:

interfaceFoo{DoTheThing(string name)=>(bool success);};

SeeReceiving responses for more information.

RepurposingIPC::ParamTraits andIPC_STRUCT* Invocations

Occasionally it is useful to do partial IPC conversions, where you want to convert a message to a Mojo interface method but you don‘t want to necessarily convert every structure passed by the message. In this case, you can leverage Mojo’stype-mapping system to repurpose existingIPC::ParamTraits.

NOTE: Although in some casesIPC::ParamTraits<T> specializations are defined manually in library code, theIPC_STRUCT* macro helpers also defineIPC::ParamTraits<T> specializations under the hood. All advice in this section pertains to both kinds of definitions.

If a mojom struct is declared without a struct body and is tagged with[Native], and a corresponding typemap is provided for the struct, the emitted C++ bindings will -- as if by magic -- replace the mojom type with the typemapped C++ type and will internally use the existingIPC::ParamTraits<T> specialization for that type in order to serialize and deserialize the struct.

For example, given theresource_messages.h header which defines an IPC mapping forcontent::ResourceRequest:

IPC_STRUCT_TRAITS_BEGIN(content::ResourceRequest)  IPC_STRUCT_TRAITS_MEMBER(method)  IPC_STRUCT_TRAITS_MEMBER(url)// ...IPC_STRUCT_TRAITS_END()

and theresource_request.h header which actually defines thecontent::ResourceRequest type:

namespace content{struct CONTENT_EXPORTResourceRequest{// ...};}// namespace content

we can declare a corresponding “native” mojom struct:

module content.mojom;[Native]structURLRequest;

and add a typemap likeurl_request.typemap to define how to map between them:

mojom="//content/public/common/url_loader.mojom"public_headers=["//content/common/resource_request.h"]traits_headers=["//content/common/resource_messages.h"]...type_mappings=["content.mojom.URLRequest=content::ResourceRequest"]

Note specifically that public_headers includes the definition of the native C++ type, and traits_headers includes the definition of the legacy IPC traits.

As a result of all this, other mojom files can now referencecontent.mojom.URLRequest as a type for method parameters and other struct fields, and the generated C++ bindings will represent those values exclusively ascontent::ResourceRequest objects.

This same basic approach can be used to leverage existingIPC_ENUM_TRAITS for invocations for[Native] mojom enum aliases.

NOTE: Use of[Native] mojom definitions is strictly limited to C++ bindings. If a mojom message depends on such definitions, it cannot be sent or received by other language bindings. This feature also depends on continued support for legacy IPC serialization and all uses of it should therefore be treated as technical debt.

Blink-Specific Advice

Variants

Let's assume we have a mojom file such as this:

module example.mojom;interfaceFoo{SendData(string param1, array<int32> param2);};

The following GN snippet will generate two concrete targets:example andexample_blink:

mojom("example") {  sources = [ "example.mojom" ]}

The targetexample will generate Chromium-style C++ bindings using STL types:

// example.mojom.hnamespace example{namespace mojom{classExample{virtualvoidSendArray(const std::string& param1,const std::vector<int32_t>& param2)=0;}}// namespace mojom}// namespace example

The targetexample_blink will generate Blink-style C++ bindings using WTF types:

// example.mojom-blink.hnamespace example{namespace mojom{namespace blink{classExample{virtualvoidSendArray(const WTF::String& param1,const WTF::Vector<int32_t>& param2)=0;}}// namespace blink}// namespace mojom}// namespace example

Thanks to these separate sets of bindings no work is necessary to convert types between Blink-style code and Chromium-style code. It is handled automatically during message serialization and deserialization.

For more information about variants, seethis section of the C++ bindings documentation.

Binding callbacks

Mojo methods that return a value take an instance ofbase::OnceCallback. UseWTF::BindOnce() and an appropriate wrapper function depending on the type of object and the callback.

For garbage-collected (Oilpan) classes owning themojo::Remote, it is recommended to useWrapWeakPersistent(this) for connection error handlers since they are not guaranteed to get called in a finite time period (wrapping the object withWrapPersistent in this case would cause memory leaks).

If the response can be discarded in case the object is not alive by the time the response is received, useWrapWeakPersistent(this) for binding the response callback:

// src/third_party/blink/renderer/modules/device_orientation/device_sensor_entry.ccsensor_.set_connection_error_handler(WTF::BindOnce(&DeviceSensorEntry::HandleSensorError,WrapWeakPersistent(this)));sensor_->ConfigureReadingChangeNotifications(/*enabled=*/false);sensor_->AddConfiguration(    std::move(config), WTF::BindOnce(&DeviceSensorEntry::OnSensorAddConfiguration,WrapWeakPersistent(this)));

Otherwise (for example, if the response callback is used to resolve a Promise), useWrapPersistent(this) to keep the object alive:

// src/third_party/blink/renderer/modules/nfc/nfc.ccScriptPromiseResolver* resolver=ScriptPromiseResolver::Create(script_state);... nfc_->CancelAllWatches(WTF::BindOnce(&NFC::OnRequestCompleted,WrapPersistent(this),WrapPersistent(resolver)));

Non-garbage-collected objects can useWTF::Unretained(this) for both response and error handler callbacks when themojo::Remote is owned by the object bound to the callback or the object is guaranteed to outlive the Mojo connection for another reason. Otherwise a weak pointer should be used. However, it is not a common pattern since using Oilpan is recommended for all Blink code.

Implementing Mojo interfaces in Blink

Only amojo::Receiver ormojo::ReceiverSet should be used when implementing a Mojo interface in an Oilpan-managed object. The object must then have a pre-finalizer to close any open pipes when the object is about to be swept as lazy sweeping means that it may be invalid long before the destructor is called. This requires setup in both the object header and implementation.

// MyObject.hclassMyObject:publicGarbageCollected,public example::mojom::blink::Example{  USING_PRE_FINALIZER(MyObject,Dispose);public:MyObject();voidDispose();// Implementation of example::mojom::blink::Example.private:  mojo::Receiver<example::mojom::blink::Example> m_receiver{this};};// MyObject.cppvoidMyObject::Dispose(){  m_receiver.Close();}

For more information about Blink's Garbage Collector, seeBlink GC API Reference.

Typemaps For Content and Blink Types

Using typemapping for messages that go between Blink and content browser code can sometimes be tricky due to things like dependency cycles or confusion over the correct place for some definition to live. There are some example CLs provided here, but feel free to also contactchromium-mojo@chromium.org with specific details if you encounter trouble.

This CL introduces a Mojom definition and typemap forui::WindowOpenDisposition as a precursor to the IPC conversion below.

Thefollow-up CL uses that definition along with several other new typemaps (including native typemaps as described above) to convert the relatively largeViewHostMsg_CreateWindow message to Mojo.

Additional Support

If this document was not helpful in some way, please post a message to your friendlychromium-mojo@chromium.org mailing list.


[8]ページ先頭

©2009-2025 Movatter.jp