Chromium网页Frame Tree创建过程分析

发表于 5年以前  | 总阅读数:2031 次

Chromium在加载一个网页之前,需要在Browser进程创建一个Frame Tree。Browser进程为网页创建了Frame Tree之后,再请求Render进程加载其内容。Frame Tree将网页抽象为Render Frame。Render Frame是为实现Out-of-Process iframes设计的。本文接下来就分析Frame Tree的创建过程,为后面分析网页加载过程打基础。

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

关于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的,它的实现如下所示:

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;  
    }

这个函数定义在文件external/chromium_org/content/shell/browser/shell.cc中。

Shell类的静态成员函数CreateNewWindow首先是调用WebContents类的静态成员函数Create创建一个WebContentsImpl对象,接着再调用Shell类的静态成员函数CreateShell根据前面创建的WebContentsImpl对象创建一个Shell对象,并且返回给调用者。

接下来,我们先分析WebContents类的静态成员函数Create的实现,接着再分析Shell类的静态成员函数CreateShell的实现。

WebContents类的静态成员函数Create的实现如下所示:

WebContents* WebContents::Create(const WebContents::CreateParams& params) {
      return WebContentsImpl::CreateWithOpener(
          params, static_cast<WebContentsImpl*>(params.opener));
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

WebContents类的静态成员函数Create调用另外一个静态成员函数CreateWithOpener创建一个WebContentsImpl对象,并且返回给调用者。

WebContents类的静态成员函数CreateWithOpener的实现如下所示:

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;
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

WebContents类的静态成员函数CreateWithOpener首先是创建一个WebContentsImpl对象。在将这个WebContentsImpl对象返回给调用者之前,会先调用它的成员函数Init对它进行初始化。

接下来,我们先分析WebContentsImpl对象的创建过程,即WebContentsImpl类的构造函数的实现,接下来再分析WebContentsImpl对象的初始化过程,即WebContentsImpl类的成员函数Init的实现。

WebContentsImpl类的构造函数的实现如下所示:

WebContentsImpl::WebContentsImpl(
        BrowserContext* browser_context,
        WebContentsImpl* opener)
        : ......,
          controller_(this, browser_context),
          ......,
          frame_tree_(new NavigatorImpl(&controller_, this),
                      this, this, this, this),
          ...... {
      ......
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

WebContentsImpl类的成员变量controller_描述的是一个NavigationControllerImpl对象。后面我们可以看到,这个NavigationControllerImpl对象负责执行加载URL的操作。现在我们分析它的创建过程,如下所示:

NavigationControllerImpl::NavigationControllerImpl(
        NavigationControllerDelegate* delegate,
        BrowserContext* browser_context)
        : ......,
          delegate_(delegate),
          ...... {
      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_controller_impl.cc中。

从前面的调用过程可以知道,参数delegate指向的是一个WebContentsImpl对象。这个WebContentsImpl对象保存在NavigationControllerImpl类的成员变量delegate_中。

回到WebContentsImpl类的构造函数中,它构造了一个NavigationControllerImpl对象之后,接下来又根据该NavigationControllerImpl对象创建一个NavigatorImpl对象。这个NavigatorImpl对象也是在加载URL时使用到的,它的创建过程如下所示:

NavigatorImpl::NavigatorImpl(
        NavigationControllerImpl* navigation_controller,
        NavigatorDelegate* delegate)
        : controller_(navigation_controller),
          delegate_(delegate) {
    }

这个函数定义在文件external/chromium_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类的构造函数的实现,如下所示:

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())),
          ...... {
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree.cc中。

从前面的调用过程可以知道,参数navigator指向的是一个NavigatorImpl对象,其余四个参数指向的是同一个WebContentsImpl对象,分别被保存在FrameTree类的成员变量render_framedelegate、render_viewdelegate、render_widget_delegate_和manager_delegate_中。

FrameTree类的构造函数接下来做的一件事情是创建一个FrameTreeNode对象,并且保存在成员变量root_中,作为当前正在创建的Frame Tree的根节点。这个根节点描述的就是后面要加载的网页。

FrameTreeNode对象的创建过程,即FrameTreeNode类的构造函数的实现,如下所示:

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),
          ...... {}

这个函数定义在文件external/chromium_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对象,它的构造过程如下所示:

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),
          ...... {
      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

从前面的调用过程可以知道,参数frame_tree_node指向的是一个FrameTreeNode对象,它被保存在RenderFrameHostManager类的成员变量frame_tree_node中,其余四个参数指向的是同一个WebContentsImpl对象,分别被保存在RenderFrameHostManager类的成员变量delegate、render_framedelegate、render_view_delegate_和render_widget_delegate_中。

这一步执行完成之后,一个Frame Tree就创建出来了。这是一个只有根节点的Frame Tree。根节点描述的网页就是接下来要进行加载的。根节点描述的网页加载完成之后,就会进行解析。在解析的过程中,如果碰到iframe标签,那么就会创建另外一个子节点,并且添加到当前正在创建的Frame Tree中去。

返回到WebContents类的静态成员函数CreateWithOpener中,它创建了一个WebContentsImpl对象之后,接下来会调用这个WebContentsImpl对象的成员函数Init执行初始化工作,如下所示:

void WebContentsImpl::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_));
      }

      ......
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

WebContentsImpl类的成员函数Init首先是调用成员函数GetRenderManager获得一个RenderFrameHostManager对象,接下来再调用这个RenderFrameHostManager对象的成员函数Init对其进行初始化。

WebContentsImpl类的成员函数GetRenderManager的实现如下所示:

RenderFrameHostManager* WebContentsImpl::GetRenderManager() const {
      return frame_tree_.root()->render_manager();
    }

这个函数定义在文件external/chromium_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的实现如下所示:

class CONTENT_EXPORT FrameTreeNode {
     public:
      ......

      RenderFrameHostManager* render_manager() {
        return &render_manager_;
      }

      ......

     private:
      ......

      RenderFrameHostManager render_manager_;

      ......
    };

这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree_node.h中。

从这里可以看到,FrameTreeNode类的成员函数render_manager返回的是成员变量render_manager_描述的一个RenderFrameHostManager对象。这个RenderFrameHostManager对象是在前面分析的FrameTreeNode类的构造函数创建的。

回到WebContentsImpl类的成员函数Init中,它获得了一个RenderFrameHostManager对象之后,就调用它的成员函数Init对它进行初始化,如下所示:

void RenderFrameHostManager::Init(BrowserContext* browser_context,
                                      SiteInstance* site_instance,
                                      int view_routing_id,
                                      int frame_routing_id) {
      ......

      SetRenderFrameHost(CreateRenderFrameHost(site_instance,
                                               view_routing_id,
                                               frame_routing_id,
                                               false,
                                               delegate_->IsHidden()));

      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

RenderFrameHostManager类的成员函数Init主要做了两件事情。第一件事情是调用成员函数CreateRenderFrameHost创建了一个RenderFrameHostImpl对象。第二件事情是调用成员函数SetRenderFrameHost将该RenderFrameHostImpl对象保存在内部,如下所示:

scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::SetRenderFrameHost(
        scoped_ptr<RenderFrameHostImpl> render_frame_host) {
      // Swap the two.
      scoped_ptr<RenderFrameHostImpl> old_render_frame_host =
          render_frame_host_.Pass();
      render_frame_host_ = render_frame_host.Pass();

      ......

      return old_render_frame_host.Pass();
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

RenderFrameHostManager类的成员函数SetRenderFrameHost主要是将参数render_frame_host描述的一个RenderFrameHostImpl对象保存在成员变量render_frame_host_中。

回到RenderFrameHostManager类的成员函数Init中,接下来我们主要分析它调用成员函数CreateRenderFrameHost创建RenderFrameHostImpl对象的过程,如下所示:

scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::CreateRenderFrameHost(
        SiteInstance* site_instance,
        int view_routing_id,
        int frame_routing_id,
        bool swapped_out,
        bool hidden) {
      ......

      FrameTree* frame_tree = frame_tree_node_->frame_tree();
      RenderViewHostImpl* render_view_host = NULL;
      if (frame_tree_node_->IsMainFrame()) {
        render_view_host = frame_tree->CreateRenderViewHostForMainFrame(
            site_instance, view_routing_id, frame_routing_id, swapped_out, hidden);
      } 

      ......

      scoped_ptr<RenderFrameHostImpl> render_frame_host =
          make_scoped_ptr(RenderFrameHostFactory::Create(render_view_host,
                                                         render_frame_delegate_,
                                                         frame_tree,
                                                         frame_tree_node_,
                                                         frame_routing_id,
                                                         swapped_out).release());
      return render_frame_host.Pass();
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

从前面的分析可以知道,当前正在处理的RenderFrameHostManager对象的成员变量frame_tree_node_描述的是一个Frame Tree的根节点。一个Frame Tree的根节点描述的网页是一个Main Frame(网页中的iframe标签描述的网页称为Sub Frame),因此RenderFrameHostManager类的成员函数CreateRenderFrameHost是调用FrameTree类的成员函数CreateRenderViewHostForMainFrame创建一个RenderViewHostImpl对象,并且保存在本地变量render_view_host中。

FrameTree类的成员函数CreateRenderViewHostForMainFrame的实现如下所示:

RenderViewHostImpl* FrameTree::CreateRenderViewHostForMainFrame(
        SiteInstance* site_instance,
        int routing_id,
        int main_frame_routing_id,
        bool swapped_out,
        bool hidden) {
      .....
      RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
          RenderViewHostFactory::Create(site_instance,
                                        render_view_delegate_,
                                        render_widget_delegate_,
                                        routing_id,
                                        main_frame_routing_id,
                                        swapped_out,
                                        hidden));

      render_view_host_map_[site_instance->GetId()] = rvh;
      return rvh;
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree.cc中。

FrameTree类的成员函数CreateRenderViewHostForMainFrame调用RenderViewHostFactory类的静态成员函数Create创建了一个RenderViewHostImpl对象。在将这个RenderViewHostImpl对象返回给调用者之前,会以参数site_instance描述的一个Site Instance的ID为键值,将该RenderViewHostImpl对象保存在成员变量render_view_host_map_描述的一个Hash Map中,以便以后可以进行查找。

RenderViewHostFactory类的静态成员函数Create的实现如下所示:

RenderViewHost* RenderViewHostFactory::Create(
        SiteInstance* instance,
        RenderViewHostDelegate* delegate,
        RenderWidgetHostDelegate* widget_delegate,
        int routing_id,
        int main_frame_routing_id,
        bool swapped_out,
        bool hidden) {
      ......
      return new RenderViewHostImpl(instance, delegate, widget_delegate, routing_id,
                                    main_frame_routing_id, swapped_out, hidden);
    }

这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_factory.cc中。

RenderViewHostFactory类的静态成员函数Create首先是创建了一个RenderViewHostImpl对象,然后再将这个RenderViewHostImpl对象返回给调用者。从前面Chromium的Render进程启动过程分析一文可以知道,在创建RenderViewHostImpl对象的过程中,将会启动一个Render进程。这个Render进程接下来将会负责加载指定的URL。

这一步执行完成之后,回到RenderFrameHostManager类的成员函数CreateRenderFrameHost中,它接下来调用RenderFrameHostFactory类的静态成员函数Create创建了一个RenderFrameHostImpl对象。这个RenderFrameHostImpl对象返回给RenderFrameHostManager类的成员函数Init后,将会被保存在成员变量render_frame_host_中。

RenderFrameHostFactory类的静态成员函数Create的实现如下所示:

scoped_ptr<RenderFrameHostImpl> RenderFrameHostFactory::Create(
        RenderViewHostImpl* render_view_host,
        RenderFrameHostDelegate* delegate,
        FrameTree* frame_tree,
        FrameTreeNode* frame_tree_node,
        int routing_id,
        bool is_swapped_out) {
      ......
      return make_scoped_ptr(new RenderFrameHostImpl(
          render_view_host, delegate, frame_tree, frame_tree_node, routing_id,
          is_swapped_out));
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_factory.cc中。

从这里可以看到,RenderFrameHostFactory类的静态成员函数Create创建的是一个RenderFrameHostImpl对象,并且将这个RenderFrameHostImpl对象返回给调用者。

RenderFrameHostImpl对象的创建过程,即RenderFrameHostImpl类的构造函数的实现,如下所示:

RenderFrameHostImpl::RenderFrameHostImpl(
        RenderViewHostImpl* render_view_host,
        RenderFrameHostDelegate* delegate,
        FrameTree* frame_tree,
        FrameTreeNode* frame_tree_node,
        int routing_id,
        bool is_swapped_out)
        : render_view_host_(render_view_host),
          ...... {
      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_impl.cc中。

RenderFrameHostImpl类的构造函数将参数render_view_host描述的一个RenderViewHostImpl对象保存在成员变量render_view_host_中,这个RenderViewHostImpl对象就是前面调用RenderViewHostFactory类的静态成员函数Create创建的。

这一步执行完成之后,回到WebContentsImpl类的成员函数Init中,它接下来还做有另外一个初始化工作,如下所示:

void WebContentsImpl::Init(const WebContents::CreateParams& params) {
      ......

      if (browser_plugin_guest_) {
        ......
      } else {
        // Regular WebContentsView.
        view_.reset(CreateWebContentsView(
            this, delegate, &render_view_host_delegate_view_));
      }

      ......
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

当WebContentsImpl类的成员变量browser_plugin_guest_指向了一个BrowserPluginGuest对象时,表示当前正在处理的WebContentsImpl对象用来为一个Browser Plugin加载网页。我们不考虑这种情况。这时候WebContentsImpl类的成员函数Init就会调用函数CreateWebContentsView创建一个WebContentsViewAndroid对象,并且保存在成员变量view_中。

函数CreateWebContentsView的实现如下所示:

WebContentsView* CreateWebContentsView(
        WebContentsImpl* web_contents,
        WebContentsViewDelegate* delegate,
        RenderViewHostDelegateView** render_view_host_delegate_view) {
      WebContentsViewAndroid* rv = new WebContentsViewAndroid(
          web_contents, delegate);
      *render_view_host_delegate_view = rv;
      return rv;
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_view_android.cc中。

从这里可以看到,在Android平台上,函数CreateWebContentsView返回给调用者的是一个WebContentsViewAndroid对象。

这一步执行完成之后,回到Shell类的静态成员函数CreateNewWindow,这时候它就创建了一个WebContentsImpl对象,接下来这个WebContentsImpl对象会用来创建一个Shell对象,这是通过调用Shell类的静态成员函数CreateShell实现的,如下所示:

Shell* Shell::CreateShell(WebContents* web_contents,
                              const gfx::Size& initial_size) {
      Shell* shell = new Shell(web_contents);
      ......

      shell->web_contents_.reset(web_contents);
      ......

      shell->PlatformSetContents();
      ......

      return shell;
    }

这个函数定义在文件external/chromium_org/content/shell/browser/shell.cc中。

从前面的调用过程可以知道,参数web_contents指向的是一个WebContentsImpl对象。

Shell类的静态成员函数CreateShell首先创建了一个Shell对象,接下来将参数web_contents指向的WebContentsImpl对象保存在该Shell对象的成员变量web_contents_中,最后又调用该Shell对象的成员函数PlatformSetContents执行一些平台相关的初始化操作。

Shell类的成员函数PlatformSetContents的实现如下所示:

void Shell::PlatformSetContents() {
      JNIEnv* env = AttachCurrentThread();
      Java_Shell_initFromNativeTabContents(
          env, java_object_.obj(), reinterpret_cast<intptr_t>(web_contents()));
    }

这个函数定义在文件external/chromium_org/content/shell/browser/shell_android.cc中。

Shell类的成员函数PlatformSetContents调用函数Java_Shell_initFromNativeTabContents调用成员变量java_object_描述的一个Java层Shell对象的成员函数initFromNativeTabContents执行平台相关的初始化操作。同时传递给该Java层Shell对象的成员函数initFromNativeTabContents一个WebContentsImpl对象。这个WebContentsImpl对象是通过调用Native层Shell类的成员函数web_contents获得的,如下所示:

class Shell : public WebContentsDelegate,
                  public WebContentsObserver {
     public:
      ......

      WebContents* web_contents() const { return web_contents_.get(); }

      ......

     private:
      ......

      scoped_ptr<WebContents> web_contents_;

      ......
    }

这个函数定义在文件external/chromium_org/content/shell/browser/shell.h中。

从前面的分析可以知道,Shell类的成员变量web_contents_指向的一个WebContentsImpl对象,Shell类的成员函数web_contents将这个WebContentsImpl对象返回给调用者。

接下来我们继续分析Java层Shell类的成员函数initFromNativeTabContents的实现,如下所示:

public class Shell extends LinearLayout {
        ......

        private ContentViewCore mContentViewCore;
        ......

        @CalledByNative
        private void initFromNativeTabContents(long nativeWebContents) {
            Context context = getContext();
            mContentViewCore = new ContentViewCore(context);
            ......
            mContentViewCore.initialize(cv, cv, nativeWebContents, mWindow);
            ......
        }

        ......
    }

这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/Shell.java中。

从前面的调用过程可以知道,参数nativeWebContents描述的实际上是一个Native层的WebContentsImpl对象。Shell类的成员函数initFromNativeTabContents主要是创建了一个ContentViewCore对象,保存在成员变量mContentViewCore中,并且调用该ContentViewCore对象的成员函数initialize执行初始化操作。

ContenttViewCore类的成员函数initialize的实现如下所示:

public class ContentViewCore
            implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
        ......

        private long mNativeContentViewCore = 0;
        ......

        public void initialize(ViewGroup containerView, InternalAccessDelegate internalDispatcher,
                long nativeWebContents, WindowAndroid windowAndroid) {
            ......

            mNativeContentViewCore = nativeInit(
                    nativeWebContents, viewAndroidNativePointer, windowNativePointer,
                    mRetainedJavaScriptObjects);

            ......
        }

        ......
    }

这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java中。

ContentViewCore类的成员函数initialize执行了一个操作,就是调用另外一个成员函数nativeInit创建了一个Native层的ContentViewCoreImpl对象,并且将它的地址保存在成员变量mNativeContentViewCore中。

ContentViewCore类的成员函数nativeInit是一个JNI函数,由Native层的Java_com_android_org_chromium_content_browser_ContentViewCore_nativeInit函数实现,如下所示:

static jlong Init(JNIEnv* env, jobject jcaller,
        jlong webContentsPtr,
        jlong viewAndroidPtr,
        jlong windowAndroidPtr,
        jobject retainedObjectSet);

    __attribute__((visibility("default")))
    jlong
        Java_com_android_org_chromium_content_browser_ContentViewCore_nativeInit(JNIEnv*
        env, jobject jcaller,
        jlong webContentsPtr,
        jlong viewAndroidPtr,
        jlong windowAndroidPtr,
        jobject retainedObjectSet) {
      return Init(env, jcaller, webContentsPtr, viewAndroidPtr, windowAndroidPtr,
          retainedObjectSet);
    }

这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewCore_jni.h中。

函数Java_com_android_org_chromium_content_browser_ContentViewCore_nativeInit调用另外一个函数Init创建一个Native层的ContentViewCoreImpl对象,并且将该ContentViewCoreImpl对象的地址返回给调用者。

函数Init的实现如下所示:

jlong Init(JNIEnv* env,
               jobject obj,
               jlong native_web_contents,
               jlong view_android,
               jlong window_android,
               jobject retained_objects_set) {
      ContentViewCoreImpl* view = new ContentViewCoreImpl(
          env, obj,
          reinterpret_cast<WebContents*>(native_web_contents),
          reinterpret_cast<ui::ViewAndroid*>(view_android),
          reinterpret_cast<ui::WindowAndroid*>(window_android),
          retained_objects_set);
      return reinterpret_cast<intptr_t>(view);
    }

这个函数定义在文件external/chromium_org/content/browser/android/content_view_core_impl.cc中。

从这里可以看到,函数Init创建了一个ContentViewCoreImpl对象,并且将该ContentViewCoreImpl对象的地址返回给调用者。

ContentViewCoreImpl对象的创建过程,即ContentViewCoreImpl类的构造函数的实现,如下所示:

ContentViewCoreImpl::ContentViewCoreImpl(
        JNIEnv* env,
        jobject obj,
        WebContents* web_contents,
        ui::ViewAndroid* view_android,
        ui::WindowAndroid* window_android,
        jobject java_bridge_retained_object_set)
        : ......,
          web_contents_(static_cast<WebContentsImpl*>(web_contents)),
          ...... {
      ......
    }

这个函数定义在文件external/chromium_org/content/browser/android/content_view_core_impl.cc中。

从前面的调用过程可以知道,参数web_contents描述的是一个WebContentsImpl对象。这个WebContentsImpl对象保存在ContentViewCoreImpl类的成员变量web_contents_中。

这一步执行完成后,Browser进程在加载一个URL之前,就在Native层创建了一个Shell对象和一个WebContentsImpl对象。创建的Native层WebContentsImpl对象内部维护有一个Frame Tree。创建的Native层Shell对象在Java层也有一个对应的Shell对象。这个Java层的Shell对象的创建过程可以参考前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文。

从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文还可以知道,Browser进程接下来会调用上述Java层Shell对象的成员函数loadUrl加载指定的URL,它的实现如下所示:

public class Shell extends LinearLayout {
        ......

        public void loadUrl(String url) {
            ......

            if (TextUtils.equals(url, mContentViewCore.getUrl())) {
                mContentViewCore.reload(true);
            } else {
                mContentViewCore.loadUrl(new LoadUrlParams(sanitizeUrl(url)));
            }
            ......
        }

        ......
    }

这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/Shell.java中。

从前面的分析可以知道,Shell类的成员变量mContentViewCore指向的是一个ContentViewCore对象,Shell类的成员函数loadUrl调用它的成员函数reload或者loadUrl加载参数url描述的URL,取决于该ContentViewCore对象之前是否加载过URL,以及加载过的URL是否与参数url描述的URL相同。

我们假设Shell类的成员变量mContentViewCore指向的ContentViewCore对象之前没有加载过URL,那么Shell类的成员函数loadUrl接下来就会调用它的成员函数loadUrl加载参数url描述的URL。

ContentViewCore类的成员函数loadUrl的实现如下所示:

public class ContentViewCore
            implements NavigationClient, AccessibilityStateChangeListener, ScreenOrientationObserver {
        ......

        public void loadUrl(LoadUrlParams params) {
            ......

            nativeLoadUrl(mNativeContentViewCore,
                    params.mUrl,
                    params.mLoadUrlType,
                    params.mTransitionType,
                    params.getReferrer() != null ? params.getReferrer().getUrl() : null,
                    params.getReferrer() != null ? params.getReferrer().getPolicy() : 0,
                    params.mUaOverrideOption,
                    params.getExtraHeadersString(),
                    params.mPostData,
                    params.mBaseUrlForDataUrl,
                    params.mVirtualUrlForDataUrl,
                    params.mCanLoadLocalResources,
                    params.mIsRendererInitiated);
        }

        ......
    }

这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java中。

ContentViewCore类的成员函数loadUrl调用另外一个成员函数nativeLoadUrl加载参数params描述的URL。

ContentViewCore类的成员函数nativeLoadUrl是一个JNI函数,由Native层的函数Java_com_android_org_chromium_content_browser_ContentViewCore_nativeLoadUrl实现,如下所示:

__attribute__((visibility("default")))
    void
        Java_com_android_org_chromium_content_browser_ContentViewCore_nativeLoadUrl(JNIEnv*
        env,
        jobject jcaller,
        jlong nativeContentViewCoreImpl,
        jstring url,
        jint loadUrlType,
        jint transitionType,
        jstring referrerUrl,
        jint referrerPolicy,
        jint uaOverrideOption,
        jstring extraHeaders,
        jbyteArray postData,
        jstring baseUrlForDataUrl,
        jstring virtualUrlForDataUrl,
        jboolean canLoadLocalResources,
        jboolean isRendererInitiated) {
      ContentViewCoreImpl* native =
          reinterpret_cast<ContentViewCoreImpl*>(nativeContentViewCoreImpl);
      CHECK_NATIVE_PTR(env, jcaller, native, "LoadUrl");
      return native->LoadUrl(env, jcaller, url, loadUrlType, transitionType,
          referrerUrl, referrerPolicy, uaOverrideOption, extraHeaders, postData,
          baseUrlForDataUrl, virtualUrlForDataUrl, canLoadLocalResources,
          isRendererInitiated);
    }

这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewCore_jni.h中。

从前面的调用过程可以知道,参数nativeContentViewCoreImpl来自Java层的ContentViewCore类的成员变量mNativeContentViewCore。ContentViewCore类的成员变量mNativeContentViewCore指向的是一个Native层的ContentViewCoreImpl对象。因此,函数Java_com_android_org_chromium_content_browser_ContentViewCore_nativeLoadUrl可以将参数nativeContentViewCoreImpl强制转换为一个Native层的ContentViewCoreImpl对象,并且会调用该Native层ContentViewCoreImpl对象的成员函数LoadUrl加载参数url描述的URL。

ContentViewCoreImpl类的成员函数LoadUrl的实现如下所示:

void ContentViewCoreImpl::LoadUrl(
        JNIEnv* env, jobject obj,
        jstring url,
        jint load_url_type,
        jint transition_type,
        jstring j_referrer_url,
        jint referrer_policy,
        jint ua_override_option,
        jstring extra_headers,
        jbyteArray post_data,
        jstring base_url_for_data_url,
        jstring virtual_url_for_data_url,
        jboolean can_load_local_resources,
        jboolean is_renderer_initiated) {
      ......
      NavigationController::LoadURLParams params(
          GURL(ConvertJavaStringToUTF8(env, url)));
      ......

      LoadUrl(params);
    }

这个函数定义在文件external/chromium_org/content/browser/android/content_view_core_impl.cc中。

ContentViewCoreImpl类的成员函数LoadUrl主要是将要加载的URL封装在一个NavigationController::LoadURLParams对象中,并且调用另外一个重载版本的成员函数LoadUrl执行加载该URL的操作,如下所示:

void ContentViewCoreImpl::LoadUrl(
        NavigationController::LoadURLParams& params) {
      GetWebContents()->GetController().LoadURLWithParams(params);
    }

这个函数定义在文件external/chromium_org/content/browser/android/content_view_core_impl.cc中。

ContentViewCoreImpl类的成员函数LoadUrl首先调用另外一个成员函数GetWebContents获得一个WebContentsImpl对象,如下所示:

WebContents* ContentViewCoreImpl::GetWebContents() const {
      return web_contents_;
    }

这个函数定义在文件external/chromium_org/content/browser/android/content_view_core_impl.cc中。

从前面的分析可以知道,ContentViewCoreImpl类的成员变量web_contents_指向的是一个WebContentsImpl对象,ContentViewCoreImpl类的成员函数GetWebContents将它返回给调用者。

回到ContentViewCoreImpl类的成员函数LoadUrl,它获得了一个WebContentsImpl对象之后,再调用该WebContentsImpl对象的成员函数GetController获得一个NavigationControllerImpl对象,如下所示:

NavigationControllerImpl& WebContentsImpl::GetController() {
      return controller_;
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

从前面的分析可以知道,WebContentsImpl类的成员变量controller_描述的是一个NavigationControllerImpl对象,WebContentsImpl类的成员函数GetController将它返回给调用者。

回到ContentViewCoreImpl类的成员函数LoadUrl,它获得了一个NavigationControllerImpl对象之后,再调用该NavigationControllerImpl对象的成员函数LoadURLWithParams加载参数params描述的URL,如下所示:

void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) {
      ......

      NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
          CreateNavigationEntry(
              params.url,
              params.referrer,
              params.transition_type,
              params.is_renderer_initiated,
              params.extra_headers,
              browser_context_));
      ......

      LoadEntry(entry);
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_controller_impl.cc中。

NavigationControllerImpl类的成员函数LoadURLWithParams首先是调用静态成员函数CreateNavigationEntry创建一个NavigationEntryImpl对象,如下所示:

NavigationEntry* NavigationController::CreateNavigationEntry(
          const GURL& url,
          const Referrer& referrer,
          PageTransition transition,
          bool is_renderer_initiated,
          const std::string& extra_headers,
          BrowserContext* browser_context) {
      ......

      NavigationEntryImpl* entry = new NavigationEntryImpl(
          NULL,  // The site instance for tabs is sent on navigation
                 // (WebContents::GetSiteInstance).
          -1,
          loaded_url,
          referrer,
          base::string16(),
          transition,
          is_renderer_initiated);
      ......
      return entry;
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_controller_impl.cc中。

从这里可以看到,NavigationControllerImpl类的静态成员函数CreateNavigationEntry创建出来的NavigationEntryImpl对象封装了参数url描述的URL。

注意,NavigationControllerImpl类的静态成员函数CreateNavigationEntry返回的是一个NavigationEntry指针,但是它实际上指向的是一个NavigationEntryImpl对象,NavigationControllerImpl类的成员函数LoadURLWithParams会通过调用NavigationEntryImpl类的静态成员函数FromNavigationEntry将该得到的NavigationEntry指针强制转换为一个NavigationEntryImpl对象,如下所示:

NavigationEntryImpl* NavigationEntryImpl::FromNavigationEntry(
        NavigationEntry* entry) {
      return static_cast<NavigationEntryImpl*>(entry);
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_entry_impl.cc中。

回到NavigationControllerImpl类的成员函数LoadURLWithParams中,它将要加载的URL封装在一个NavigationEntryImpl对象之后,传递给另外一个成员函数LoadEntry,让后者执行加载URL的操作。

NavigationControllerImpl类的成员函数LoadEntry的实现如下所示:

void NavigationControllerImpl::LoadEntry(NavigationEntryImpl* entry) {
      // When navigating to a new page, we don't know for sure if we will actually
      // end up leaving the current page.  The new page load could for example
      // result in a download or a 'no content' response (e.g., a mailto: URL).
      SetPendingEntry(entry);
      NavigateToPendingEntry(NO_RELOAD);
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_controller_impl.cc中。

NavigationControllerImpl类的成员函数LoadEntry首先调用另外一个成员函数SetPendingEntry将参数entry指向的一个NavigationEntryImpl对象保存在成员变量pending_entry_中,表示NavigationEntryImpl对象封装的URL正在等待加载,如下所示:

void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) {
      ......
      pending_entry_ = entry;
      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_controller_impl.cc中。

回到NavigationControllerImpl类的成员函数LoadEntry中,将要加载的URL保存在成员变量pending_entry_之后,接下来就调用另外一个成员函数NavigateToPendingEntry对其进行加载,如下所示:

void NavigationControllerImpl::NavigateToPendingEntry(ReloadType reload_type) {
      ......

      bool success = delegate_->NavigateToPendingEntry(reload_type);

      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_controller_impl.cc中。

从前面的分析可以知道,NavigationControllerImpl类的成员变量delegate_指向的是一个WebContentsImpl对象,NavigationControllerImpl类的成员函数NavigateToPendingEntry调用它的成员函数NavigateToPendingEntry加载前面指定的URL。

WebContentsImpl类的成员函数NavigateToPendingEntry的实现如下所示:

bool WebContentsImpl::NavigateToPendingEntry(
        NavigationController::ReloadType reload_type) {
      FrameTreeNode* node = frame_tree_.root();

      ......

      return node->navigator()->NavigateToPendingEntry(
          node->current_frame_host(), reload_type);
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

从前面的分析可以知道,WebContentsImpl类的成员变量frame_tree_指向的是一个FrameTree对象。这个FrameTree对象描述的是一个Frame Tree。WebContentsImpl类的成员函数NavigateToPendingEntry调用这个FrameTree对象的成员函数root可以获得一个FrameTreeNode对象。这个FrameTreeNode对象代表的就是正在等待加载的网页的Main Frame。

WebContentsImpl类的成员函数NavigateToPendingEntry接下来调用前面获得的FrameTreeNode对象的成员函数navigator和current_frame_host分别获得一个NavigatorImpl对象和一个RenderFrameHostImpl对象。

FrameTreeNode类的成员函数navigator的实现如下所示:

class CONTENT_EXPORT FrameTreeNode {
     public:
      ......

      Navigator* navigator() {
        return navigator_.get();
      }

      ......

     private:
      ......

      scoped_refptr<Navigator> navigator_;

      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree_node.h中。

从前面的分析可以知道,FrameTreeNode类的成员变量navigator_指向的是一个NavigatorImpl对象,FrameTreeNode对象的成员函数navigator会将这个NavigatorImpl对象返回给调用者。

FrameTreeNode类的成员函数current_frame_host的实现如下所示:

class CONTENT_EXPORT FrameTreeNode {
     public:
      ......

      RenderFrameHostImpl* current_frame_host() const {
        return render_manager_.current_frame_host();
      }

     private:
      ......

      RenderFrameHostManager render_manager_;

      ......
    };

这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree_node.h中。

FrameTreeNode类的成员函数current_frame_host调用成员变量render_manager_描述的一个RenderFrameHostManager对象的成员函数current_frame_host获得一个RenderFrameHostImpl对象返回给调用者。

RenderFrameHostManager类的成员函数current_frame_host的实现如下所示:

class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver {
     public:
      ......

      RenderFrameHostImpl* current_frame_host() const {
        return render_frame_host_.get();
      }

      ......

     private:
      ......

      scoped_ptr<RenderFrameHostImpl> render_frame_host_;

      ......
    };

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.h

RenderFrameHostManager类的成员函数current_frame_host返回的是成员变量render_frame_host_描述的一个RenderFrameHostImpl对象。这个RenderFrameHostImpl对象是在前面分析的RenderFrameHostFactory类的静态成员函数Create中创建的。

回到WebContentsImpl类的成员函数NavigateToPendingEntry中,它获得了一个NavigatorImpl对象和一个RenderFrameHostImpl对象之后,就调用获得的NavigatorImpl对象的成员函数NavigateToPendingEntry加载正在等待加载的URL,如下所示:

bool NavigatorImpl::NavigateToPendingEntry(
        RenderFrameHostImpl* render_frame_host,
        NavigationController::ReloadType reload_type) {
      return NavigateToEntry(
          render_frame_host,
          *NavigationEntryImpl::FromNavigationEntry(controller_->GetPendingEntry()),
          reload_type);
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigator_impl.cc中。

从前面的分析可以知道,NavigatorImpl类的成员变量controller_描述的是一个NavigationControllerImpl对象,NavigatorImpl对象的成员函数NavigateToPendingEntry首先会调用这个NavigationControllerImpl对象的成员函数GetPendingEntry获得正在等待加载的URL,如下所示:

NavigationEntry* NavigationControllerImpl::GetPendingEntry() const {
      return pending_entry_;
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigation_controller_impl.cc中。

NavigationControllerImpl对象的成员函数GetPendingEntry返回的是成员变量pending_entry_描述的一个NavigationEntryImpl对象。从前面的分析可以知道,这个NavigationEntryImpl对象描述的就是正在等待加载的URL。

回到NavigatorImpl类的成员函数NavigateToPendingEntry中,获得了等待加载的URL之后,它接下来调用另外一个成员函数NavigateToEntry对该URL进行加载,如下所示:

bool NavigatorImpl::NavigateToEntry(
        RenderFrameHostImpl* render_frame_host,
        const NavigationEntryImpl& entry,
        NavigationController::ReloadType reload_type) {
      ......

      RenderFrameHostManager* manager =
          render_frame_host->frame_tree_node()->render_manager();
      RenderFrameHostImpl* dest_render_frame_host = manager->Navigate(entry);

      // Navigate in the desired RenderFrameHost.
      FrameMsg_Navigate_Params navigate_params;
      MakeNavigateParams(entry, *controller_, reload_type, &navigate_params);
      dest_render_frame_host->Navigate(navigate_params);

      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/navigator_impl.cc中。

从前面的调用过程可以知道,参数render_frame_host描述的就是前面调用RenderFrameHostManager类的成员函数current_frame_host获得的RenderFrameHostImpl对象。NavigatorImpl类的成员函数NavigateToEntry首先获得这个RenderFrameHostImpl对象对应的Frame Tree Node,接着又获得该Frame Tree Node所关联的RenderFrameHostManager对象。有了这个RenderFrameHostManager对象之后,NavigatorImpl类的成员函数NavigateToEntry就调用它的成员函数Navigate,用来通知它即将要导航到指定的URL去。

RenderFrameHostManager类的成员函数Navigate会返回一个RenderFrameHostImpl对象dest_render_frame_host。RenderFrameHostImpl对象dest_render_frame_host与RenderFrameHostImpl对象render_frame_host有可能是相同的,也有可能是不同的。什么情况下相同呢?如果上述RenderFrameHostManager对象即将要导航到的URL与它之前已经导航至的URL属于相同站点,那么就是相同的。反之则是不同的。无论是哪一种情况,导航至指定的URL的操作最后都是通过调用RenderFrameHostImpl对象dest_render_frame_host的成员函数Navigate完成的。

接下来我们就先分析RenderFrameHostManager类的成员函数Navigate的实现,然后再分析RenderFrameHostImpl类的成员函数Navigate的实现。

RenderFrameHostManager类的成员函数Navigate的实现如下所示:

RenderFrameHostImpl* RenderFrameHostManager::Navigate(
        const NavigationEntryImpl& entry) {
      ......

      RenderFrameHostImpl* dest_render_frame_host = UpdateStateForNavigate(entry);
      ......

      if (!dest_render_frame_host->render_view_host()->IsRenderViewLive()) {
        ......
        if (!InitRenderView(dest_render_frame_host->render_view_host(),
                            opener_route_id,
                            MSG_ROUTING_NONE,
                            frame_tree_node_->IsMainFrame()))
          return NULL;

        ......
      }

      ......

      return dest_render_frame_host;
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

RenderFrameHostManager类的成员函数Navigate首先调用另外一个成员函数UpdateStateForNavigate获得与即将要加载的URL对应的一个RenderFrameHostImpl对象。获得了这个RenderFrameHostImpl对象之后,调用它的成员函数render_view_host可以获得一个RenderViewHostImpl对象。这个RenderViewHostImpl对象是在前面分析的RenderFrameHostManager类的成员函数CreateRenderFrameHost中创建的。有了这个RenderViewHostImpl对象之后,就可以调用它的成员函数IsRenderViewLive判断它是否关联有一个Render View控件。这个Render View控件是一个由平台实现的控件,描述的是用来显示网页的一个区域。

在两种情况下,一个RenderViewHostImpl对象没有关联一个Render View控件。第一种情况是这个RenderViewHostImpl对象还没有加载过URL。第二种情况下是这个RenderViewHostImpl对象加载过URL,但是由于某种原因,负责加载该URL的Render进程崩溃了。在第二种情况下,一个RenderViewHostImpl对象关联的Render View控件会被销毁,所以会导致它没有关联Render View控件。无论是上述两种情况的哪一种,RenderFrameHostManager类的成员函数Navigate都会调用另外一个成员函数InitRenderView为其关联一个Render View控件。

接下来,我们就分别分析RenderFrameHostManager类的成员函数UpdateStateForNavigate和InitRenderView的实现。

RenderFrameHostManager类的成员函数UpdateStateForNavigate的实现如下所示:

RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate(
        const NavigationEntryImpl& entry) {
      ......

      SiteInstance* current_instance = render_frame_host_->GetSiteInstance();
      SiteInstance* new_instance = current_instance;
      ......

      const NavigationEntry* current_entry =
          delegate_->GetLastCommittedNavigationEntryForRenderManager();
      bool force_swap = !is_guest_scheme &&
          ShouldSwapBrowsingInstancesForNavigation(current_entry, &entry);
      if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap))
        new_instance = GetSiteInstanceForEntry(entry, current_instance, force_swap);
      ......

      if (new_instance != current_instance) {
        ......

        return pending_render_frame_host_.get();
      }

      ......

      return render_frame_host_.get();
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

RenderFrameHostManager类的成员变量render_frame_host_描述的是一个RenderFrameHostImpl对象。这个RenderFrameHostImpl对象是在前面分析的RenderFrameHostFactory类的静态成员函数Create中创建的,与前面分析的NavigatorImpl类的成员函数NavigateToEntry的参数render_frame_host描述的RenderFrameHostImpl对象是相同的。

RenderFrameHostManager类的成员函数UpdateStateForNavigate主要做的事情就是检查即将要加载的URL,即参数entry描述的一个URL,与当前已经加载的URL,是否属于相同的站点。如果是不相同的站点,那么RenderFrameHostManager类的成员变量pending_render_frame_host_会指向另外一个RenderFrameHostImpl对象。这个RenderFrameHostImpl对象负责加载参数entry描述的URL。因此,这个RenderFrameHostImpl对象会返回给调用者。

另一方面,如果即将要加载的URL与当前已经加载的URL是相同的站点,那么RenderFrameHostManager类的成员函数UpdateStateForNavigate返回的是成员变量render_frame_host_描述的RenderFrameHostImpl对象。

回到RenderFrameHostManager类的成员函数Navigate中,我们假设它通过调用成员函数UpdateStateForNavigate获得的RenderFrameHostImpl对象还没有关联一个Render View控件,这时候它就会调用另外一个成员函数InitRenderView为这个RenderFrameHostImpl关联一个Render View控件。

RenderFrameHostManager类的成员函数InitRenderView的实现如下所示:

bool RenderFrameHostManager::InitRenderView(RenderViewHost* render_view_host,
                                                int opener_route_id,
                                                int proxy_routing_id,
                                                bool for_main_frame) {
      ......

      return delegate_->CreateRenderViewForRenderManager(
          render_view_host, opener_route_id, proxy_routing_id, for_main_frame);
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

从前面的分析可以知道,RenderFrameHostManager类的成员变量delegate_描述的是一个WebContentsImpl对象。RenderFrameHostManager类的成员函数InitRenderView调用这个WebContentsImpl对象的成员函数CreateRenderViewForRenderManager为参数render_view_host描述的一个RenderViewHostImpl对象创建一个Render View控件。这个RenderViewHostImpl对象是与RenderFrameHostManager类的成员函数UpdateStateForNavigate返回的RenderFrameHostImpl对象关联的,因此,这里调用成员变量delegate_描述的WebContentsImpl对象的成员函数CreateRenderViewForRenderManager实际上是为该RenderFrameHostImpl对象创建一个Render View控件。

WebContentsImpl类的成员函数CreateRenderViewForRenderManager的实现如下所示:

bool WebContentsImpl::CreateRenderViewForRenderManager(
        RenderViewHost* render_view_host,
        int opener_route_id,
        int proxy_routing_id,
        bool for_main_frame) {
      ......
      RenderWidgetHostViewBase* rwh_view;
      ......
      if (!for_main_frame) {
        RenderWidgetHostViewChildFrame* rwh_view_child =
            new RenderWidgetHostViewChildFrame(render_view_host);
        rwh_view = rwh_view_child;
      } else {
        rwh_view = view_->CreateViewForWidget(render_view_host);
      }

      ......

      if (!static_cast<RenderViewHostImpl*>(
              render_view_host)->CreateRenderView(base::string16(),
                                                  opener_route_id,
                                                  proxy_routing_id,
                                                  max_page_id,
                                                  created_with_opener_)) {
        return false;
      }

      ......

      return true;
    }

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

当参数for_main_frame的值等于true的时候,表示WebContentsImpl类的成员函数CreateRenderViewForRenderManager要为一个网页的Main Frame创建一个Render View控件,而当参数for_main_frame的值等于false的时候,表示WebContentsImpl类的成员函数CreateRenderViewForRenderManager要为一个网页的Sub Frame创建一个Render View控件。

从这里可以看到,网页的Sub Frame关联的Render View控件是通过一个RenderWidgetHostViewChildFrame对象描述的,而网页的Main Frame关联的Render View控件是通过调用WebContentsImpl类的成员变量view_指向的一个WebContentsViewAndroid对象的成员函数CreateViewForWidget创建的。这个WebContentsViewAndroid对象的创建过程可以参考前面分析的WebContentsImpl类的成员函数Init。

WebContentsViewAndroid类的成员函数CreateViewForWidget创建的是一个RenderWidgetHostViewAndroid对象。这一点可以参考前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文。这也意味着在Android平台上,用来显示网页的Render View控件是通过RenderWidgetHostViewAndroid对象描述的。从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文又可以知道,RenderWidgetHostViewAndroid对象描述的Render View控件实际上是一个Surface View。

为参数render_view_host描述的RenderViewHostImpl对象创建了一个Render View控件之后,WebContentsImpl类的成员函数CreateRenderViewForRenderManager接下来还会调用这个RenderViewHostImpl对象的成员函数CreateRenderView请求在对应的Render进程中创建一个RenderFrameImpl对象。这个RenderFrameImpl对象与前面通过调用成员函数UpdateStateForNavigate获得的RenderFrameHostImpl对象相对应,也就是这两个对象以后可以进行Render Frame相关的进程间通信操作。

RenderViewHostImpl类的成员函数CreateRenderView的实现如下所示:

bool RenderViewHostImpl::CreateRenderView(
        const base::string16& frame_name,
        int opener_route_id,
        int proxy_route_id,
        int32 max_page_id,
        bool window_was_created_with_opener) {
      ......

      ViewMsg_New_Params params;
      params.renderer_preferences =
          delegate_->GetRendererPrefs(GetProcess()->GetBrowserContext());
      params.web_preferences = delegate_->GetWebkitPrefs();
      params.view_id = GetRoutingID();
      params.main_frame_routing_id = main_frame_routing_id_;
      params.surface_id = surface_id();
      params.session_storage_namespace_id =
          delegate_->GetSessionStorageNamespace(instance_)->id();
      params.frame_name = frame_name;
      // Ensure the RenderView sets its opener correctly.
      params.opener_route_id = opener_route_id;
      params.swapped_out = !IsRVHStateActive(rvh_state_);
      params.proxy_routing_id = proxy_route_id;
      params.hidden = is_hidden();
      params.never_visible = delegate_->IsNeverVisible();
      params.window_was_created_with_opener = window_was_created_with_opener;
      params.next_page_id = next_page_id;
      GetWebScreenInfo(&params.screen_info);
      params.accessibility_mode = accessibility_mode();

      Send(new ViewMsg_New(params));

      ......

      return true;
    }

这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc中。

RenderViewHostImpl类的成员函数CreateRenderView主要就是向与当前正在处理的RenderViewHostImpl对象对应的Render进程发送一个类型为ViewMsg_New的IPC消息。这个Render进程是在创建当前正在处理的RenderViewHostImpl对象时启动的,这一点可以参考前面分析的RenderViewHostFactory类的静态成员函数Create。

在Render进程中,类型为ViewMsg_New的IPC消息由RenderThreadImpl类的成员函数OnControlMessageReceived进行接收,如下所示:

bool RenderThreadImpl::OnControlMessageReceived(const IPC::Message& msg) {
      ......

      bool handled = true;
      IPC_BEGIN_MESSAGE_MAP(RenderThreadImpl, msg)
        ......
        IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView)
        ......
        IPC_MESSAGE_UNHANDLED(handled = false)
      IPC_END_MESSAGE_MAP()
      return handled;
    }

这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

从这里可以看到,RenderThreadImpl类的成员函数OnControlMessageReceived将类型为ViewMsg_New的IPC消息分发给成员函数OnCreateNewView处理。

RenderThreadImpl类的成员函数OnCreateNewView的实现如下所示:

void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) {
      EnsureWebKitInitialized();
      // When bringing in render_view, also bring in webkit's glue and jsbindings.
      RenderViewImpl::Create(params.opener_route_id,
                             params.window_was_created_with_opener,
                             params.renderer_preferences,
                             params.web_preferences,
                             params.view_id,
                             params.main_frame_routing_id,
                             params.surface_id,
                             params.session_storage_namespace_id,
                             params.frame_name,
                             false,
                             params.swapped_out,
                             params.proxy_routing_id,
                             params.hidden,
                             params.never_visible,
                             params.next_page_id,
                             params.screen_info,
                             params.accessibility_mode);
    }

这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

RenderThreadImpl类的成员函数OnCreateNewView首先调用成员函数EnsureWebKitInitialized保证WebKit已经初始化,因为接下来调用RenderViewImpl类的静态成员函数Create创建一个RenderViewImpl对象时会创建一些WebKit相关的对象。

RenderViewImpl类的静态成员函数Create的实现如下所示:

RenderViewImpl* RenderViewImpl::Create(
        int32 opener_id,
        bool window_was_created_with_opener,
        const RendererPreferences& renderer_prefs,
        const WebPreferences& webkit_prefs,
        int32 routing_id,
        int32 main_frame_routing_id,
        int32 surface_id,
        int64 session_storage_namespace_id,
        const base::string16& frame_name,
        bool is_renderer_created,
        bool swapped_out,
        int32 proxy_routing_id,
        bool hidden,
        bool never_visible,
        int32 next_page_id,
        const blink::WebScreenInfo& screen_info,
        AccessibilityMode accessibility_mode) {
      DCHECK(routing_id != MSG_ROUTING_NONE);
      RenderViewImplParams params(opener_id,
                                  window_was_created_with_opener,
                                  renderer_prefs,
                                  webkit_prefs,
                                  routing_id,
                                  main_frame_routing_id,
                                  surface_id,
                                  session_storage_namespace_id,
                                  frame_name,
                                  is_renderer_created,
                                  swapped_out,
                                  proxy_routing_id,
                                  hidden,
                                  never_visible,
                                  next_page_id,
                                  screen_info,
                                  accessibility_mode);
      RenderViewImpl* render_view = NULL;
      if (g_create_render_view_impl)
        render_view = g_create_render_view_impl(&params);
      else
        render_view = new RenderViewImpl(&params);

      render_view->Initialize(&params);
      return render_view;
    }

这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。

RenderViewImpl类的静态成员函数Create主要就是创建了一个RenderViewImpl对象,并且调用这个RenderViewImpl对象的成员函数Initialize对其进行初始化。

RenderViewImpl类的成员函数Initialize的实现如下所示:

void RenderViewImpl::Initialize(RenderViewImplParams* params) {
      ......

      main_render_frame_.reset(RenderFrameImpl::Create(
          this, params->main_frame_routing_id));
      ......

      WebLocalFrame* web_frame = WebLocalFrame::create(main_render_frame_.get());
      main_render_frame_->SetWebFrame(web_frame);
      ......

      webwidget_ = WebView::create(this);
      ......

      webview()->setMainFrame(main_render_frame_->GetWebFrame());

      ......
    }

这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。

RenderViewImpl类的成员函数Initialize首先调用RenderFrameImpl类的静态成员函数Create创建了一个RenderFrameImpl对象,如下所示:

RenderFrameImpl* RenderFrameImpl::Create(RenderViewImpl* render_view,
                                             int32 routing_id) {
      DCHECK(routing_id != MSG_ROUTING_NONE);

      if (g_create_render_frame_impl)
        return g_create_render_frame_impl(render_view, routing_id);
      else
        return new RenderFrameImpl(render_view, routing_id);
    }

这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。

从这里可以看到,RenderFrameImpl类的静态成员函数Create创建的是一个RenderFrameImpl对象。这个RenderFrameImpl对象保存在RenderViewImpl类的成员变量main_render_frame_中。

回到RenderViewImpl类的成员函数Initialize中,它接下来调用WebLocalFrame类的静态成员函数create创建一个WebLocalFrameImpl对象,如下所示:

WebLocalFrame* WebLocalFrame::create(WebFrameClient* client)
    {
        return WebLocalFrameImpl::create(client);
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp中。

WebLocalFrame类的静态成员函数create调用了WebLocalFrameImpl类的静态成员函数create创建一个WebLocalFrameImpl对象,后者的实现如下所示:

WebLocalFrameImpl* WebLocalFrameImpl::create(WebFrameClient* client)
    {
        return adoptRef(new WebLocalFrameImpl(client)).leakRef();
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp中。

从这里可以看到,WebLocalFrameImpl类的静态成员函数create创建的是一个WebLocalFrameImpl对象。这个WebLocalFrameImpl对象返回到RenderViewImpl类的成员函数Initialize之后,会设置给成员变量main_render_frame_描述的一个RenderFrameImpl对象。这是通过调用RenderFrameImpl类的成员函数SetWebFrame实现的。

RenderFrameImpl类的成员函数SetWebFrame的实现如下所示:

void RenderFrameImpl::SetWebFrame(blink::WebLocalFrame* web_frame) {
      ......

      frame_ = web_frame;
    }

这个函数定义在文件external/chromium_org/content/renderer/render_frame_impl.cc中。

RenderFrameImpl类的成员函数SetWebFrame将参数web_frame指向的一个WebLocalFrameImpl对象保存在成员变量frame_中。

回到RenderViewImpl类的成员函数Initialize中,接下来它又调用WebView类的静态成员函数create创建了一个WebViewImpl对象,并且保存在从父类RenderWidget继承下来的成员变量webwidget_中。

WebView类的静态成员函数create的实现如下所示:

WebView* WebView::create(WebViewClient* client)
    {
        // Pass the WebViewImpl's self-reference to the caller.
        return WebViewImpl::create(client);
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。

WebView类的静态成员函数create调用WebViewImpl类的静态成员函数create创建了一个WebViewImpl对象,后者的实现如下所示:

WebViewImpl* WebViewImpl::create(WebViewClient* client)
    {
        // Pass the WebViewImpl's self-reference to the caller.
        return adoptRef(new WebViewImpl(client)).leakRef();
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。

从前面的调用过程可以知道,参数client指向的是一个RenderViewImpl对象。WebViewImpl类的静态成员函数create根据这个RenderViewImpl对象创建了一个WebViewImpl对象,并且返回给调用者。

WebViewImpl对象的创建过程,即WebViewImpl类的构造函数的实现,如下所示:

WebViewImpl::WebViewImpl(WebViewClient* client)
        : m_client(client)
        , ...... {
        Page::PageClients pageClients;
        ......

        m_page = adoptPtrWillBeNoop(new Page(pageClients));

    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。

WebViewImpl类的构造函数将参数RenderViewImpl指向的一个RenderViewImpl对象保存在成员变量m_client中,并且还会创建一个Page对象,保存在成员变量m_page中。

回到RenderViewImpl类的成员函数Initialize中,它接下来调用成员变量main_render_frame_指向的RenderFrameImpl对象的成员函数GetWebFrame获得前面创建的WebLocalFrameImpl对象,并且将这个WebLocalFrameImpl对象设置给调用WebView类的静态成员函数create创建的WebViewImpl对象,这是通过调用WebViewImpl类的成员函数setMainFrame实现的。

WebViewImpl类的成员函数setMainFrame的实现如下所示:

void WebViewImpl::setMainFrame(WebFrame* frame)
    {
        if (frame->isWebLocalFrame())
            toWebLocalFrameImpl(frame)->initializeAsMainFrame(page());
        else
            toWebRemoteFrameImpl(frame)->initializeAsMainFrame(page());
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebViewImpl.cpp中。

如前所述,参数frame指向的是一个WebLocalFrameImpl对象。调用这个WebLocalFrameImpl对象的成员函数isWebLocalFrame得到的返回值等于true。在WebKit中,Web Frame有Local和Remote之分。Local类型的Web Frame描述的是网页的Main Frame,它是在当前Render进程中加载和渲染的。Remote类型的Web Frame描述的是网页的Sub Frame,它是在其他Render进程中加载和渲染的。Sub Frame描述的网页是通过iframe标签嵌入在Main Frame描述的网页中的。这一点可以参考前面的图3。

WebViewImpl类的成员函数setMainFrame首先调用成员函数toWebLocalFrameImpl将参数frame强制转换成一个WebLocalFrameImpl对象,以及调用成员函数page获得前面在构造函数中创建的Page对象,接下来将这个Page对象设置给上述转换得到的WebLocalFrameImpl对象,这是通过调用WebLocalFrameImpl类的成员函数initializeAsMainFrame实现的。

WebLocalFrameImpl类的成员函数initializeAsMainFrame的实现如下所示:

void WebLocalFrameImpl::initializeAsMainFrame(WebCore::Page* page)
    {
        setWebCoreFrame(LocalFrame::create(&m_frameLoaderClientImpl, &page->frameHost(), 0));

        // We must call init() after m_frame is assigned because it is referenced
        // during init().
        m_frame->init();
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp中。

WebLocalFrameImpl类的成员变量m_frameLoaderClientImpl描述的是一个FrameLoaderClientImpl对象,WebLocalFrameImpl类的成员函数initializeAsMainFrame首先调用LocalFrame类的静态成员函数create根据这个FrameLoaderClientImpl对象创建了一个LocalFrame对象。

LocalFrame类的静态成员函数create的实现如下所示:

PassRefPtr<LocalFrame> LocalFrame::create(FrameLoaderClient* client, FrameHost* host, FrameOwner* owner)
    {
        RefPtr<LocalFrame> frame = adoptRef(new LocalFrame(client, host, owner));
        ......
        return frame.release();
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/LocalFrame.cpp中。

从这里可以看到,LocalFrame类的静态成员函数create创建了一个LocalFrame对象,并且返回给调用者。

LocalFrame对象的创建过程,也就是LocalFrame类的构造函数的实现,如下所示:

inline LocalFrame::LocalFrame(FrameLoaderClient* client, FrameHost* host, FrameOwner* owner)
        : Frame(client, host, owner)
        , m_loader(this)
        , ......
    {
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/LocalFrame.cpp中。

LocalFrame类的构造函数首先调用父类Frame的构造函数,后者的实现如下所示:

Frame::Frame(FrameClient* client, FrameHost* host, FrameOwner* owner)
        : ......
        , m_client(client)
        , ......
    {
        ......
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/Frame.cpp中。

从前面的调用过程可以知道,参数client指向的是一个FrameLoaderClientImp对象,Frame类的构造函数将这个FrameLoaderClientImpl对象保存在成员变量m_client中。

回到LocalFrame类的构造函数中,接下来它还会构造一个FrameLoader对象,并且保存成员变量m_loader中。

FrameLoader对象的构造过程,即FrameLoader类的构造函数的实现,如下所示:

FrameLoader::FrameLoader(LocalFrame* frame)
        : m_frame(frame)
        , ......
    {
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/Frame.cpp中。

FrameLoader类的构造函数将参数frame描述的一个LocalFrame对象保存在成员变量m_frame中。

这一步执行完成后,回到WebLocalFrameImpl类的成员函数initializeAsMainFrame中,它接下来调用另外一个成员变量setWebCoreFrame将前面调用LocalFrame类的静态成员函数create创建的LocalFrame对象保存成员变量m_frame中,如下所示:

void WebLocalFrameImpl::setWebCoreFrame(PassRefPtr<WebCore::LocalFrame> frame)
    {
        m_frame = frame;
        ......
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp中。

再回到WebLocalFrameImpl类的成员函数initializeAsMainFrame中,它最后调用成员变量m_frame描述的LocalFrame对象的成员函数init执行初始化操作。

LocalFrame类的成员函数init的实现如下所示:

namespace WebCore {
        ......

        inline void LocalFrame::init()
        {
            m_loader.init();
        }

        ......
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/frame/LocalFrame.h中。

LocalFrame类的成员变量m_loader描述的是一个FrameLoader对象,LocalFrame类的成员函数init调用这个FrameLoader对象的成员函数init执行初始化操作。

FrameLoader类的成员函数init的实现如下所示:

void FrameLoader::init()
    {
        m_provisionalDocumentLoader = client()->createDocumentLoader(m_frame, ResourceRequest(KURL(ParsedURLString, emptyString())), SubstituteData());
        ......
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/FrameLoader.cpp中。

FrameLoader类的成员函数init首先调用成员函数client获得一个FrameLoaderClientImpl对象,如下所示:

FrameLoaderClient* FrameLoader::client() const
    {
        return static_cast<FrameLoaderClient*>(m_frame->client());
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/FrameLoader.cpp中。

从前面的分析可以知道,FrameLoader类的成员变量m_frame描述的是一个LocalFrame对象,FrameLoader类的成员函数调用它的成员函数client获得一个FrameLoaderClientImpl对象。

LocalFrame类的成员函数client是从父类Frame继承下来的,它的实现如下所示:

inline FrameClient* Frame::client() const
    {
        return m_client;
    }

这个函数定义在文件chromium_org/third_party/WebKit/Source/core/frame/Frame.h中。

从前面的分析可以知道,Frame类的成员变量m_client指向的是一个FrameLoaderClientImpl对象,Frame类的成员函数client将这个FrameLoaderClientImpl对象返回给调用者。

回到FrameLoader类的成员函数init中,它获得了一个FrameLoaderClientImpl对象之后,接下来调用这个FrameLoaderClientImpl对象的成员函数createDocumentLoader创建了一个WebDataSourceImpl对象,并且保存在成员变量m_provisionalDocumentLoader中。这个WebDataSourceImpl对象以后可以用来加载网页。

FrameLoaderClientImpl类的成员函数createDocumentLoader的实现如下所示:

PassRefPtr<DocumentLoader> FrameLoaderClientImpl::createDocumentLoader(LocalFrame* frame, const ResourceRequest& request, const SubstituteData& data)
    {
        RefPtr<WebDataSourceImpl> ds = WebDataSourceImpl::create(frame, request, data);
        ......
        return ds.release();
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/FrameLoaderClientImpl.cpp中。

FrameLoaderClientImpl类的成员函数createDocumentLoader调用WebDataSourceImpl类的静态成员函数create创建了一个WebDataSourceImpl对象,并且返回给调用者。

WebDataSourceImpl类的静态成员函数create的实现如下所示:

PassRefPtr<WebDataSourceImpl> WebDataSourceImpl::create(LocalFrame* frame, const ResourceRequest& request, const SubstituteData& data)
    {
        return adoptRef(new WebDataSourceImpl(frame, request, data));
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebDataSourceImpl.cpp中。

从这里可以看到,WebDataSourceImpl类的静态成员函数create创建的是一个WebDataSourceImpl对象,并且将这个WebDataSourceImpl对象返回给调用者。

WebDataSourceImpl对象的创建过程,即WebDataSourceImpl类的构造函数的实现,如下所示:

WebDataSourceImpl::WebDataSourceImpl(LocalFrame* frame, const ResourceRequest& request, const SubstituteData& data)
        : DocumentLoader(frame, request, data)
    {
        ......
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebDataSourceImpl.cpp中。

WebDataSourceImpl类是从DocumentLoader类继承下来的,因此WebDataSourceImpl类构造函数调用了DocumentLoader类的构造函数。

DocumentLoader类的构造函数的实现如下所示:

DocumentLoader::DocumentLoader(LocalFrame* frame, const ResourceRequest& req, const SubstituteData& substituteData)
        : m_frame(frame)
        , m_fetcher(ResourceFetcher::create(this))
        , .....
    {
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/loader/DocumentLoader.cpp中。

DocumentLoader类的构造函数首先将参数frame描述的一个LocalFrame对象保存在成员变量m_frame中,接下来又调用ResourceFetcher类的静态成员函数create创建了一个ResourceFetcher对象,并且保存在成员变量m_fetcher中。

ResourceFetcher类的静态成员函数create的实现如下所示:

class ResourceFetcher FINAL : public RefCountedWillBeGarbageCollectedFinalized<ResourceFetcher>, public ResourceLoaderHost {
        WTF_MAKE_NONCOPYABLE(ResourceFetcher); WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
        WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(ResourceFetcher);
        ......

    public:
        static PassRefPtrWillBeRawPtr<ResourceFetcher> create(DocumentLoader* documentLoader) { return adoptRefWillBeNoop(new ResourceFetcher(documentLoader)); }

        ......
    };

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.h中。

从这里可以看到,ResourceFetcher类的静态成员函数create创建的是一个ResourceFetcher对象,并且会将这个ResourceFetcher对象返回给调用者。

ResourceFetcher对象的创建过程,即ResourceFetcher类的构造函数的实现,如下所示:

ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader)
        : ......
        , m_documentLoader(documentLoader)
        , .....
    {
    }

这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp中。

从前面的调用过程可以知道,参数documentLoader指向的实际上是一个WebDataSourceImpl对象,这个WebDataSourceImpl对象保存在ResourceFetcher类的成员变量m_documentLoader中。

这一步执行完成后,Render进程就处理完毕从Browser进程发送过来的类型为ViewMsg_New的IPC消息。Render进程在处理IPC消息的过程中,主要就是创建了一个RenderViewImpl对象、一个RenderFrameImpl对象、一个WebLocalFrameImpl对象、一个LocalFrame对象。这些对象的关系和作用可以参考前面Chromium网页加载过程简要介绍和学习计划一文的介绍。创建这些对象是为后面加载网页内容作准备的。

回到NavigatorImpl类的成员函数NavigateToEntry,它向Render进程发送了一个类型为ViewMsg_New的IPC消息之后,接下来又会调用RenderFrameHostImpl类的成员函数Navigate请求导航到指定的URL。

RenderFrameHostImpl类的成员函数Navigate的实现如下所示:

void RenderFrameHostImpl::Navigate(const FrameMsg_Navigate_Params& params) {
      ......

      // Only send the message if we aren't suspended at the start of a cross-site
      // request.
      if (render_view_host_->navigations_suspended_) {
        // Shouldn't be possible to have a second navigation while suspended, since
        // navigations will only be suspended during a cross-site request.  If a
        // second navigation occurs, RenderFrameHostManager will cancel this pending
        // RFH and create a new pending RFH.
        DCHECK(!render_view_host_->suspended_nav_params_.get());
        render_view_host_->suspended_nav_params_.reset(
            new FrameMsg_Navigate_Params(params));
      } else {
        // Get back to a clean state, in case we start a new navigation without
        // completing a RVH swap or unload handler.
        render_view_host_->SetState(RenderViewHostImpl::STATE_DEFAULT);

        Send(new FrameMsg_Navigate(routing_id_, params));
      }

      ......
    }

这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_impl.cc中。

从前面的分析可以知道,RenderFrameHostImpl类的成员变量render_view_host_描述的是一个RenderViewHostImpl对象。当这个RenderViewHostImpl对象的成员变量navigations_suspended_的值等于true的时候,表示参数params描述的URL被挂起加载,这时候RenderFrameHostImpl类的成员函数Navigate将要加载的URL记录起来,等到挂起被恢复时,再进行加载。

另一方面,RenderFrameHostImpl类的成员变量render_view_host_描述的RenderViewHostImpl对象的成员变量navigations_suspended_的值等于false时,表示要马上加载参数params描述的URL,这时候RenderFrameHostImpl类的成员函数Navigate就会向一个Render进程发送一个类型为FrameMsg_Navigate的IPC消息,请求该Render进程加载该URL对应的网页。Render进程加载网页的过程,我们在接下来的一篇文章中再分析。

至此,我们就分析完成一个网页对应的Frame Tree的创建过程了。这个Frame Tree是在网页内容加载过程之前在Browser进程中创建的。要加载的网页对应于这个Frame Tree的一个Node。这个Node又关联有一个RenderFrameHostImpl对象。这个RenderFrameHostImpl对象在Render进程又对应有一个RenderFrameImpl对象。RenderFrameHostImpl对象和RenderFrameImpl对象负责在Browser进程和Render进程之间处理网页导航相关的操作。

在接下来的一篇文章分析网页内容的加载过程中,我们就可以看到,Render进程中的RenderFrameImpl对象处理其在Browser进程中的对应的RenderFrameHostImpl对象发送过来的类型为FrameMsg_Navigate的IPC消息的过程,也就是Render进程加载网页内容的过程,敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:6月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:6月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:6月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:6月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:6月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:6月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:6月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:6月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:6月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:6月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:6月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:6月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:6月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:6月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:6月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:6月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:6月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:6月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:6月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:6月以前  |  398次阅读  |  详细内容 »
 目录