上一次说到需要调用这个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, &params);

#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,下一次再来分析它的功能。

谷歌浏览器的源码分析(20)

标签: 浏览器securityattributesdescriptorwindows通讯
2008-10-04 13:38 6124人阅读 评论(2) 收藏 举报
 分类:
谷歌浏览器(31) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

上一次介绍到把网页连接地址生成一个消息通过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,下一次再来分析它的功能。

谷歌浏览器的源码分析(20)

标签: 浏览器securityattributesdescriptorwindows通讯
2008-10-04 13:38 6124人阅读 评论(2) 收藏 举报
 分类:
谷歌浏览器(31) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

上一次介绍到把网页连接地址生成一个消息通过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把消息分发处理。

http://blog.csdn.net/caimouse/article/details/3015345

上一次说到需要调用这个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,下一次再来分析它的功能。

谷歌浏览器的源码分析(20)

标签: 浏览器securityattributesdescriptorwindows通讯
2008-10-04 13:38 6124人阅读 评论(2) 收藏 举报

 分类:
谷歌浏览器(31) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

上一次介绍到把网页连接地址生成一个消息通过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把消息分发处理。

谷歌chrome浏览器的源码分析(四)相关推荐

  1. 谷歌chrome浏览器的源码分析(一)

    随着网络技术的发展,越来越多应用都已经离不开网络,特别像人类大脑一样的知识库的搜索引擎,更加是离不开功能强大的云计算.不过,即便云计算非常强大,但它还不能直接地把结果呈现给用户,这样就需要一个客户端来 ...

  2. 谷歌chrome浏览器的源码分析(五)

    上一次说到类RenderThread和类RenderView把消息处理,那么这两个类是怎么样处理消息的呢?又是怎么样处理浏览的消息呢?现在就带着这两个问题去分析它的源码,理解它处理消息的方法.类Ren ...

  3. 谷歌chrome浏览器的源码分析(六)

    消息的流通过程,是一个不同类相互交流的过程,如果不了解这个过程,根本就不知道这些类是怎么样相互协作的.由于上一次说到ViewHostMsg_RequestResource消息已经发送出来,它的处理过徎 ...

  4. 谷歌chrome浏览器的源码分析(二)

    前面已经介绍了这么引人的输入自动完成功能,并且可以在输入超级连接框里直接通过GOOGLE搜索所有的内容,这是比较大的创新,不但可以节省界面的占用面积,还很方便大家查询的需要,比如记不住的连接,根本不需 ...

  5. 谷歌chrome浏览器的源码分析(三)

    上一次介绍到怎么样从其它地方返回搜索到的超级连接,现在就来分析一下使用搜索引擎去查找的类SearchProvider,它是通过搜索引擎来查找出来的,在这里是通过GOOGLE搜索引擎来查找出来.它的声明 ...

  6. 谷歌chrome浏览器的源码分析(七)

    上一次说到通过管道把接收到的HTTP数据通知另一个线程处理,它不是直接发送数据过去,而是把数据在共享内存里的句柄发送过去,达到高效通讯的目的.下面就来分析资源处理进程里,接收到这个消息之后,做些什么处 ...

  7. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

  8. ABP源码分析四十七:ABP中的异常处理

    ABP源码分析四十七:ABP中的异常处理 参考文章: (1)ABP源码分析四十七:ABP中的异常处理 (2)https://www.cnblogs.com/1zhk/p/5538983.html (3 ...

  9. 【投屏】Scrcpy源码分析四(最终章 - Server篇)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

最新文章

  1. OpenCV中颜色分布直方图及其应用
  2. Linux ps aux指令詳解--转
  3. MATLAB-字符串
  4. python coroutine_笔记-python-coroutine
  5. python中一个汉字点3个字节? utf-8
  6. 规模化微服务——《微服务设计》读书笔记
  7. Java大新闻不断涌现:Java SE 6和OpenJDK
  8. android 动态生成fragment,Android动态加载fragment(fragment复用)
  9. 牙齿间隙变大怎么办_牙齿之间的间隙越来越大怎么办?
  10. pop3常用命令记录
  11. 计算鞍点(信息学奥赛一本通-T1122)
  12. 小汤学编程之JAVA基础day13——I/O流
  13. 2018CHD-ACM新生赛(正式赛)E.解救迷茫的草滩小王子
  14. 运筹作业题:一个正三角形平面,在三个角的部分减去一部分,然后沿着剪开部分折叠起来,使折叠后的三棱台体积最大
  15. php 检查数据库查询结果,php数据库连接、查询、显示结果的小例子
  16. 不想用宝塔面板建站,如何手动搭建LAMP环境安装wordpress
  17. JavaScript的DOM操作.
  18. python破解excel进入密码(密码字典)
  19. ApacheCN 活动汇总 2019.7.5
  20. MT6771平台简要了解

热门文章

  1. 董明珠的葫芦到底卖的什么药:董姐开店卖口罩
  2. 满足人工智能日益增长的要求
  3. linux添加硬盘分区设置柱面,linux 下添加新硬盘设备和硬盘分区格式化挂载使用磁盘配额限制...
  4. 白话Elasticsearch73_ES生产集群中的索引管理02
  5. Spring Boot2.x-07Spring Boot2.1.2整合Mybatis
  6. Spring Cache抽象-缓存管理器
  7. Oracle-OLAP和OLTP解读
  8. matlab抓取股票数据,Matlab经过sina web接口获取个数即时股票数据函数实现代码
  9. 复习笔记(四)——C++内联函数
  10. C语言——常见的字符串函数+内存操作函数的介绍及实现