上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只能通过代码的分析来理解它的实现,代码如下:
这个函数的参数意思:
source是TAB内容。
url是网络连接地址。
disposition是窗口打开的位置。
transition是连接传送的类型。
override_encoding是编码类型。
#001 void Browser::OpenURLFromTab(TabContents* source,
#002 const GURL& url,
#003 WindowOpenDisposition disposition,
#004 PageTransition::Type transition,
#005 const std::string& override_encoding) {
调试时检查代码。
#006 // No code for these yet
#007 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
#008
获取当前的TAB页。
#009 TabContents* current_tab = source ? source : GetSelectedTabContents();
判断是否当前TAB页选中。
#010 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
#011 TabContents* new_contents = NULL;
#012
#013 // If the URL is part of the same web site, then load it in the same
#014 // SiteInstance (and thus the same process). This is an optimization to
#015 // reduce process overhead; it is not necessary for compatibility. (That is,
#016 // the new tab will not have script connections to the previous tab, so it
#017 // does not need to be part of the same SiteInstance or BrowsingInstance.)
#018 // Default to loading in a new SiteInstance and BrowsingInstance.
#019 // TODO(creis): should this apply to applications?
保存打开连接的实例指针。
#020 SiteInstance* instance = NULL;
如果不使用每个TAB页一个进程的方式,就不需要进行下面的处理。因为同一个连接在一个进程里打开是比较快,这里主要做优化。
#021 // Don't use this logic when "--process-per-tab" is specified.
判断是否有每一个TAB一个进程的方式。
#022 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
有当前进程页。
#023 if (current_tab) {
#024 const WebContents* const web_contents = current_tab->AsWebContents();
判断是否相同的网络连接地址。
#025 if (web_contents) {
#026 const GURL& current_url = web_contents->GetURL();
如果相同的网络地址,并且有实例打开,就返回这个实例在instance。
#027 if (SiteInstance::IsSameWebSite(current_url, url))
#028 instance = web_contents->site_instance();
#029 }
#030 }
#031 }
#032
#033 // If this is an application we can only have one tab so a new tab always
#034 // goes into a tabbed browser window.
下面进行不打开新窗口的处理。
#035 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
#036 // If the disposition is OFF_THE_RECORD we don't want to create a new
#037 // browser that will itself create another OTR browser. This will result in
#038 // a browser leak (and crash below because no tab is created or selected).
#039 if (disposition == OFF_THE_RECORD) {
#040 OpenURLOffTheRecord(profile_, url);
#041 return;
#042 }
#043
#044 Browser* b = GetOrCreateTabbedBrowser();
#045 DCHECK(b);
#046
#047 // If we have just created a new browser window, make sure we select the
#048 // tab.
#049 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
#050 disposition = NEW_FOREGROUND_TAB;
#051
#052 b->OpenURL(url, disposition, transition);
#053 b->Show();
#054 b->MoveToFront(true);
#055 return;
#056 }
#057
#058 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
#059 disposition = NEW_FOREGROUND_TAB;
#060
这里开始处理打开一个新窗口显示网络连接。
#061 if (disposition == NEW_WINDOW) {
创建一个新的Browser浏览器对象。
#062 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
#063 BrowserType::TABBED_BROWSER, L"");
创建一个TAB内容。
#064 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
这里开始显示这个网络连接的内容。
#065 new_browser->Show();
#066 } else if ((disposition == CURRENT_TAB) && current_tab) {
下面开始在当前TAB页里打开连接,同时判断处理的类型。
#067 if (transition == PageTransition::TYPED ||
#068 transition == PageTransition::AUTO_BOOKMARK ||
#069 transition == PageTransition::GENERATED ||
#070 transition == PageTransition::START_PAGE) {
#071 // Don't forget the openers if this tab is a New Tab page opened at the
#072 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
#073 // navigation of one of these transition types before resetting the
#074 // opener relationships (this allows for the use case of opening a new
#075 // tab to do a quick look-up of something while viewing a tab earlier in
#076 // the strip). We can make this heuristic more permissive if need be.
#077 // TODO(beng): (http://b/1306495) write unit tests for this once this
#078 // object is unit-testable.
#079 int current_tab_index =
#080 tabstrip_model_.GetIndexOfTabContents(current_tab);
#081 bool forget_openers =
#082 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
#083 current_tab_index == (tab_count() - 1) &&
#084 current_tab->controller()->GetEntryCount() == 1);
#085 if (forget_openers) {
#086 // If the user navigates the current tab to another page in any way
#087 // other than by clicking a link, we want to pro-actively forget all
#088 // TabStrip opener relationships since we assume they're beginning a
#089 // different task by reusing the current tab.
#090 tabstrip_model_.ForgetAllOpeners();
#091 // In this specific case we also want to reset the group relationship,
#092 // since it is now technically invalid.
#093 tabstrip_model_.ForgetGroup(current_tab);
#094 }
#095 }
这里开始在当前TAB页里加载网络地址连接。
#096 current_tab->controller()->LoadURL(url, transition);
#097 // The TabContents might have changed as part of the navigation (ex: new tab
#098 // page can become WebContents).
获取当前显示的内容。
#099 new_contents = current_tab->controller()->active_contents();
隐藏最下面状态提示窗口。
#100 GetStatusBubble()->Hide();
#101
#102 // Synchronously update the location bar. This allows us to immediately
#103 // have the URL bar update when the user types something, rather than
#104 // going through the normal system of ScheduleUIUpdate which has a delay.
更新本地的工具条。
#105 UpdateToolBar(false);
后面的内容先不分析,主要分析目前打开当前连接的内容。
#106 } else if (disposition == OFF_THE_RECORD) {
#107 OpenURLOffTheRecord(profile_, url);
#108 return;
#109 } else if (disposition != SUPPRESS_OPEN) {
#110 new_contents =
#111 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
#112 instance);
#113 }
#114
#115 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
#116 // Give the focus to the newly navigated tab, if the source tab was
#117 // front-most.
#118 new_contents->Focus();
#119 }
#120
#121 if (!override_encoding.empty()) {
#122 // The new tab needs a special encoding, such as a view source page
#123 // which should use the same encoding as the original page.
#124 WebContents* web_contents = new_contents->AsWebContents();
#125 if (web_contents)
#126 web_contents->set_override_encoding(override_encoding);
#127 }
#128 }
#129
上面函数的过程是这样的:主要根据打开网页的方式来选择窗口,比如是创建新窗口,还是只是打开一个TAB页,然后在TAB页显示。在这里还做了同一个网页地址的优化,不让它打开两个相同的进程来处理。
Browser对象是创建一个浏览器对象,接着调用LoadURL函数来加载输入的网页连接,隐藏当前窗口最下面的加载状态条,更新当前窗口的状态条,就完成了这个函数对当前TAB页加载网页的功能。下一次再来分析LoadURL函数是怎么样打开网页连接了。
上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只能通过代码的分析来理解它的实现,代码如下:
这个函数的参数意思:
source是TAB内容。
url是网络连接地址。
disposition是窗口打开的位置。
transition是连接传送的类型。
override_encoding是编码类型。
#001 void Browser::OpenURLFromTab(TabContents* source,
#002 const GURL& url,
#003 WindowOpenDisposition disposition,
#004 PageTransition::Type transition,
#005 const std::string& override_encoding) {
调试时检查代码。
#006 // No code for these yet
#007 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
#008
获取当前的TAB页。
#009 TabContents* current_tab = source ? source : GetSelectedTabContents();
判断是否当前TAB页选中。
#010 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
#011 TabContents* new_contents = NULL;
#012
#013 // If the URL is part of the same web site, then load it in the same
#014 // SiteInstance (and thus the same process). This is an optimization to
#015 // reduce process overhead; it is not necessary for compatibility. (That is,
#016 // the new tab will not have script connections to the previous tab, so it
#017 // does not need to be part of the same SiteInstance or BrowsingInstance.)
#018 // Default to loading in a new SiteInstance and BrowsingInstance.
#019 // TODO(creis): should this apply to applications?
保存打开连接的实例指针。
#020 SiteInstance* instance = NULL;
如果不使用每个TAB页一个进程的方式,就不需要进行下面的处理。因为同一个连接在一个进程里打开是比较快,这里主要做优化。
#021 // Don't use this logic when "--process-per-tab" is specified.
判断是否有每一个TAB一个进程的方式。
#022 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
有当前进程页。
#023 if (current_tab) {
#024 const WebContents* const web_contents = current_tab->AsWebContents();
判断是否相同的网络连接地址。
#025 if (web_contents) {
#026 const GURL& current_url = web_contents->GetURL();
如果相同的网络地址,并且有实例打开,就返回这个实例在instance。
#027 if (SiteInstance::IsSameWebSite(current_url, url))
#028 instance = web_contents->site_instance();
#029 }
#030 }
#031 }
#032
#033 // If this is an application we can only have one tab so a new tab always
#034 // goes into a tabbed browser window.
下面进行不打开新窗口的处理。
#035 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
#036 // If the disposition is OFF_THE_RECORD we don't want to create a new
#037 // browser that will itself create another OTR browser. This will result in
#038 // a browser leak (and crash below because no tab is created or selected).
#039 if (disposition == OFF_THE_RECORD) {
#040 OpenURLOffTheRecord(profile_, url);
#041 return;
#042 }
#043
#044 Browser* b = GetOrCreateTabbedBrowser();
#045 DCHECK(b);
#046
#047 // If we have just created a new browser window, make sure we select the
#048 // tab.
#049 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
#050 disposition = NEW_FOREGROUND_TAB;
#051
#052 b->OpenURL(url, disposition, transition);
#053 b->Show();
#054 b->MoveToFront(true);
#055 return;
#056 }
#057
#058 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
#059 disposition = NEW_FOREGROUND_TAB;
#060
这里开始处理打开一个新窗口显示网络连接。
#061 if (disposition == NEW_WINDOW) {
创建一个新的Browser浏览器对象。
#062 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
#063 BrowserType::TABBED_BROWSER, L"");
创建一个TAB内容。
#064 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
这里开始显示这个网络连接的内容。
#065 new_browser->Show();
#066 } else if ((disposition == CURRENT_TAB) && current_tab) {
下面开始在当前TAB页里打开连接,同时判断处理的类型。
#067 if (transition == PageTransition::TYPED ||
#068 transition == PageTransition::AUTO_BOOKMARK ||
#069 transition == PageTransition::GENERATED ||
#070 transition == PageTransition::START_PAGE) {
#071 // Don't forget the openers if this tab is a New Tab page opened at the
#072 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
#073 // navigation of one of these transition types before resetting the
#074 // opener relationships (this allows for the use case of opening a new
#075 // tab to do a quick look-up of something while viewing a tab earlier in
#076 // the strip). We can make this heuristic more permissive if need be.
#077 // TODO(beng): (http://b/1306495) write unit tests for this once this
#078 // object is unit-testable.
#079 int current_tab_index =
#080 tabstrip_model_.GetIndexOfTabContents(current_tab);
#081 bool forget_openers =
#082 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
#083 current_tab_index == (tab_count() - 1) &&
#084 current_tab->controller()->GetEntryCount() == 1);
#085 if (forget_openers) {
#086 // If the user navigates the current tab to another page in any way
#087 // other than by clicking a link, we want to pro-actively forget all
#088 // TabStrip opener relationships since we assume they're beginning a
#089 // different task by reusing the current tab.
#090 tabstrip_model_.ForgetAllOpeners();
#091 // In this specific case we also want to reset the group relationship,
#092 // since it is now technically invalid.
#093 tabstrip_model_.ForgetGroup(current_tab);
#094 }
#095 }
这里开始在当前TAB页里加载网络地址连接。
#096 current_tab->controller()->LoadURL(url, transition);
#097 // The TabContents might have changed as part of the navigation (ex: new tab
#098 // page can become WebContents).
获取当前显示的内容。
#099 new_contents = current_tab->controller()->active_contents();
隐藏最下面状态提示窗口。
#100 GetStatusBubble()->Hide();
#101
#102 // Synchronously update the location bar. This allows us to immediately
#103 // have the URL bar update when the user types something, rather than
#104 // going through the normal system of ScheduleUIUpdate which has a delay.
更新本地的工具条。
#105 UpdateToolBar(false);
后面的内容先不分析,主要分析目前打开当前连接的内容。
#106 } else if (disposition == OFF_THE_RECORD) {
#107 OpenURLOffTheRecord(profile_, url);
#108 return;
#109 } else if (disposition != SUPPRESS_OPEN) {
#110 new_contents =
#111 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
#112 instance);
#113 }
#114
#115 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
#116 // Give the focus to the newly navigated tab, if the source tab was
#117 // front-most.
#118 new_contents->Focus();
#119 }
#120
#121 if (!override_encoding.empty()) {
#122 // The new tab needs a special encoding, such as a view source page
#123 // which should use the same encoding as the original page.
#124 WebContents* web_contents = new_contents->AsWebContents();
#125 if (web_contents)
#126 web_contents->set_override_encoding(override_encoding);
#127 }
#128 }
#129
上面函数的过程是这样的:主要根据打开网页的方式来选择窗口,比如是创建新窗口,还是只是打开一个TAB页,然后在TAB页显示。在这里还做了同一个网页地址的优化,不让它打开两个相同的进程来处理。
Browser对象是创建一个浏览器对象,接着调用LoadURL函数来加载输入的网页连接,隐藏当前窗口最下面的加载状态条,更新当前窗口的状态条,就完成了这个函数对当前TAB页加载网页的功能。下一次再来分析LoadURL函数是怎么样打开网页连接了。
当输入的网页连接传送给LoadURL函数之后,还需要处理很多内容,其实这是由类NavigationController来管理的,NavigationController类主要就是管理加载网页、退回、前进等等控制。
#001 void NavigationController::LoadURL(const GURL& url,
#002 PageTransition::Type transition) {
#003 // The user initiated a load, we don't need to reload anymore.
#004 needs_reload_ = false;
#005
#006 NavigationEntry* entry = CreateNavigationEntry(url, transition);
#007
#008 LoadEntry(entry);
#009 }
这个函数的第一个参数url是网络连接地址,第二个参数transition是传送的类型。
第4行代码里设置不是重新加载。
第6行里创建了一个处理网页浏览的入口对象,它是由类NavigationEntry管理。
第8行里就调用函数LoadEntry来加载网页。
LoadEntry函数更进一步去加载网页的内容,它的代码如下:
#001 void NavigationController::LoadEntry(NavigationEntry* entry) {
#002 // When navigating to a new page, we don't know for sure if we will actually
#003 // end up leaving the current page. The new page load could for example
#004 // result in a download or a 'no content' response (e.g., a mailto: URL).
#005
#006 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
清除内部变量。
#007 DiscardPendingEntryInternal();
保存当前的入口对象。
#008 pending_entry_ = entry;
通知服务器有一个浏览器对象加入。
#009 NotificationService::current()->Notify(
#010 NOTIFY_NAV_ENTRY_PENDING,
#011 Source<NavigationController>(this),
#012 NotificationService::NoDetails());
下面开始进入加载网页的动作。
#013 NavigateToPendingEntry(false);
#014 }
第9行里的类NotificationService是使用OBSERVER的设计模式来实现一对多的显示关系。这个设计模式也是跟MVC与生具来的,显然设计这个浏览器代码的人,已经是对设计模式是专家式的人物了。
第13行里调用函数NavigateToPendingEntry,下一次再来分析它的功能。
上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只能通过代码的分析来理解它的实现,代码如下:
这个函数的参数意思:
source是TAB内容。
url是网络连接地址。
disposition是窗口打开的位置。
transition是连接传送的类型。
override_encoding是编码类型。
#001 void Browser::OpenURLFromTab(TabContents* source,
#002 const GURL& url,
#003 WindowOpenDisposition disposition,
#004 PageTransition::Type transition,
#005 const std::string& override_encoding) {
调试时检查代码。
#006 // No code for these yet
#007 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
#008
获取当前的TAB页。
#009 TabContents* current_tab = source ? source : GetSelectedTabContents();
判断是否当前TAB页选中。
#010 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
#011 TabContents* new_contents = NULL;
#012
#013 // If the URL is part of the same web site, then load it in the same
#014 // SiteInstance (and thus the same process). This is an optimization to
#015 // reduce process overhead; it is not necessary for compatibility. (That is,
#016 // the new tab will not have script connections to the previous tab, so it
#017 // does not need to be part of the same SiteInstance or BrowsingInstance.)
#018 // Default to loading in a new SiteInstance and BrowsingInstance.
#019 // TODO(creis): should this apply to applications?
保存打开连接的实例指针。
#020 SiteInstance* instance = NULL;
如果不使用每个TAB页一个进程的方式,就不需要进行下面的处理。因为同一个连接在一个进程里打开是比较快,这里主要做优化。
#021 // Don't use this logic when "--process-per-tab" is specified.
判断是否有每一个TAB一个进程的方式。
#022 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
有当前进程页。
#023 if (current_tab) {
#024 const WebContents* const web_contents = current_tab->AsWebContents();
判断是否相同的网络连接地址。
#025 if (web_contents) {
#026 const GURL& current_url = web_contents->GetURL();
如果相同的网络地址,并且有实例打开,就返回这个实例在instance。
#027 if (SiteInstance::IsSameWebSite(current_url, url))
#028 instance = web_contents->site_instance();
#029 }
#030 }
#031 }
#032
#033 // If this is an application we can only have one tab so a new tab always
#034 // goes into a tabbed browser window.
下面进行不打开新窗口的处理。
#035 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
#036 // If the disposition is OFF_THE_RECORD we don't want to create a new
#037 // browser that will itself create another OTR browser. This will result in
#038 // a browser leak (and crash below because no tab is created or selected).
#039 if (disposition == OFF_THE_RECORD) {
#040 OpenURLOffTheRecord(profile_, url);
#041 return;
#042 }
#043
#044 Browser* b = GetOrCreateTabbedBrowser();
#045 DCHECK(b);
#046
#047 // If we have just created a new browser window, make sure we select the
#048 // tab.
#049 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
#050 disposition = NEW_FOREGROUND_TAB;
#051
#052 b->OpenURL(url, disposition, transition);
#053 b->Show();
#054 b->MoveToFront(true);
#055 return;
#056 }
#057
#058 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
#059 disposition = NEW_FOREGROUND_TAB;
#060
这里开始处理打开一个新窗口显示网络连接。
#061 if (disposition == NEW_WINDOW) {
创建一个新的Browser浏览器对象。
#062 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
#063 BrowserType::TABBED_BROWSER, L"");
创建一个TAB内容。
#064 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
这里开始显示这个网络连接的内容。
#065 new_browser->Show();
#066 } else if ((disposition == CURRENT_TAB) && current_tab) {
下面开始在当前TAB页里打开连接,同时判断处理的类型。
#067 if (transition == PageTransition::TYPED ||
#068 transition == PageTransition::AUTO_BOOKMARK ||
#069 transition == PageTransition::GENERATED ||
#070 transition == PageTransition::START_PAGE) {
#071 // Don't forget the openers if this tab is a New Tab page opened at the
#072 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
#073 // navigation of one of these transition types before resetting the
#074 // opener relationships (this allows for the use case of opening a new
#075 // tab to do a quick look-up of something while viewing a tab earlier in
#076 // the strip). We can make this heuristic more permissive if need be.
#077 // TODO(beng): (http://b/1306495) write unit tests for this once this
#078 // object is unit-testable.
#079 int current_tab_index =
#080 tabstrip_model_.GetIndexOfTabContents(current_tab);
#081 bool forget_openers =
#082 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
#083 current_tab_index == (tab_count() - 1) &&
#084 current_tab->controller()->GetEntryCount() == 1);
#085 if (forget_openers) {
#086 // If the user navigates the current tab to another page in any way
#087 // other than by clicking a link, we want to pro-actively forget all
#088 // TabStrip opener relationships since we assume they're beginning a
#089 // different task by reusing the current tab.
#090 tabstrip_model_.ForgetAllOpeners();
#091 // In this specific case we also want to reset the group relationship,
#092 // since it is now technically invalid.
#093 tabstrip_model_.ForgetGroup(current_tab);
#094 }
#095 }
这里开始在当前TAB页里加载网络地址连接。
#096 current_tab->controller()->LoadURL(url, transition);
#097 // The TabContents might have changed as part of the navigation (ex: new tab
#098 // page can become WebContents).
获取当前显示的内容。
#099 new_contents = current_tab->controller()->active_contents();
隐藏最下面状态提示窗口。
#100 GetStatusBubble()->Hide();
#101
#102 // Synchronously update the location bar. This allows us to immediately
#103 // have the URL bar update when the user types something, rather than
#104 // going through the normal system of ScheduleUIUpdate which has a delay.
更新本地的工具条。
#105 UpdateToolBar(false);
后面的内容先不分析,主要分析目前打开当前连接的内容。
#106 } else if (disposition == OFF_THE_RECORD) {
#107 OpenURLOffTheRecord(profile_, url);
#108 return;
#109 } else if (disposition != SUPPRESS_OPEN) {
#110 new_contents =
#111 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
#112 instance);
#113 }
#114
#115 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
#116 // Give the focus to the newly navigated tab, if the source tab was
#117 // front-most.
#118 new_contents->Focus();
#119 }
#120
#121 if (!override_encoding.empty()) {
#122 // The new tab needs a special encoding, such as a view source page
#123 // which should use the same encoding as the original page.
#124 WebContents* web_contents = new_contents->AsWebContents();
#125 if (web_contents)
#126 web_contents->set_override_encoding(override_encoding);
#127 }
#128 }
#129
上面函数的过程是这样的:主要根据打开网页的方式来选择窗口,比如是创建新窗口,还是只是打开一个TAB页,然后在TAB页显示。在这里还做了同一个网页地址的优化,不让它打开两个相同的进程来处理。
Browser对象是创建一个浏览器对象,接着调用LoadURL函数来加载输入的网页连接,隐藏当前窗口最下面的加载状态条,更新当前窗口的状态条,就完成了这个函数对当前TAB页加载网页的功能。下一次再来分析LoadURL函数是怎么样打开网页连接了。
当输入的网页连接传送给LoadURL函数之后,还需要处理很多内容,其实这是由类NavigationController来管理的,NavigationController类主要就是管理加载网页、退回、前进等等控制。
#001 void NavigationController::LoadURL(const GURL& url,
#002 PageTransition::Type transition) {
#003 // The user initiated a load, we don't need to reload anymore.
#004 needs_reload_ = false;
#005
#006 NavigationEntry* entry = CreateNavigationEntry(url, transition);
#007
#008 LoadEntry(entry);
#009 }
这个函数的第一个参数url是网络连接地址,第二个参数transition是传送的类型。
第4行代码里设置不是重新加载。
第6行里创建了一个处理网页浏览的入口对象,它是由类NavigationEntry管理。
第8行里就调用函数LoadEntry来加载网页。
LoadEntry函数更进一步去加载网页的内容,它的代码如下:
#001 void NavigationController::LoadEntry(NavigationEntry* entry) {
#002 // When navigating to a new page, we don't know for sure if we will actually
#003 // end up leaving the current page. The new page load could for example
#004 // result in a download or a 'no content' response (e.g., a mailto: URL).
#005
#006 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
清除内部变量。
#007 DiscardPendingEntryInternal();
保存当前的入口对象。
#008 pending_entry_ = entry;
通知服务器有一个浏览器对象加入。
#009 NotificationService::current()->Notify(
#010 NOTIFY_NAV_ENTRY_PENDING,
#011 Source<NavigationController>(this),
#012 NotificationService::NoDetails());
下面开始进入加载网页的动作。
#013 NavigateToPendingEntry(false);
#014 }
第9行里的类NotificationService是使用OBSERVER的设计模式来实现一对多的显示关系。这个设计模式也是跟MVC与生具来的,显然设计这个浏览器代码的人,已经是对设计模式是专家式的人物了。
第13行里调用函数NavigateToPendingEntry,下一次再来分析它的功能。
现在继续分析浏览器去下载网页的过程,上一次说到需要分析函数NavigateToPendingEntry,在这个函数又是怎么去处理下载网页的呢?那就需要分析它的源码了,如下:
#001 void NavigationController::NavigateToPendingEntry(bool reload) {
#002 TabContents* from_contents = active_contents_;
#003
#004 // For session history navigations only the pending_entry_index_ is set.
下面从历史里找到入口选项。
#005 if (!pending_entry_) {
#006 DCHECK(pending_entry_index_ != -1);
#007 pending_entry_ = entries_[pending_entry_index_].get();
#008 }
#009
复位当前的SSL状态。
#010 // Reset the security states as any SSL error may have been resolved since we
#011 // last visited that page.
#012 pending_entry_->ssl() = NavigationEntry::SSLStatus();
#013
设置内容是否可以显示。
#014 if (from_contents && from_contents->type() != pending_entry_->tab_type())
#015 from_contents->SetActive(false);
#016
获取当前的父窗口的句柄。
#017 HWND parent =
#018 from_contents ? GetParent(from_contents->GetContainerHWND()) : 0;
获取当前显示的TAB内容对象。
#019 TabContents* contents =
#020 GetTabContentsCreateIfNecessary(parent, *pending_entry_);
#021
#022 contents->SetActive(true);
#023 active_contents_ = contents;
#024
修改委托对象。
#025 if (from_contents && from_contents != contents) {
#026 if (from_contents->delegate())
#027 from_contents->delegate()->ReplaceContents(from_contents, contents);
#028 }
#029
现在开始打开入口对象里指定的网站。
#030 if (!contents->Navigate(*pending_entry_, reload))
#031 DiscardPendingEntry();
#032 }
TabContents类主要描述主显示区的内容,在第30行里就调用它的函数Navigate去浏览网页的内容。pending_entry_成员变量是NavigationEntry类的对象,它主要保存所有创建浏览时需要的信息,比如网络连接地址。
当我再跟踪contents->Navigate这行代码时,它不是运行TabContents类的Navigate,这比较奇怪,但回过头来再看一下它的声明如下:
virtual bool Navigate(const NavigationEntry& entry, bool reload);
可见,它前面加了virtual关键字,说明它是虚函数,也就是说设计时,就让它是多态的出现,因此在什么情况下运行什么样的函数内容是不定的,对于这些样的函数,就需要小心一点了,只有实际运行的类才知道它是什么内容。由于我是输入URL关键字,所以它调用的函数是类WebContents里的Navigate函数。下一次再来分析类WebContents里的Navigate函数。
上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只能通过代码的分析来理解它的实现,代码如下:
这个函数的参数意思:
source是TAB内容。
url是网络连接地址。
disposition是窗口打开的位置。
transition是连接传送的类型。
override_encoding是编码类型。
#001 void Browser::OpenURLFromTab(TabContents* source,
#002 const GURL& url,
#003 WindowOpenDisposition disposition,
#004 PageTransition::Type transition,
#005 const std::string& override_encoding) {
调试时检查代码。
#006 // No code for these yet
#007 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
#008
获取当前的TAB页。
#009 TabContents* current_tab = source ? source : GetSelectedTabContents();
判断是否当前TAB页选中。
#010 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
#011 TabContents* new_contents = NULL;
#012
#013 // If the URL is part of the same web site, then load it in the same
#014 // SiteInstance (and thus the same process). This is an optimization to
#015 // reduce process overhead; it is not necessary for compatibility. (That is,
#016 // the new tab will not have script connections to the previous tab, so it
#017 // does not need to be part of the same SiteInstance or BrowsingInstance.)
#018 // Default to loading in a new SiteInstance and BrowsingInstance.
#019 // TODO(creis): should this apply to applications?
保存打开连接的实例指针。
#020 SiteInstance* instance = NULL;
如果不使用每个TAB页一个进程的方式,就不需要进行下面的处理。因为同一个连接在一个进程里打开是比较快,这里主要做优化。
#021 // Don't use this logic when "--process-per-tab" is specified.
判断是否有每一个TAB一个进程的方式。
#022 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
有当前进程页。
#023 if (current_tab) {
#024 const WebContents* const web_contents = current_tab->AsWebContents();
判断是否相同的网络连接地址。
#025 if (web_contents) {
#026 const GURL& current_url = web_contents->GetURL();
如果相同的网络地址,并且有实例打开,就返回这个实例在instance。
#027 if (SiteInstance::IsSameWebSite(current_url, url))
#028 instance = web_contents->site_instance();
#029 }
#030 }
#031 }
#032
#033 // If this is an application we can only have one tab so a new tab always
#034 // goes into a tabbed browser window.
下面进行不打开新窗口的处理。
#035 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
#036 // If the disposition is OFF_THE_RECORD we don't want to create a new
#037 // browser that will itself create another OTR browser. This will result in
#038 // a browser leak (and crash below because no tab is created or selected).
#039 if (disposition == OFF_THE_RECORD) {
#040 OpenURLOffTheRecord(profile_, url);
#041 return;
#042 }
#043
#044 Browser* b = GetOrCreateTabbedBrowser();
#045 DCHECK(b);
#046
#047 // If we have just created a new browser window, make sure we select the
#048 // tab.
#049 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
#050 disposition = NEW_FOREGROUND_TAB;
#051
#052 b->OpenURL(url, disposition, transition);
#053 b->Show();
#054 b->MoveToFront(true);
#055 return;
#056 }
#057
#058 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
#059 disposition = NEW_FOREGROUND_TAB;
#060
这里开始处理打开一个新窗口显示网络连接。
#061 if (disposition == NEW_WINDOW) {
创建一个新的Browser浏览器对象。
#062 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
#063 BrowserType::TABBED_BROWSER, L"");
创建一个TAB内容。
#064 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
这里开始显示这个网络连接的内容。
#065 new_browser->Show();
#066 } else if ((disposition == CURRENT_TAB) && current_tab) {
下面开始在当前TAB页里打开连接,同时判断处理的类型。
#067 if (transition == PageTransition::TYPED ||
#068 transition == PageTransition::AUTO_BOOKMARK ||
#069 transition == PageTransition::GENERATED ||
#070 transition == PageTransition::START_PAGE) {
#071 // Don't forget the openers if this tab is a New Tab page opened at the
#072 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
#073 // navigation of one of these transition types before resetting the
#074 // opener relationships (this allows for the use case of opening a new
#075 // tab to do a quick look-up of something while viewing a tab earlier in
#076 // the strip). We can make this heuristic more permissive if need be.
#077 // TODO(beng): (http://b/1306495) write unit tests for this once this
#078 // object is unit-testable.
#079 int current_tab_index =
#080 tabstrip_model_.GetIndexOfTabContents(current_tab);
#081 bool forget_openers =
#082 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
#083 current_tab_index == (tab_count() - 1) &&
#084 current_tab->controller()->GetEntryCount() == 1);
#085 if (forget_openers) {
#086 // If the user navigates the current tab to another page in any way
#087 // other than by clicking a link, we want to pro-actively forget all
#088 // TabStrip opener relationships since we assume they're beginning a
#089 // different task by reusing the current tab.
#090 tabstrip_model_.ForgetAllOpeners();
#091 // In this specific case we also want to reset the group relationship,
#092 // since it is now technically invalid.
#093 tabstrip_model_.ForgetGroup(current_tab);
#094 }
#095 }
这里开始在当前TAB页里加载网络地址连接。
#096 current_tab->controller()->LoadURL(url, transition);
#097 // The TabContents might have changed as part of the navigation (ex: new tab
#098 // page can become WebContents).
获取当前显示的内容。
#099 new_contents = current_tab->controller()->active_contents();
隐藏最下面状态提示窗口。
#100 GetStatusBubble()->Hide();
#101
#102 // Synchronously update the location bar. This allows us to immediately
#103 // have the URL bar update when the user types something, rather than
#104 // going through the normal system of ScheduleUIUpdate which has a delay.
更新本地的工具条。
#105 UpdateToolBar(false);
后面的内容先不分析,主要分析目前打开当前连接的内容。
#106 } else if (disposition == OFF_THE_RECORD) {
#107 OpenURLOffTheRecord(profile_, url);
#108 return;
#109 } else if (disposition != SUPPRESS_OPEN) {
#110 new_contents =
#111 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
#112 instance);
#113 }
#114
#115 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
#116 // Give the focus to the newly navigated tab, if the source tab was
#117 // front-most.
#118 new_contents->Focus();
#119 }
#120
#121 if (!override_encoding.empty()) {
#122 // The new tab needs a special encoding, such as a view source page
#123 // which should use the same encoding as the original page.
#124 WebContents* web_contents = new_contents->AsWebContents();
#125 if (web_contents)
#126 web_contents->set_override_encoding(override_encoding);
#127 }
#128 }
#129
上面函数的过程是这样的:主要根据打开网页的方式来选择窗口,比如是创建新窗口,还是只是打开一个TAB页,然后在TAB页显示。在这里还做了同一个网页地址的优化,不让它打开两个相同的进程来处理。
Browser对象是创建一个浏览器对象,接着调用LoadURL函数来加载输入的网页连接,隐藏当前窗口最下面的加载状态条,更新当前窗口的状态条,就完成了这个函数对当前TAB页加载网页的功能。下一次再来分析LoadURL函数是怎么样打开网页连接了。
当输入的网页连接传送给LoadURL函数之后,还需要处理很多内容,其实这是由类NavigationController来管理的,NavigationController类主要就是管理加载网页、退回、前进等等控制。
#001 void NavigationController::LoadURL(const GURL& url,
#002 PageTransition::Type transition) {
#003 // The user initiated a load, we don't need to reload anymore.
#004 needs_reload_ = false;
#005
#006 NavigationEntry* entry = CreateNavigationEntry(url, transition);
#007
#008 LoadEntry(entry);
#009 }
这个函数的第一个参数url是网络连接地址,第二个参数transition是传送的类型。
第4行代码里设置不是重新加载。
第6行里创建了一个处理网页浏览的入口对象,它是由类NavigationEntry管理。
第8行里就调用函数LoadEntry来加载网页。
LoadEntry函数更进一步去加载网页的内容,它的代码如下:
#001 void NavigationController::LoadEntry(NavigationEntry* entry) {
#002 // When navigating to a new page, we don't know for sure if we will actually
#003 // end up leaving the current page. The new page load could for example
#004 // result in a download or a 'no content' response (e.g., a mailto: URL).
#005
#006 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
清除内部变量。
#007 DiscardPendingEntryInternal();
保存当前的入口对象。
#008 pending_entry_ = entry;
通知服务器有一个浏览器对象加入。
#009 NotificationService::current()->Notify(
#010 NOTIFY_NAV_ENTRY_PENDING,
#011 Source<NavigationController>(this),
#012 NotificationService::NoDetails());
下面开始进入加载网页的动作。
#013 NavigateToPendingEntry(false);
#014 }
第9行里的类NotificationService是使用OBSERVER的设计模式来实现一对多的显示关系。这个设计模式也是跟MVC与生具来的,显然设计这个浏览器代码的人,已经是对设计模式是专家式的人物了。
第13行里调用函数NavigateToPendingEntry,下一次再来分析它的功能。
继续上一次分析到类WebContents的Navigate函数,在这个函数里通过参数entry传送入来,这样它只需要根据这个参数去下载网页回来显示,应就可以了吧,但到底是怎么样工作的呢?这需要深入去分析它,才知道它是什么样的结果。
#001
#002 bool WebContents::Navigate(const NavigationEntry& entry, bool reload) {
从渲染显示管理器里获取当前连接渲染显示对象。
#003 RenderViewHost* dest_render_view_host = render_manager_.Navigate(entry);
#004
设置开始下载计时的时钟。
#005 // Used for page load time metrics.
#006 current_load_start_ = TimeTicks::Now();
#007
在渲染显示对象里进行浏览处理。
#008 // Navigate in the desired RenderViewHost
#009 dest_render_view_host->NavigateToEntry(entry, reload);
#010
#011 if (entry.page_id() == -1) {
#012 // HACK!! This code suppresses JavaScript: URLs from being added to
#013 // session history, which is what we want to do for javascript: URLs that
#014 // do not generate content. What we really need is a message from the
#015 // renderer telling us that a new page was not created. The same message
#016 // could be used for mailto: URLs and the like.
#017 if (entry.url().SchemeIs("javascript"))
#018 return false;
#019 }
#020
判断是否重新加载旧的连接处理。
#021 if (reload && !profile()->IsOffTheRecord()) {
#022 HistoryService* history =
#023 profile()->GetHistoryService(Profile::IMPLICIT_ACCESS);
#024 if (history)
#025 history->SetFavIconOutOfDateForPage(entry.url());
#026 }
#027
#028 return true;
#029 }
在这个函数最主要的工作,就是调用类RenderViewHost函数NavigateToEntry,这个函数的代码如下:
#001 void RenderViewHost::NavigateToEntry(const NavigationEntry& entry,
#002 bool is_reload) {
创建浏览参数。
#003 ViewMsg_Navigate_Params params;
#004 MakeNavigateParams(entry, is_reload, ¶ms);
#005
授权渲染进程可以显示这个连接。
#006 RendererSecurityPolicy::GetInstance()->GrantRequestURL(
#007 process()->host_id(), params.url);
#008
发送浏览下载连接参数给进程处理。
#009 DoNavigate(new ViewMsg_Navigate(routing_id_, params));
#010
更新列表计数。
#011 UpdateBackForwardListCount();
#012 }
在这个函数里,主要创建浏览参数,然后调用函数DoNavigate来发送一个消息ViewMsg_Navigate给RHV进程,在UpdateBackForwardListCount函数里也发送一个消息ViewMsg_UpdateBackForwardListCount给RHV进程。
继续分析函数DoNavigate:
#001 void RenderViewHost::DoNavigate(ViewMsg_Navigate* nav_message) {
#002 // Only send the message if we aren't suspended at the start of a cross-site
#003 // request.
如果已经挂起,就开始重新复位这个消息。
#004 if (navigations_suspended_) {
#005 // Shouldn't be possible to have a second navigation while suspended, since
#006 // navigations will only be suspended during a cross-site request. If a
#007 // second navigation occurs, WebContents will cancel this pending RVH
#008 // create a new pending RVH.
#009 DCHECK(!suspended_nav_message_.get());
#010 suspended_nav_message_.reset(nav_message);
#011 } else {
或者直接发送这个消息出去。
#012 Send(nav_message);
#013 }
#014 }
函数UpdateBackForwardListCount的代码如下:
#001 void RenderViewHost::UpdateBackForwardListCount() {
#002 int back_list_count, forward_list_count;
#003 delegate_->GetHistoryListCount(&back_list_count, &forward_list_count);
#004 Send(new ViewMsg_UpdateBackForwardListCount(
#005 routing_id_, back_list_count, forward_list_count));
#006 }
可以从函数DoNavigate和UpdateBackForwardListCount里看到,最后都把这些事件变成消息,通过类RenderProcessHost来发送出去,主要使用IPC的通讯机制。具体是怎么样通讯的呢?下一次再来分析它。
上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只能通过代码的分析来理解它的实现,代码如下:
这个函数的参数意思:
source是TAB内容。
url是网络连接地址。
disposition是窗口打开的位置。
transition是连接传送的类型。
override_encoding是编码类型。
#001 void Browser::OpenURLFromTab(TabContents* source,
#002 const GURL& url,
#003 WindowOpenDisposition disposition,
#004 PageTransition::Type transition,
#005 const std::string& override_encoding) {
调试时检查代码。
#006 // No code for these yet
#007 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
#008
获取当前的TAB页。
#009 TabContents* current_tab = source ? source : GetSelectedTabContents();
判断是否当前TAB页选中。
#010 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
#011 TabContents* new_contents = NULL;
#012
#013 // If the URL is part of the same web site, then load it in the same
#014 // SiteInstance (and thus the same process). This is an optimization to
#015 // reduce process overhead; it is not necessary for compatibility. (That is,
#016 // the new tab will not have script connections to the previous tab, so it
#017 // does not need to be part of the same SiteInstance or BrowsingInstance.)
#018 // Default to loading in a new SiteInstance and BrowsingInstance.
#019 // TODO(creis): should this apply to applications?
保存打开连接的实例指针。
#020 SiteInstance* instance = NULL;
如果不使用每个TAB页一个进程的方式,就不需要进行下面的处理。因为同一个连接在一个进程里打开是比较快,这里主要做优化。
#021 // Don't use this logic when "--process-per-tab" is specified.
判断是否有每一个TAB一个进程的方式。
#022 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
有当前进程页。
#023 if (current_tab) {
#024 const WebContents* const web_contents = current_tab->AsWebContents();
判断是否相同的网络连接地址。
#025 if (web_contents) {
#026 const GURL& current_url = web_contents->GetURL();
如果相同的网络地址,并且有实例打开,就返回这个实例在instance。
#027 if (SiteInstance::IsSameWebSite(current_url, url))
#028 instance = web_contents->site_instance();
#029 }
#030 }
#031 }
#032
#033 // If this is an application we can only have one tab so a new tab always
#034 // goes into a tabbed browser window.
下面进行不打开新窗口的处理。
#035 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
#036 // If the disposition is OFF_THE_RECORD we don't want to create a new
#037 // browser that will itself create another OTR browser. This will result in
#038 // a browser leak (and crash below because no tab is created or selected).
#039 if (disposition == OFF_THE_RECORD) {
#040 OpenURLOffTheRecord(profile_, url);
#041 return;
#042 }
#043
#044 Browser* b = GetOrCreateTabbedBrowser();
#045 DCHECK(b);
#046
#047 // If we have just created a new browser window, make sure we select the
#048 // tab.
#049 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
#050 disposition = NEW_FOREGROUND_TAB;
#051
#052 b->OpenURL(url, disposition, transition);
#053 b->Show();
#054 b->MoveToFront(true);
#055 return;
#056 }
#057
#058 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
#059 disposition = NEW_FOREGROUND_TAB;
#060
这里开始处理打开一个新窗口显示网络连接。
#061 if (disposition == NEW_WINDOW) {
创建一个新的Browser浏览器对象。
#062 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
#063 BrowserType::TABBED_BROWSER, L"");
创建一个TAB内容。
#064 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
这里开始显示这个网络连接的内容。
#065 new_browser->Show();
#066 } else if ((disposition == CURRENT_TAB) && current_tab) {
下面开始在当前TAB页里打开连接,同时判断处理的类型。
#067 if (transition == PageTransition::TYPED ||
#068 transition == PageTransition::AUTO_BOOKMARK ||
#069 transition == PageTransition::GENERATED ||
#070 transition == PageTransition::START_PAGE) {
#071 // Don't forget the openers if this tab is a New Tab page opened at the
#072 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
#073 // navigation of one of these transition types before resetting the
#074 // opener relationships (this allows for the use case of opening a new
#075 // tab to do a quick look-up of something while viewing a tab earlier in
#076 // the strip). We can make this heuristic more permissive if need be.
#077 // TODO(beng): (http://b/1306495) write unit tests for this once this
#078 // object is unit-testable.
#079 int current_tab_index =
#080 tabstrip_model_.GetIndexOfTabContents(current_tab);
#081 bool forget_openers =
#082 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
#083 current_tab_index == (tab_count() - 1) &&
#084 current_tab->controller()->GetEntryCount() == 1);
#085 if (forget_openers) {
#086 // If the user navigates the current tab to another page in any way
#087 // other than by clicking a link, we want to pro-actively forget all
#088 // TabStrip opener relationships since we assume they're beginning a
#089 // different task by reusing the current tab.
#090 tabstrip_model_.ForgetAllOpeners();
#091 // In this specific case we also want to reset the group relationship,
#092 // since it is now technically invalid.
#093 tabstrip_model_.ForgetGroup(current_tab);
#094 }
#095 }
这里开始在当前TAB页里加载网络地址连接。
#096 current_tab->controller()->LoadURL(url, transition);
#097 // The TabContents might have changed as part of the navigation (ex: new tab
#098 // page can become WebContents).
获取当前显示的内容。
#099 new_contents = current_tab->controller()->active_contents();
隐藏最下面状态提示窗口。
#100 GetStatusBubble()->Hide();
#101
#102 // Synchronously update the location bar. This allows us to immediately
#103 // have the URL bar update when the user types something, rather than
#104 // going through the normal system of ScheduleUIUpdate which has a delay.
更新本地的工具条。
#105 UpdateToolBar(false);
后面的内容先不分析,主要分析目前打开当前连接的内容。
#106 } else if (disposition == OFF_THE_RECORD) {
#107 OpenURLOffTheRecord(profile_, url);
#108 return;
#109 } else if (disposition != SUPPRESS_OPEN) {
#110 new_contents =
#111 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
#112 instance);
#113 }
#114
#115 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
#116 // Give the focus to the newly navigated tab, if the source tab was
#117 // front-most.
#118 new_contents->Focus();
#119 }
#120
#121 if (!override_encoding.empty()) {
#122 // The new tab needs a special encoding, such as a view source page
#123 // which should use the same encoding as the original page.
#124 WebContents* web_contents = new_contents->AsWebContents();
#125 if (web_contents)
#126 web_contents->set_override_encoding(override_encoding);
#127 }
#128 }
#129
上面函数的过程是这样的:主要根据打开网页的方式来选择窗口,比如是创建新窗口,还是只是打开一个TAB页,然后在TAB页显示。在这里还做了同一个网页地址的优化,不让它打开两个相同的进程来处理。
Browser对象是创建一个浏览器对象,接着调用LoadURL函数来加载输入的网页连接,隐藏当前窗口最下面的加载状态条,更新当前窗口的状态条,就完成了这个函数对当前TAB页加载网页的功能。下一次再来分析LoadURL函数是怎么样打开网页连接了。
当输入的网页连接传送给LoadURL函数之后,还需要处理很多内容,其实这是由类NavigationController来管理的,NavigationController类主要就是管理加载网页、退回、前进等等控制。
#001 void NavigationController::LoadURL(const GURL& url,
#002 PageTransition::Type transition) {
#003 // The user initiated a load, we don't need to reload anymore.
#004 needs_reload_ = false;
#005
#006 NavigationEntry* entry = CreateNavigationEntry(url, transition);
#007
#008 LoadEntry(entry);
#009 }
这个函数的第一个参数url是网络连接地址,第二个参数transition是传送的类型。
第4行代码里设置不是重新加载。
第6行里创建了一个处理网页浏览的入口对象,它是由类NavigationEntry管理。
第8行里就调用函数LoadEntry来加载网页。
LoadEntry函数更进一步去加载网页的内容,它的代码如下:
#001 void NavigationController::LoadEntry(NavigationEntry* entry) {
#002 // When navigating to a new page, we don't know for sure if we will actually
#003 // end up leaving the current page. The new page load could for example
#004 // result in a download or a 'no content' response (e.g., a mailto: URL).
#005
#006 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
清除内部变量。
#007 DiscardPendingEntryInternal();
保存当前的入口对象。
#008 pending_entry_ = entry;
通知服务器有一个浏览器对象加入。
#009 NotificationService::current()->Notify(
#010 NOTIFY_NAV_ENTRY_PENDING,
#011 Source<NavigationController>(this),
#012 NotificationService::NoDetails());
下面开始进入加载网页的动作。
#013 NavigateToPendingEntry(false);
#014 }
第9行里的类NotificationService是使用OBSERVER的设计模式来实现一对多的显示关系。这个设计模式也是跟MVC与生具来的,显然设计这个浏览器代码的人,已经是对设计模式是专家式的人物了。
第13行里调用函数NavigateToPendingEntry,下一次再来分析它的功能。
版权声明:本文为博主原创文章,未经博主允许不得转载。
上一次介绍到把网页连接地址生成一个消息通过IPC机制把消息发送出去,那么IPC的通讯机制是怎么样的呢?又是发送给谁呢?
由于这个浏览器是使用多进程的架构来工作的,所以进程之间就需要相互交流,这种交流是就是通讯,可以从源码里看到它是使用IPC的机制来通讯,实际采用的技术,就是Windows的命名管道的方式。可以看到这段代码:
#001 bool Channel::CreatePipe(const wstring& channel_id, Mode mode) {
#002 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
#003 const wstring pipe_name = PipeName(channel_id);
#004 if (mode == MODE_SERVER) {
#005 SECURITY_ATTRIBUTES security_attributes = {0};
#006 security_attributes.bInheritHandle = FALSE;
#007 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
#008 if (!win_util::GetLogonSessionOnlyDACL(
#009 reinterpret_cast<SECURITY_DESCRIPTOR**>(
#010 &security_attributes.lpSecurityDescriptor))) {
#011 NOTREACHED();
#012 }
#013
#014 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
#015 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
#016 FILE_FLAG_FIRST_PIPE_INSTANCE,
#017 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
#018 1, // number of pipe instances
#019 BUF_SIZE, // output buffer size (XXX tune)
#020 BUF_SIZE, // input buffer size (XXX tune)
#021 5000, // timeout in milliseconds (XXX tune)
#022 &security_attributes);
#023 LocalFree(security_attributes.lpSecurityDescriptor);
#024 } else {
#025 pipe_ = CreateFileW(pipe_name.c_str(),
#026 GENERIC_READ | GENERIC_WRITE,
#027 0,
#028 NULL,
#029 OPEN_EXISTING,
#030 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
#031 FILE_FLAG_OVERLAPPED,
#032 NULL);
#033 }
上面这段代码通过WINDOWS API函数CreateNamedPipeW函数来创建命名管道的服务器端,而通过WINDOWS API函数CreateFileW来打开客户端,这样两个进程之间就建立起来通讯的管道,两个进程之间的消息就可以相互发送了。
在浏览网页连接的消息,就是通过IPC的机制,让类RenderProcessHost把消息发送出去,那么这个消息是谁在另一端接收的呢?按照IPC的机制可知是另外一个进程在接收,这个进程就是类RenderProcess。
类RenderProcessHost把所有的消息通过类IPC::ChannelProxy发送出去,在另一个子进程里通过类RenderThread和类RenderView来接收消息,然后在类RenderThread和类RenderView把消息分发处理。
from:
上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只能通过代码的分析来理解它的实现,代码如下:
这个函数的参数意思:
source是TAB内容。
url是网络连接地址。
disposition是窗口打开的位置。
transition是连接传送的类型。
override_encoding是编码类型。
#001 void Browser::OpenURLFromTab(TabContents* source,
#002 const GURL& url,
#003 WindowOpenDisposition disposition,
#004 PageTransition::Type transition,
#005 const std::string& override_encoding) {
调试时检查代码。
#006 // No code for these yet
#007 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
#008
获取当前的TAB页。
#009 TabContents* current_tab = source ? source : GetSelectedTabContents();
判断是否当前TAB页选中。
#010 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
#011 TabContents* new_contents = NULL;
#012
#013 // If the URL is part of the same web site, then load it in the same
#014 // SiteInstance (and thus the same process). This is an optimization to
#015 // reduce process overhead; it is not necessary for compatibility. (That is,
#016 // the new tab will not have script connections to the previous tab, so it
#017 // does not need to be part of the same SiteInstance or BrowsingInstance.)
#018 // Default to loading in a new SiteInstance and BrowsingInstance.
#019 // TODO(creis): should this apply to applications?
保存打开连接的实例指针。
#020 SiteInstance* instance = NULL;
如果不使用每个TAB页一个进程的方式,就不需要进行下面的处理。因为同一个连接在一个进程里打开是比较快,这里主要做优化。
#021 // Don't use this logic when "--process-per-tab" is specified.
判断是否有每一个TAB一个进程的方式。
#022 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
有当前进程页。
#023 if (current_tab) {
#024 const WebContents* const web_contents = current_tab->AsWebContents();
判断是否相同的网络连接地址。
#025 if (web_contents) {
#026 const GURL& current_url = web_contents->GetURL();
如果相同的网络地址,并且有实例打开,就返回这个实例在instance。
#027 if (SiteInstance::IsSameWebSite(current_url, url))
#028 instance = web_contents->site_instance();
#029 }
#030 }
#031 }
#032
#033 // If this is an application we can only have one tab so a new tab always
#034 // goes into a tabbed browser window.
下面进行不打开新窗口的处理。
#035 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
#036 // If the disposition is OFF_THE_RECORD we don't want to create a new
#037 // browser that will itself create another OTR browser. This will result in
#038 // a browser leak (and crash below because no tab is created or selected).
#039 if (disposition == OFF_THE_RECORD) {
#040 OpenURLOffTheRecord(profile_, url);
#041 return;
#042 }
#043
#044 Browser* b = GetOrCreateTabbedBrowser();
#045 DCHECK(b);
#046
#047 // If we have just created a new browser window, make sure we select the
#048 // tab.
#049 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
#050 disposition = NEW_FOREGROUND_TAB;
#051
#052 b->OpenURL(url, disposition, transition);
#053 b->Show();
#054 b->MoveToFront(true);
#055 return;
#056 }
#057
#058 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
#059 disposition = NEW_FOREGROUND_TAB;
#060
这里开始处理打开一个新窗口显示网络连接。
#061 if (disposition == NEW_WINDOW) {
创建一个新的Browser浏览器对象。
#062 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
#063 BrowserType::TABBED_BROWSER, L"");
创建一个TAB内容。
#064 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
这里开始显示这个网络连接的内容。
#065 new_browser->Show();
#066 } else if ((disposition == CURRENT_TAB) && current_tab) {
下面开始在当前TAB页里打开连接,同时判断处理的类型。
#067 if (transition == PageTransition::TYPED ||
#068 transition == PageTransition::AUTO_BOOKMARK ||
#069 transition == PageTransition::GENERATED ||
#070 transition == PageTransition::START_PAGE) {
#071 // Don't forget the openers if this tab is a New Tab page opened at the
#072 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
#073 // navigation of one of these transition types before resetting the
#074 // opener relationships (this allows for the use case of opening a new
#075 // tab to do a quick look-up of something while viewing a tab earlier in
#076 // the strip). We can make this heuristic more permissive if need be.
#077 // TODO(beng): (http://b/1306495) write unit tests for this once this
#078 // object is unit-testable.
#079 int current_tab_index =
#080 tabstrip_model_.GetIndexOfTabContents(current_tab);
#081 bool forget_openers =
#082 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
#083 current_tab_index == (tab_count() - 1) &&
#084 current_tab->controller()->GetEntryCount() == 1);
#085 if (forget_openers) {
#086 // If the user navigates the current tab to another page in any way
#087 // other than by clicking a link, we want to pro-actively forget all
#088 // TabStrip opener relationships since we assume they're beginning a
#089 // different task by reusing the current tab.
#090 tabstrip_model_.ForgetAllOpeners();
#091 // In this specific case we also want to reset the group relationship,
#092 // since it is now technically invalid.
#093 tabstrip_model_.ForgetGroup(current_tab);
#094 }
#095 }
这里开始在当前TAB页里加载网络地址连接。
#096 current_tab->controller()->LoadURL(url, transition);
#097 // The TabContents might have changed as part of the navigation (ex: new tab
#098 // page can become WebContents).
获取当前显示的内容。
#099 new_contents = current_tab->controller()->active_contents();
隐藏最下面状态提示窗口。
#100 GetStatusBubble()->Hide();
#101
#102 // Synchronously update the location bar. This allows us to immediately
#103 // have the URL bar update when the user types something, rather than
#104 // going through the normal system of ScheduleUIUpdate which has a delay.
更新本地的工具条。
#105 UpdateToolBar(false);
后面的内容先不分析,主要分析目前打开当前连接的内容。
#106 } else if (disposition == OFF_THE_RECORD) {
#107 OpenURLOffTheRecord(profile_, url);
#108 return;
#109 } else if (disposition != SUPPRESS_OPEN) {
#110 new_contents =
#111 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
#112 instance);
#113 }
#114
#115 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
#116 // Give the focus to the newly navigated tab, if the source tab was
#117 // front-most.
#118 new_contents->Focus();
#119 }
#120
#121 if (!override_encoding.empty()) {
#122 // The new tab needs a special encoding, such as a view source page
#123 // which should use the same encoding as the original page.
#124 WebContents* web_contents = new_contents->AsWebContents();
#125 if (web_contents)
#126 web_contents->set_override_encoding(override_encoding);
#127 }
#128 }
#129
上面函数的过程是这样的:主要根据打开网页的方式来选择窗口,比如是创建新窗口,还是只是打开一个TAB页,然后在TAB页显示。在这里还做了同一个网页地址的优化,不让它打开两个相同的进程来处理。
Browser对象是创建一个浏览器对象,接着调用LoadURL函数来加载输入的网页连接,隐藏当前窗口最下面的加载状态条,更新当前窗口的状态条,就完成了这个函数对当前TAB页加载网页的功能。下一次再来分析LoadURL函数是怎么样打开网页连接了。
当输入的网页连接传送给LoadURL函数之后,还需要处理很多内容,其实这是由类NavigationController来管理的,NavigationController类主要就是管理加载网页、退回、前进等等控制。
#001 void NavigationController::LoadURL(const GURL& url,
#002 PageTransition::Type transition) {
#003 // The user initiated a load, we don't need to reload anymore.
#004 needs_reload_ = false;
#005
#006 NavigationEntry* entry = CreateNavigationEntry(url, transition);
#007
#008 LoadEntry(entry);
#009 }
这个函数的第一个参数url是网络连接地址,第二个参数transition是传送的类型。
第4行代码里设置不是重新加载。
第6行里创建了一个处理网页浏览的入口对象,它是由类NavigationEntry管理。
第8行里就调用函数LoadEntry来加载网页。
LoadEntry函数更进一步去加载网页的内容,它的代码如下:
#001 void NavigationController::LoadEntry(NavigationEntry* entry) {
#002 // When navigating to a new page, we don't know for sure if we will actually
#003 // end up leaving the current page. The new page load could for example
#004 // result in a download or a 'no content' response (e.g., a mailto: URL).
#005
#006 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
清除内部变量。
#007 DiscardPendingEntryInternal();
保存当前的入口对象。
#008 pending_entry_ = entry;
通知服务器有一个浏览器对象加入。
#009 NotificationService::current()->Notify(
#010 NOTIFY_NAV_ENTRY_PENDING,
#011 Source<NavigationController>(this),
#012 NotificationService::NoDetails());
下面开始进入加载网页的动作。
#013 NavigateToPendingEntry(false);
#014 }
第9行里的类NotificationService是使用OBSERVER的设计模式来实现一对多的显示关系。这个设计模式也是跟MVC与生具来的,显然设计这个浏览器代码的人,已经是对设计模式是专家式的人物了。
第13行里调用函数NavigateToPendingEntry,下一次再来分析它的功能。
版权声明:本文为博主原创文章,未经博主允许不得转载。
上一次介绍到把网页连接地址生成一个消息通过IPC机制把消息发送出去,那么IPC的通讯机制是怎么样的呢?又是发送给谁呢?
由于这个浏览器是使用多进程的架构来工作的,所以进程之间就需要相互交流,这种交流是就是通讯,可以从源码里看到它是使用IPC的机制来通讯,实际采用的技术,就是Windows的命名管道的方式。可以看到这段代码:
#001 bool Channel::CreatePipe(const wstring& channel_id, Mode mode) {
#002 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
#003 const wstring pipe_name = PipeName(channel_id);
#004 if (mode == MODE_SERVER) {
#005 SECURITY_ATTRIBUTES security_attributes = {0};
#006 security_attributes.bInheritHandle = FALSE;
#007 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
#008 if (!win_util::GetLogonSessionOnlyDACL(
#009 reinterpret_cast<SECURITY_DESCRIPTOR**>(
#010 &security_attributes.lpSecurityDescriptor))) {
#011 NOTREACHED();
#012 }
#013
#014 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
#015 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
#016 FILE_FLAG_FIRST_PIPE_INSTANCE,
#017 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
#018 1, // number of pipe instances
#019 BUF_SIZE, // output buffer size (XXX tune)
#020 BUF_SIZE, // input buffer size (XXX tune)
#021 5000, // timeout in milliseconds (XXX tune)
#022 &security_attributes);
#023 LocalFree(security_attributes.lpSecurityDescriptor);
#024 } else {
#025 pipe_ = CreateFileW(pipe_name.c_str(),
#026 GENERIC_READ | GENERIC_WRITE,
#027 0,
#028 NULL,
#029 OPEN_EXISTING,
#030 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
#031 FILE_FLAG_OVERLAPPED,
#032 NULL);
#033 }
上面这段代码通过WINDOWS API函数CreateNamedPipeW函数来创建命名管道的服务器端,而通过WINDOWS API函数CreateFileW来打开客户端,这样两个进程之间就建立起来通讯的管道,两个进程之间的消息就可以相互发送了。
在浏览网页连接的消息,就是通过IPC的机制,让类RenderProcessHost把消息发送出去,那么这个消息是谁在另一端接收的呢?按照IPC的机制可知是另外一个进程在接收,这个进程就是类RenderProcess。
类RenderProcessHost把所有的消息通过类IPC::ChannelProxy发送出去,在另一个子进程里通过类RenderThread和类RenderView来接收消息,然后在类RenderThread和类RenderView把消息分发处理。
上一次说到需要调用这个OpenURLFromTab函数,那么这个函数是做什么的呢?从名称上可能猜到它是打开网页,但是是从目前TAB页里打开呢?还是新建一个?或者使用每个TAB页一个进程呢?这些疑惑,只能通过代码的分析来理解它的实现,代码如下:
这个函数的参数意思:
source是TAB内容。
url是网络连接地址。
disposition是窗口打开的位置。
transition是连接传送的类型。
override_encoding是编码类型。
#001 void Browser::OpenURLFromTab(TabContents* source,
#002 const GURL& url,
#003 WindowOpenDisposition disposition,
#004 PageTransition::Type transition,
#005 const std::string& override_encoding) {
调试时检查代码。
#006 // No code for these yet
#007 DCHECK((disposition != NEW_POPUP) && (disposition != SAVE_TO_DISK));
#008
获取当前的TAB页。
#009 TabContents* current_tab = source ? source : GetSelectedTabContents();
判断是否当前TAB页选中。
#010 bool source_tab_was_frontmost = (current_tab == GetSelectedTabContents());
#011 TabContents* new_contents = NULL;
#012
#013 // If the URL is part of the same web site, then load it in the same
#014 // SiteInstance (and thus the same process). This is an optimization to
#015 // reduce process overhead; it is not necessary for compatibility. (That is,
#016 // the new tab will not have script connections to the previous tab, so it
#017 // does not need to be part of the same SiteInstance or BrowsingInstance.)
#018 // Default to loading in a new SiteInstance and BrowsingInstance.
#019 // TODO(creis): should this apply to applications?
保存打开连接的实例指针。
#020 SiteInstance* instance = NULL;
如果不使用每个TAB页一个进程的方式,就不需要进行下面的处理。因为同一个连接在一个进程里打开是比较快,这里主要做优化。
#021 // Don't use this logic when "--process-per-tab" is specified.
判断是否有每一个TAB一个进程的方式。
#022 if (!CommandLine().HasSwitch(switches::kProcessPerTab)) {
有当前进程页。
#023 if (current_tab) {
#024 const WebContents* const web_contents = current_tab->AsWebContents();
判断是否相同的网络连接地址。
#025 if (web_contents) {
#026 const GURL& current_url = web_contents->GetURL();
如果相同的网络地址,并且有实例打开,就返回这个实例在instance。
#027 if (SiteInstance::IsSameWebSite(current_url, url))
#028 instance = web_contents->site_instance();
#029 }
#030 }
#031 }
#032
#033 // If this is an application we can only have one tab so a new tab always
#034 // goes into a tabbed browser window.
下面进行不打开新窗口的处理。
#035 if (disposition != NEW_WINDOW && type_ == BrowserType::APPLICATION) {
#036 // If the disposition is OFF_THE_RECORD we don't want to create a new
#037 // browser that will itself create another OTR browser. This will result in
#038 // a browser leak (and crash below because no tab is created or selected).
#039 if (disposition == OFF_THE_RECORD) {
#040 OpenURLOffTheRecord(profile_, url);
#041 return;
#042 }
#043
#044 Browser* b = GetOrCreateTabbedBrowser();
#045 DCHECK(b);
#046
#047 // If we have just created a new browser window, make sure we select the
#048 // tab.
#049 if (b->tab_count() == 0 && disposition == NEW_BACKGROUND_TAB)
#050 disposition = NEW_FOREGROUND_TAB;
#051
#052 b->OpenURL(url, disposition, transition);
#053 b->Show();
#054 b->MoveToFront(true);
#055 return;
#056 }
#057
#058 if (profile_->IsOffTheRecord() && disposition == OFF_THE_RECORD)
#059 disposition = NEW_FOREGROUND_TAB;
#060
这里开始处理打开一个新窗口显示网络连接。
#061 if (disposition == NEW_WINDOW) {
创建一个新的Browser浏览器对象。
#062 Browser* new_browser = new Browser(gfx::Rect(), SW_SHOWNORMAL, profile_,
#063 BrowserType::TABBED_BROWSER, L"");
创建一个TAB内容。
#064 new_contents = new_browser->AddTabWithURL(url, transition, true, instance);
这里开始显示这个网络连接的内容。
#065 new_browser->Show();
#066 } else if ((disposition == CURRENT_TAB) && current_tab) {
下面开始在当前TAB页里打开连接,同时判断处理的类型。
#067 if (transition == PageTransition::TYPED ||
#068 transition == PageTransition::AUTO_BOOKMARK ||
#069 transition == PageTransition::GENERATED ||
#070 transition == PageTransition::START_PAGE) {
#071 // Don't forget the openers if this tab is a New Tab page opened at the
#072 // end of the TabStrip (e.g. by pressing Ctrl+T). Give the user one
#073 // navigation of one of these transition types before resetting the
#074 // opener relationships (this allows for the use case of opening a new
#075 // tab to do a quick look-up of something while viewing a tab earlier in
#076 // the strip). We can make this heuristic more permissive if need be.
#077 // TODO(beng): (http://b/1306495) write unit tests for this once this
#078 // object is unit-testable.
#079 int current_tab_index =
#080 tabstrip_model_.GetIndexOfTabContents(current_tab);
#081 bool forget_openers =
#082 !(current_tab->type() == TAB_CONTENTS_NEW_TAB_UI &&
#083 current_tab_index == (tab_count() - 1) &&
#084 current_tab->controller()->GetEntryCount() == 1);
#085 if (forget_openers) {
#086 // If the user navigates the current tab to another page in any way
#087 // other than by clicking a link, we want to pro-actively forget all
#088 // TabStrip opener relationships since we assume they're beginning a
#089 // different task by reusing the current tab.
#090 tabstrip_model_.ForgetAllOpeners();
#091 // In this specific case we also want to reset the group relationship,
#092 // since it is now technically invalid.
#093 tabstrip_model_.ForgetGroup(current_tab);
#094 }
#095 }
这里开始在当前TAB页里加载网络地址连接。
#096 current_tab->controller()->LoadURL(url, transition);
#097 // The TabContents might have changed as part of the navigation (ex: new tab
#098 // page can become WebContents).
获取当前显示的内容。
#099 new_contents = current_tab->controller()->active_contents();
隐藏最下面状态提示窗口。
#100 GetStatusBubble()->Hide();
#101
#102 // Synchronously update the location bar. This allows us to immediately
#103 // have the URL bar update when the user types something, rather than
#104 // going through the normal system of ScheduleUIUpdate which has a delay.
更新本地的工具条。
#105 UpdateToolBar(false);
后面的内容先不分析,主要分析目前打开当前连接的内容。
#106 } else if (disposition == OFF_THE_RECORD) {
#107 OpenURLOffTheRecord(profile_, url);
#108 return;
#109 } else if (disposition != SUPPRESS_OPEN) {
#110 new_contents =
#111 AddTabWithURL(url, transition, disposition != NEW_BACKGROUND_TAB,
#112 instance);
#113 }
#114
#115 if (disposition != NEW_BACKGROUND_TAB && source_tab_was_frontmost) {
#116 // Give the focus to the newly navigated tab, if the source tab was
#117 // front-most.
#118 new_contents->Focus();
#119 }
#120
#121 if (!override_encoding.empty()) {
#122 // The new tab needs a special encoding, such as a view source page
#123 // which should use the same encoding as the original page.
#124 WebContents* web_contents = new_contents->AsWebContents();
#125 if (web_contents)
#126 web_contents->set_override_encoding(override_encoding);
#127 }
#128 }
#129
上面函数的过程是这样的:主要根据打开网页的方式来选择窗口,比如是创建新窗口,还是只是打开一个TAB页,然后在TAB页显示。在这里还做了同一个网页地址的优化,不让它打开两个相同的进程来处理。
Browser对象是创建一个浏览器对象,接着调用LoadURL函数来加载输入的网页连接,隐藏当前窗口最下面的加载状态条,更新当前窗口的状态条,就完成了这个函数对当前TAB页加载网页的功能。下一次再来分析LoadURL函数是怎么样打开网页连接了。
当输入的网页连接传送给LoadURL函数之后,还需要处理很多内容,其实这是由类NavigationController来管理的,NavigationController类主要就是管理加载网页、退回、前进等等控制。
#001 void NavigationController::LoadURL(const GURL& url,
#002 PageTransition::Type transition) {
#003 // The user initiated a load, we don't need to reload anymore.
#004 needs_reload_ = false;
#005
#006 NavigationEntry* entry = CreateNavigationEntry(url, transition);
#007
#008 LoadEntry(entry);
#009 }
这个函数的第一个参数url是网络连接地址,第二个参数transition是传送的类型。
第4行代码里设置不是重新加载。
第6行里创建了一个处理网页浏览的入口对象,它是由类NavigationEntry管理。
第8行里就调用函数LoadEntry来加载网页。
LoadEntry函数更进一步去加载网页的内容,它的代码如下:
#001 void NavigationController::LoadEntry(NavigationEntry* entry) {
#002 // When navigating to a new page, we don't know for sure if we will actually
#003 // end up leaving the current page. The new page load could for example
#004 // result in a download or a 'no content' response (e.g., a mailto: URL).
#005
#006 // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
清除内部变量。
#007 DiscardPendingEntryInternal();
保存当前的入口对象。
#008 pending_entry_ = entry;
通知服务器有一个浏览器对象加入。
#009 NotificationService::current()->Notify(
#010 NOTIFY_NAV_ENTRY_PENDING,
#011 Source<NavigationController>(this),
#012 NotificationService::NoDetails());
下面开始进入加载网页的动作。
#013 NavigateToPendingEntry(false);
#014 }
第9行里的类NotificationService是使用OBSERVER的设计模式来实现一对多的显示关系。这个设计模式也是跟MVC与生具来的,显然设计这个浏览器代码的人,已经是对设计模式是专家式的人物了。
第13行里调用函数NavigateToPendingEntry,下一次再来分析它的功能。
版权声明:本文为博主原创文章,未经博主允许不得转载。
上一次介绍到把网页连接地址生成一个消息通过IPC机制把消息发送出去,那么IPC的通讯机制是怎么样的呢?又是发送给谁呢?
由于这个浏览器是使用多进程的架构来工作的,所以进程之间就需要相互交流,这种交流是就是通讯,可以从源码里看到它是使用IPC的机制来通讯,实际采用的技术,就是Windows的命名管道的方式。可以看到这段代码:
#001 bool Channel::CreatePipe(const wstring& channel_id, Mode mode) {
#002 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
#003 const wstring pipe_name = PipeName(channel_id);
#004 if (mode == MODE_SERVER) {
#005 SECURITY_ATTRIBUTES security_attributes = {0};
#006 security_attributes.bInheritHandle = FALSE;
#007 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
#008 if (!win_util::GetLogonSessionOnlyDACL(
#009 reinterpret_cast<SECURITY_DESCRIPTOR**>(
#010 &security_attributes.lpSecurityDescriptor))) {
#011 NOTREACHED();
#012 }
#013
#014 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
#015 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
#016 FILE_FLAG_FIRST_PIPE_INSTANCE,
#017 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
#018 1, // number of pipe instances
#019 BUF_SIZE, // output buffer size (XXX tune)
#020 BUF_SIZE, // input buffer size (XXX tune)
#021 5000, // timeout in milliseconds (XXX tune)
#022 &security_attributes);
#023 LocalFree(security_attributes.lpSecurityDescriptor);
#024 } else {
#025 pipe_ = CreateFileW(pipe_name.c_str(),
#026 GENERIC_READ | GENERIC_WRITE,
#027 0,
#028 NULL,
#029 OPEN_EXISTING,
#030 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
#031 FILE_FLAG_OVERLAPPED,
#032 NULL);
#033 }
上面这段代码通过WINDOWS API函数CreateNamedPipeW函数来创建命名管道的服务器端,而通过WINDOWS API函数CreateFileW来打开客户端,这样两个进程之间就建立起来通讯的管道,两个进程之间的消息就可以相互发送了。
在浏览网页连接的消息,就是通过IPC的机制,让类RenderProcessHost把消息发送出去,那么这个消息是谁在另一端接收的呢?按照IPC的机制可知是另外一个进程在接收,这个进程就是类RenderProcess。
类RenderProcessHost把所有的消息通过类IPC::ChannelProxy发送出去,在另一个子进程里通过类RenderThread和类RenderView来接收消息,然后在类RenderThread和类RenderView把消息分发处理。