Index: chrome/browser/google_url_tracker.cc |
diff --git a/chrome/browser/google_url_tracker.cc b/chrome/browser/google_url_tracker.cc |
index b7033930c60d3cc9b6715fbc11cb51fead659bf7..b6f111e1edf0321190fb5d546d773f33eb295143 100644 |
--- a/chrome/browser/google_url_tracker.cc |
+++ b/chrome/browser/google_url_tracker.cc |
@@ -6,31 +6,116 @@ |
#include <vector> |
+#include "app/l10n_util.h" |
#include "base/compiler_specific.h" |
#include "base/string_util.h" |
#include "base/utf_string_conversions.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/pref_service.h" |
#include "chrome/browser/profile.h" |
+#include "chrome/browser/search_engines/template_url.h" |
+#include "chrome/browser/tab_contents/infobar_delegate.h" |
+#include "chrome/browser/tab_contents/navigation_controller.h" |
+#include "chrome/browser/tab_contents/tab_contents.h" |
+#include "chrome/common/net/url_fetcher_protect.h" |
#include "chrome/common/notification_service.h" |
#include "chrome/common/pref_names.h" |
+#include "grit/generated_resources.h" |
#include "net/base/load_flags.h" |
#include "net/url_request/url_request_status.h" |
const char GoogleURLTracker::kDefaultGoogleHomepage[] = |
"http://www.google.com/"; |
+const char GoogleURLTracker::kSearchDomainCheckURL[] = |
+ "https://www.google.com/searchdomaincheck?format=domain&type=chrome"; |
+ |
+namespace { |
+ |
+class GoogleURLTrackerInfoBarDelegate : public ConfirmInfoBarDelegate { |
+ public: |
+ GoogleURLTrackerInfoBarDelegate(TabContents* tab_contents, |
+ GoogleURLTracker* google_url_tracker, |
+ const GURL& new_google_url) |
+ : ConfirmInfoBarDelegate(tab_contents), |
+ google_url_tracker_(google_url_tracker), |
+ new_google_url_(new_google_url) {} |
+ |
+ // ConfirmInfoBarDelegate |
+ virtual string16 GetMessageText() const { |
+ // TODO(ukai): change new_google_url to google_base_domain? |
+ return l10n_util::GetStringFUTF16(IDS_GOOGLE_URL_TRACKER_INFOBAR_MESSAGE, |
+ UTF8ToUTF16(new_google_url_.spec())); |
+ } |
+ |
+ virtual int GetButtons() const { |
+ return BUTTON_OK | BUTTON_CANCEL; |
+ } |
+ |
+ virtual string16 GetButtonLabel(InfoBarButton button) const { |
+ return l10n_util::GetStringUTF16((button == BUTTON_OK) ? |
+ IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL : |
+ IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL); |
+ } |
+ |
+ virtual bool Accept() { |
+ google_url_tracker_->AcceptGoogleURL(new_google_url_); |
+ google_url_tracker_->RedoSearch(); |
+ return true; |
+ } |
+ |
+ virtual void InfoBarClosed() { |
+ google_url_tracker_->InfoBarClosed(); |
+ delete this; |
+ } |
+ |
+ private: |
+ virtual ~GoogleURLTrackerInfoBarDelegate() {} |
+ |
+ GoogleURLTracker* google_url_tracker_; |
+ const GURL new_google_url_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GoogleURLTrackerInfoBarDelegate); |
+}; |
+ |
+} // anonymous namespace |
+ |
+InfoBarDelegate* GoogleURLTracker::InfoBarDelegateFactory::CreateInfoBar( |
+ TabContents* tab_contents, |
+ GoogleURLTracker* google_url_tracker, |
+ const GURL& new_google_url) { |
+ InfoBarDelegate* infobar = |
+ new GoogleURLTrackerInfoBarDelegate(tab_contents, |
+ google_url_tracker, |
+ new_google_url); |
+ tab_contents->AddInfoBar(infobar); |
+ return infobar; |
+} |
GoogleURLTracker::GoogleURLTracker() |
: google_url_(g_browser_process->local_state()->GetString( |
prefs::kLastKnownGoogleURL)), |
- ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_factory_(this)), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(runnable_method_factory_(this)), |
+ fetcher_id_(0), |
in_startup_sleep_(true), |
already_fetched_(false), |
need_to_fetch_(false), |
- request_context_available_(!!Profile::GetDefaultRequestContext()) { |
+ request_context_available_(!!Profile::GetDefaultRequestContext()), |
+ need_to_prompt_(false), |
+ controller_(NULL), |
+ infobar_factory_(new InfoBarDelegateFactory), |
+ infobar_(NULL) { |
registrar_.Add(this, NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE, |
NotificationService::AllSources()); |
+ net::NetworkChangeNotifier::AddObserver(this); |
+ |
+ // Configure to max_retries at most kMaxRetries times for 5xx errors. |
+ URLFetcherProtectEntry* protect = |
+ URLFetcherProtectManager::GetInstance()->Register( |
+ GURL(kSearchDomainCheckURL).host()); |
+ static const int kMaxRetries = 5; |
+ protect->SetMaxRetries(kMaxRetries); |
+ |
// Because this function can be called during startup, when kicking off a URL |
// fetch can eat up 20 ms of time, we delay five seconds, which is hopefully |
// long enough to be after startup, but still get results back quickly. |
@@ -39,11 +124,14 @@ GoogleURLTracker::GoogleURLTracker() |
// no function to do this. |
static const int kStartFetchDelayMS = 5000; |
MessageLoop::current()->PostDelayedTask(FROM_HERE, |
- fetcher_factory_.NewRunnableMethod(&GoogleURLTracker::FinishSleep), |
+ runnable_method_factory_.NewRunnableMethod( |
+ &GoogleURLTracker::FinishSleep), |
kStartFetchDelayMS); |
} |
GoogleURLTracker::~GoogleURLTracker() { |
+ runnable_method_factory_.RevokeAll(); |
+ net::NetworkChangeNotifier::RemoveObserver(this); |
} |
// static |
@@ -64,42 +152,14 @@ void GoogleURLTracker::RequestServerCheck() { |
void GoogleURLTracker::RegisterPrefs(PrefService* prefs) { |
prefs->RegisterStringPref(prefs::kLastKnownGoogleURL, |
kDefaultGoogleHomepage); |
+ prefs->RegisterStringPref(prefs::kLastPromptedGoogleURL, std::string()); |
} |
// static |
-bool GoogleURLTracker::CheckAndConvertToGoogleBaseURL(const GURL& url, |
- GURL* base_url) { |
- // Only allow updates if the new URL appears to be on google.xx, google.co.xx, |
- // or google.com.xx. Cases other than this are either malicious, or doorway |
- // pages for hotel WiFi connections and the like. |
- // NOTE: Obviously the above is not as secure as whitelisting all known Google |
- // frontpage domains, but for now we're trying to prevent login pages etc. |
- // from ruining the user experience, rather than preventing hijacking. |
- std::vector<std::string> host_components; |
- SplitStringDontTrim(url.host(), '.', &host_components); |
- if (host_components.size() < 2) |
- return false; |
- size_t google_component = host_components.size() - 2; |
- const std::string& component = host_components[google_component]; |
- if (component != "google") { |
- if ((host_components.size() < 3) || |
- ((component != "co") && (component != "com"))) |
- return false; |
- google_component = host_components.size() - 3; |
- if (host_components[google_component] != "google") |
- return false; |
- } |
- // For Google employees only: If the URL appears to be on |
- // [*.]corp.google.com, it's likely a doorway (e.g. |
- // wifi.corp.google.com), so ignore it. |
- if ((google_component > 0) && |
- (host_components[google_component - 1] == "corp")) |
- return false; |
- |
- // If the url's path does not begin "/intl/", reset it to "/". Other paths |
- // represent services such as iGoogle that are irrelevant to the baseURL. |
- *base_url = url.path().compare(0, 6, "/intl/") ? url.GetWithEmptyPath() : url; |
- return true; |
+void GoogleURLTracker::GoogleURLSearchCommitted() { |
+ GoogleURLTracker* tracker = g_browser_process->google_url_tracker(); |
+ if (tracker) |
+ tracker->SearchCommitted(); |
} |
void GoogleURLTracker::SetNeedToFetch() { |
@@ -123,12 +183,10 @@ void GoogleURLTracker::StartFetchIfDesirable() { |
!request_context_available_) |
return; |
- need_to_fetch_ = false; |
- already_fetched_ = true; // If fetching fails, we don't bother to reset this |
- // flag; we just live with an outdated URL for this |
- // run of the browser. |
- fetcher_.reset(new URLFetcher(GURL(kDefaultGoogleHomepage), URLFetcher::HEAD, |
- this)); |
+ already_fetched_ = true; |
+ fetcher_.reset(URLFetcher::Create(fetcher_id_, GURL(kSearchDomainCheckURL), |
+ URLFetcher::GET, this)); |
+ ++fetcher_id_; |
// We don't want this fetch to affect existing state in the profile. For |
// example, if a user has no Google cookies, this automatic check should not |
// cause one to be set, lest we alarm the user. |
@@ -148,32 +206,148 @@ void GoogleURLTracker::OnURLFetchComplete(const URLFetcher* source, |
scoped_ptr<URLFetcher> clean_up_fetcher(fetcher_.release()); |
// Don't update the URL if the request didn't succeed. |
- if (!status.is_success() || (response_code != 200)) |
+ if (!status.is_success() || (response_code != 200)) { |
+ already_fetched_ = false; |
return; |
+ } |
- // See if the response URL was one we want to use, and if so, convert to the |
+ // See if the response data was one we want to use, and if so, convert to the |
// appropriate Google base URL. |
- GURL base_url; |
- if (!CheckAndConvertToGoogleBaseURL(url, &base_url)) |
+ std::string url_str; |
+ TrimWhitespace(data, TRIM_ALL, &url_str); |
+ |
+ if (!StartsWithASCII(url_str, ".google.", false)) |
return; |
- // Update the saved base URL if it has changed. |
- const std::string base_url_str(base_url.spec()); |
- if (g_browser_process->local_state()->GetString(prefs::kLastKnownGoogleURL) != |
- base_url_str) { |
- g_browser_process->local_state()->SetString(prefs::kLastKnownGoogleURL, |
- base_url_str); |
- google_url_ = base_url; |
- NotificationService::current()->Notify(NotificationType::GOOGLE_URL_UPDATED, |
- NotificationService::AllSources(), |
- NotificationService::NoDetails()); |
+ fetched_google_url_ = GURL("http://www" + url_str); |
+ GURL last_prompted_url( |
+ g_browser_process->local_state()->GetString( |
+ prefs::kLastPromptedGoogleURL)); |
+ need_to_prompt_ = false; |
+ // On the very first run of Chrome, when we've never looked up the URL at all, |
+ // we should just silently switch over to whatever we get immediately. |
+ if (last_prompted_url.is_empty()) { |
+ AcceptGoogleURL(fetched_google_url_); |
+ // Set fetched_google_url_ as an initial value of last prompted URL. |
+ g_browser_process->local_state()->SetString(prefs::kLastPromptedGoogleURL, |
+ fetched_google_url_.spec()); |
+ return; |
} |
+ |
+ if (fetched_google_url_ == last_prompted_url) |
+ return; |
+ if (fetched_google_url_ == google_url_) { |
+ // The user came back to their original location after having temporarily |
+ // moved. Reset the prompted URL so we'll prompt again if they move again. |
+ g_browser_process->local_state()->SetString(prefs::kLastPromptedGoogleURL, |
+ fetched_google_url_.spec()); |
+ return; |
+ } |
+ |
+ need_to_prompt_ = true; |
+} |
+ |
+void GoogleURLTracker::AcceptGoogleURL(const GURL& new_google_url) { |
+ google_url_ = new_google_url; |
+ g_browser_process->local_state()->SetString(prefs::kLastKnownGoogleURL, |
+ google_url_.spec()); |
+ NotificationService::current()->Notify(NotificationType::GOOGLE_URL_UPDATED, |
+ NotificationService::AllSources(), |
+ NotificationService::NoDetails()); |
+ need_to_prompt_ = false; |
+} |
+ |
+void GoogleURLTracker::InfoBarClosed() { |
+ registrar_.RemoveAll(); |
+ controller_ = NULL; |
+ infobar_ = NULL; |
+ search_url_ = GURL(); |
+} |
+ |
+void GoogleURLTracker::RedoSearch() { |
+ // re-do the user's search on the new domain. |
+ DCHECK(controller_); |
+ url_canon::Replacements<char> replacements; |
+ replacements.SetHost(google_url_.host().data(), |
+ url_parse::Component(0, google_url_.host().length())); |
+ search_url_ = search_url_.ReplaceComponents(replacements); |
+ if (search_url_.is_valid()) |
+ controller_->tab_contents()->OpenURL(search_url_, GURL(), CURRENT_TAB, |
+ PageTransition::GENERATED); |
} |
void GoogleURLTracker::Observe(NotificationType type, |
const NotificationSource& source, |
const NotificationDetails& details) { |
- DCHECK_EQ(NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE, type.value); |
- request_context_available_ = true; |
+ switch (type.value) { |
+ case NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE: |
+ request_context_available_ = true; |
+ StartFetchIfDesirable(); |
+ break; |
+ |
+ case NotificationType::NAV_ENTRY_PENDING: |
+ // If we've already received a notification for the same controller, we |
+ // should reset infobar as that indicates that the page is being |
+ // re-loaded |
+ if (!infobar_ && |
+ controller_ == Source<NavigationController>(source).ptr()) { |
+ infobar_ = NULL; |
+ } else if (!controller_) { |
+ controller_ = Source<NavigationController>(source).ptr(); |
+ NavigationEntry* entry = controller_->pending_entry(); |
+ DCHECK(entry); |
+ search_url_ = entry->url(); |
+ |
+ // Start listening for the commit notification. We also need to listen |
+ // for the tab close command since that means the load will never |
+ // commit! |
+ registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, |
+ Source<NavigationController>(controller_)); |
+ registrar_.Add(this, NotificationType::TAB_CLOSED, |
+ Source<NavigationController>(controller_)); |
+ } |
+ break; |
+ |
+ case NotificationType::NAV_ENTRY_COMMITTED: |
+ DCHECK(controller_); |
+ registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED, |
+ Source<NavigationController>(controller_)); |
+ ShowGoogleURLInfoBarIfNecessary(controller_->tab_contents()); |
+ break; |
+ |
+ case NotificationType::TAB_CLOSED: |
+ registrar_.RemoveAll(); |
+ controller_ = NULL; |
+ infobar_ = NULL; |
+ break; |
+ |
+ default: |
+ NOTREACHED() << "Unknown notification received:" << type.value; |
+ } |
+} |
+ |
+void GoogleURLTracker::OnIPAddressChanged() { |
+ already_fetched_ = false; |
StartFetchIfDesirable(); |
} |
+ |
+void GoogleURLTracker::SearchCommitted() { |
+ registrar_.Add(this, NotificationType::NAV_ENTRY_PENDING, |
+ NotificationService::AllSources()); |
+} |
+ |
+void GoogleURLTracker::ShowGoogleURLInfoBarIfNecessary( |
+ TabContents* tab_contents) { |
+ if (!need_to_prompt_) |
+ return; |
+ if (infobar_) |
+ return; |
+ DCHECK(!fetched_google_url_.is_empty()); |
+ DCHECK(infobar_factory_.get()); |
+ |
+ infobar_ = infobar_factory_->CreateInfoBar(tab_contents, |
+ this, |
+ fetched_google_url_); |
+ g_browser_process->local_state()->SetString(prefs::kLastPromptedGoogleURL, |
+ fetched_google_url_.spec()); |
+} |