OLD | NEW |
| (Empty) |
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/tabs/tab_finder.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/command_line.h" | |
10 #include "base/stl_util.h" | |
11 #include "chrome/browser/history/history.h" | |
12 #include "chrome/browser/history/history_service_factory.h" | |
13 #include "chrome/browser/profiles/profile.h" | |
14 #include "chrome/browser/ui/browser.h" | |
15 #include "chrome/browser/ui/browser_list.h" | |
16 #include "chrome/browser/ui/tab_contents/tab_contents.h" | |
17 #include "chrome/common/chrome_notification_types.h" | |
18 #include "chrome/common/chrome_switches.h" | |
19 #include "content/public/browser/navigation_details.h" | |
20 #include "content/public/browser/navigation_entry.h" | |
21 #include "content/public/browser/notification_service.h" | |
22 #include "content/public/browser/notification_source.h" | |
23 #include "content/public/browser/web_contents.h" | |
24 #include "content/public/browser/web_contents_observer.h" | |
25 #include "content/public/common/frame_navigate_params.h" | |
26 #include "content/public/common/page_transition_types.h" | |
27 | |
28 using content::NavigationEntry; | |
29 using content::WebContents; | |
30 | |
31 class TabFinder::WebContentsObserverImpl : public content::WebContentsObserver { | |
32 public: | |
33 WebContentsObserverImpl(WebContents* tab, TabFinder* finder); | |
34 virtual ~WebContentsObserverImpl(); | |
35 | |
36 WebContents* web_contents() { | |
37 return content::WebContentsObserver::web_contents(); | |
38 } | |
39 | |
40 // content::WebContentsObserver overrides: | |
41 virtual void DidNavigateAnyFrame( | |
42 const content::LoadCommittedDetails& details, | |
43 const content::FrameNavigateParams& params) OVERRIDE; | |
44 virtual void WebContentsDestroyed(WebContents* tab) OVERRIDE; | |
45 | |
46 private: | |
47 TabFinder* finder_; | |
48 | |
49 DISALLOW_COPY_AND_ASSIGN(WebContentsObserverImpl); | |
50 }; | |
51 | |
52 TabFinder::WebContentsObserverImpl::WebContentsObserverImpl( | |
53 WebContents* tab, | |
54 TabFinder* finder) | |
55 : content::WebContentsObserver(tab), | |
56 finder_(finder) { | |
57 } | |
58 | |
59 TabFinder::WebContentsObserverImpl::~WebContentsObserverImpl() { | |
60 } | |
61 | |
62 void TabFinder::WebContentsObserverImpl::DidNavigateAnyFrame( | |
63 const content::LoadCommittedDetails& details, | |
64 const content::FrameNavigateParams& params) { | |
65 finder_->DidNavigateAnyFrame(web_contents(), details, params); | |
66 } | |
67 | |
68 void TabFinder::WebContentsObserverImpl::WebContentsDestroyed( | |
69 WebContents* tab) { | |
70 finder_->TabDestroyed(this); | |
71 delete this; | |
72 } | |
73 | |
74 // static | |
75 TabFinder* TabFinder::GetInstance() { | |
76 return IsEnabled() ? Singleton<TabFinder>::get() : NULL; | |
77 } | |
78 | |
79 // static | |
80 bool TabFinder::IsEnabled() { | |
81 return CommandLine::ForCurrentProcess()->HasSwitch( | |
82 switches::kFocusExistingTabOnOpen); | |
83 } | |
84 | |
85 WebContents* TabFinder::FindTab(Browser* browser, | |
86 const GURL& url, | |
87 Browser** existing_browser) { | |
88 if (browser->profile()->IsOffTheRecord()) | |
89 return NULL; | |
90 | |
91 // If the current tab matches the url, ignore it and let the user reload the | |
92 // existing tab. | |
93 WebContents* selected_tab = browser->GetActiveWebContents(); | |
94 if (TabMatchesURL(selected_tab, url)) | |
95 return NULL; | |
96 | |
97 // See if the current browser has a tab matching the specified url. | |
98 WebContents* tab_in_browser = FindTabInBrowser(browser, url); | |
99 if (tab_in_browser) { | |
100 *existing_browser = browser; | |
101 return tab_in_browser; | |
102 } | |
103 | |
104 // Then check other browsers. | |
105 for (BrowserList::const_iterator i = BrowserList::begin(); | |
106 i != BrowserList::end(); ++i) { | |
107 if (!(*i)->profile()->IsOffTheRecord() && | |
108 (*i)->profile()->IsSameProfile(browser->profile())) { | |
109 tab_in_browser = FindTabInBrowser(*i, url); | |
110 if (tab_in_browser) { | |
111 *existing_browser = *i; | |
112 return tab_in_browser; | |
113 } | |
114 } | |
115 } | |
116 | |
117 return NULL; | |
118 } | |
119 | |
120 void TabFinder::Observe(int type, | |
121 const content::NotificationSource& source, | |
122 const content::NotificationDetails& details) { | |
123 DCHECK_EQ(type, chrome::NOTIFICATION_TAB_PARENTED); | |
124 | |
125 // The tab was added to a browser. Query for its state now. | |
126 TabContents* tab = content::Source<TabContents>(source).ptr(); | |
127 TrackTab(tab->web_contents()); | |
128 } | |
129 | |
130 TabFinder::TabFinder() { | |
131 registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED, | |
132 content::NotificationService::AllSources()); | |
133 } | |
134 | |
135 TabFinder::~TabFinder() { | |
136 STLDeleteElements(&tab_contents_observers_); | |
137 } | |
138 | |
139 void TabFinder::DidNavigateAnyFrame( | |
140 WebContents* source, | |
141 const content::LoadCommittedDetails& details, | |
142 const content::FrameNavigateParams& params) { | |
143 CancelRequestsFor(source); | |
144 | |
145 if (content::PageTransitionIsRedirect(params.transition)) { | |
146 // If this is a redirect, we need to go to the db to get the start. | |
147 FetchRedirectStart(source); | |
148 } else if (params.redirects.size() > 1 || | |
149 params.redirects[0] != details.entry->GetURL()) { | |
150 web_contents_to_url_[source] = params.redirects[0]; | |
151 } | |
152 } | |
153 | |
154 bool TabFinder::TabMatchesURL(WebContents* tab_contents, const GURL& url) { | |
155 if (tab_contents->GetURL() == url) | |
156 return true; | |
157 | |
158 WebContentsToURLMap::const_iterator i = | |
159 web_contents_to_url_.find(tab_contents); | |
160 return i != web_contents_to_url_.end() && i->second == url; | |
161 } | |
162 | |
163 WebContents* TabFinder::FindTabInBrowser(Browser* browser, const GURL& url) { | |
164 if (!browser->is_type_tabbed()) | |
165 return NULL; | |
166 | |
167 for (int i = 0; i < browser->tab_count(); ++i) { | |
168 if (TabMatchesURL(browser->GetWebContentsAt(i), url)) | |
169 return browser->GetWebContentsAt(i); | |
170 } | |
171 return NULL; | |
172 } | |
173 | |
174 void TabFinder::TrackTab(WebContents* tab) { | |
175 for (WebContentsObservers::const_iterator i = tab_contents_observers_.begin(); | |
176 i != tab_contents_observers_.end(); ++i) { | |
177 if ((*i)->web_contents() == tab) { | |
178 // Already tracking the tab. | |
179 return; | |
180 } | |
181 } | |
182 WebContentsObserverImpl* observer = new WebContentsObserverImpl(tab, this); | |
183 tab_contents_observers_.insert(observer); | |
184 FetchRedirectStart(tab); | |
185 } | |
186 | |
187 void TabFinder::TabDestroyed(WebContentsObserverImpl* observer) { | |
188 DCHECK_GT(tab_contents_observers_.count(observer), 0u); | |
189 tab_contents_observers_.erase(observer); | |
190 } | |
191 | |
192 void TabFinder::CancelRequestsFor(WebContents* web_contents) { | |
193 Profile* profile = | |
194 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
195 if (profile->IsOffTheRecord()) | |
196 return; | |
197 | |
198 web_contents_to_url_.erase(web_contents); | |
199 | |
200 HistoryService* history = | |
201 HistoryServiceFactory::GetForProfile(profile, | |
202 Profile::EXPLICIT_ACCESS); | |
203 if (history) { | |
204 CancelableRequestProvider::Handle request_handle; | |
205 if (callback_consumer_.GetFirstHandleForClientData(web_contents, | |
206 &request_handle)) { | |
207 history->CancelRequest(request_handle); | |
208 } | |
209 } | |
210 } | |
211 | |
212 void TabFinder::FetchRedirectStart(WebContents* tab) { | |
213 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); | |
214 if (profile->IsOffTheRecord()) | |
215 return; | |
216 | |
217 NavigationEntry* committed_entry = | |
218 tab->GetController().GetLastCommittedEntry(); | |
219 if (!committed_entry || committed_entry->GetURL().is_empty()) | |
220 return; | |
221 | |
222 HistoryService* history = | |
223 HistoryServiceFactory::GetForProfile(profile, | |
224 Profile::EXPLICIT_ACCESS); | |
225 if (history) { | |
226 CancelableRequestProvider::Handle request_handle = | |
227 history->QueryRedirectsTo( | |
228 committed_entry->GetURL(), | |
229 &callback_consumer_, | |
230 base::Bind(&TabFinder::QueryRedirectsToComplete, | |
231 base::Unretained(this))); | |
232 callback_consumer_.SetClientData(history, request_handle, tab); | |
233 } | |
234 } | |
235 | |
236 void TabFinder::QueryRedirectsToComplete(HistoryService::Handle handle, | |
237 GURL url, | |
238 bool success, | |
239 history::RedirectList* redirects) { | |
240 if (success && !redirects->empty()) { | |
241 WebContents* web_contents = | |
242 callback_consumer_.GetClientDataForCurrentRequest(); | |
243 DCHECK(web_contents); | |
244 web_contents_to_url_[web_contents] = redirects->back(); | |
245 } | |
246 } | |
OLD | NEW |