https://chromium.googlesource.com/chromium/src/+/master/mojo/README.md#System-Overview
之前看博客关注比较多的都是render、browser或者是资源下载,IPC会关注的比较少,后面发现这个模块还是建议梳理下,便于对整个流程的理解。IPC 就是进程间通信,如何发送消息,发送的消息格式是怎样的,如何接收消息。 如果能静心看浏览器代码,会有很多意外收获的,虽然浏览的代码很多很大。下面我们就以URL加载过程进行介绍。
之前URL加载流程中,从JNI函数中获取URL地址,调用LoadURLWithParams传递URL等设置参数,最终会调用到Navigate函数,这整个流程的调用都在CrBrowserMain中。在Navigate中会调用一个SendNavigateMessage函数。
SendNavigateMessage函数的实现如下,看代码,其实功能很简单就调用一个Send函数发送一个MSG。
void RenderFrameHostImpl::SendNavigateMessage(
const CommonNavigationParams& common_params,
const StartNavigationParams& start_params,
const RequestNavigationParams& request_params) {
RenderFrameDevToolsAgentHost::OnBeforeNavigation(
frame_tree_node_->current_frame_host(), this);
Send(new FrameMsg_Navigate(
routing_id_, common_params, start_params, request_params));
}
代码路径: ./content/browser/frame_host/render_frame_host_impl.cc
我们来看看Send 函数的实现,send函数很简单,先调用ipc定义的IPC_MESSAGE_ID_CLASS将获取的type右移16位,同IPCMessageStart枚举中的变量InputMsgStart(4)进行比较,然后将消息发送出去。再往回去看,发送消息是send(msg),这个msg 都是在发送之前新建的。之前一直没找到FrameMsg_Navigate的定义,后面找到相关的是如下代码:
bool RenderFrameHostImpl::Send(IPC::Message* message) {
if (IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart) {
return GetRenderWidgetHost()->input_router()->SendInput(
base::WrapUnique(message));
}
return GetProcess()->Send(message);
}
代码路径:./content/browser/frame_host/render_frame_host_impl.cc
IPC_MESSAGE_ROUTED3(FrameMsg_Navigate,
content::CommonNavigationParams, /* common_params */
content::StartNavigationParams, /* start_params */
content::RequestNavigationParams /* request_params */)
代码路径:./content/common/frame_messages.h
可以从IPC模块中找到IPC_MESSAGE_ROUNTED3的定义,原来大神们通过一串的define将两类消息(CONTROL、ROUTED)定义成相关的数据结构,也就是msg的格式。
#define IPC_MESSAGE_ROUTED0(msg) IPC_MESSAGE_ROUTED(msg)
#define IPC_MESSAGE_ROUTED1(msg, a) IPC_MESSAGE_ROUTED(msg, a)
#define IPC_MESSAGE_ROUTED2(msg, a, b) IPC_MESSAGE_ROUTED(msg, a, b)
#define IPC_MESSAGE_ROUTED3(msg, a, b, c) IPC_MESSAGE_ROUTED(msg, a, b, c)
#define IPC_MESSAGE_ROUTED4(msg, a, b, c, d) IPC_MESSAGE_ROUTED(msg, a, b, c, d)
#define IPC_MESSAGE_ROUTED5(msg, a, b, c, d, e) \
IPC_MESSAGE_ROUTED(msg, a, b, c, d, e)
#define IPC_MESSAGE_CONTROL(msg_class, ...) \
IPC_MESSAGE_DECL(msg_class, CONTROL, IPC_TUPLE(__VA_ARGS__), void)
#define IPC_MESSAGE_ROUTED(msg_class, ...) \
IPC_MESSAGE_DECL(msg_class, ROUTED, IPC_TUPLE(__VA_ARGS__), void)
#define IPC_MESSAGE_DECL(msg_name, kind, in_tuple, out_tuple) \
struct IPC_MESSAGE_EXPORT msg_name##_Meta { \
using InTuple = in_tuple; \
using OutTuple = out_tuple; \
enum { ID = IPC_MESSAGE_ID() }; \
static const IPC::MessageKind kKind = IPC::MessageKind::kind; \
static const char kName[]; \
}; \
extern template class EXPORT_TEMPLATE_DECLARE(IPC_MESSAGE_EXPORT) \
IPC::MessageT; \
using msg_name = IPC::MessageT; \
IPC_MESSAGE_EXTRA(msg_name)
代码路径:./ipc/ipc_message_macros.h
看到这消息格式应该算是明朗了,接下来分析的是如何进行发送消息。
然后我们来看看msg的接收,先看OnMessageReceived的实现
bool RenderProcessHostImpl::OnMessageReceived(const IPC::Message& msg) {
......
if (msg.routing_id() == MSG_ROUTING_CONTROL) {
// Dispatch control messages.
IPC_BEGIN_MESSAGE_MAP(RenderProcessHostImpl, msg)
IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ShutdownRequest,
OnShutdownRequest)
IPC_MESSAGE_HANDLER(RenderProcessHostMsg_SuddenTerminationChanged,
SuddenTerminationChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_UserMetricsRecordAction,
OnUserMetricsRecordAction)
IPC_MESSAGE_HANDLER(ViewHostMsg_Close_ACK, OnCloseACK)
#if defined(ENABLE_WEBRTC)
IPC_MESSAGE_HANDLER(AecDumpMsg_RegisterAecDumpConsumer,
OnRegisterAecDumpConsumer)
IPC_MESSAGE_HANDLER(AecDumpMsg_UnregisterAecDumpConsumer,
OnUnregisterAecDumpConsumer)
#endif
// Adding single handlers for your service here is fine, but once your
// service needs more than one handler, please extract them into a new
// message filter and add that filter to CreateMessageFilters().
IPC_END_MESSAGE_MAP()
return true;
}
代码路径:./content/browser/renderer_host/render_process_host_impl.cc
看上面的代码,还是要在IPC模块中先看IPC_BEGIN_MESSAGE_MAP、IPC_MESSAGE_HANDLER、 IPC_END_MESSAGE_MAP的定义。IPC_BEGIN_MESSAGE_MAP是一个switch开始,IPC_MESSAGE_HANDLER是一个case语句dispatch message,其中的一个入参是函数,作为回调函数,即在dispatch message的时候就调用相关的函数,IPC_END_MESSAGE_MAP就只是括号。看到这,我算是长见识了,原来代码是可以怎么写的。
#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \
{ \
typedef class_name _IpcMessageHandlerClass ALLOW_UNUSED_TYPE; \
void* param__ = NULL; \
(void)param__; \
const IPC::Message& ipc_message__ = msg; \
switch (ipc_message__.type()) {
#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \
case msg_class::ID: { \
TRACK_RUN_IN_THIS_SCOPED_REGION(member_func); \
if (!msg_class::Dispatch(&ipc_message__, obj, this, param__, \
&member_func)) \
ipc_message__.set_dispatch_error(); \
} \
break;
#define IPC_MESSAGE_HANDLER(msg_class, member_func) \
IPC_MESSAGE_FORWARD(msg_class, this, _IpcMessageHandlerClass::member_func)
代码路径:./ipc/ipc_message_macros.h
#define IPC_END_MESSAGE_MAP() \
} \
}
代码路径:./ipc/ipc_message_macros.h
将class 入参传入,函数作为回调函数,所以会有在对应的class Dispatch msg时,调用相关的函数。