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