普通文本  |  243行  |  7.69 KB

// 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();
  }
}