1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/singleton_tabs.h"
6 
7 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/search/search.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_list.h"
12 #include "chrome/browser/ui/browser_navigator.h"
13 #include "chrome/browser/ui/browser_navigator_params.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
15 #include "chrome/common/url_constants.h"
16 #include "content/public/browser/browser_url_handler.h"
17 #include "content/public/browser/web_contents.h"
18 
19 namespace {
20 
21 // Returns true if two URLs are equal after taking |replacements| into account.
CompareURLsWithReplacements(const GURL & url,const GURL & other,const url::Replacements<char> & replacements,ChromeAutocompleteProviderClient * client)22 bool CompareURLsWithReplacements(const GURL& url,
23                                  const GURL& other,
24                                  const url::Replacements<char>& replacements,
25                                  ChromeAutocompleteProviderClient* client) {
26   GURL url_replaced = url.ReplaceComponents(replacements);
27   GURL other_replaced = other.ReplaceComponents(replacements);
28   return client->StrippedURLsAreEqual(url_replaced, other_replaced, nullptr);
29 }
30 
31 }  // namespace
32 
ShowSingletonTab(Browser * browser,const GURL & url)33 void ShowSingletonTab(Browser* browser, const GURL& url) {
34   NavigateParams params(GetSingletonTabNavigateParams(browser, url));
35   Navigate(&params);
36 }
37 
ShowSingletonTabRespectRef(Browser * browser,const GURL & url)38 void ShowSingletonTabRespectRef(Browser* browser, const GURL& url) {
39   NavigateParams params(GetSingletonTabNavigateParams(browser, url));
40   Navigate(&params);
41 }
42 
ShowSingletonTabOverwritingNTP(Browser * browser,NavigateParams params)43 void ShowSingletonTabOverwritingNTP(Browser* browser, NavigateParams params) {
44   DCHECK(browser);
45   DCHECK_EQ(params.disposition, WindowOpenDisposition::SINGLETON_TAB);
46   content::WebContents* contents =
47       browser->tab_strip_model()->GetActiveWebContents();
48   if (contents) {
49     const GURL& contents_url = contents->GetVisibleURL();
50     if (contents_url == chrome::kChromeUINewTabURL ||
51         search::IsInstantNTP(contents) || contents_url == url::kAboutBlankURL) {
52       int tab_index = GetIndexOfExistingTab(browser, params);
53       if (tab_index < 0) {
54         params.disposition = WindowOpenDisposition::CURRENT_TAB;
55       } else {
56         params.switch_to_singleton_tab =
57             browser->tab_strip_model()->GetWebContentsAt(tab_index);
58       }
59     }
60   }
61 
62   Navigate(&params);
63 }
64 
GetSingletonTabNavigateParams(Browser * browser,const GURL & url)65 NavigateParams GetSingletonTabNavigateParams(Browser* browser,
66                                              const GURL& url) {
67   NavigateParams params(browser, url, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
68   params.disposition = WindowOpenDisposition::SINGLETON_TAB;
69   params.window_action = NavigateParams::SHOW_WINDOW;
70   params.user_gesture = true;
71   params.tabstrip_add_types |= TabStripModel::ADD_INHERIT_OPENER;
72   return params;
73 }
74 
75 // Returns the index of an existing singleton tab in |browser| matching
76 // the URL specified in |params|.
GetIndexOfExistingTab(Browser * browser,const NavigateParams & params)77 int GetIndexOfExistingTab(Browser* browser, const NavigateParams& params) {
78   if (params.disposition != WindowOpenDisposition::SINGLETON_TAB &&
79       params.disposition != WindowOpenDisposition::SWITCH_TO_TAB)
80     return -1;
81 
82   // In case the URL was rewritten by the BrowserURLHandler we need to ensure
83   // that we do not open another URL that will get redirected to the rewritten
84   // URL.
85   GURL rewritten_url(params.url);
86   content::BrowserURLHandler::GetInstance()->RewriteURLIfNecessary(
87       &rewritten_url, browser->profile());
88 
89   ChromeAutocompleteProviderClient client(browser->profile());
90   // If there are several matches: prefer the active tab by starting there.
91   int start_index = std::max(0, browser->tab_strip_model()->active_index());
92   int tab_count = browser->tab_strip_model()->count();
93   for (int i = 0; i < tab_count; ++i) {
94     int tab_index = (start_index + i) % tab_count;
95     content::WebContents* tab =
96         browser->tab_strip_model()->GetWebContentsAt(tab_index);
97 
98     GURL tab_url = tab->GetVisibleURL();
99 
100     // Skip view-source tabs. This is needed because RewriteURLIfNecessary
101     // removes the "view-source:" scheme which leads to incorrect matching.
102     if (tab_url.SchemeIs(content::kViewSourceScheme))
103       continue;
104 
105     GURL rewritten_tab_url = tab_url;
106     content::BrowserURLHandler::GetInstance()->RewriteURLIfNecessary(
107         &rewritten_tab_url, browser->profile());
108 
109     url::Replacements<char> replacements;
110     replacements.ClearRef();
111     if (params.path_behavior == NavigateParams::IGNORE_AND_NAVIGATE) {
112       replacements.ClearPath();
113       replacements.ClearQuery();
114     }
115 
116     if (CompareURLsWithReplacements(tab_url, params.url, replacements,
117                                     &client) ||
118         CompareURLsWithReplacements(rewritten_tab_url, rewritten_url,
119                                     replacements, &client)) {
120       return tab_index;
121     }
122   }
123 
124   return -1;
125 }
126 
GetIndexAndBrowserOfExistingTab(Profile * profile,const NavigateParams & params)127 std::pair<Browser*, int> GetIndexAndBrowserOfExistingTab(
128     Profile* profile,
129     const NavigateParams& params) {
130   for (auto browser_it = BrowserList::GetInstance()->begin_last_active();
131        browser_it != BrowserList::GetInstance()->end_last_active();
132        ++browser_it) {
133     Browser* browser = *browser_it;
134     // When tab switching, only look at same profile and anonymity level.
135     if (profile == browser->profile()) {
136       int index = GetIndexOfExistingTab(browser, params);
137       if (index >= 0)
138         return {browser, index};
139     }
140   }
141   return {nullptr, -1};
142 }
143