// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/tabs/tab_finder.h" #include "base/command_line.h" #include "base/stl_util-inl.h" #include "chrome/browser/history/history.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/common/chrome_switches.h" #include "content/browser/tab_contents/navigation_entry.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents_observer.h" #include "content/common/notification_service.h" #include "content/common/notification_source.h" #include "content/common/notification_type.h" #include "content/common/page_transition_types.h" #include "content/common/view_messages.h" class TabFinder::TabContentsObserverImpl : public TabContentsObserver { public: TabContentsObserverImpl(TabContents* tab, TabFinder* finder); virtual ~TabContentsObserverImpl(); TabContents* tab_contents() { return TabContentsObserver::tab_contents(); } // TabContentsObserver overrides: virtual void DidNavigateAnyFramePostCommit( const NavigationController::LoadCommittedDetails& details, const ViewHostMsg_FrameNavigate_Params& params) OVERRIDE; virtual void TabContentsDestroyed(TabContents* tab) OVERRIDE; private: TabFinder* finder_; DISALLOW_COPY_AND_ASSIGN(TabContentsObserverImpl); }; TabFinder::TabContentsObserverImpl::TabContentsObserverImpl( TabContents* tab, TabFinder* finder) : TabContentsObserver(tab), finder_(finder) { } TabFinder::TabContentsObserverImpl::~TabContentsObserverImpl() { } void TabFinder::TabContentsObserverImpl::DidNavigateAnyFramePostCommit( const NavigationController::LoadCommittedDetails& details, const ViewHostMsg_FrameNavigate_Params& params) { finder_->DidNavigateAnyFramePostCommit(tab_contents(), details, params); } void TabFinder::TabContentsObserverImpl::TabContentsDestroyed( TabContents* tab) { finder_->TabDestroyed(this); delete this; } // static TabFinder* TabFinder::GetInstance() { return IsEnabled() ? Singleton<TabFinder>::get() : NULL; } // static bool TabFinder::IsEnabled() { return CommandLine::ForCurrentProcess()->HasSwitch( switches::kFocusExistingTabOnOpen); } TabContents* TabFinder::FindTab(Browser* browser, const GURL& url, Browser** existing_browser) { if (browser->profile()->IsOffTheRecord()) return NULL; // If the current tab matches the url, ignore it and let the user reload the // existing tab. TabContents* selected_tab = browser->GetSelectedTabContents(); if (TabMatchesURL(selected_tab, url)) return NULL; // See if the current browser has a tab matching the specified url. TabContents* tab_in_browser = FindTabInBrowser(browser, url); if (tab_in_browser) { *existing_browser = browser; return tab_in_browser; } // Then check other browsers. for (BrowserList::const_iterator i = BrowserList::begin(); i != BrowserList::end(); ++i) { if (!(*i)->profile()->IsOffTheRecord()) { tab_in_browser = FindTabInBrowser(*i, url); if (tab_in_browser) { *existing_browser = *i; return tab_in_browser; } } } return NULL; } void TabFinder::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { DCHECK_EQ(type.value, NotificationType::TAB_PARENTED); // The tab was added to a browser. Query for its state now. NavigationController* controller = Source<NavigationController>(source).ptr(); TrackTab(controller->tab_contents()); } TabFinder::TabFinder() { registrar_.Add(this, NotificationType::TAB_PARENTED, NotificationService::AllSources()); } TabFinder::~TabFinder() { STLDeleteElements(&tab_contents_observers_); } void TabFinder::Init() { for (BrowserList::const_iterator i = BrowserList::begin(); i != BrowserList::end(); ++i) { if (!(*i)->profile()->IsOffTheRecord()) TrackBrowser(*i); } } void TabFinder::DidNavigateAnyFramePostCommit( TabContents* source, const NavigationController::LoadCommittedDetails& details, const ViewHostMsg_FrameNavigate_Params& params) { CancelRequestsFor(source); if (PageTransition::IsRedirect(params.transition)) { // If this is a redirect, we need to go to the db to get the start. FetchRedirectStart(source); } else if (params.redirects.size() > 1 || params.redirects[0] != details.entry->url()) { tab_contents_to_url_[source] = params.redirects[0]; } } bool TabFinder::TabMatchesURL(TabContents* tab_contents, const GURL& url) { if (tab_contents->GetURL() == url) return true; TabContentsToURLMap::const_iterator i = tab_contents_to_url_.find(tab_contents); return i != tab_contents_to_url_.end() && i->second == url; } TabContents* TabFinder::FindTabInBrowser(Browser* browser, const GURL& url) { if (browser->type() != Browser::TYPE_NORMAL) return NULL; for (int i = 0; i < browser->tab_count(); ++i) { if (TabMatchesURL(browser->GetTabContentsAt(i), url)) return browser->GetTabContentsAt(i); } return NULL; } void TabFinder::TrackTab(TabContents* tab) { for (TabContentsObservers::const_iterator i = tab_contents_observers_.begin(); i != tab_contents_observers_.end(); ++i) { if ((*i)->tab_contents() == tab) { // Already tracking the tab. return; } } TabContentsObserverImpl* observer = new TabContentsObserverImpl(tab, this); tab_contents_observers_.insert(observer); FetchRedirectStart(tab); } void TabFinder::TrackBrowser(Browser* browser) { for (int i = 0; i < browser->tab_count(); ++i) FetchRedirectStart(browser->GetTabContentsAt(i)); } void TabFinder::TabDestroyed(TabContentsObserverImpl* observer) { DCHECK_GT(tab_contents_observers_.count(observer), 0u); tab_contents_observers_.erase(observer); } void TabFinder::CancelRequestsFor(TabContents* tab_contents) { if (tab_contents->profile()->IsOffTheRecord()) return; tab_contents_to_url_.erase(tab_contents); HistoryService* history = tab_contents->profile()->GetHistoryService( Profile::EXPLICIT_ACCESS); if (history) { CancelableRequestProvider::Handle request_handle; if (callback_consumer_.GetFirstHandleForClientData(tab_contents, &request_handle)) { history->CancelRequest(request_handle); } } } void TabFinder::FetchRedirectStart(TabContents* tab) { if (tab->profile()->IsOffTheRecord()) return; NavigationEntry* committed_entry = tab->controller().GetLastCommittedEntry(); if (!committed_entry || committed_entry->url().is_empty()) return; HistoryService* history =tab->profile()->GetHistoryService( Profile::EXPLICIT_ACCESS); if (history) { CancelableRequestProvider::Handle request_handle = history->QueryRedirectsTo( committed_entry->url(), &callback_consumer_, NewCallback(this, &TabFinder::QueryRedirectsToComplete)); callback_consumer_.SetClientData(history, request_handle, tab); } } void TabFinder::QueryRedirectsToComplete(HistoryService::Handle handle, GURL url, bool success, history::RedirectList* redirects) { if (success && !redirects->empty()) { TabContents* tab_contents = callback_consumer_.GetClientDataForCurrentRequest(); DCHECK(tab_contents); tab_contents_to_url_[tab_contents] = redirects->back(); } }