[Chrome源码阅读]Chrome增加一个New Tab时都干了些什么

当我们通过TAB栏上的"+"标签增加一个新的空TAB时,Chrome内部的代码是怎么样的呢?这篇文章将会介绍它内部的流程。

首先用一个callstack截图来看下它的前一段处理流程:

[Chrome源码阅读]Chrome增加一个New Tab时都干了些什么_第1张图片

TAB旁边的那个“+”(注意不是"New Tab (Ctrl+T)")菜单,在Chrome中是一个button。这个button是放置在TabStrip 视图类中的。TabStrip类捕捉到了这个button pressed事件,立即响应,调用TabStripModel实例的AddBlankTab函数(前面文章讲到过TabStrip维护一个TabStripModel的指针,同时TabStrip视图也是TabStripModel数据类的观察者)。看上去很简单。

TabStripModel::AddBlankTab函数实现也很简单,创建一个TabContents实例,然后将其添加到TabContentsData列表中进行管理。

TabContents* TabStripModel::AddBlankTab(bool foreground) {
  TabContents* contents = delegate_->CreateTabContentsForURL(
      delegate_->GetBlankTabURL(), GURL(), profile_, PageTransition::TYPED,
      false, NULL);
  AddTabContents(contents, -1, PageTransition::TYPED, foreground);
  return contents;
}
上面的delegate_是Browser实例。Browser才是真正干活的人。根据content类型(Web/DOMUI/,etc)创建一个具体的TabContents实例。注意此时传入的instance为NULL,表明这是个空的site instance,不会进行导航。而且def_load为false。

  TabContents* contents = TabContents::CreateWithType(type, profile, instance);
  contents->SetupController(profile);

  if (!defer_load) {
    // Load the initial URL before adding the new tab contents to the tab strip
    // so that the tab contents has navigation state.
    contents->controller()->LoadURL(url, referrer, transition);
  }
TabContents::CreateWithType做了很多很多的事情。因为WebContents是一个很复杂的类,看看它的构造函数就知道了。

WebContents::WebContents(Profile* profile,
                         SiteInstance* site_instance,
                         RenderViewHostFactory* render_view_factory,
                         int routing_id,
                         base::WaitableEvent* modal_dialog_event)
    : TabContents(TAB_CONTENTS_WEB),
      view_(WebContentsView::Create(this)),
      ALLOW_THIS_IN_INITIALIZER_LIST(
          render_manager_(render_view_factory, this, this)),
      render_view_factory_(render_view_factory),
      printing_(*this),
      notify_disconnection_(false),
      received_page_title_(false),
      is_starred_(false),
#if defined(OS_WIN)
      message_box_active_(CreateEvent(NULL, TRUE, FALSE, NULL)),
#endif
      ALLOW_THIS_IN_INITIALIZER_LIST(fav_icon_helper_(this)),
      suppress_javascript_messages_(false),
      load_state_(net::LOAD_STATE_IDLE),
      find_ui_active_(false),
      find_op_aborted_(false),
      current_find_request_id_(find_request_id_counter_++) {
  pending_install_.page_id = 0;
  pending_install_.callback_functor = NULL;

  render_manager_.Init(profile, site_instance, routing_id, modal_dialog_event);

  // Register for notifications about all interested prefs change.
  PrefService* prefs = profile->GetPrefs();
  if (prefs) {
    for (int i = 0; i < kPrefsToObserveLength; ++i)
      prefs->AddPrefObserver(kPrefsToObserve[i], this);
  }

  // Register for notifications about URL starredness changing on any profile.
  NotificationService::current()->AddObserver(
      this, NotificationType::URLS_STARRED, NotificationService::AllSources());
  NotificationService::current()->AddObserver(
      this, NotificationType::BOOKMARK_MODEL_LOADED,
      NotificationService::AllSources());
  NotificationService::current()->AddObserver(
      this, NotificationType::RENDER_WIDGET_HOST_DESTROYED,
      NotificationService::AllSources());
}
构造函数首先构造基类TabContents,它的构造仅仅是初始化一些变量而已,没多少事情可以做。但是WebContents实现了很多接口:RenderViewHostManager::Delegate,RenderViewHostDelegate。它维护着一个RenderViewHostManager,所以需要初始化。RenderViewHostManager::Init函数会实例化一个RenderViewHost,要知道RenderViewHost继承自RenderWidgetHost,实例化RenderWidgetHost时需要RenderProcessHost实例的,也就是说实例化一个RenderViewHost就会实例化一个RenderProcessHost,由SiteInstance进行管理。

创建完WebContents之后,就要Setup一个Navigation Controller,用profile来实例化一个Navigation Controller。

总体来说,实例化一个WebContents时,就需要顺带着实例化RenderViewHost/RenderProcessHost,和NavigationController。当然在某些情况下会重用已经存在的RenderViewHost/RenderProcessHost。

调用NavigationController的LoadURL函数来导航到新的URL地址。这里面还有更多内容。继续跟踪代码:


我们通常叫这个新的Site为pending状态,所以就是NavigateToPendingEntry。每个NavigationController会维护这个一个Entry列表,代表这个TAB页面导航多的site历史。

bool WebContents::NavigateToPendingEntry(bool reload) {
  NavigationEntry* entry = controller()->GetPendingEntry();
  RenderViewHost* dest_render_view_host = render_manager_.Navigate(*entry);
  if (!dest_render_view_host)
    return false;  // Unable to create the desired render view host.

  // Used for page load time metrics.
  current_load_start_ = TimeTicks::Now();

  // Navigate in the desired RenderViewHost.
  dest_render_view_host->NavigateToEntry(*entry, reload);
请注意,之前在实例化一个TabContents时,实例化一个RenderViewHost,那时的SiteInstance表示为空。但是Navigation时,SiteInstance不可能为空,也就是这是的entry需要另外一个RenderViewHost,也就是因为这个Navigate进行了复杂的处理。

RenderViewHostManager维护2个RenderViewHost,一个是Pending render view host,另一个是Active render view host。当初始化Render View Host Manager时,那时初始化的render view host就是active render view host。现在导航是需要的render view host,就是pending render view host。所以render_manager_.Navigate函数将会创建一个新的render view host。而且更重要的是此时会创建一个RenderView与pending RenderViewHost对应。WebContents::CreatePendingRenderView函数将这2件事都做了。

// Create a pending RVH and navigate it.
    bool success = CreatePendingRenderView(new_instance);
    if (!success)
      return NULL;

    // Check if our current RVH is live before we set up a transition.
    if (!render_view_host_->IsRenderViewLive()) {
      if (!cross_navigation_pending_) {
        // The current RVH is not live.  There's no reason to sit around with a
        // sad tab or a newly created RVH while we wait for the pending RVH to
        // navigate.  Just switch to the pending RVH now and go back to non
        // cross-navigating (Note that we don't care about on{before}unload
        // handlers if the current RVH isn't live.)
        SwapToRenderView(&pending_render_view_host_, true);
        return render_view_host_;
CreatePendingRenderView函数实现:
  pending_render_view_host_ =
      CreateRenderViewHost(instance, MSG_ROUTING_NONE, NULL);

  bool success = delegate_->CreateRenderViewForRenderManager(
      pending_render_view_host_);

所以在创建一个新的RenderView时,就需要启动一个新的RenderView进程了


注意,在创建一个pending render view host之后,就需要将这个render view host与之前的active render view host进行交换,此时的pending就变成了active render view了。销毁之前的render view host。

[Chrome源码阅读]Chrome增加一个New Tab时都干了些什么_第2张图片

有一个全局的RenderProcessHost列表,仅仅是维护一个RenderProcessHost指针。

// the global list of all renderer processes
IDMap<RenderProcessHost> all_hosts;

注意,销毁每个RenderViewHost时会调用RenderProcessHost的Release函数,这个函数里可能会delete自己,根据当前监听的对象个数。RenderProcessHost维护一个监听者列表,以ID为key,其实就是RenderViewHost对象,因为它继承于IPC::Channel::Listener,而且多个RenderViewHost可共享同一个RenderProcessHost,比如在--process-per-site-instance进程模式下,由同一个网址链接打开的所有的网址/TAB。在构造每个RenderViewHost时,都会将自己添加到RenderProcessHost的这个listeners_列表中建立连接,一旦连接的所有的RenderViewHost都销毁了,RenderProcessHost才销毁。

void RenderProcessHost::Release(int listener_id) {
  DCHECK(listeners_.Lookup(listener_id) != NULL);
  listeners_.Remove(listener_id);

  // Make sure that all associated resource requests are stopped.
  CancelResourceRequests(listener_id);

  // When no other owners of this object, we can delete ourselves
  if (listeners_.IsEmpty()) {
    if (!notified_termination_) {
      bool close_expected = true;
      NotificationService::current()->Notify(
          NotificationType::RENDERER_PROCESS_TERMINATED,
          Source<RenderProcessHost>(this),
          Details<bool>(&close_expected));
      notified_termination_ = true;
    }
    Unregister();
    MessageLoop::current()->DeleteSoon(FROM_HERE, this);
  }
}





你可能感兴趣的:([Chrome源码阅读]Chrome增加一个New Tab时都干了些什么)