Chromium在加载一个网页之前,需要在Browser进程创建一个Frame Tree。Browser进程为网页创建了Frame Tree之后,再请求Render进程加载其内容。Frame Tree将网页抽象为Render Frame。Render Frame是为实现Out-of-Process iframes设计的。本文接下来就分析Frame Tree的创建过程,为后面分析网页加载过程打基础。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
       关于Chromium的Out-of-Process iframes的详细介绍,可以参考官方文档:Out-of-Process iframes (OOPIFs),或者前面Chromium网页加载过程简要介绍和学习计划一文。我们通过图1所示的例子简单介绍,如下所示:
图1 Out-of-Process iframes       在图1中,网页A在一个Tab显示,并且它通过iframe标签包含了网页B和网页C。网页C通过window.open在另外一个Tab中打开了另外一个网页C实例。新打开的网页C通过iframe标签包含了网页D,网页D又通过iframe标签包含了网页A的另外一个实例。       这时候Browser进程会分别为图1的两个Tab创建一个WebContents对象,如图2所示:
图2 RenderFrameHost/RenderFrameProxyHost        在Browser进程中,一个网页使用一个WebContents对象描述。每一个WebContents对象都又关联有一个Frame Tree。Frame Tree的每一个Node代表一个网页。其中,Frame Tree的根Node描述的是主网页,子Node描述的是通过iframe标签嵌入的子网页。例如,第一个Tab的Frame Tree包含有三个Node,分别代表网页A、B和C,第二个Tab的Frame Tree也包含有三个Node,分别代表网页C、D和A。
        网页A、B、C和D在Chromium中形成了一个Browsing Instance。关于Chromium的Browsing Instance,可以参考官方文档:Process Models,它对应于HTML5规范中的Browsing Context。        我们观察代表网页B的子Node,它关联有一个RenderFrameHost对象和三个RenderFrameProxyHost对象。其中,RenderFrameHost对象描述的是网页B本身,另外三个RenderFrameProxyHosts对象描述的是网页A、C和D。表示网页B可以通过JavaScript接口window.postMessage向网页A、C和D发送消息。        假设图1所示的网页A、B、C和D分别在不同的Render进程中渲染,如图3所示:
图3 RenderFrame/RenderFrameProxy       在负责加载和渲染网页B的Render进程中,有一个RenderFrame对象,代表图1所示的网页B。负责加载和渲染网页B的Render进程还包含有五个RenderFrameProxy对象,分别代表图1所示的两个网页A实例、两个网页C实例和一个网页D实例。在负责渲染网页A、C和D的Render进程中,也有类似的RenderFrame对象和RenderFrameProxy对象。       Browser进程的RenderFrameHost对象和Render进程的RenderFrame对象是一一对应的。同样,Browser进程的RenderFrameHostProxy对象和Render进程的RenderFrameProxy对象也是一一对应的。RenderFrame对象和RenderFrameProxy对象的存在,使得在同一个Browsing Instance或者Browsing Context中的网页可以通过Javascript接口window.postMessage相互发送消息。       例如,当网页B要向网页A发送消息时,负责加载和渲染网页B的Render进程就会在当前进程中找到与网页B对应的RenderFrame对象,以及与网页A对应的RenderFrameProxy对象,然后将要发送的消息从找到的RenderFrame对象传递给找到的RenderFrameProxy对象即可。与网页A对应RenderFrameProxy对象接收到消息之后,会进一步将该消息发送给负责加载和渲染网页A的Render进程处理,从而完成消息的发送和处理过程。       接下来,我们就结合源码分析Chromium为网页创建Frame Tree的过程,这个过程涉及到WebContents、RenderFrameHost和RenderFrame等重要对象的创建。       前面提到,Frame Tree是在Browser进程中创建的,并且与一个WebContents对象关联。从前面Chromium网页加载过程简要介绍和学习计划一文可以知道,WebContents是Chromium的Content模块提供的一个高级接口,通过这个接口可以将一个网页渲染在一个可视区域中。我们可以认为,一个WebContents对象就是用来实现一个网页的加载、解析、渲染和导航等功能的。       Frame Tree是在与其关联的WebContents对象的创建过程中创建的,因此接下来我们就从WebContents对象的创建过程开始分析Frame Tree的创建过程。以Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文提到的Shell APK为例,它会为每一个要加载的网页在Native层创建一个Shell。在创建这个Shell的过程中,就会同时创建一个WebContents对象。       从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文可以知道,Shell APK是通过调用Native层的Shell类的静态成员函数CreateNewWindow为要加载的网页创建一个Shell的,它的实现如下所示:
[cpp] view plain copy
 
Shell* Shell::CreateNewWindow(BrowserContext* browser_context,

const GURL& url,

SiteInstance* site_instance,

int routing_id,

const gfx::Size& initial_size) {

WebContents::CreateParams create_params(browser_context, site_instance);

create_params.routing_id = routing_id;

create_params.initial_size = AdjustWindowSize(initial_size);

WebContents* web_contents = WebContents::Create(create_params);

Shell* shell = CreateShell(web_contents, create_params.initial_size);

......

return shell;

}  
       这个函数定义在文件externalromium_org/content/shell/browser/shell.cc中。
       Shell类的静态成员函数CreateNewWindow首先是调用WebContents类的静态成员函数Create创建一个WebContentsImpl对象,接着再调用Shell类的静态成员函数CreateShell根据前面创建的WebContentsImpl对象创建一个Shell对象,并且返回给调用者。       接下来,我们先分析WebContents类的静态成员函数Create的实现,接着再分析Shell类的静态成员函数CreateShell的实现。       WebContents类的静态成员函数Create的实现如下所示:
[cpp] view plain copy
 
WebContents* WebContents::Create(const WebContents::CreateParams& params) {

return WebContentsImpl::CreateWithOpener(

params, static_cast<WebContentsImpl*>(params.opener));

}  
      这个函数定义在文件externalromium_org/content/browser/web_contents/web_contents_impl.cc中。      WebContents类的静态成员函数Create调用另外一个静态成员函数CreateWithOpener创建一个WebContentsImpl对象,并且返回给调用者。      WebContents类的静态成员函数CreateWithOpener的实现如下所示:
[cpp] view plain copy
 
WebContentsImpl* WebContentsImpl::CreateWithOpener(

const WebContents::CreateParams& params,

WebContentsImpl* opener) {

......

WebContentsImpl* new_contents = new WebContentsImpl(

params.browser_context, params.opener_suppressed ? NULL : opener);

......

new_contents->Init(params);

return new_contents;

}  
       这个函数定义在文件externalromium_org/content/browser/web_contents/web_contents_impl.cc中。       WebContents类的静态成员函数CreateWithOpener首先是创建一个WebContentsImpl对象。在将这个WebContentsImpl对象返回给调用者之前,会先调用它的成员函数Init对它进行初始化。       接下来,我们先分析WebContentsImpl对象的创建过程,即WebContentsImpl类的构造函数的实现,接下来再分析WebContentsImpl对象的初始化过程,即WebContentsImpl类的成员函数Init的实现。       WebContentsImpl类的构造函数的实现如下所示:
[cpp] view plain copy
 
WebContentsImpl::WebContentsImpl(

BrowserContext* browser_context,

WebContentsImpl* opener)

: ......,

controller_(this, browser_context),

......,

frame_tree_(new NavigatorImpl(&controller_, this),

this, this, this, this),

...... {

......

}  
       这个函数定义在文件externalromium_org/content/browser/web_contents/web_contents_impl.cc中。
       WebContentsImpl类的成员变量controller_描述的是一个NavigationControllerImpl对象。后面我们可以看到,这个NavigationControllerImpl对象负责执行加载URL的操作。现在我们分析它的创建过程,如下所示:
[cpp] view plain copy
 
NavigationControllerImpl::NavigationControllerImpl(

NavigationControllerDelegate* delegate,

BrowserContext* browser_context)

: ......,

delegate_(delegate),

...... {

......

}  
       这个函数定义在文件externalromium_org/content/browser/frame_host/navigation_controller_impl.cc中。       从前面的调用过程可以知道,参数delegate指向的是一个WebContentsImpl对象。这个WebContentsImpl对象保存在NavigationControllerImpl类的成员变量delegate_中。       回到WebContentsImpl类的构造函数中,它构造了一个NavigationControllerImpl对象之后,接下来又根据该NavigationControllerImpl对象创建一个NavigatorImpl对象。这个NavigatorImpl对象也是在加载URL时使用到的,它的创建过程如下所示:
[cpp] view plain copy
 
NavigatorImpl::NavigatorImpl(

NavigationControllerImpl* navigation_controller,

NavigatorDelegate* delegate)

: controller_(navigation_controller),

delegate_(delegate) {

}  
       这个函数定义在文件externalromium_org/content/browser/frame_host/navigator_impl.cc        从前面的调用过程可以知道,参数navigation_controller和delegate指向的分别是一个NavigationControllerImpl对象和一个WebContentsImpl对象,它们分别保存在NavigatorImpl类的成员变量controller_和delegate_中。       再回到WebContentsImpl类的构造函数中,它创建了一个NavigatorImpl对象之后,又根据该NavigatorImpl对象创建一个FrameTree对象,并且保存在成员变量frame_tree_中。这个FrameTree对象描述的就是一个Frame Tree。       接下来我们继续分析FrameTree对象的创建过程,也就是FrameTree类的构造函数的实现,如下所示:
[cpp] view plain copy
 
FrameTree::FrameTree(Navigator* navigator,

RenderFrameHostDelegate* render_frame_delegate,

RenderViewHostDelegate* render_view_delegate,

RenderWidgetHostDelegate* render_widget_delegate,

RenderFrameHostManager::Delegate* manager_delegate)

: render_frame_delegate_(render_frame_delegate),

render_view_delegate_(render_view_delegate),

render_widget_delegate_(render_widget_delegate),

manager_delegate_(manager_delegate),

root_(new FrameTreeNode(this,

navigator,

render_frame_delegate,

render_view_delegate,

render_widget_delegate,

manager_delegate,

std::string())),

...... {

}  
       这个函数定义在文件externalromium_org/content/browser/frame_host/frame_tree.cc中。       从前面的调用过程可以知道,参数navigator指向的是一个NavigatorImpl对象,其余四个参数指向的是同一个WebContentsImpl对象,分别被保存在FrameTree类的成员变量render_frame_delegate_、render_view_delegate_、render_widget_delegate_和manager_delegate_中。       FrameTree类的构造函数接下来做的一件事情是创建一个FrameTreeNode对象,并且保存在成员变量root_中,作为当前正在创建的Frame Tree的根节点。这个根节点描述的就是后面要加载的网页。       FrameTreeNode对象的创建过程,即FrameTreeNode类的构造函数的实现,如下所示:
[cpp] view plain copy
 
FrameTreeNode::FrameTreeNode(FrameTree* frame_tree,

Navigator* navigator,

RenderFrameHostDelegate* render_frame_delegate,

RenderViewHostDelegate* render_view_delegate,

RenderWidgetHostDelegate* render_widget_delegate,

RenderFrameHostManager::Delegate* manager_delegate,

const std::string& name)

: frame_tree_(frame_tree),

navigator_(navigator),

render_manager_(this,

render_frame_delegate,

render_view_delegate,

render_widget_delegate,

manager_delegate),

...... {}  
       这个函数定义在文件externalromium_org/content/browser/frame_host/frame_tree_node.cc中。       从前面的调用过程可以知道,参数frame_tree和navigator指向的分别是一个FrameTree对象和一个NavigatorImpl对象,它们分别被保存在FrameTreeNode类的成员变量frame_tree_和navigator_中。       FrameTreeNode类的构造函数接下来做的一件事情是构造一个RenderFrameHostManager对象,并且保存在成员变量render_manager_中。这个RenderFrameHostManager对象负责管理与当前正在创建的FrameTreeNode对象关联的一个RenderFrameHost对象,它的构造过程如下所示:
[cpp] view plain copy
 
RenderFrameHostManager::RenderFrameHostManager(

FrameTreeNode* frame_tree_node,

RenderFrameHostDelegate* render_frame_delegate,

RenderViewHostDelegate* render_view_delegate,

RenderWidgetHostDelegate* render_widget_delegate,

Delegate* delegate)

: frame_tree_node_(frame_tree_node),

delegate_(delegate),

......,

render_frame_delegate_(render_frame_delegate),

render_view_delegate_(render_view_delegate),

render_widget_delegate_(render_widget_delegate),

...... {

......

}  
      这个函数定义在文件externalromium_org/content/browser/frame_host/render_frame_host_manager.cc中。      从前面的调用过程可以知道,参数frame_tree_node指向的是一个FrameTreeNode对象,它被保存在RenderFrameHostManager类的成员变量frame_tree_node_中,其余四个参数指向的是同一个WebContentsImpl对象,分别被保存在RenderFrameHostManager类的成员变量delegate_、render_frame_delegate_、render_view_delegate_和renderwww.yyzx66.cn/ _widget_delegate_中。       这一步执行完成之后,一个Frame Tree就创建出来了。这是一个只有根节点的Frame Tree。根节点描述的网页就是接下来要进行加载的。根节点描述的网页加载完成之后,就会进行解析。在解析的过程中,如果碰到iframe标签,那么就会创建另外一个子节点,并且添加到当前正在创建的Frame Tree中去。       返回到WebContents类的静态成员函数CreateWithOpener中,它创建了一个WebContentsImpl对象之后,接下来会调用这个WebContentsImpl对象的成员函数Init执行初始化工作,如下所示:
[cpp] view plain copy
 
void WebContentsImpl::www.ludingyule66.cn Init(const WebContents::CreateParams& params) {

......

GetRenderManager()->Init(

params.browser_context, params.site_instance, params.routing_id,

params.main_frame_routing_id);

......

if (browser_plugin_guest_) {

......

} else {

// Regular WebContentsView.

view_.reset(CreateWebContentsView(

this, delegate, &render_view_host_delegate_view_));

}

......

}  
       这个函数定义在文件externalromium_org/content/browser/web_contents/web_contents_impl.cc中。       WebContentsImpl类的成员函数Init首先是调用成员函数GetRenderManager获得一个RenderFrameHostManager对象,接下来再调用这个RenderFrameHostManager对象的成员函数Init对其进行初始化。       WebContentsImpl类的成员函数GetRenderManager的实现如下所示:
[cpp] view plain copy
 
RenderFrameHostManager* WebContentsImpl::GetRenderManager() const {

return frame_tree_.root()->render_manager();

}  
       这个函数定义在文件externalromium_org/content/browser/web_contents/web_contents_impl.cc中。
       从前面的分析可以知道,WebContentsImpl类的成员变量frame_tree_指向的是一个FrameTree对象。调用这个FrameTree对象的成员函数root获得的是一个FrameTreeNode对象。这个FrameTreeNode对象描述的是前面创建的Frame Tree的根节点。WebContentsImpl类的成员函数GetRenderManager最后调用该FrameTreeNode对象的成员函数render_manager获得一个RenderFrameHostManager对象,并且返回给调用者。       FrameTreeNode类的成员函数render_manager的实现如下所示:
[cpp] view plain copy
 
class CONTENT_EXPORT FrameTreeNode {

public:

......

RenderFrameHostManager* render_manager() {

return &render_manager_;

}

......

private:

......

RenderFrameHostManager render_manager_;

......

};  
      这个函数定义在文件externalromium_org/content/browser/frame_host/frame_tree_node.h中。      从这里可以看到,FrameTreeNode类的成员函数render_manager返回的是成员变量render_manager_描述的一个RenderFrameHostManager对象。这个RenderFrameHostManager对象是在前面分析的FrameTreeNode类的构造函数创建的。      回到WebContentsImpl类的成员函数Init中,它获得了一个RenderFrameHostManager对象之后,就调用它的成员函数Init对它进行初始化, 

Chromium网页Frame Tree创建过程分析的更多相关文章

  1. Chromium网页Layer Tree创建过程分析

    在Chromium中.WebKit会创建一个Graphics Layer Tree描写叙述网页.Graphics Layer Tree是和网页渲染相关的一个Tree. 网页渲染终于由Chromium的 ...

  2. Chromium网页Graphics Layer Tree创建过程分析

    在前面一文中.我们分析了网页Render Layer Tree的创建过程.在创建Render Layer的同一时候,WebKit还会为其创建Graphics Layer.这些Graphics Laye ...

  3. Chromium网页URL载入过程分析

    Chromium在Browser进程中为网页创建了一个Frame Tree之后,会将网页的URL发送给Render进程进行载入.Render进程接收到网页URL载入请求之后,会做一些必要的初始化工作, ...

  4. Chromium网页输入事件捕捉和手势检測过程分析

    连续的输入事件可能会产生一定的手势操作.比如滑动手势和捏合手势. 在Chromium中,网页的输入事件是在Browser进程中捕捉的.Browser进程捕获输入事件之后,会进行手势操作检測.检測出来的 ...

  5. Frame - 快速创建高品质的 Web 应用原型

    Frame 是一个让你够能够快速创建高品质的网站或应用程序产品原型的工具.你上传的图片将被包裹在真实的设备环境中.它是一个用于创建宣传资料的专业工具.Frame 完全免费供给商业和个人使用.他们也正探 ...

  6. Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8303098 在前文中,我们分析了应用程序窗口连 ...

  7. Android应用程序窗口(Activity)的视图对象(View)的创建过程分析

    从前文可知道,每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口.每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图.应用程序窗口视图是真正用来 ...

  8. Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析(转)

    在前文中,我们分析了Android应用程序窗口的运行上下文环境的创建过程.由此可知,每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一 ...

  9. Dalvik虚拟机进程和线程的创建过程分析

    从前面Dalvik虚拟机的运行过程分析一文可以知道,Dalvik虚拟机除了可以执行Java代码之外,还可以执行Native代码,也就是C/C++函数. 这些C/C++函数在执行的过程中,又可以通过本地 ...

随机推荐

  1. PC-网络教程之宽带小型组网方案

    由于某些家庭或小型局域网用户的各种需求和设备不同,所以继续写出几个组网方案让大家参考参考.如有错误之处,欢迎大家多多指点. 1,用网桥实现增加接入点(比如你有5台机子要上网,而你的小型路由只有4个接口 ...

  2. FZU2132 - LQX的作业(概率论)

    Problem Description LQX在做作业时遇到一个难题不会做,请你帮她计算一下:在N个独立地分布于0和1之间的随机变量排为非递减顺序之后,这些变量中第M个小于等于x的概率是多少? Inp ...

  3. SSKeyChains的使用小节

    我是前言: 最近在项目中需要使用钥匙串来进行账户密码的保存,小结一下.贴上框架地址:https://github.com/soffes/SAMKeychain. 它提供了5个类方法使用: + (NSA ...

  4. C# 自己对delegate的总结和认识

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  5. c# 扩展方法奇思妙用集锦

    本文转载:http://www.cnblogs.com/ldp615/archive/2009/08/07/1541404.html 其中本人觉得很经典的:c# 扩展方法奇思妙用基础篇五:Dictio ...

  6. PowerShell 导出SharePoint管理中心解决方式

    PowerShell 导出SharePoint管理中心解决方式         SharePoint QQ群有人问能不能下载(导出)管理中心里的解决方式.由于在管理中心中点击解决方式会进入还有一个页面 ...

  7. EasyUI-在行内进行表格的增删改操作

    第一篇笔记中记录了如何实现表格的增删改,那个是点击之后跳出来一个对话框然后进行的,这里是在表格本身上进行的操作,也很简单,但是这里发现一个版本问题,也可以说是兼容性问题. 1.首先我们看引用的js和c ...

  8. [rxjs] Shares a single subscription -- publish()

    If have an observable and you subscribe it twice, those tow subscritions have no connection. console ...

  9. java笔试题13-11-21

    中xxx科技公司java笔试题 今天去参加一个公司的面试,去先做了一份笔试题,妈的,太他妈难了(对于我来说,最后做完一个员工说你是不是投错简历了,都是空白,我说我做的大部分都对了..最后面试都没有,就 ...

  10. iOS 开发之 ReactiveCocoa(进阶)

    Map : 映射 UITextField *textField =[[UITextField alloc]initWithFrame:CGRectMake(100, 100, 100, 40)]; t ...