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(¶ms);
36 }
37
ShowSingletonTabRespectRef(Browser * browser,const GURL & url)38 void ShowSingletonTabRespectRef(Browser* browser, const GURL& url) {
39 NavigateParams params(GetSingletonTabNavigateParams(browser, url));
40 Navigate(¶ms);
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(¶ms);
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