Chromium Mojo - 4

0. 前置逻辑

http://chromium.world/?id=1


incoming_receiver_通过InterfaceEndpointClient::InterfaceEndpointClient创建时设置。

在多个地方可以新建InterfaceEndpointClient

InterfacePtrStateBase::InitializeEndpointClientBindingStateBase::BindInternalAssociatedBindingBase::BindImpl


分别在这三条线上被调用:

[1] mojo::InterfacePtrStateBase::ConfigureProxyIfNecessary --> mojo::InterfacePtrStateBase::InitializeEndpointClient 。

[2] mojo::internal::BindingState<...>::Bind --> mojo::internal::BindingStateBase::BindInternal 

[3] mojo::AssociatedBinding::Bind --> mojo::AssociatedBindingBase::BindImpl 


本文将分别介绍这三条线。

InitializeEndpointClient分支,这通常是Browser中某个功能需要进行连接以执行一些时会调用到它。

[1.0]. 前置的具体逻辑

稍后举例。

[1.1]. mojo::InterfacePtr<chrome::mojom::XXXX>::*

这个步骤不是必须的。如果你观察interface_ptr_state.h文件,你会发现里面几乎所有操作都会调用ConfigureProxyIfNecessary。这个操作也不例外,通常在调用具体功能前,代码会调用到InterfacePtr中的操作函数,并继续后面的流程。

[1.2]. mojo::internal::InterfacePtrState<chrome::mojom::XXXX>::*

这里XXXX和上面一层的是一样的,代表具体的mojom消息的实现接口类。函数名也会和上一层的一样。因为这里只是具体的操作实现辅助类。

[1.3]. mojo::internal::InterfacePtrState<chrome::mojom::XXXX>::ConfigureProxyIfNecessary

在上一步的操作中通常都会走到本函数。这个函数会调用InitializeEndpointClient创建InterfaceEndpointClient对象,并将自己模板类中的mojom接口类XXXX作为其实现,以注册端点客户端。

[1.4]. mojo::internal::InterfacePtrStateBase::InitializeEndpointClient

我们这次的目标函数。通过endpoint_client_.reset(new InterfaceEndpointClient(...))来建立新的InterfaceEndpointClient

=================================================================================================

看一下[1.0].的前置逻辑,以Chrome的设备性能调查器为例。

前置有如下调用链:

…… --> performance_manager::MetricsCollector::RecordExpectedQueueingTimeForUkm --> 

             ukm::internal::UkmEntryBuilderBase::Record --> 

             ukm::MojoUkmRecorder::AddEntry


AddEntry函数接受一个mojom::UkmEntryPtr entry作为参数,并调用interface_->AddEntry(std::move(entry))来添加项目。

MojoUkmRecorder::MojoUkmRecorder构造函数时,interface_即会被初始化为mojom::UkmRecorderInterfacePtr

当调用UkmRecorderInterfacePtr::AddEntry 时,实际上需要从ProxyClass走,也即根据生成的代码,调用:UkmRecorderInterfaceProxy::AddEntry(UkmEntryPtr in_entry),并转入receiver_->Accept

receiver_在UkmRecorderInterfaceProxy::UkmRecorderInterfaceProxy(mojo::MessageReceiverWithResponder* receiver)构造时即被传入。

mojo::MessageReceiverWithResponder::Accept会调用ForwardMessageCallback,forward_,将消息转发。


ForwardMessageCallback是base::Callback<void(Message)>的语法糖。随后base::Callback又是RepeatingCallback<Signature>的语法糖。最终,回调会返回到InterfacePtr<chrome::mojom::UkmRecorderInterface>::Accept

,然后它调用InterfacePtr<chrome::mojom::UkmRecorderInterface>::ForwardMessage函数,并在调用的第一时间调用[1.1]。


--------------------------------------------------------------------------------------------------------------------------------------------------------------------

BindInternal 分支

通常用于代码需要将自己和后端处理代码进行绑定时被调用到,具体参考示例。

[2.0]. 前置的具体逻辑

稍后举例。

[2.1]. mojo::Binding<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::Bind

这个函数会直接调用BindState::Bind

[2.2]. mojo::internal::BindingState<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::Bind

这个函数会直接调用BindInternal

[2.3]. mojo::internal::BindingStateBase::BindInternal

目标函数,通过endpoint_client_.reset(new InterfaceEndpointClient(...))新建一个InterfaceEndpointClient对象。


以这个函数network::`anonymous namespace'::SimpleURLLoaderImpl::StartRequest的调用为例:

  mojom::URLLoaderClientPtr client_ptr;
  client_binding_.Bind(mojo::MakeRequest(&client_ptr));
  client_binding_.set_connection_error_handler(base::BindOnce(&SimpleURLLoaderImpl::OnConnectionError, base::Unretained(this)));

mojo::Binding<mojom::URLLoaderClient>,client_binding_是URLLoaderClient的消息管道(message pipe)。通过将URLLoaderClientPtr绑定到管道上,这样后续就可以通过这个管道进行通信了。client_binding_在SimpleURLLoaderImpl构造时即会被初始化为this。


根据https://www.chromium.org/developers/design-documents/mojo/associated-interfaces 的说法,Binding,与C++绑定API类似,我们有:

*`mojo.interfacePtrInfo`和`mojo.interfaceRequest`封装消息管道的两端。它们分别表示接口连接的客户端服务端

*对于每个Mojom接口`Foo`,都有一个生成的`FooPtr`类。它拥有一个`InterfacePtrInfo`;提供了一种:通过使用`InterfacePtrInfo`的消息管道句柄,来发送接口调用的方法。

*`mojo.binding`拥有`InterfaceRequest`,它侦听消息管道句柄,并将传入的消息分派给用户定义的接口实现。


--------------------------------------------------------------------------------------------------------------------------------------------------------------------

C BindImpl 分支

[3.0]. 前置的具体逻辑

稍后举例。

[3.1]. mojo::AssociatedBinding<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::AssociatedBinding

[3.2]. mojo::AssociatedBinding<XXXXX::mojom::XXXXX,mojo::RawPtrImplRefTraits<XXXXX::mojom::XXXXX> >::Bind

[3.3]. mojo::AssociatedBindingBase::BindImpl

这个分支和B分支很像,上三层的结构都非常简单。直接调用到BindImpl后,通过endpoint_client_.reset(new InterfaceEndpointClient(...))新建一个InterfaceEndpointClient对象。


根据https://www.chromium.org/developers/design-documents/mojo/associated-interfaces 的说法,

associated interface连接没有自己的基础消息管道。它必须与现有的消息管道(即interface connection)相关联。

与非关联的接口连接相似的是,

  • `mojo.AssociatedInterfacePtrInfo`和`mojo.AssociatedInterfaceRequest`封装route ID,表示消息管道上的逻辑连接。

  • 对于每个Mojom接口`Foo`,都有一个生成的`FooAssociatedPtr`类。它拥有一个`AssociatedInterfacePtrInfo`,即接口的客户端。

  • `mojo.AssociatedBinding`拥有`AssociatedInterfaceRequest`,它侦听连接并将传入消息分派给用户定义的接口实现。