Frame Tree 创建

站在老罗的肩膀上:https://blog.csdn.net/luoshengyang/article/details/50450100

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

       关于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::CreateNewWindowWithSessionStorageNamespace(
    BrowserContext* browser_context,
    const GURL& url,
    const scoped_refptr<SiteInstance>& site_instance,
    const gfx::Size& initial_size,
    scoped_refptr<SessionStorageNamespace> session_storage_namespace) {
  WebContents::CreateParams create_params(browser_context, site_instance);
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kForcePresentationReceiverForTesting)) {
    create_params.starting_sandbox_flags =
        blink::kPresentationReceiverSandboxFlags;
  }
  create_params.initial_size = AdjustWindowSize(initial_size);
  std::map<std::string, scoped_refptr<SessionStorageNamespace>>
      session_storages;
  session_storages[""] = session_storage_namespace;
  std::unique_ptr<WebContents> web_contents =
      WebContents::CreateWithSessionStorage(create_params, session_storages);
  Shell* shell =
      CreateShell(std::move(web_contents), create_params.initial_size,
                  true /* should_set_delegate */);
  if (!url.is_empty())
    shell->LoadURL(url);
  return shell;
}

src/content/shell/browser/shell.cc

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

std::unique_ptr<WebContents> WebContents::CreateWithSessionStorage(
    const WebContents::CreateParams& params,
    const SessionStorageNamespaceMap& session_storage_namespace_map) {
  std::unique_ptr<WebContentsImpl> new_contents(
      new WebContentsImpl(params.browser_context));
  ...
  new_contents->Init(params);
  return new_contents;
}

src/content/browser/web_contents/web_contents_impl.cc

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

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

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

WebContentsImpl::WebContentsImpl(BrowserContext* browser_context)
    : delegate_(nullptr),
      controller_(this, browser_context),
      render_view_host_delegate_view_(nullptr),
      created_with_opener_(false),
      frame_tree_(new NavigatorImpl(&controller_, this),
                  this,
                  this,
                  this,
                  this),
      ...
}

src/content/browser/web_contents/web_contents_impl.cc

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

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

src/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) {}

src/content/browser/frame_host/navigator_impl.cc

 

再回到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_widget_delegate,
                              manager_delegate,
                              nullptr,
                              // The top-level frame must always be in a
                              // document scope.
                              blink::WebTreeScopeType::kDocument,
                              std::string(),
                              std::string(),
                              false,
                              base::UnguessableToken::Create(),
                              FrameOwnerProperties())),
      focused_frame_tree_node_id_(FrameTreeNode::kFrameTreeNodeInvalidId),
      load_progress_(0.0) {}

src/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类的构造函数的实现,如下所示:

 

FrameTreeNode::FrameTreeNode(FrameTree* frame_tree,
                             Navigator* navigator,
                             RenderFrameHostDelegate* render_frame_delegate,
                             RenderWidgetHostDelegate* render_widget_delegate,
                             RenderFrameHostManager::Delegate* manager_delegate,
                             FrameTreeNode* parent,
                             blink::WebTreeScopeType scope,
                             const std::string& name,
                             const std::string& unique_name,
                             bool is_created_by_script,
                             const base::UnguessableToken& devtools_frame_token,
                             const FrameOwnerProperties& frame_owner_properties)
    : frame_tree_(frame_tree),
      navigator_(navigator),
      render_manager_(this,
                      render_frame_delegate,
                      render_widget_delegate,
                      manager_delegate),
      frame_tree_node_id_(next_frame_tree_node_id_++),
      ...
}

src/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,
    RenderWidgetHostDelegate* render_widget_delegate,
    Delegate* delegate)
    : frame_tree_node_(frame_tree_node),
      delegate_(delegate),
      render_frame_delegate_(render_frame_delegate),
      render_widget_delegate_(render_widget_delegate),
      weak_factory_(this) {
  DCHECK(frame_tree_node_);
}

 

src/content/browser/frame_host/render_frame_host_manager.cc

从前面的调用过程可以知道,参数frame_tree_node指向的是一个FrameTreeNode对象,它被保存在RenderFrameHostManager类的成员变量frame_tree_node_中,其余四个参数指向的是同一个WebContentsImpl对象,分别被保存在RenderFrameHostManager类的成员变量render_frame_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(
      site_instance.get(), view_routing_id, params.main_frame_routing_id,
      main_frame_widget_routing_id, params.renderer_initiated_creation);

  ...
  if (GuestMode::IsCrossProcessFrameGuest(this)) {
    view_.reset(new WebContentsViewChildFrame(
        this, delegate, &render_view_host_delegate_view_));
  } else {
    view_.reset(CreateWebContentsView(this, delegate,
                                      &render_view_host_delegate_view_));
    if (browser_plugin_guest_) {
      ...
    }
  }
  ...
}

src/content/browser/web_contents/web_contents_impl.cc

 WebContentsImpl类的成员函数GetRenderManager的实现如下所示, 指向一个RenderFrameHostManager对象:

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

src/content/browser/web_contents/web_contents_impl.cc

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

void RenderFrameHostManager::Init(SiteInstance* site_instance,
                                  int32_t view_routing_id,
                                  int32_t frame_routing_id,
                                  int32_t widget_routing_id,
                                  bool renderer_initiated_creation) {
  DCHECK(site_instance);
  SetRenderFrameHost(CreateRenderFrameHost(site_instance, view_routing_id,
                                           frame_routing_id, widget_routing_id,
                                           delegate_->IsHidden(),
                                           renderer_initiated_creation));

  ...
}

src/content/browser/frame_host/render_frame_host_manager.cc

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

std::unique_ptr<RenderFrameHostImpl> RenderFrameHostManager::SetRenderFrameHost(
    std::unique_ptr<RenderFrameHostImpl> render_frame_host) {
  // Swap the two.
  std::unique_ptr<RenderFrameHostImpl> old_render_frame_host =
      std::move(render_frame_host_);
  render_frame_host_ = std::move(render_frame_host);

  ...

  return old_render_frame_host;
}

src/content/browser/frame_host/render_frame_host_manager.cc

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

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

std::unique_ptr<RenderFrameHostImpl>
RenderFrameHostManager::CreateRenderFrameHost(
    SiteInstance* site_instance,
    int32_t view_routing_id,
    int32_t frame_routing_id,
    int32_t widget_routing_id,
    bool hidden,
    bool renderer_initiated_creation) {
  ...
  // Create a RVH for main frames, or find the existing one for subframes.
  FrameTree* frame_tree = frame_tree_node_->frame_tree();
  RenderViewHostImpl* render_view_host = nullptr;
  if (frame_tree_node_->IsMainFrame()) {
    render_view_host = frame_tree->CreateRenderViewHost(
        site_instance, view_routing_id, frame_routing_id, widget_routing_id,
        false, hidden);
    ...
  } else {
    render_view_host = frame_tree->GetRenderViewHost(site_instance);
    CHECK(render_view_host);
  }

  return RenderFrameHostFactory::Create(
      site_instance, render_view_host, render_frame_delegate_,
      render_widget_delegate_, frame_tree, frame_tree_node_, frame_routing_id,
      widget_routing_id, hidden, renderer_initiated_creation);
}

src/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类的成员函数FrameTree::CreateRenderViewHost创建一个RenderViewHostImpl对象,并且保存在本地变量render_view_host中。

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

RenderViewHostImpl* FrameTree::CreateRenderViewHost(
    SiteInstance* site_instance,
    int32_t routing_id,
    int32_t main_frame_routing_id,
    int32_t widget_routing_id,
    bool swapped_out,
    bool hidden) {
  RenderViewHostMap::iterator iter =
      render_view_host_map_.find(site_instance->GetId());
  if (iter != render_view_host_map_.end())
    return iter->second;

  RenderViewHostImpl* rvh =
      static_cast<RenderViewHostImpl*>(RenderViewHostFactory::Create(
          site_instance, render_view_delegate_, render_widget_delegate_,
          routing_id, main_frame_routing_id, widget_routing_id, swapped_out,
          hidden));

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

src/content/browser/frame_host/frame_tree.cc

 

FrameTree类的成员函数CreateRenderViewHost调用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,
    int32_t routing_id,
    int32_t main_frame_routing_id,
    int32_t widget_routing_id,
    bool swapped_out,
    bool hidden) {
  ...
  return new RenderViewHostImpl(
      instance,
      base::WrapUnique(RenderWidgetHostFactory::Create(
          widget_delegate, instance->GetProcess(), widget_routing_id, nullptr,
          hidden)),
      delegate, routing_id, main_frame_routing_id, swapped_out,
      true /* has_initialized_audio_host */);
}

src/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的实现如下所示:

// static
std::unique_ptr<RenderFrameHostImpl> RenderFrameHostFactory::Create(
    SiteInstance* site_instance,
    RenderViewHostImpl* render_view_host,
    RenderFrameHostDelegate* delegate,
    RenderWidgetHostDelegate* rwh_delegate,
    FrameTree* frame_tree,
    FrameTreeNode* frame_tree_node,
    int32_t routing_id,
    int32_t widget_routing_id,
    bool hidden,
    bool renderer_initiated_creation) {
  ...
  return base::WrapUnique(new RenderFrameHostImpl(
      site_instance, render_view_host, delegate, rwh_delegate, frame_tree,
      frame_tree_node, routing_id, widget_routing_id, hidden,
      renderer_initiated_creation));
}

src/content/browser/frame_host/render_frame_host_factory.cc

 

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

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


RenderFrameHostImpl::RenderFrameHostImpl(SiteInstance* site_instance,
                                         RenderViewHostImpl* render_view_host,
                                         RenderFrameHostDelegate* delegate,
                                         RenderWidgetHostDelegate* rwh_delegate,
                                         FrameTree* frame_tree,
                                         FrameTreeNode* frame_tree_node,
                                         int32_t routing_id,
                                         int32_t widget_routing_id,
                                         bool hidden,
                                         bool renderer_initiated_creation)
    : render_view_host_(render_view_host),
      delegate_(delegate),
      ...
      frame_tree_(frame_tree),
      frame_tree_node_(frame_tree_node),
      ...
}

src/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) {
  ...
  GetRenderManager()->Init(
      site_instance.get(), view_routing_id, params.main_frame_routing_id,
      main_frame_widget_routing_id, params.renderer_initiated_creation);

  ...
  if (GuestMode::IsCrossProcessFrameGuest(this)) {
    view_.reset(new WebContentsViewChildFrame(
        this, delegate, &render_view_host_delegate_view_));
  } else {
    view_.reset(CreateWebContentsView(this, delegate,
                                      &render_view_host_delegate_view_));
    if (browser_plugin_guest_) {
      ...
    }
  }
  ...
}

src/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) {
  WebContentsViewAura* rv = new WebContentsViewAura(web_contents, delegate);
  *render_view_host_delegate_view = rv;
  return rv;
}

src/content/browser/web_contents/web_contents_view_aura.cc 

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

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

Shell* Shell::CreateShell(std::unique_ptr<WebContents> web_contents,
                          const gfx::Size& initial_size,
                          bool should_set_delegate) {
  WebContents* raw_web_contents = web_contents.get();
  Shell* shell = new Shell(std::move(web_contents), should_set_delegate);
  shell->PlatformCreateWindow(initial_size.width(), initial_size.height());

  shell->PlatformSetContents();

  shell->PlatformResizeSubViews();

  // Note: Do not make RenderFrameHost or RenderViewHost specific state changes
  // here, because they will be forgotten after a cross-process navigation. Use
  // RenderFrameCreated or RenderViewCreated instead.
  if (switches::IsRunWebTestsSwitchPresent()) {
    raw_web_contents->GetMutableRendererPrefs()->use_custom_colors = false;
    raw_web_contents->GetRenderViewHost()->SyncRendererPrefs();
  }

  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kForceWebRtcIPHandlingPolicy)) {
    raw_web_contents->GetMutableRendererPrefs()->webrtc_ip_handling_policy =
        command_line->GetSwitchValueASCII(
            switches::kForceWebRtcIPHandlingPolicy);
  }

  return shell;
}

src/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_,
                                       web_contents()->GetJavaWebContents());
}

src/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_;
 
  ......
}

src/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(WebContents webContents) {
        Context context = getContext();
        ContentView cv = ContentView.createContentView(context, webContents);
        mViewAndroidDelegate = new ShellViewAndroidDelegate(cv);
        webContents.initialize(
                "", mViewAndroidDelegate, cv, mWindow, WebContents.createDefaultInternalsHolder());
        ...
    }
}

src/content/shell/android/java/src/org/chromium/content_shell/Shell.java

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

....MIssing sth about java functions....

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

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

  // The user initiated a load, we don't need to reload anymore.
  needs_reload_ = false;

  NavigateWithoutEntry(params);
}

src/content/browser/frame_host/navigation_controller_impl.cc

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

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

  // The user initiated a load, we don't need to reload anymore.
  needs_reload_ = false;

  NavigateWithoutEntry(params);
}
void NavigationControllerImpl::NavigateWithoutEntry(
    const LoadURLParams& params) {
  ...

  // Javascript URLs should not create NavigationEntries. All other navigations
  // do, including navigations to chrome renderer debug URLs.
  std::unique_ptr<NavigationEntryImpl> entry;
  if (!params.url.SchemeIs(url::kJavaScriptScheme)) {
    entry = CreateNavigationEntryFromLoadParams(node, params);
    DiscardPendingEntry(false);
    SetPendingEntry(std::move(entry));
  }

  // Renderer-debug URLs are sent to the renderer process immediately for
  // processing and don't need to create a NavigationRequest.
  // Note: this includes navigations to JavaScript URLs, which are considered
  // renderer-debug URLs.
  // Note: we intentionally leave the pending entry in place for renderer debug
  // URLs, unlike the cases below where we clear it if the navigation doesn't
  // proceed.
  if (IsRendererDebugURL(params.url)) {
    HandleRendererDebugURL(node, params.url);
    return;
  }

  // Convert navigations to the current URL to a reload.
  // TODO(clamy): We should be using FrameTreeNode::IsMainFrame here instead of
  // relying on the frame tree node id from LoadURLParams. Unfortunately,
  // DevTools sometimes issues navigations to main frames that they do not
  // expect to see treated as reload, and it only works because they pass a
  // FrameTreeNode id in their LoadURLParams. Change this once they no longer do
  // that. See https://crbug.com/850926.
  ReloadType reload_type = ReloadType::NONE;
  if (ShouldTreatNavigationAsReload(
          params.url, pending_entry_->GetVirtualURL(),
          params.base_url_for_data_url, params.transition_type,
          params.frame_tree_node_id == RenderFrameHost::kNoFrameTreeNodeId,
          params.load_type ==
              NavigationController::LOAD_TYPE_HTTP_POST /* is_post */,
          false /* is_reload */, false /* is_navigation_to_existing_entry */,
          transient_entry_index_ != -1 /* has_interstitial */,
          GetLastCommittedEntry())) {
    reload_type = ReloadType::NORMAL;
  }

  // navigation_ui_data should only be present for main frame navigations.
  DCHECK(node->IsMainFrame() || !params.navigation_ui_data);

  // TODO(clamy): Create the NavigationRequest directly from the LoadURLParams
  // instead of relying on the NavigationEntry.
  DCHECK(pending_entry_);
  std::unique_ptr<NavigationRequest> request = CreateNavigationRequest(
      node, *pending_entry_, pending_entry_->GetFrameEntry(node), reload_type,
      false /* is_same_document_history_load */,
      false /* is_history_navigation_in_new_child */, nullptr,
      params.navigation_ui_data ? params.navigation_ui_data->Clone() : nullptr);

  // If the navigation couldn't start, return immediately and discard the
  // pending NavigationEntry.
  if (!request) {
    DiscardPendingEntry(false);
    return;
  }

  // If an interstitial page is showing, the previous renderer is blocked and
  // cannot make new requests.  Unblock (and disable) it to allow this
  // navigation to succeed.  The interstitial will stay visible until the
  // resulting DidNavigate.
  if (delegate_->GetInterstitialPage()) {
    static_cast<InterstitialPageImpl*>(delegate_->GetInterstitialPage())
        ->CancelForNavigation();
  }

  // This call does not support re-entrancy.  See http://crbug.com/347742.
  CHECK(!in_navigate_to_pending_entry_);
  in_navigate_to_pending_entry_ = true;

  node->navigator()->Navigate(std::move(request), reload_type,
                              RestoreType::NONE);

  in_navigate_to_pending_entry_ = false;
}

创建request:

std::unique_ptr<NavigationRequest>
NavigationControllerImpl::CreateNavigationRequest(
    FrameTreeNode* frame_tree_node,
    const NavigationEntryImpl& entry,
    FrameNavigationEntry* frame_entry,
    ReloadType reload_type,
    bool is_same_document_history_load,
    bool is_history_navigation_in_new_child,
    const scoped_refptr<network::ResourceRequestBody>& post_body,
    std::unique_ptr<NavigationUIData> navigation_ui_data) {
  GURL dest_url = frame_entry->url();
  Referrer dest_referrer = frame_entry->referrer();
  if (reload_type == ReloadType::ORIGINAL_REQUEST_URL &&
      entry.GetOriginalRequestURL().is_valid() && !entry.GetHasPostData()) {
    // We may have been redirected when navigating to the current URL.
    // Use the URL the user originally intended to visit as signaled by the
    // ReloadType, if it's valid and if a POST wasn't involved; the latter
    // case avoids issues with sending data to the wrong page.
    dest_url = entry.GetOriginalRequestURL();
    dest_referrer = Referrer();
  }

  // Don't attempt to navigate if the virtual URL is non-empty and invalid.
  if (frame_tree_node->IsMainFrame()) {
    const GURL& virtual_url = entry.GetVirtualURL();
    if (!virtual_url.is_valid() && !virtual_url.is_empty()) {
      LOG(WARNING) << "Refusing to load for invalid virtual URL: "
                   << virtual_url.possibly_invalid_spec();
      return nullptr;
    }
  }

  // Don't attempt to navigate to non-empty invalid URLs.
  if (!dest_url.is_valid() && !dest_url.is_empty()) {
    LOG(WARNING) << "Refusing to load invalid URL: "
                 << dest_url.possibly_invalid_spec();
    return nullptr;
  }

  // The renderer will reject IPC messages with URLs longer than
  // this limit, so don't attempt to navigate with a longer URL.
  if (dest_url.spec().size() > url::kMaxURLChars) {
    LOG(WARNING) << "Refusing to load URL as it exceeds " << url::kMaxURLChars
                 << " characters.";
    return nullptr;
  }

  // Determine if Previews should be used for the navigation.
  PreviewsState previews_state = PREVIEWS_UNSPECIFIED;
  if (!frame_tree_node->IsMainFrame()) {
    // For subframes, use the state of the top-level frame.
    previews_state = frame_tree_node->frame_tree()
                         ->root()
                         ->current_frame_host()
                         ->last_navigation_previews_state();
  }

  // Give the delegate an opportunity to adjust the previews state.
  if (delegate_)
    delegate_->AdjustPreviewsStateForNavigation(&previews_state);

  // This will be used to set the Navigation Timing API navigationStart
  // parameter for browser navigations in new tabs (intents, tabs opened through
  // "Open link in new tab"). If the navigation must wait on the current
  // RenderFrameHost to execute its BeforeUnload event, the navigation start
  // will be updated when the BeforeUnload ack is received.
  base::TimeTicks navigation_start = base::TimeTicks::Now();
  TRACE_EVENT_INSTANT_WITH_TIMESTAMP0(
      "navigation,rail", "NavigationTiming navigationStart",
      TRACE_EVENT_SCOPE_GLOBAL, navigation_start);

  FrameMsg_Navigate_Type::Value navigation_type = GetNavigationType(
      frame_tree_node->current_url(),  // old_url
      dest_url,                        // new_url
      reload_type,                     // reload_type
      entry,                           // entry
      *frame_entry,                    // frame_entry
      is_same_document_history_load);  // is_same_document_history_load
  return NavigationRequest::CreateBrowserInitiated(
      frame_tree_node, dest_url, dest_referrer, *frame_entry, entry,
      navigation_type, previews_state, is_same_document_history_load,
      is_history_navigation_in_new_child, post_body, navigation_start, this,
      std::move(navigation_ui_data));
}

src/content/browser/frame_host/navigation_controller_impl.cc

加载URL:

void NavigatorImpl::Navigate(std::unique_ptr<NavigationRequest> request,
                             ReloadType reload_type,
                             RestoreType restore_type) {
  TRACE_EVENT0("browser,navigation", "NavigatorImpl::Navigate");

  const GURL& dest_url = request->common_params().url;
  FrameTreeNode* frame_tree_node = request->frame_tree_node();

  navigation_data_.reset(new NavigationMetricsData(
      request->common_params().navigation_start, dest_url, restore_type));

  // Check if the BeforeUnload event needs to execute before assigning the
  // NavigationRequest to the FrameTreeNode. Assigning it to the FrameTreeNode
  // has the side effect of initializing the current RenderFrameHost, which will
  // return that it should execute the BeforeUnload event (even though we don't
  // need to wait for it in the case of a brand new RenderFrameHost).
  //
  // We don't want to dispatch a beforeunload handler if
  // is_history_navigation_in_new_child is true. This indicates a newly created
  // child frame which does not have a beforeunload handler.
  bool should_dispatch_beforeunload =
      !FrameMsg_Navigate_Type::IsSameDocument(
          request->common_params().navigation_type) &&
      !request->request_params().is_history_navigation_in_new_child &&
      frame_tree_node->current_frame_host()->ShouldDispatchBeforeUnload(
          false /* check_subframes_only */);

  int nav_entry_id = request->nav_entry_id();
  bool is_pending_entry =
      controller_->GetPendingEntry() &&
      (nav_entry_id == controller_->GetPendingEntry()->GetUniqueID());
  frame_tree_node->CreatedNavigationRequest(std::move(request));
  DCHECK(frame_tree_node->navigation_request());

  // Have the current renderer execute its beforeunload event if needed. If it
  // is not needed then NavigationRequest::BeginNavigation should be directly
  // called instead.
  if (should_dispatch_beforeunload) {
    frame_tree_node->navigation_request()->SetWaitingForRendererResponse();
    frame_tree_node->current_frame_host()->DispatchBeforeUnload(
        RenderFrameHostImpl::BeforeUnloadType::BROWSER_INITIATED_NAVIGATION,
        reload_type != ReloadType::NONE);
  } else {
    frame_tree_node->navigation_request()->BeginNavigation();
    // WARNING: The NavigationRequest might have been destroyed in
    // BeginNavigation(). Do not use |frame_tree_node->navigation_request()|
    // after this point without null checking it first.
  }

  if (frame_tree_node->IsMainFrame() && frame_tree_node->navigation_request()) {
    // For the trace below we're using the navigation handle as the async
    // trace id, |navigation_start| as the timestamp and reporting the
    // FrameTreeNode id as a parameter. For navigations where no network
    // request is made (data URLs, JavaScript URLs, etc) there is no handle
    // and so no tracing is done.
    TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1(
        "navigation", "Navigation timeToNetworkStack",
        frame_tree_node->navigation_request()->navigation_handle(),
        frame_tree_node->navigation_request()->common_params().navigation_start,
        "FrameTreeNode id", frame_tree_node->frame_tree_node_id());
  }

  // Make sure no code called via RFH::Navigate clears the pending entry.
  if (is_pending_entry)
    CHECK_EQ(nav_entry_id, controller_->GetPendingEntry()->GetUniqueID());

  // Notify observers about navigation.
  if (delegate_ && is_pending_entry)
    delegate_->DidStartNavigationToPendingEntry(dest_url, reload_type);
}

src/content/browser/frame_host/navigation_controller_impl.cc

...Missing sth....

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

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

bool RenderFrameHostManager::InitRenderView(
    RenderViewHostImpl* render_view_host,
    RenderFrameProxyHost* proxy) {
  ...
  bool created = delegate_->CreateRenderViewForRenderManager(
      render_view_host, opener_frame_routing_id,
      proxy ? proxy->GetRoutingID() : MSG_ROUTING_NONE,
      frame_tree_node_->devtools_frame_token(),
      frame_tree_node_->current_replication_state());

  if (created && proxy)
    proxy->set_render_frame_proxy_created(true);

  return created;
}

src/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_frame_routing_id,
    int proxy_routing_id,
    const base::UnguessableToken& devtools_frame_token,
    const FrameReplicationState& replicated_frame_state) {
  TRACE_EVENT0("browser,navigation",
               "WebContentsImpl::CreateRenderViewForRenderManager");

  if (proxy_routing_id == MSG_ROUTING_NONE)
    CreateRenderWidgetHostViewForRenderManager(render_view_host);

  if (!static_cast<RenderViewHostImpl*>(render_view_host)
           ->CreateRenderView(opener_frame_routing_id, proxy_routing_id,
                              devtools_frame_token, replicated_frame_state,
                              created_with_opener_)) {
    return false;
  }

  ...

  return true;
}

 

src/content/browser/web_contents/web_contents_impl.cc

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

bool RenderViewHostImpl::CreateRenderView(
    int opener_frame_route_id,
    int proxy_route_id,
    const base::UnguessableToken& devtools_frame_token,
    const FrameReplicationState& replicated_frame_state,
    bool window_was_created_with_opener) {
  ...
  GetWidget()->set_renderer_initialized(true);

  mojom::CreateViewParamsPtr params = mojom::CreateViewParams::New();
  ...

  // Let our delegate know that we created a RenderView.
  DispatchRenderViewCreated();

  ...

  return true;
}

src/content/browser/renderer_host/render_view_host_impl.cc

使用DispatchRenderViewCreated()通知delegate已经创建了Render View控件.

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

bool RenderThreadImpl::OnControlMessageReceived(const IPC::Message& msg) {
  for (auto& observer : observers_) {
    if (observer.OnControlMessageReceived(msg))
      return true;
  }

  // Some messages are handled by delegates.
  if (dom_storage_dispatcher_->OnMessageReceived(msg)) {
    return true;
  }
  return false;
}

src/content/renderer/render_thread_impl.cc

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

...Missing who calls CreateView...

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

void RenderThreadImpl::CreateView(mojom::CreateViewParamsPtr params) {
  CompositorDependencies* compositor_deps = this;
  is_scroll_animator_enabled_ = params->web_preferences.enable_scroll_animator;
  // When bringing in render_view, also bring in webkit's glue and jsbindings.
  RenderViewImpl::Create(compositor_deps, std::move(params),
                         RenderWidget::ShowCallback(),
                         GetWebMainThreadScheduler()->DefaultTaskRunner());
}
/*static*/
RenderViewImpl* RenderViewImpl::Create(
    CompositorDependencies* compositor_deps,
    mojom::CreateViewParamsPtr params,
    RenderWidget::ShowCallback show_callback,
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
  DCHECK(params->view_id != MSG_ROUTING_NONE);
  DCHECK(params->main_frame_widget_routing_id != MSG_ROUTING_NONE);
  RenderViewImpl* render_view;
  if (g_create_render_view_impl)
    render_view = g_create_render_view_impl(compositor_deps, *params);
  else
    render_view = new RenderViewImpl(compositor_deps, *params, task_runner);

  render_view->Initialize(std::move(params), std::move(show_callback),
                          std::move(task_runner));
  return render_view;
}

src/content/renderer/render_thread_impl.cc 

void RenderViewImpl::Initialize(
    mojom::CreateViewParamsPtr params,
    RenderWidget::ShowCallback show_callback,
    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
  ...
  WebFrame* opener_frame =
      RenderFrameImpl::ResolveOpener(params->opener_frame_route_id);

  webview_ =
      WebView::Create(this, WidgetClient(),
                      is_hidden() ? blink::mojom::PageVisibilityState::kHidden
                                  : blink::mojom::PageVisibilityState::kVisible,
                      opener_frame ? opener_frame->View() : nullptr);
  RenderWidget::Init(std::move(show_callback), webview_->GetWidget());

  ...
    main_render_frame_ = RenderFrameImpl::CreateMainFrame(
        this, params->main_frame_routing_id,
        std::move(main_frame_interface_provider),
        params->main_frame_widget_routing_id, params->hidden,
        GetWidget()->GetWebScreenInfo(), GetWidget()->compositor_deps(),
        opener_frame, params->devtools_main_frame_token,
        params->replicated_frame_state, params->has_committed_real_load);
  }

  ...
  
  if (main_render_frame_)
    main_render_frame_->Initialize();
  ...
  GetContentClient()->renderer()->RenderViewCreated(this);
  page_zoom_level_ = 0;

  nav_state_sync_timer_.SetTaskRunner(task_runner);
}

src/content/renderer/render_view_impl.cc

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

WebView* WebView::Create(WebViewClient* client,
                         WebWidgetClient* widget_client,
                         mojom::PageVisibilityState visibility_state,
                         WebView* opener) {
  return WebViewImpl::Create(client, widget_client, visibility_state,
                             static_cast<WebViewImpl*>(opener));
}

src/third_party/blink/renderer/core/exported/web_view_impl.cc 

if (main_render_frame_)
    main_render_frame_->Initialize();

src/content/renderer/render_view_impl.cc 

在WebKit中,Web Frame有Local和Remote之分。Local类型的Web Frame描述的是网页的Main Frame,它是在当前Render进程中加载和渲染的。Remote类型的Web Frame描述的是网页的Sub Frame,它是在其他Render进程中加载和渲染的。Sub Frame描述的网页是通过iframe标签嵌入在Main Frame描述的网页中的。这一点可以参考前面的图3。

// static
LocalFrame* LocalFrame::Create(LocalFrameClient* client,
                               Page& page,
                               FrameOwner* owner,
                               InterfaceRegistry* interface_registry) {
  LocalFrame* frame = new LocalFrame(
      client, page, owner,
      interface_registry ? interface_registry
                         : InterfaceRegistry::GetEmptyInterfaceRegistry());
  PageScheduler* page_scheduler = page.GetPageScheduler();
  if (frame->IsMainFrame() && page_scheduler)
    page_scheduler->SetIsMainFrameLocal(true);
  probe::frameAttachedToParent(frame);
  return frame;
}

初始化FrameLoader创建document_loader, 用于关联之后的DOM,

void FrameLoader::Init() {
  ...
  provisional_document_loader_ = Client()->CreateDocumentLoader(
      frame_, initial_request, SubstituteData(),
      ClientRedirectPolicy::kNotClientRedirect,
      base::UnguessableToken::Create(), nullptr /* extra_data */,
      WebNavigationTimings());
  ...
}

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

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

       在接下来的一篇文章分析网页内容的加载过程中,我们就可以看到,Render进程中的RenderFrameImpl对象处理其在Browser进程中的对应的RenderFrameHostImpl对象发送过来的类型为FrameMsg_Navigate的IPC消息的过程,也就是Render进程加载网页内容的过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值