Chrome如何显示网页

概念应用层

(此插图的原始Google文档为http://goo.gl/MsEJX,任何@ chromium.org均可开放对其进行编辑)
每个框代表一个概念性的应用程序层。任何层都不应该了解或依赖任何更高层。
  • WebKit:在Safari,Chromium和所有其他基于WebKit的浏览器之间共享的渲染引擎。该港口是WebKit的一部分,与平台相关的系统集成服务,如资源加载和图形。
  • 胶水:将WebKit类型转换为Chromium类型。这是我们的“ WebKit嵌入层”。它是两个浏览器Chromium和test_shell(允许我们测试WebKit)的基础。
  • 渲染器/渲染主机:这是Chromium的“多进程嵌入层”。它跨流程边界代理通知和命令。
  • WebContents:一个可重用的组件,它是Content模块的主要类。它很容易嵌入,以允许将HTML的多进程呈现到视图中。有关更多信息,请参见内容模块页面
  • 浏览器: 代表浏览器窗口,其中包含多个WebContent。
  • 选项卡助手:可以附加到WebContents的单个对象(通过WebContentsUserData mixin)。浏览器将其中的各种附加到它所拥有的WebContentses(一个用于收藏夹图标,一个用于信息栏等)。

WebKit

我们使用WebKit开源项目来布局网页。此代码从Apple中提取,并存储在/ third_party / WebKit目录中。WebKit主要由代表核心布局功能的“ WebCore”和运行JavaScript的“ JavaScriptCore”组成。我们仅出于测试目的运行JavaScriptCore,通常我们将其替换为高性能的V8 JavaScript引擎。我们实际上并没有使用Apple称为“ WebKit”的层,它是WebCore和OS X应用程序(例如Safari)之间的嵌入API。为了方便起见,我们通常将Apple的代码统称为“ WebKit”。

WebKit端口

在最低级别,我们有WebKit“端口”。这是我们所需的特定于平台的功能的实现,该功能可与独立于平台的WebCore代码对接。这些文件位于WebKit树中,通常位于目录中或作为带有铬后缀的文件。我们的许多端口实际上并不是特定于操作系统的:您可以将其视为WebCore的“ Chromium端口”。对于每个平台,某些部分(如字体渲染)必须以不同的方式处理。

  • 网络流量是由我们的多进程资源加载系统处理的,而不是直接从渲染过程传递给OS的。
  • 图形使用为Android开发的Skia图形库。这是一个跨平台的图形库,可处理除文本以外的所有图像和图形基元。Skia位于t hird_party / skia中。图形操作的主要入口点是/webkit/port/platform/graphics/GraphicsContextSkia.cpp。它使用同一目录以及/ base / gfx中的许多其他文件。

WebKit胶

与第三方WebKit代码相比,Chromium应用程序使用不同的类型,编码样式和代码布局。WebKit“胶水”为使用Google编码约定和类型的WebKit提供了更方便的嵌入API(例如,我们使用std :: string代替WebCore :: StringGURL代替KURL)。粘合代码位于/ webkit / glue中。胶合对象的名称通常类似于WebKit对象,但开头带有“ Web”。例如,WebCore :: Frame变为WebFrame

WebKit的“胶水”层将其余的Chromium代码库与WebCore数据类型隔离开来,以帮助最小化WebCore更改对Chromium代码库的影响。因此,Chromium永远不会直接使用WebCore数据类型。当Chromium需要戳入某些WebCore对象时,API会被添加到WebKit的“胶水”中。

“测试外壳”应用程序是一个准系统的Web浏览器,用于测试我们的WebKit端口和粘合代码。它使用与Chromium相同的粘合接口与WebKit进行通信。它为开发人员提供了一种更简单的方法来测试新代码,而无需具有许多复杂的浏览器功能,线程和进程。此应用程序还用于运行自动化的WebKit测试。但是,“测试外壳”的缺点是,它不像Chromium那样以多进程方式使用WebKit。内容模块嵌入在称为“内容外壳”的应用程序中,该应用程序将很快运行测试。

渲染过程

Chromium的渲染过程使用胶水接口嵌入了我们的WebKit端口。它不包含太多代码:它的工作主要是作为浏览器IPC通道的呈现器端。

渲染器中最重要的类是RenderView,它位于/content/renderer/render_view_impl.cc中。该对象表示一个网页。它处理往返于浏览器进程的所有与导航有关的命令。它源自提供绘画和输入事件处理的RenderWidget。所述的RenderView经由全球浏览器进程通信(每呈现进程)RenderProcess对象。

FAQ:RenderWidget和RenderView有什么区别?RenderWidget通过在称为WebWidgetDelegate的粘合层中实现抽象接口,映射到一个WebCore :: Widget对象。基本上,这是屏幕上的一个窗口,用于接收输入事件并绘制到该窗口中。甲的RenderView从继承RenderWidget并且是选项卡或弹出窗口的内容。除了小部件的绘制和输入事件外,它还处理导航命令。仅在一种情况下存在RenderWidget而没有RenderView ,这是网页上的选择框。这些是带有向下箭头的框,这些框会弹出一个选项列表。选择框必须使用本机窗口渲染,以便它们可以显示在其他所有框的上方,并在必要时弹出框。这些窗口需要绘制并接收输入,但是没有单独的“网页”(RenderView)。

渲染器中的线程

每个渲染器都有两个线程(有关图表,请参见多进程体系结构页面;有关如何编程,请参见Chromium中的线程)。渲染线程是主要对象(例如RenderView)所在的位置并运行所有WebKit代码。当它与浏览器通信时,消息首先发送到主线程,然后主线程将消息分派给浏览器进程。除其他外,这使我们能够将消息从渲染器同步发送到浏览器。发生这种情况的原因是少数操作需要浏览器的结果才能继续。一个示例是在JavaScript请求时获取页面的Cookie。渲染器线程将阻塞,并且主线程将对所有收到的消息进行排队,直到找到正确的响应为止。随后将任何同时接收到的消息发布到渲染器线程以进行正常处理。

浏览器过程

 

低级浏览器过程对象

与渲染过程的所有IPC通信都是在浏览器的I / O线程上完成的。该线程还处理所有网络通信,以防止其干扰用户界面。

在主线程(运行用户界面的地方)上初始化RenderProcessHost时,它将创建新的渲染器进程和带有指向渲染器的命名管道的ChannelProxy IPC对象。该对象在浏览器的I / O线程上运行,侦听到渲染器的命名管道,并自动将所有消息转发回UI线程上的RenderProcessHost。甲ResourceMessageFilter将被安装在该通道中,这将筛选出可直接在I / O线程上处理诸如网络请求的某些消息。此过滤发生在ResourceMessageFilter :: OnMessageReceived中

所述RenderProcessHost在UI线程上是负责分派所有视图专用消息到适当的RenderViewHost(它处理非观看专用消息本身的有限的数目)。这种调度发生在RenderProcessHost :: OnMessageReceived中

高级浏览器过程对象

特定于视图的消息进入RenderViewHost :: OnMessageReceived。大多数消息都在这里处理,其余消息转发到RenderWidgetHost基类。这两个对象映射到渲染器中的RenderViewRenderWidget(有关这些含义,请参见上面的“ Render Process”)。每个平台都有一个视图类(RenderWidgetHostView [Aura | Gtk | Mac | Win]),以实现与本机视图系统的集成。

RenderView / Widget上方是WebContents对象,大多数消息实际上最终都作为对该对象的函数调用而结束。一个WebContents代表网页的内容。它是内容模块中的顶级对象,并负责以矩形视图显示网页。有关更多信息,请参见内容模块页面

所述WebContents对象被包含在TabContentsWrapper。那是chrome /并负责制表符。

说明性例子

围绕Chromium源代码 涵盖了导航和启动的其他示例。

“设置游标”消息的寿命

设置光标是从渲染器发送到浏览器的典型消息的示例。在渲染器中,将发生以下情况。

  • 设置光标消息是由WebKit在内部生成的,通常是响应输入事件而生成的。设置光标消息将从content / renderer / render_widget.cc中的RenderWidget :: SetCursor开始
  • 它将调用RenderWidget :: Send来分派消息。RenderView也使用此方法将消息发送到浏览器。它将调用RenderThread :: Send 
  • 这将调用IPC :: SyncChannel,它将在内部将消息代理到渲染器的主线程,并将其发布到命名管道以发送到浏览器。

然后浏览器控制:

  • RenderProcessHost中的IPC :: ChannelProxy在浏览器的I / O线程上接收所有消息。它首先通过ResourceMessageFilter发送它们,后者直接在I / O线程上调度网络请求和相关消息。由于我们的消息没有被过滤掉,因此它将继续进入浏览器的UI线程(IPC :: ChannelProxy在内部执行此操作)。
  • RenderProcessHost :: OnMessageReceived内容/浏览器/ renderer_host / render_process_host_impl.cc获取相应的渲染过程的所有意见的消息。它直接处理几种类型的消息,其余的则转发到与发送消息的源RenderView对应的适当的RenderViewHost
  • 该消息到达RenderViewHost :: OnMessageReceived内容/浏览器/ renderer_host / render_view_host_impl.cc。很多消息都在这里处理,但我们是不是因为它是从发送的消息RenderWidget和处理由RenderWidgetHost
  • RenderViewHost中所有未处理的消息都将自动转发到RenderWidgetHost,包括我们设置的光标消息。
  • 内容/浏览器/renderer_host/render_view_host_impl.cc中的消息映射  最终在RenderWidgetHost :: OnMsgSetCursor中接收到消息,并调用适当的UI函数来设置鼠标光标。

“鼠标单击”消息的寿命

发送鼠标单击是从浏览器到渲染器的典型消息示例。

  • Windows消息由RenderWidgetHostViewWin :: OnMouseEvent在浏览器的UI线程上接收,然后在同一类中调用ForwardMouseEventToRenderer
  • 转发器功能将输入事件打包到一个跨平台的WebMouseEvent中,并最终将其发送到与之关联的RenderWidgetHost
  • RenderWidgetHost :: ForwardInputEvent创建一条IPC消息ViewMsg_HandleInputEvent,将WebInputEvent序列化为其,然后调用RenderWidgetHost :: Send
  • 这只是转发给拥有的RenderProcessHost :: Send函数,后者将消息提供给IPC :: ChannelProxy
  • 在内部,IPC :: ChannelProxy将消息代理到浏览器的I / O线程,并将其写入相应渲染器的命名管道。

请注意,WebContents中还会创建许多其他类型的消息,尤其是导航消息。它们遵循从WebContentsRenderViewHost的相似路径。

然后渲染器控制:

  •  渲染器主线程上的IPC :: Channel读取浏览器发送的消息,而IPC :: ChannelProxy代理到渲染器线程。
  • RenderView :: OnMessageReceived获取消息。许多类型的消息直接在这里处理。由于单击消息不是,因此它(与所有其他未处理的消息一起)进入RenderWidget :: OnMessageReceived  ,后者又将其转发到  RenderWidget :: OnHandleInputEvent
  • 将输入事件提供给  WebWidgetImpl :: HandleInputEvent,在此将其转换为WebKit PlatformMouseEvent类,并提供给WebKit 中的WebCore :: Widget类。