Home | History | Annotate | Download | only in tabs
      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 
     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 
     42 TabFinder::TabContentsObserverImpl::TabContentsObserverImpl(
     43     TabContents* tab,
     44     TabFinder* finder)
     45     : TabContentsObserver(tab),
     46       finder_(finder) {
     47 }
     48 
     49 TabFinder::TabContentsObserverImpl::~TabContentsObserverImpl() {
     50 }
     51 
     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 
     58 void TabFinder::TabContentsObserverImpl::TabContentsDestroyed(
     59     TabContents* tab) {
     60   finder_->TabDestroyed(this);
     61   delete this;
     62 }
     63 
     64 // static
     65 TabFinder* TabFinder::GetInstance() {
     66   return IsEnabled() ? Singleton<TabFinder>::get() : NULL;
     67 }
     68 
     69 // static
     70 bool TabFinder::IsEnabled() {
     71   return CommandLine::ForCurrentProcess()->HasSwitch(
     72       switches::kFocusExistingTabOnOpen);
     73 }
     74 
     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 
    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 
    120 TabFinder::TabFinder() {
    121   registrar_.Add(this, NotificationType::TAB_PARENTED,
    122                  NotificationService::AllSources());
    123 }
    124 
    125 TabFinder::~TabFinder() {
    126   STLDeleteElements(&tab_contents_observers_);
    127 }
    128 
    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 
    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 
    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 
    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 
    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 
    185 void TabFinder::TrackBrowser(Browser* browser) {
    186   for (int i = 0; i < browser->tab_count(); ++i)
    187     FetchRedirectStart(browser->GetTabContentsAt(i));
    188 }
    189 
    190 void TabFinder::TabDestroyed(TabContentsObserverImpl* observer) {
    191   DCHECK_GT(tab_contents_observers_.count(observer), 0u);
    192   tab_contents_observers_.erase(observer);
    193 }
    194 
    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 
    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 
    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