Chromium 怎样显示网页

http://www.chromium.org/developers/design-documents/displaying-a-web-page-in-chrome

这篇文档描述了Chromium 怎样自上而下显示网页。
确保你已经读了multi-process architecture design document.
你会特别想了解主要部件的框图。
你也许对多进程加载网页资源感兴趣。

概念上的应用层

每个方框代表一个概念上的应用层。每一层都不知道也不依赖它的上层。
WebKit: Safari, Chromium, 和所有其他的基于WebKit的浏览器使用的渲染引擎。
(WebKit)Port是WebKit的一部分,集成了平台相关的系统服务,如资源加载,图形库。  
Glue:将WebKit类型转换为Chromium类型,这是我们的“WebKit嵌套层”。
它是Chromium和test_shell(允许我们测试WebKit)这两个浏览器的基础。
Renderer/Render host: 这是 Chromium的“多进程嵌套层”。它代理跨越进程边界的通知和命令。
WebContents:可重用的组件,Content模块的主类。这个模块很容易嵌入,以便多个进程可以
渲染html内容到view中。更多信息可参见the content module pages.
Browser:代表浏览器窗口,包含多个WebContentses。
Tab Helpers:可以连接到WebContents(通过混入WebContentsUserData)的单个对象。
浏览器把各种各样的Tab Helpers连接到WebContentses.(one for favicons, one for infobars, etc).

WebKit
我们使用WebKit开源项目来布局网页内容。WebKit代码是从Apple拉下来的,
放在 /third_party/WebKit目录下。WebKit主要由WebCore组成和JavaScriptCore组成,
WebCore实现核心布局功能,JavaScriptCore负责运行JavaScript。
我们只为测试目的运行JavaScriptCore。通常情况下,我们使用性能更高的V8 JavaScript引擎代替它。
我们实际上并没有使用Apple称之为“WebKit”的那一层,
这一层是WebCore和OS X 应用(比如Safariwhich)之间的嵌入API.方便起见,
我们通常泛指来自Apple的代码为WebKit.

The WebKit port
在最低层,我们有自己的WebKit "port".
这是我们自己实现的与平台相关的功能,这些功能与平台独立的WebCore代码对接。
这些文件位于 WebKit tree,通常在chromium目录下,或者Chromium-suffixed 文件。
大部分的port其实都不是OS相关的:你可以它是WebCore的"Chromium port"。
port中的某些部分,像 font rendering, 必须为每个平台实现不同的处理。
网络事务由我们的多进程资源加载系统处理,而不是直接由render进程移交给操作系统处理。
Graphics使用的是专为android开发的Skia 图形库。
这是一个跨平台的图形库,处理除了文本外的所有图片和基本图元。
Skia 位于/third_party/skia.
图形操作的主要入口是/webkit/port/platform/graphics/GraphicsContextSkia.cpp.
这个文件用到了同一目录下和/base/gfx目录下的很多其他文件.

The WebKit glue
Chromium应用用到了与第三方的WebKit代码不同的类型,代码风格以及代码布局。
WebKit "glue"提供了一个更方便的内嵌的API,使WebKit可以使用Google的编码规范和类型
(比如,我们使用std::string代替WebCore::String,GURL代替KURL).
glue代码位于/webkit/glue.glue对象和WebKit对象使用相似的名称,除了在开头加上"Web".
比如,WebCore::Frame 变成了WebFrame.
WebKit "glue"层隔离了WebCore的数据类型与Chromium code base的其余部分,这将有助于
减小WebCore的变化对Chromium code base的影响。因此,WebCore的数据类型永远都不会
在Chromium中直接使用。为了使Chromium 在需要的时候可以直接使用一些WebCore对象,
WebKit "glue"中添加了API。“test shell”是为测试WebKit port和WebKit glue代码的简单的网页浏览器应用。
它和Chromium使用同一glue接口与WebKit通信。
它为开发者提供了一个更加简单的方式来测试新代码,不需要拥有很多复杂的浏览器特性,线程和进程。
这个应用也被用来运行自动化的WebKit测试。尽管如此,“test shell“的不足之处在于
它并不像Chromium 那样以多进程的方式执行WebKit."content shell"应用中内嵌的content模块,
将会取代test shell 用来运行测试代码。

The Render Process

Chromium的render进程使用glue接口内嵌了我们的WebKit port.
render进程并没有包含太多代码:它的主要工作是作为与browser进程相连接的IPC 通道的Renderer端。
renderer中最重要的类是RenderView,代码位于/content/renderer/render_view_impl.cc.
这个对象代表一个网页。处理所有来自和发给browser进程的导航相关的命令。
RenderView继承自RenderWidget,RenderWidget负责绘制和input事件的处理。
RenderView通过全局的RenderProcess对象(每个render进程拥有一个)与browser进程通信。

FAQ:RenderWidget和RenderView的区别
RenderWidget实现了glue层的抽象接口WebWidgetDelegate,对应一个WebCore::Widget对象。
RenderWidget基本上是屏幕上的一个窗口,接收input事件,我们向其中绘制内容。
RenderView继承自RenderWidget,是一个tab或者弹出窗口的内容。
除了widget负责的绘制和input事件,RenderView负责处理导航命令。
只有一种情况下,RenderWidget会脱离RenderView存在,就是网页上的select boxes,这些是
通过下拉箭头弹出选项列表的box.这些select boxes必须使用本地window来渲染,
以便它们可以出现在任何东西之上,并且在需要的时候弹出frame.
这些窗口需要绘制和接收input 事件,但是没有单独的网页(RenderView).

Threads in the renderer
每个renderer进程都有两个线程(见multi-process architecture 中的图,
或者threading in Chromium for how to program with them)。render线程是主要对象比如RenderView和
所有WebKit代码运行的地方。当render线程与browser进程通信时,消息会先传递给主线程,
主线程再依次将消息传给browser进程。不论其他,这允许我们同步的从render进程向browser进程发消息。
This happens for a small set of operations where a result from the browser is required to continue.
以JavaScript请求获取一个网页的cookes为例。
render线程将会阻塞,主线程在接收到正确的回应之前会将收到的所有的消息入队列。
在这期间收到的任何消息随后会传给render线程正常处理。

The browser process

Low-level browser process objects
与render进程的所有IPC通信都是在browser进程的I/O线程中完成的。
I/O线程处理所有的网络通信,这使它免受用户接口的干扰。
当RenderProcessHost在主线程(用户接口运行在主线程)中被初始化时,它会创建一个新的renderer进程
和一个ChannelProxy IPC对象,ChannelProxy IPC对象与一个通向render进程的命名管道相关联。
ChannelProxy IPC对象运行在browser进程的I/O线程中,监听通向renderer进程的命名管道,并自动将
所有的消息转发给UI线程中的RenderProcessHost。在这条通道中会安装ResourceMessageFilter,
这个模块负责过滤掉可以在I/O线程中直接处理的特定信息,比如网络请求。
过滤过程发生在ResourceMessageFilter::OnMessageReceived.
UI线程中的RenderProcessHost负责将view相关的消息转发给合适的RenderViewHost,
RenderProcessHost本身只处理有限的一些与view无关的消息。
消息转发发生在RenderProcessHost::OnMessageReceived.

High-level browser process objects
View相关的消息会进入RenderViewHost::OnMessageReceived.大部分消息是在这里处理的,
其余的被转发给基类RenderWidgetHost。RenderViewHost和RenderWidgetHost这两个对象分别映射render进程
中的RenderView 和RenderWidget(参见上文的”The Render Process“了解详情)。
每个平台都有一个view类(RenderWidgetHostView[Aura|Gtk|Mac|Win]),用来实现与本地view系统的融合。
RenderView/Widget之上是WebContents对象,实际上大多数消息都以调用WebContents对象的函数结束的。
WebContents代表了网页内容,是content 模块的顶层对象,负责在长方形的view中显示网页。
参见content module pages了解更多信息。
webContents对象包含在TabContentsWrapper中。TabContentsWrapper位于chrome/目录下,负责一个标签。

说明性的例子
涵盖导航和启动的其他例子在Getting Around the Chromium Source Code这篇文章中。
"set cursor"消息的生命周期
设置cursor是典型的render进程向browser进程发送的消息。
在render进程中:
设置cursor消息是WebKit内部产生的,通常是对输入事件的回应。设置cursor消息是RenderWidget::SetCursor
发出的,RenderWidget::SetCursor位于content/renderer/render_widget.cc。
render进程调用RenderWidget::Send发送这个消息。RenderView也使用RenderWidget::Send方法向browser
进程发送消息。RenderWidget::Send会调用RenderThread::Send.
这会调用IPC::SyncChannel,IPC::SyncChannel会在内部将消息代理给render进程的主线程,并将消息传递给
命名管道,进而发给browser进程。
然后,browser进程开始控制:
RenderProcessHost中的IPC::ChannelProxy接收发给browser进程的I/O线程的所有消息。IPC::ChannelProxy
先把消息传递给ResourceMessageFilter,ResourceMessageFilter将网络请求以及相关消息直接发给I/O线程。
由于我们的消息没有被过滤掉,它继续被发给了browser进程的UI线程,
(这个过程在IPC::ChannelProxy内部完成).
content/browser/renderer_host/render_process_host_impl.cc中的RenderProcessHost::OnMessageReceived
会得到相应的render进程中所有view的消息。RenderProcessHost::OnMessageReceived直接处理几种类型的
消息,其余的消息都会转发给与发送消息的RenderView相对应的RenderViewHost.    
消息会到达content/browser/renderer_host/render_view_host_impl.cc中的
RenderViewHost::OnMessageReceived。很多消息都在这里处理,但是我们的消息不是在这处理的,
因为这个消息是由RenderWidget发出的,所以会由RenderWidgetHost处理。
RenderViewHost中没有处理的消息都会自动转发给RenderWidgetHost,包括我们的设置cursor消息。
content/browser/renderer_host/render_view_host_impl.cc中的message map最终会在
RenderWidgetHost::OnMsgSetCursor中接收到消息,然后调用合适的UI函数设置mouse cursor.
"mouse click" 消息的生命周期
鼠标点击消息是典型的从browser进程发往renderer进程的消息。
窗口消息被运行在browser进程UI线程中的RenderWidgetHostViewWin::OnMouseEvent接收,
RenderWidgetHostViewWin::OnMouseEvent接着会调用同一个类中的ForwardMouseEventToRenderer函数。
转发函数将input event封装到跨平台的对象WebMouseEvent中,并最终将其发送给相关连的RenderWidgetHost。
RenderWidgetHost::ForwardInputEvent创建一个IPC消息ViewMsg_HandleInputEvent,
并将WebInputEvent序列化到ViewMsg_HandleInputEvent中,然后调用RenderWidgetHost::Send。
这个过程只将消息转发给了browser进程自己的RenderProcessHost::Send函数,
RenderProcessHost::Send将消息交给了IPC::ChannelProxy。

IPC::ChannelProxy会在内部将消息代理给browser进程的I/O线程,并将消息写入与相应的renderer进程
相连的命名管道。
需要注意的是有许多其余类型的消息是在WebContents中创建的,尤其是一些导航相关的消息。这些消息
沿着相似的路径,从WebContents传递到the RenderViewHost。
然后renderer进程开始控制:
运行在renderer进程主线程中的IPC::Channel读取browser进程发送过来的消息,
然后IPC::ChannelProxy把这个消息代理给renderer线程。
RenderView::OnMessageReceived获得这个消息。许多类型的消息都在这里被直接处理。
不过点击消息并不在这里处理,点击消息(和其他未被处理的消息)落到了RenderWidget::OnMessageReceived中,
RenderWidget::OnMessageReceived随后将消息转发给RenderWidget::OnHandleInputEvent。
输入事件被交给了WebWidgetImpl::HandleInputEvent,在WebWidgetImpl::HandleInputEvent中输入事件
被转化成WebKit PlatformMouseEvent类,并传给Webkit内部的WebCore::Widget类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值