当我们通过TAB栏上的"+"标签增加一个新的空TAB时,Chrome内部的代码是怎么样的呢?这篇文章将会介绍它内部的流程。
首先用一个callstack截图来看下它的前一段处理流程:
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。
有一个全局的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); } }