当我们通过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 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(this),
Details(&close_expected));
notified_termination_ = true;
}
Unregister();
MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
}