blob: 0b213513d820a98ddb9640862381a64787a83232 [file] [log] [blame]
// Copyright (c) 2012 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.
#ifndef CHROME_BROWSER_GOOGLE_GOOGLE_URL_TRACKER_H_
#define CHROME_BROWSER_GOOGLE_GOOGLE_URL_TRACKER_H_
#include <map>
#include <string>
#include <utility>
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/api/infobars/confirm_infobar_delegate.h"
#include "chrome/browser/profiles/profile_keyed_service.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "googleurl/src/gurl.h"
#include "net/base/network_change_notifier.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
class GoogleURLTrackerInfoBarDelegate;
class InfoBarTabHelper;
class PrefService;
class Profile;
namespace content {
class NavigationController;
class WebContents;
}
// This object is responsible for checking the Google URL once per network
// change, and if necessary prompting the user to see if they want to change to
// using it. The current and last prompted values are saved to prefs.
//
// Most consumers should only call GoogleURL(), which is guaranteed to
// synchronously return a value at all times (even during startup or in unittest
// mode). Consumers who need to be notified when things change should listen to
// the notification service for NOTIFICATION_GOOGLE_URL_UPDATED, which provides
// the original and updated values.
//
// To protect users' privacy and reduce server load, no updates will be
// performed (ever) unless at least one consumer registers interest by calling
// RequestServerCheck().
class GoogleURLTracker : public net::URLFetcherDelegate,
public content::NotificationObserver,
public net::NetworkChangeNotifier::IPAddressObserver,
public ProfileKeyedService {
public:
// The contents of the Details for a NOTIFICATION_GOOGLE_URL_UPDATED.
typedef std::pair<GURL, GURL> UpdatedDetails;
// The constructor does different things depending on which of these values
// you pass it. Hopefully these are self-explanatory.
enum Mode {
NORMAL_MODE,
UNIT_TEST_MODE,
};
// Only the GoogleURLTrackerFactory and tests should call this. No code other
// than the GoogleURLTracker itself should actually use
// GoogleURLTrackerFactory::GetForProfile().
GoogleURLTracker(Profile* profile, Mode mode);
virtual ~GoogleURLTracker();
// Returns the current Google URL. This will return a valid URL even if
// |profile| is NULL or a testing profile.
//
// This is the only function most code should ever call.
static GURL GoogleURL(Profile* profile);
// Requests that the tracker perform a server check to update the Google URL
// as necessary. This will happen at most once per network change, not
// sooner than five seconds after startup (checks requested before that time
// will occur then; checks requested afterwards will occur immediately, if
// no other checks have been made during this run).
//
// When |profile| is NULL or a testing profile, this function does nothing.
static void RequestServerCheck(Profile* profile);
// Notifies the tracker that the user has started a Google search.
// If prompting is necessary, we then listen for the subsequent
// NAV_ENTRY_PENDING notification to get the appropriate NavigationController.
// When the load commits, we'll show the infobar.
//
// When |profile| is NULL or a testing profile, this function does nothing.
static void GoogleURLSearchCommitted(Profile* profile);
static const char kDefaultGoogleHomepage[];
static const char kSearchDomainCheckURL[];
private:
friend class GoogleURLTrackerInfoBarDelegate;
friend class GoogleURLTrackerTest;
struct MapEntry {
MapEntry(); // Required by STL.
MapEntry(GoogleURLTrackerInfoBarDelegate* infobar,
const content::NotificationSource& navigation_controller_source,
const content::NotificationSource& tab_contents_source);
~MapEntry();
GoogleURLTrackerInfoBarDelegate* infobar;
content::NotificationSource navigation_controller_source;
content::NotificationSource tab_contents_source;
};
typedef std::map<const InfoBarTabHelper*, MapEntry> InfoBarMap;
typedef GoogleURLTrackerInfoBarDelegate* (*InfoBarCreator)(
InfoBarTabHelper* infobar_helper,
GoogleURLTracker* google_url_tracker,
const GURL& new_google_url);
// net::URLFetcherDelegate:
virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
// content::NotificationObserver:
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
// NetworkChangeNotifier::IPAddressObserver:
virtual void OnIPAddressChanged() OVERRIDE;
// ProfileKeyedService:
virtual void Shutdown() OVERRIDE;
// Callbacks from GoogleURLTrackerInfoBarDelegate:
void AcceptGoogleURL(const GURL& google_url, bool redo_searches);
void CancelGoogleURL(const GURL& google_url);
void InfoBarClosed(const InfoBarTabHelper* infobar_helper);
// Registers consumer interest in getting an updated URL from the server.
// Observe chrome::NOTIFICATION_GOOGLE_URL_UPDATED to be notified when the URL
// changes.
void SetNeedToFetch();
// Called when the five second startup sleep has finished. Runs any pending
// fetch.
void FinishSleep();
// Starts the fetch of the up-to-date Google URL if we actually want to fetch
// it and can currently do so.
void StartFetchIfDesirable();
// Called each time the user performs a search. This checks whether we need
// to prompt the user about a domain change, and if so, starts listening for
// the notifications sent when the actual load is triggered.
void SearchCommitted();
// Called by Observe() after SearchCommitted() registers notification
// listeners, to indicate that we've received the "load now pending"
// notification. |navigation_controller_source| and |tab_contents_source| are
// NotificationSources pointing to the associated NavigationController and
// TabContents, respectively, for this load; |infobar_helper| is the
// InfoBarTabHelper of the associated tab; and |pending_id| is the unique ID
// of the newly pending NavigationEntry. If there is already a visible
// GoogleURLTracker infobar for this tab, this function resets its associated
// pending entry ID to the new ID. Otherwise this function creates a
// (still-invisible) InfoBarDelegate for the associated tab.
void OnNavigationPending(
const content::NotificationSource& navigation_controller_source,
const content::NotificationSource& tab_contents_source,
InfoBarTabHelper* infobar_helper,
int pending_id);
// Called by Observe() once a load we're watching commits, or the associated
// tab is closed. |infobar_helper| is the same as for OnNavigationPending();
// |search_url| is valid when this call is due to a successful navigation
// (indicating that we should show or update the relevant infobar) as opposed
// to tab closure (which means we should delete the infobar).
void OnNavigationCommittedOrTabClosed(const InfoBarTabHelper* infobar_helper,
const GURL& search_url);
// Called by Observe() when an instant navigation occurs. This will call
// OnNavigationPending(), and, depending on whether this is a search we were
// listening for, may then also call OnNavigationCommittedOrTabClosed().
void OnInstantCommitted(
const content::NotificationSource& navigation_controller_source,
const content::NotificationSource& tab_contents_source,
InfoBarTabHelper* infobar_helper,
const GURL& search_url);
// Closes all open infobars. If |redo_searches| is true, this also triggers
// each tab to re-perform the user's search, but on the new Google TLD.
void CloseAllInfoBars(bool redo_searches);
// Unregisters any listeners for the notification sources in |map_entry|.
// This sanity-DCHECKs that these are registered (or not) in the specific
// cases we expect. (|must_be_listening_for_commit| is used purely for this
// sanity-checking.) This also unregisters our global NAV_ENTRY_PENDING/
// INSTANT_COMMITTED listeners if there are no remaining listeners for
// NAV_ENTRY_COMMITTED, as we no longer need them until another search is
// committed.
void UnregisterForEntrySpecificNotifications(
const MapEntry& map_entry,
bool must_be_listening_for_commit);
Profile* profile_;
content::NotificationRegistrar registrar_;
InfoBarCreator infobar_creator_;
// TODO(ukai): GoogleURLTracker should track google domain (e.g. google.co.uk)
// rather than URL (e.g. http://www.google.co.uk/), so that user could
// configure to use https in search engine templates.
GURL google_url_;
GURL fetched_google_url_;
base::WeakPtrFactory<GoogleURLTracker> weak_ptr_factory_;
scoped_ptr<net::URLFetcher> fetcher_;
int fetcher_id_;
bool in_startup_sleep_; // True if we're in the five-second "no fetching"
// period that begins at browser start.
bool already_fetched_; // True if we've already fetched a URL once this run;
// we won't fetch again until after a restart.
bool need_to_fetch_; // True if a consumer actually wants us to fetch an
// updated URL. If this is never set, we won't
// bother to fetch anything.
// Consumers should observe
// chrome::NOTIFICATION_GOOGLE_URL_UPDATED.
bool need_to_prompt_; // True if the last fetched Google URL is not
// matched with current user's default Google URL
// nor the last prompted Google URL.
bool search_committed_; // True when we're expecting a notification of a new
// pending search navigation.
InfoBarMap infobar_map_;
DISALLOW_COPY_AND_ASSIGN(GoogleURLTracker);
};
// This infobar delegate is declared here (rather than in the .cc file) so test
// code can subclass it.
class GoogleURLTrackerInfoBarDelegate : public ConfirmInfoBarDelegate {
public:
GoogleURLTrackerInfoBarDelegate(InfoBarTabHelper* infobar_helper,
GoogleURLTracker* google_url_tracker,
const GURL& new_google_url);
// ConfirmInfoBarDelegate:
virtual bool Accept() OVERRIDE;
virtual bool Cancel() OVERRIDE;
virtual string16 GetLinkText() const OVERRIDE;
virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE;
virtual bool ShouldExpireInternal(
const content::LoadCommittedDetails& details) const OVERRIDE;
// Allows GoogleURLTracker to change the Google base URL after the infobar has
// been instantiated. This should only be called with an URL with the same
// TLD as the existing one, so that the prompt we're displaying will still be
// correct.
void SetGoogleURL(const GURL& new_google_url);
bool showing() const { return showing_; }
void set_pending_id(int pending_id) { pending_id_ = pending_id; }
// These are virtual so test code can override them in a subclass.
virtual void Show(const GURL& search_url);
virtual void Close(bool redo_search);
protected:
virtual ~GoogleURLTrackerInfoBarDelegate();
InfoBarTabHelper* map_key_; // What |google_url_tracker_| uses to track us.
GURL search_url_;
GoogleURLTracker* google_url_tracker_;
GURL new_google_url_;
bool showing_; // True if this delegate has been added to a TabContents.
int pending_id_;
private:
// ConfirmInfoBarDelegate:
virtual string16 GetMessageText() const OVERRIDE;
virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
DISALLOW_COPY_AND_ASSIGN(GoogleURLTrackerInfoBarDelegate);
};
#endif // CHROME_BROWSER_GOOGLE_GOOGLE_URL_TRACKER_H_