summaryrefslogtreecommitdiffstats
path: root/chrome/browser/google
diff options
context:
space:
mode:
authorevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-30 22:22:49 +0000
committerevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-30 22:22:49 +0000
commitf7578f5d14dbd1c2931b7f574ead27cc978ab73a (patch)
treeddb316a54abc01a6219e4d129062390af5759be9 /chrome/browser/google
parent439764b703a5edd48aa878b86fbd07a117b6a3cc (diff)
downloadchromium_src-f7578f5d14dbd1c2931b7f574ead27cc978ab73a.zip
chromium_src-f7578f5d14dbd1c2931b7f574ead27cc978ab73a.tar.gz
chromium_src-f7578f5d14dbd1c2931b7f574ead27cc978ab73a.tar.bz2
Move Google-specific code under browser/ into browser/google/.
BUG=50548 TEST=compiles Review URL: http://codereview.chromium.org/3280008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57922 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/google')
-rw-r--r--chrome/browser/google/google_update.cc331
-rw-r--r--chrome/browser/google/google_update.h144
-rw-r--r--chrome/browser/google/google_update_settings_posix.cc84
-rw-r--r--chrome/browser/google/google_update_settings_unittest.cc35
-rw-r--r--chrome/browser/google/google_url_tracker.cc348
-rw-r--r--chrome/browser/google/google_url_tracker.h162
-rw-r--r--chrome/browser/google/google_url_tracker_unittest.cc458
-rw-r--r--chrome/browser/google/google_util.cc58
-rw-r--r--chrome/browser/google/google_util.h27
9 files changed, 1647 insertions, 0 deletions
diff --git a/chrome/browser/google/google_update.cc b/chrome/browser/google/google_update.cc
new file mode 100644
index 0000000..ee7547d
--- /dev/null
+++ b/chrome/browser/google/google_update.cc
@@ -0,0 +1,331 @@
+// Copyright (c) 2006-2008 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/google/google_update.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/scoped_comptr_win.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "base/win_util.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/installer/util/browser_distribution.h"
+#include "chrome/installer/util/google_update_constants.h"
+#include "chrome/installer/util/helper.h"
+#include "chrome/installer/util/install_util.h"
+#include "views/window/window.h"
+#include "google_update_idl_i.c"
+
+using views::Window;
+
+namespace {
+// Check if the currently running instance can be updated by Google Update.
+// Returns true only if the instance running is a Google Chrome
+// distribution installed in a standard location.
+bool CanUpdateCurrentChrome(const std::wstring& chrome_exe_path) {
+#if !defined(GOOGLE_CHROME_BUILD)
+ return false;
+#else
+ std::wstring user_exe_path = installer::GetChromeInstallPath(false);
+ std::wstring machine_exe_path = installer::GetChromeInstallPath(true);
+ std::transform(user_exe_path.begin(), user_exe_path.end(),
+ user_exe_path.begin(), tolower);
+ std::transform(machine_exe_path.begin(), machine_exe_path.end(),
+ machine_exe_path.begin(), tolower);
+ if (chrome_exe_path != user_exe_path &&
+ chrome_exe_path != machine_exe_path ) {
+ LOG(ERROR) << L"Google Update cannot update Chrome installed in a "
+ << L"non-standard location: " << chrome_exe_path.c_str()
+ << L". The standard location is: " << user_exe_path.c_str()
+ << L" or " << machine_exe_path.c_str() << L".";
+ return false;
+ }
+
+ return true;
+#endif
+}
+
+// Creates an instance of a COM Local Server class using either plain vanilla
+// CoCreateInstance, or using the Elevation moniker if running on Vista.
+// hwnd must refer to a foregound window in order to get the UAC prompt
+// showing up in the foreground if running on Vista. It can also be NULL if
+// background UAC prompts are desired.
+HRESULT CoCreateInstanceAsAdmin(REFCLSID class_id, REFIID interface_id,
+ HWND hwnd, void** interface_ptr) {
+ if (!interface_ptr)
+ return E_POINTER;
+
+ // For Vista we need to instantiate the COM server via the elevation
+ // moniker. This ensures that the UAC dialog shows up.
+ if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
+ wchar_t class_id_as_string[MAX_PATH] = {0};
+ StringFromGUID2(class_id, class_id_as_string,
+ arraysize(class_id_as_string));
+
+ std::wstring elevation_moniker_name =
+ StringPrintf(L"Elevation:Administrator!new:%ls", class_id_as_string);
+
+ BIND_OPTS3 bind_opts;
+ memset(&bind_opts, 0, sizeof(bind_opts));
+ bind_opts.cbStruct = sizeof(bind_opts);
+ bind_opts.dwClassContext = CLSCTX_LOCAL_SERVER;
+ bind_opts.hwnd = hwnd;
+
+ return CoGetObject(elevation_moniker_name.c_str(), &bind_opts,
+ interface_id, reinterpret_cast<void**>(interface_ptr));
+ }
+
+ return CoCreateInstance(class_id, NULL, CLSCTX_LOCAL_SERVER,
+ interface_id,
+ reinterpret_cast<void**>(interface_ptr));
+}
+
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The GoogleUpdateJobObserver COM class is responsible for receiving status
+// reports from google Update. It keeps track of the progress as Google Update
+// notifies us and ends the message loop we are spinning in once Google Update
+// reports that it is done.
+//
+////////////////////////////////////////////////////////////////////////////////
+class GoogleUpdateJobObserver
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public IJobObserver {
+ public:
+ BEGIN_COM_MAP(GoogleUpdateJobObserver)
+ COM_INTERFACE_ENTRY(IJobObserver)
+ END_COM_MAP()
+
+ GoogleUpdateJobObserver()
+ : result_(UPGRADE_ERROR) {
+ }
+ virtual ~GoogleUpdateJobObserver() {}
+
+ // Notifications from Google Update:
+ STDMETHOD(OnShow)() {
+ return S_OK;
+ }
+ STDMETHOD(OnCheckingForUpdate)() {
+ result_ = UPGRADE_CHECK_STARTED;
+ return S_OK;
+ }
+ STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) {
+ result_ = UPGRADE_IS_AVAILABLE;
+ new_version_ = version_string;
+ return S_OK;
+ }
+ STDMETHOD(OnWaitingToDownload)() {
+ return S_OK;
+ }
+ STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) {
+ return S_OK;
+ }
+ STDMETHOD(OnWaitingToInstall)() {
+ return S_OK;
+ }
+ STDMETHOD(OnInstalling)() {
+ result_ = UPGRADE_STARTED;
+ return S_OK;
+ }
+ STDMETHOD(OnPause)() {
+ return S_OK;
+ }
+ STDMETHOD(OnComplete)(CompletionCodes code, const TCHAR* text) {
+ switch (code) {
+ case COMPLETION_CODE_SUCCESS_CLOSE_UI:
+ case COMPLETION_CODE_SUCCESS: {
+ if (result_ == UPGRADE_STARTED)
+ result_ = UPGRADE_SUCCESSFUL;
+ else if (result_ == UPGRADE_CHECK_STARTED)
+ result_ = UPGRADE_ALREADY_UP_TO_DATE;
+ break;
+ }
+ default: {
+ NOTREACHED();
+ result_ = UPGRADE_ERROR;
+ break;
+ }
+ }
+
+ event_sink_ = NULL;
+
+ // We no longer need to spin the message loop that we started spinning in
+ // InitiateGoogleUpdateCheck.
+ MessageLoop::current()->Quit();
+ return S_OK;
+ }
+ STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) {
+ event_sink_ = event_sink;
+ return S_OK;
+ }
+
+ // Returns the results of the update operation.
+ STDMETHOD(GetResult)(GoogleUpdateUpgradeResult* result) {
+ // Intermediary steps should never be reported to the client.
+ DCHECK(result_ != UPGRADE_STARTED && result_ != UPGRADE_CHECK_STARTED);
+
+ *result = result_;
+ return S_OK;
+ }
+
+ // Returns which version Google Update found on the server (if a more
+ // recent version was found). Otherwise, this will be blank.
+ STDMETHOD(GetVersionInfo)(std::wstring* version_string) {
+ *version_string = new_version_;
+ return S_OK;
+ }
+
+ private:
+ // The status/result of the Google Update operation.
+ GoogleUpdateUpgradeResult result_;
+
+ // The version string Google Update found.
+ std::wstring new_version_;
+
+ // Allows us control the upgrade process to a small degree. After OnComplete
+ // has been called, this object can not be used.
+ ScopedComPtr<IProgressWndEvents> event_sink_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// GoogleUpdate, public:
+
+GoogleUpdate::GoogleUpdate()
+ : listener_(NULL) {
+}
+
+GoogleUpdate::~GoogleUpdate() {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// GoogleUpdate, views::DialogDelegate implementation:
+
+void GoogleUpdate::CheckForUpdate(bool install_if_newer, Window* window) {
+ // We need to shunt this request over to InitiateGoogleUpdateCheck and have
+ // it run in the file thread.
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ this, &GoogleUpdate::InitiateGoogleUpdateCheck, install_if_newer,
+ window, MessageLoop::current()));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// GoogleUpdate, private:
+
+bool GoogleUpdate::InitiateGoogleUpdateCheck(bool install_if_newer,
+ Window* window,
+ MessageLoop* main_loop) {
+
+ std::wstring chrome_exe_path;
+ if (!PathService::Get(base::DIR_EXE, &chrome_exe_path)) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::transform(chrome_exe_path.begin(), chrome_exe_path.end(),
+ chrome_exe_path.begin(), tolower);
+
+ if (!CanUpdateCurrentChrome(chrome_exe_path)) {
+ main_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &GoogleUpdate::ReportResults, UPGRADE_ERROR,
+ CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY));
+ return false;
+ }
+
+ CComObject<GoogleUpdateJobObserver>* job_observer;
+ HRESULT hr =
+ CComObject<GoogleUpdateJobObserver>::CreateInstance(&job_observer);
+ if (hr != S_OK) {
+ return ReportFailure(hr, GOOGLE_UPDATE_JOB_SERVER_CREATION_FAILED,
+ main_loop);
+ }
+
+ ScopedComPtr<IJobObserver> job_holder(job_observer);
+
+ ScopedComPtr<IGoogleUpdate> on_demand;
+
+ if (InstallUtil::IsPerUserInstall(chrome_exe_path.c_str())) {
+ hr = on_demand.CreateInstance(CLSID_OnDemandUserAppsClass);
+ } else {
+ // The Update operation needs Admin privileges for writing
+ // to %ProgramFiles%. On Vista we need to elevate before instantiating
+ // the updater instance.
+ if (!install_if_newer) {
+ hr = on_demand.CreateInstance(CLSID_OnDemandMachineAppsClass);
+ } else {
+ HWND foreground_hwnd = NULL;
+ if (window != NULL) {
+ foreground_hwnd = window->GetNativeWindow();
+ }
+
+ hr = CoCreateInstanceAsAdmin(CLSID_OnDemandMachineAppsClass,
+ IID_IGoogleUpdate, foreground_hwnd,
+ reinterpret_cast<void**>(on_demand.Receive()));
+ }
+ }
+
+ if (hr != S_OK)
+ return ReportFailure(hr, GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND, main_loop);
+
+ BrowserDistribution* dist = BrowserDistribution::GetDistribution();
+ if (!install_if_newer)
+ hr = on_demand->CheckForUpdate(dist->GetAppGuid().c_str(), job_observer);
+ else
+ hr = on_demand->Update(dist->GetAppGuid().c_str(), job_observer);
+
+ if (hr != S_OK)
+ return ReportFailure(hr, GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR,
+ main_loop);
+
+ // We need to spin the message loop while Google Update is running so that it
+ // can report back to us through GoogleUpdateJobObserver. This message loop
+ // will terminate once Google Update sends us the completion status
+ // (success/error). See OnComplete().
+ MessageLoop::current()->Run();
+
+ GoogleUpdateUpgradeResult results;
+ hr = job_observer->GetResult(&results);
+ if (hr != S_OK)
+ return ReportFailure(hr, GOOGLE_UPDATE_GET_RESULT_CALL_FAILED, main_loop);
+
+ if (results == UPGRADE_ERROR)
+ return ReportFailure(hr, GOOGLE_UPDATE_ERROR_UPDATING, main_loop);
+
+ hr = job_observer->GetVersionInfo(&version_available_);
+ if (hr != S_OK)
+ return ReportFailure(hr, GOOGLE_UPDATE_GET_VERSION_INFO_FAILED, main_loop);
+
+ main_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &GoogleUpdate::ReportResults, results, GOOGLE_UPDATE_NO_ERROR));
+ job_holder = NULL;
+ on_demand = NULL;
+ return true;
+}
+
+void GoogleUpdate::ReportResults(GoogleUpdateUpgradeResult results,
+ GoogleUpdateErrorCode error_code) {
+ // If we get an error, then error code must not be blank, and vice versa.
+ DCHECK(results == UPGRADE_ERROR ? error_code != GOOGLE_UPDATE_NO_ERROR :
+ error_code == GOOGLE_UPDATE_NO_ERROR);
+ if (listener_)
+ listener_->OnReportResults(results, error_code, version_available_);
+}
+
+bool GoogleUpdate::ReportFailure(HRESULT hr, GoogleUpdateErrorCode error_code,
+ MessageLoop* main_loop) {
+ NOTREACHED() << "Communication with Google Update failed: " << hr
+ << " error: " << error_code;
+ main_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &GoogleUpdate::ReportResults, UPGRADE_ERROR, error_code));
+ return false;
+}
diff --git a/chrome/browser/google/google_update.h b/chrome/browser/google/google_update.h
new file mode 100644
index 0000000..24ff093
--- /dev/null
+++ b/chrome/browser/google/google_update.h
@@ -0,0 +1,144 @@
+// Copyright (c) 2010 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_UPDATE_H_
+#define CHROME_BROWSER_GOOGLE_GOOGLE_UPDATE_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#if defined(OS_WIN)
+#include "google_update_idl.h"
+#endif
+
+class MessageLoop;
+namespace views {
+class Window;
+}
+
+// The status of the upgrade. UPGRADE_STARTED and UPGRADE_CHECK_STARTED are
+// internal states and will not be reported as results to the listener.
+enum GoogleUpdateUpgradeResult {
+ // The upgrade has started.
+ UPGRADE_STARTED = 0,
+ // A check for upgrade has been initiated.
+ UPGRADE_CHECK_STARTED,
+ // An update is available.
+ UPGRADE_IS_AVAILABLE,
+ // The upgrade happened successfully.
+ UPGRADE_SUCCESSFUL,
+ // No need to upgrade, we are up to date.
+ UPGRADE_ALREADY_UP_TO_DATE,
+ // An error occurred.
+ UPGRADE_ERROR,
+};
+
+enum GoogleUpdateErrorCode {
+ // The upgrade completed successfully (or hasn't been started yet).
+ GOOGLE_UPDATE_NO_ERROR = 0,
+ // Google Update only supports upgrading if Chrome is installed in the default
+ // location. This error will appear for developer builds and with
+ // installations unzipped to random locations.
+ CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY,
+ // Failed to create Google Update JobServer COM class.
+ GOOGLE_UPDATE_JOB_SERVER_CREATION_FAILED,
+ // Failed to create Google Update OnDemand COM class.
+ GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND,
+ // Google Update OnDemand COM class reported an error during a check for
+ // update (or while upgrading).
+ GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR,
+ // A call to GetResults failed.
+ GOOGLE_UPDATE_GET_RESULT_CALL_FAILED,
+ // A call to GetVersionInfo failed.
+ GOOGLE_UPDATE_GET_VERSION_INFO_FAILED,
+ // An error occurred while upgrading (or while checking for update).
+ // Check the Google Update log in %TEMP% for more details.
+ GOOGLE_UPDATE_ERROR_UPDATING,
+};
+
+// The GoogleUpdateStatusListener interface is used by components to receive
+// notifications about the results of an Google Update operation.
+class GoogleUpdateStatusListener {
+ public:
+ // This function is called when Google Update has finished its operation and
+ // wants to notify us about the results. |results| represents what the end
+ // state is, |error_code| represents what error occurred and |version|
+ // specifies what new version Google Update detected (or installed). This
+ // value can be a blank string, if the version tag in the Update{} block
+ // (in Google Update's server config for Chrome) is blank.
+ virtual void OnReportResults(GoogleUpdateUpgradeResult results,
+ GoogleUpdateErrorCode error_code,
+ const std::wstring& version) = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// The Google Update class is responsible for communicating with Google Update
+// and get it to perform operations on our behalf (for example, CheckForUpdate).
+// This class will report back to its parent via the GoogleUpdateStatusListener
+// interface and will delete itself after reporting back.
+//
+////////////////////////////////////////////////////////////////////////////////
+class GoogleUpdate : public base::RefCountedThreadSafe<GoogleUpdate> {
+ public:
+ GoogleUpdate();
+
+ // Ask Google Update to see if a new version is available. If the parameter
+ // |install_if_newer| is true then Google Update will also install that new
+ // version.
+ // |window| should point to a foreground window. This is needed to ensure
+ // that Vista/Windows 7 UAC prompts show up in the foreground. It may also
+ // be null.
+ void CheckForUpdate(bool install_if_newer, views::Window* window);
+
+ // Pass NULL to clear the listener
+ void set_status_listener(GoogleUpdateStatusListener* listener) {
+ listener_ = listener;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<GoogleUpdate>;
+
+ virtual ~GoogleUpdate();
+
+// The chromeos implementation is in browser/chromeos/google_update.cpp
+
+#if defined(OS_WIN)
+
+ // This function reports failure from the Google Update operation to the
+ // listener.
+ // Note, after this function completes, this object will have deleted itself.
+ bool ReportFailure(HRESULT hr, GoogleUpdateErrorCode error_code,
+ MessageLoop* main_loop);
+
+#endif
+
+ // We need to run the update check on another thread than the main thread, and
+ // therefore CheckForUpdate will delegate to this function. |main_loop| points
+ // to the message loop that we want the response to come from.
+ // |window| should point to a foreground window. This is needed to ensure that
+ // Vista/Windows 7 UAC prompts show up in the foreground. It may also be null.
+ bool InitiateGoogleUpdateCheck(bool install_if_newer, views::Window* window,
+ MessageLoop* main_loop);
+
+ // This function reports the results of the GoogleUpdate operation to the
+ // listener. If results indicates an error, the error_code will indicate which
+ // error occurred.
+ // Note, after this function completes, this object will have deleted itself.
+ void ReportResults(GoogleUpdateUpgradeResult results,
+ GoogleUpdateErrorCode error_code);
+
+ // Which version string Google Update found (if a new one was available).
+ // Otherwise, this will be blank.
+ std::wstring version_available_;
+
+ // The listener who is interested in finding out the result of the operation.
+ GoogleUpdateStatusListener* listener_;
+
+ DISALLOW_COPY_AND_ASSIGN(GoogleUpdate);
+};
+
+#endif // CHROME_BROWSER_GOOGLE_GOOGLE_UPDATE_H_
diff --git a/chrome/browser/google/google_update_settings_posix.cc b/chrome/browser/google/google_update_settings_posix.cc
new file mode 100644
index 0000000..58be4b9
--- /dev/null
+++ b/chrome/browser/google/google_update_settings_posix.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2009 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/installer/util/google_update_settings.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+
+namespace google_update {
+std::string posix_guid;
+}
+
+// File name used in the user data dir to indicate consent.
+static const char kConsentToSendStats[] = "Consent To Send Stats";
+
+// static
+bool GoogleUpdateSettings::GetCollectStatsConsent() {
+ bool forced_enable = CommandLine::ForCurrentProcess()->
+ HasSwitch(switches::kEnableCrashReporter);
+ FilePath consent_file;
+ PathService::Get(chrome::DIR_USER_DATA, &consent_file);
+ consent_file = consent_file.Append(kConsentToSendStats);
+ std::string tmp_guid;
+ bool consented = file_util::ReadFileToString(consent_file, &tmp_guid);
+ if (forced_enable || consented)
+ google_update::posix_guid.assign(tmp_guid);
+ return forced_enable || consented;
+}
+
+// static
+bool GoogleUpdateSettings::SetCollectStatsConsent(bool consented) {
+ FilePath consent_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &consent_dir);
+ if (!file_util::DirectoryExists(consent_dir))
+ return false;
+
+ FilePath consent_file = consent_dir.AppendASCII(kConsentToSendStats);
+ if (consented) {
+ if ((!file_util::PathExists(consent_file)) ||
+ (file_util::PathExists(consent_file) &&
+ !google_update::posix_guid.empty())) {
+ const char* c_str = google_update::posix_guid.c_str();
+ int size = google_update::posix_guid.size();
+ return file_util::WriteFile(consent_file, c_str, size) == size;
+ }
+ } else {
+ google_update::posix_guid.clear();
+ return file_util::Delete(consent_file, false);
+ }
+ return true;
+}
+
+bool GoogleUpdateSettings::SetMetricsId(const std::wstring& client_id) {
+ // Make sure that user has consented to send crashes.
+ FilePath consent_dir;
+ PathService::Get(chrome::DIR_USER_DATA, &consent_dir);
+ if (!file_util::DirectoryExists(consent_dir) ||
+ !GoogleUpdateSettings::GetCollectStatsConsent())
+ return false;
+
+ // Since user has consented, write the metrics id to the file.
+ google_update::posix_guid = WideToASCII(client_id);
+ return GoogleUpdateSettings::SetCollectStatsConsent(true);
+}
+
+// GetLastRunTime and SetLastRunTime are not implemented for posix. Their
+// current return values signal failure which the caller is designed to
+// handle.
+
+// static
+int GoogleUpdateSettings::GetLastRunTime() {
+ return -1;
+}
+
+// static
+bool GoogleUpdateSettings::SetLastRunTime() {
+ return false;
+}
diff --git a/chrome/browser/google/google_update_settings_unittest.cc b/chrome/browser/google/google_update_settings_unittest.cc
new file mode 100644
index 0000000..8dc56bb
--- /dev/null
+++ b/chrome/browser/google/google_update_settings_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2009 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/installer/util/google_update_settings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+class GoogleUpdateTest : public PlatformTest {
+};
+
+TEST_F(GoogleUpdateTest, StatsConstent) {
+ // Stats are off by default.
+ EXPECT_FALSE(GoogleUpdateSettings::GetCollectStatsConsent());
+ // Stats reporting is ON.
+ EXPECT_TRUE(GoogleUpdateSettings::SetCollectStatsConsent(true));
+ EXPECT_TRUE(GoogleUpdateSettings::GetCollectStatsConsent());
+ // Stats reporting is OFF.
+ EXPECT_TRUE(GoogleUpdateSettings::SetCollectStatsConsent(false));
+ EXPECT_FALSE(GoogleUpdateSettings::GetCollectStatsConsent());
+}
+
+#if defined(OS_WIN)
+
+TEST_F(GoogleUpdateTest, LastRunTime) {
+ // Querying the value that does not exists should fail.
+ EXPECT_TRUE(GoogleUpdateSettings::RemoveLastRunTime());
+ EXPECT_EQ(-1, GoogleUpdateSettings::GetLastRunTime());
+ // Setting and querying the last update time in fast sequence
+ // should give 0 days.
+ EXPECT_TRUE(GoogleUpdateSettings::SetLastRunTime());
+ EXPECT_EQ(0, GoogleUpdateSettings::GetLastRunTime());
+}
+
+#endif // defined(OS_WIN)
diff --git a/chrome/browser/google/google_url_tracker.cc b/chrome/browser/google/google_url_tracker.cc
new file mode 100644
index 0000000..6760483
--- /dev/null
+++ b/chrome/browser/google/google_url_tracker.cc
@@ -0,0 +1,348 @@
+// Copyright (c) 2010 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/google/google_url_tracker.h"
+
+#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/prefs/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(runnable_method_factory_(this)),
+ fetcher_id_(0),
+ in_startup_sleep_(true),
+ already_fetched_(false),
+ need_to_fetch_(false),
+ 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.
+ // Ideally, instead of this timer, we'd do something like "check if the
+ // browser is starting up, and if so, come back later", but there is currently
+ // no function to do this.
+ static const int kStartFetchDelayMS = 5000;
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ runnable_method_factory_.NewRunnableMethod(
+ &GoogleURLTracker::FinishSleep),
+ kStartFetchDelayMS);
+}
+
+GoogleURLTracker::~GoogleURLTracker() {
+ runnable_method_factory_.RevokeAll();
+ net::NetworkChangeNotifier::RemoveObserver(this);
+}
+
+// static
+GURL GoogleURLTracker::GoogleURL() {
+ const GoogleURLTracker* const tracker =
+ g_browser_process->google_url_tracker();
+ return tracker ? tracker->google_url_ : GURL(kDefaultGoogleHomepage);
+}
+
+// static
+void GoogleURLTracker::RequestServerCheck() {
+ GoogleURLTracker* const tracker = g_browser_process->google_url_tracker();
+ if (tracker)
+ tracker->SetNeedToFetch();
+}
+
+// static
+void GoogleURLTracker::RegisterPrefs(PrefService* prefs) {
+ prefs->RegisterStringPref(prefs::kLastKnownGoogleURL,
+ kDefaultGoogleHomepage);
+ prefs->RegisterStringPref(prefs::kLastPromptedGoogleURL, std::string());
+}
+
+// static
+void GoogleURLTracker::GoogleURLSearchCommitted() {
+ GoogleURLTracker* tracker = g_browser_process->google_url_tracker();
+ if (tracker)
+ tracker->SearchCommitted();
+}
+
+void GoogleURLTracker::SetNeedToFetch() {
+ need_to_fetch_ = true;
+ StartFetchIfDesirable();
+}
+
+void GoogleURLTracker::FinishSleep() {
+ in_startup_sleep_ = false;
+ StartFetchIfDesirable();
+}
+
+void GoogleURLTracker::StartFetchIfDesirable() {
+ // Bail if a fetch isn't appropriate right now. This function will be called
+ // again each time one of the preconditions changes, so we'll fetch
+ // immediately once all of them are met.
+ //
+ // See comments in header on the class, on RequestServerCheck(), and on the
+ // various members here for more detail on exactly what the conditions are.
+ if (in_startup_sleep_ || already_fetched_ || !need_to_fetch_ ||
+ !request_context_available_)
+ return;
+
+ 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.
+ fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE |
+ net::LOAD_DO_NOT_SAVE_COOKIES);
+ fetcher_->set_request_context(Profile::GetDefaultRequestContext());
+ fetcher_->Start();
+}
+
+void GoogleURLTracker::OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ // Delete the fetcher on this function's exit.
+ 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)) {
+ already_fetched_ = false;
+ return;
+ }
+
+ // See if the response data was one we want to use, and if so, convert to the
+ // appropriate Google base URL.
+ std::string url_str;
+ TrimWhitespace(data, TRIM_ALL, &url_str);
+
+ if (!StartsWithASCII(url_str, ".google.", false))
+ return;
+
+ 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) {
+ switch (type.value) {
+ case NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE:
+ registrar_.Remove(this,
+ NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE,
+ NotificationService::AllSources());
+ request_context_available_ = true;
+ StartFetchIfDesirable();
+ break;
+
+ case NotificationType::NAV_ENTRY_PENDING:
+ controller_ = Source<NavigationController>(source).ptr();
+ search_url_ = controller_->pending_entry()->url();
+ // We don't need to listen NAV_ENTRY_PENDING any more, until another
+ // search is committed.
+ registrar_.Remove(this, NotificationType::NAV_ENTRY_PENDING,
+ NotificationService::AllSources());
+ // 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:
+ registrar_.RemoveAll();
+ DCHECK(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() {
+ if (!registrar_.IsEmpty() || (!need_to_prompt_ && !fetcher_.get()))
+ return;
+ registrar_.Add(this, NotificationType::NAV_ENTRY_PENDING,
+ NotificationService::AllSources());
+}
+
+void GoogleURLTracker::ShowGoogleURLInfoBarIfNecessary(
+ TabContents* tab_contents) {
+ if (!need_to_prompt_)
+ 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());
+}
diff --git a/chrome/browser/google/google_url_tracker.h b/chrome/browser/google/google_url_tracker.h
new file mode 100644
index 0000000..4b9ba57
--- /dev/null
+++ b/chrome/browser/google/google_url_tracker.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2010 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_
+#pragma once
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/net/url_fetcher.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/network_change_notifier.h"
+
+class InfoBarDelegate;
+class NavigationController;
+class PrefService;
+class TabContents;
+class TemplateURL;
+
+// 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 NOTIFY_GOOGLE_URL_UPDATED, and call GoogleURL()
+// again after receiving it, in order to get the updated value.
+//
+// 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 URLFetcher::Delegate,
+ public NotificationObserver,
+ public net::NetworkChangeNotifier::Observer {
+ public:
+ class InfoBarDelegateFactory {
+ public:
+ virtual ~InfoBarDelegateFactory() {}
+ virtual InfoBarDelegate* CreateInfoBar(TabContents* tab_contents,
+ GoogleURLTracker* google_url_tracker,
+ const GURL& new_google_url);
+ };
+
+ // Only the main browser process loop should call this, when setting up
+ // g_browser_process->google_url_tracker_. No code other than the
+ // GoogleURLTracker itself should actually use
+ // g_browser_process->google_url_tracker() (which shouldn't be hard, since
+ // there aren't useful public functions on this object for consumers to access
+ // anyway).
+ GoogleURLTracker();
+
+ virtual ~GoogleURLTracker();
+
+ // Returns the current Google URL. This will return a valid URL even in
+ // unittest mode.
+ //
+ // This is the only function most code should ever call.
+ static GURL GoogleURL();
+
+ // 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).
+ //
+ // In unittest mode, this function does nothing.
+ static void RequestServerCheck();
+
+ static void RegisterPrefs(PrefService* prefs);
+
+ // 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.
+ static void GoogleURLSearchCommitted();
+
+ static const char kDefaultGoogleHomepage[];
+ static const char kSearchDomainCheckURL[];
+
+ // Methods called from InfoBar delegate.
+ void AcceptGoogleURL(const GURL& google_url);
+ void InfoBarClosed();
+ void RedoSearch();
+
+ NavigationController* controller() const { return controller_; }
+
+ private:
+ friend class GoogleURLTrackerTest;
+
+ // Registers consumer interest in getting an updated URL from the server.
+ // It will be notified as NotificationType::GOOGLE_URL_UPDATED, so the
+ // consumer should observe this notification before calling this.
+ 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();
+
+ // URLFetcher::Delegate
+ virtual void OnURLFetchComplete(const URLFetcher *source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // NetworkChangeNotifier::Observer
+ virtual void OnIPAddressChanged();
+
+ void SearchCommitted();
+
+ void ShowGoogleURLInfoBarIfNecessary(TabContents* tab_contents);
+
+ NotificationRegistrar registrar_;
+ // 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_;
+ ScopedRunnableMethodFactory<GoogleURLTracker> runnable_method_factory_;
+ scoped_ptr<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
+ // NotificationType::GOOGLE_URL_UPDATED.
+ bool request_context_available_;
+ // True when the profile has been loaded and the
+ // default request context created, so we can
+ // actually do the fetch with the right data.
+ 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.
+ NavigationController* controller_;
+ scoped_ptr<InfoBarDelegateFactory> infobar_factory_;
+ InfoBarDelegate* infobar_;
+ GURL search_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(GoogleURLTracker);
+};
+
+#endif // CHROME_BROWSER_GOOGLE_GOOGLE_URL_TRACKER_H_
diff --git a/chrome/browser/google/google_url_tracker_unittest.cc b/chrome/browser/google/google_url_tracker_unittest.cc
new file mode 100644
index 0000000..d23e9ea
--- /dev/null
+++ b/chrome/browser/google/google_url_tracker_unittest.cc
@@ -0,0 +1,458 @@
+// Copyright (c) 2010 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/google/google_url_tracker.h"
+#include "base/command_line.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/infobar_delegate.h"
+#include "chrome/common/net/url_fetcher.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "chrome/common/net/test_url_fetcher_factory.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_browser_process.h"
+#include "chrome/test/testing_pref_service.h"
+#include "chrome/test/testing_profile.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class TestNotificationObserver : public NotificationObserver {
+ public:
+ TestNotificationObserver() : notified_(false) {}
+ virtual ~TestNotificationObserver() {}
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ notified_ = true;
+ }
+ bool notified() const { return notified_; }
+ void ClearNotified() { notified_ = false; }
+ private:
+ bool notified_;
+};
+
+class TestInfoBar : public InfoBarDelegate {
+ public:
+ TestInfoBar(GoogleURLTracker* google_url_tracker,
+ const GURL& new_google_url)
+ : InfoBarDelegate(NULL),
+ google_url_tracker_(google_url_tracker),
+ new_google_url_(new_google_url) {}
+ virtual ~TestInfoBar() {}
+ GoogleURLTracker* google_url_tracker() const { return google_url_tracker_; }
+ const GURL& new_google_url() const { return new_google_url_; }
+
+ virtual InfoBar* CreateInfoBar() { return NULL; }
+
+ private:
+ GoogleURLTracker* google_url_tracker_;
+ GURL new_google_url_;
+};
+
+class TestInfoBarDelegateFactory
+ : public GoogleURLTracker::InfoBarDelegateFactory {
+ public:
+ virtual InfoBarDelegate* CreateInfoBar(TabContents* tab_contents,
+ GoogleURLTracker* google_url_tracker,
+ const GURL& new_google_url) {
+ return new TestInfoBar(google_url_tracker, new_google_url);
+ }
+};
+
+} // anonymous namespace
+
+class GoogleURLTrackerTest : public testing::Test {
+ protected:
+ GoogleURLTrackerTest()
+ : message_loop_(NULL),
+ io_thread_(NULL),
+ original_default_request_context_(NULL) {
+ }
+
+ void SetUp() {
+ original_default_request_context_ = Profile::GetDefaultRequestContext();
+ Profile::set_default_request_context(NULL);
+ message_loop_ = new MessageLoop(MessageLoop::TYPE_IO);
+ io_thread_ = new ChromeThread(ChromeThread::IO, message_loop_);
+ network_change_notifier_.reset(net::NetworkChangeNotifier::CreateMock());
+ testing_profile_.reset(new TestingProfile);
+ TestingBrowserProcess* testing_browser_process =
+ static_cast<TestingBrowserProcess*>(g_browser_process);
+ PrefService* pref_service = testing_profile_->GetPrefs();
+ testing_browser_process->SetPrefService(pref_service);
+ testing_browser_process->SetGoogleURLTracker(new GoogleURLTracker);
+
+ URLFetcher::set_factory(&fetcher_factory_);
+ observer_.reset(new TestNotificationObserver);
+ g_browser_process->google_url_tracker()->infobar_factory_.reset(
+ new TestInfoBarDelegateFactory);
+ }
+
+ void TearDown() {
+ URLFetcher::set_factory(NULL);
+ TestingBrowserProcess* testing_browser_process =
+ static_cast<TestingBrowserProcess*>(g_browser_process);
+ testing_browser_process->SetGoogleURLTracker(NULL);
+ testing_browser_process->SetPrefService(NULL);
+ testing_profile_.reset();
+ network_change_notifier_.reset();
+ delete io_thread_;
+ delete message_loop_;
+ Profile::set_default_request_context(original_default_request_context_);
+ original_default_request_context_ = NULL;
+ }
+
+ void CreateRequestContext() {
+ testing_profile_->CreateRequestContext();
+ Profile::set_default_request_context(testing_profile_->GetRequestContext());
+ NotificationService::current()->Notify(
+ NotificationType::DEFAULT_REQUEST_CONTEXT_AVAILABLE,
+ NotificationService::AllSources(), NotificationService::NoDetails());
+ }
+
+ TestURLFetcher* GetFetcherByID(int expected_id) {
+ return fetcher_factory_.GetFetcherByID(expected_id);
+ }
+
+ void MockSearchDomainCheckResponse(
+ int expected_id, const std::string& domain) {
+ TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(expected_id);
+ ASSERT_TRUE(fetcher);
+ fetcher->delegate()->OnURLFetchComplete(
+ fetcher,
+ GURL(GoogleURLTracker::kSearchDomainCheckURL),
+ URLRequestStatus(),
+ 200,
+ ResponseCookies(),
+ domain);
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void RequestServerCheck() {
+ registrar_.Add(observer_.get(), NotificationType::GOOGLE_URL_UPDATED,
+ NotificationService::AllSources());
+ GoogleURLTracker::RequestServerCheck();
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void FinishSleep() {
+ g_browser_process->google_url_tracker()->FinishSleep();
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void NotifyIPAddressChanged() {
+ net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ MessageLoop::current()->RunAllPending();
+ }
+
+ GURL GetFetchedGoogleURL() {
+ return g_browser_process->google_url_tracker()->fetched_google_url_;
+ }
+
+ void SetGoogleURL(const GURL& url) {
+ g_browser_process->google_url_tracker()->google_url_ = url;
+ }
+
+ void SetLastPromptedGoogleURL(const GURL& url) {
+ g_browser_process->local_state()->SetString(
+ prefs::kLastPromptedGoogleURL, url.spec());
+ }
+
+ GURL GetLastPromptedGoogleURL() {
+ return GURL(g_browser_process->local_state()->GetString(
+ prefs::kLastPromptedGoogleURL));
+ }
+
+ void SearchCommitted(const GURL& search_url) {
+ GoogleURLTracker* google_url_tracker =
+ g_browser_process->google_url_tracker();
+
+ google_url_tracker->SearchCommitted();
+ // GoogleURLTracker wait for NAV_ENTRY_PENDING.
+ // In NAV_ENTRY_PENDING, it will set search_url_.
+ google_url_tracker->search_url_ = search_url;
+ }
+
+ void NavEntryCommitted() {
+ GoogleURLTracker* google_url_tracker =
+ g_browser_process->google_url_tracker();
+ google_url_tracker->ShowGoogleURLInfoBarIfNecessary(NULL);
+ }
+
+ bool InfoBarIsShown() {
+ return (g_browser_process->google_url_tracker()->infobar_ != NULL);
+ }
+
+ GURL GetInfoBarShowingURL() {
+ TestInfoBar* infobar = static_cast<TestInfoBar*>(
+ g_browser_process->google_url_tracker()->infobar_);
+ return infobar->new_google_url();
+ }
+
+ void AcceptGoogleURL() {
+ TestInfoBar* infobar = static_cast<TestInfoBar*>(
+ g_browser_process->google_url_tracker()->infobar_);
+ ASSERT_TRUE(infobar);
+ ASSERT_TRUE(infobar->google_url_tracker());
+ infobar->google_url_tracker()->AcceptGoogleURL(infobar->new_google_url());
+ }
+
+ void InfoBarClosed() {
+ TestInfoBar* infobar = static_cast<TestInfoBar*>(
+ g_browser_process->google_url_tracker()->infobar_);
+ ASSERT_TRUE(infobar);
+ ASSERT_TRUE(infobar->google_url_tracker());
+ infobar->google_url_tracker()->InfoBarClosed();
+ delete infobar;
+ }
+
+ void ExpectDefaultURLs() {
+ EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage),
+ GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL(), GetFetchedGoogleURL());
+ }
+
+ private:
+ MessageLoop* message_loop_;
+ ChromeThread* io_thread_;
+ scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
+ scoped_ptr<TestingProfile> testing_profile_;
+
+ TestURLFetcherFactory fetcher_factory_;
+ NotificationRegistrar registrar_;
+ scoped_ptr<NotificationObserver> observer_;
+
+ URLRequestContextGetter* original_default_request_context_;
+};
+
+TEST_F(GoogleURLTrackerTest, StartupSleepFinish) {
+ CreateRequestContext();
+
+ RequestServerCheck();
+ EXPECT_FALSE(GetFetcherByID(0));
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.uk");
+
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ // GoogleURL is updated, becase it was not the last prompted URL.
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, StartupSleepFinishWithLastPrompted) {
+ CreateRequestContext();
+ SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
+
+ RequestServerCheck();
+ EXPECT_FALSE(GetFetcherByID(0));
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.uk");
+
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ // GoogleURL should not be updated.
+ EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage),
+ GoogleURLTracker::GoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, StartupSleepFinishNoObserver) {
+ CreateRequestContext();
+
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ EXPECT_FALSE(GetFetcherByID(0));
+
+ ExpectDefaultURLs();
+}
+
+TEST_F(GoogleURLTrackerTest, MonitorNetworkChange) {
+ CreateRequestContext();
+
+ RequestServerCheck();
+ EXPECT_FALSE(GetFetcherByID(0));
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.uk");
+
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ // GoogleURL is updated, becase it was not the last prompted URL.
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+
+ NotifyIPAddressChanged();
+ MockSearchDomainCheckResponse(1, ".google.co.in");
+
+ EXPECT_EQ(GURL("http://www.google.co.in/"), GetFetchedGoogleURL());
+ // Don't update GoogleURL.
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, MonitorNetworkChangeNoObserver) {
+ CreateRequestContext();
+
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ NotifyIPAddressChanged();
+ EXPECT_FALSE(GetFetcherByID(0));
+
+ ExpectDefaultURLs();
+}
+
+TEST_F(GoogleURLTrackerTest, MonitorNetworkChangeAndObserverRegister) {
+ CreateRequestContext();
+
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ NotifyIPAddressChanged();
+ EXPECT_FALSE(GetFetcherByID(0));
+
+ ExpectDefaultURLs();
+
+ RequestServerCheck();
+ MockSearchDomainCheckResponse(0, ".google.co.uk");
+
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, NoSearchCommitedAndPromptedNotChanged) {
+ CreateRequestContext();
+ SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
+ RequestServerCheck();
+ EXPECT_FALSE(GetFetcherByID(0));
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.jp");
+
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, SearchCommitedAndUserSayNo) {
+ CreateRequestContext();
+ SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
+
+ ExpectDefaultURLs();
+
+ RequestServerCheck();
+ EXPECT_FALSE(GetFetcherByID(0));
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.jp");
+
+ EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage),
+ GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
+
+ SearchCommitted(GURL("http://www.google.co.uk/search?q=test"));
+ NavEntryCommitted();
+
+ EXPECT_TRUE(InfoBarIsShown());
+ EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage),
+ GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetInfoBarShowingURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetLastPromptedGoogleURL());
+
+ InfoBarClosed();
+ EXPECT_FALSE(InfoBarIsShown());
+ EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage),
+ GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetLastPromptedGoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, SearchCommitedAndUserSayYes) {
+ CreateRequestContext();
+ SetLastPromptedGoogleURL(GURL("http://www.google.co.uk/"));
+
+ ExpectDefaultURLs();
+
+ RequestServerCheck();
+ EXPECT_FALSE(GetFetcherByID(0));
+ ExpectDefaultURLs();
+
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.jp");
+
+ EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage),
+ GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
+
+ SearchCommitted(GURL("http://www.google.co.uk/search?q=test"));
+ NavEntryCommitted();
+
+ EXPECT_TRUE(InfoBarIsShown());
+ EXPECT_EQ(GURL(GoogleURLTracker::kDefaultGoogleHomepage),
+ GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetInfoBarShowingURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetLastPromptedGoogleURL());
+
+ AcceptGoogleURL();
+ InfoBarClosed();
+
+ EXPECT_FALSE(InfoBarIsShown());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.jp/"), GetLastPromptedGoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, InitialUpdate) {
+ CreateRequestContext();
+ ExpectDefaultURLs();
+ EXPECT_EQ(GURL(), GetLastPromptedGoogleURL());
+
+ RequestServerCheck();
+ EXPECT_FALSE(GetFetcherByID(0));
+ ExpectDefaultURLs();
+ EXPECT_EQ(GURL(), GetLastPromptedGoogleURL());
+
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.uk");
+
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
+
+ SearchCommitted(GURL("http://www.google.co.uk/search?q=test"));
+ NavEntryCommitted();
+
+ EXPECT_FALSE(InfoBarIsShown());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
+}
+
+TEST_F(GoogleURLTrackerTest, UpdatePromptedURLWhenBack) {
+ CreateRequestContext();
+ SetLastPromptedGoogleURL(GURL("http://www.google.co.jp/"));
+ SetGoogleURL(GURL("http://www.google.co.uk/"));
+
+ RequestServerCheck();
+ FinishSleep();
+ MockSearchDomainCheckResponse(0, ".google.co.uk");
+
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
+
+ SearchCommitted(GURL("http://www.google.co.uk/search?q=test"));
+ NavEntryCommitted();
+
+ EXPECT_FALSE(InfoBarIsShown());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetFetchedGoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GoogleURLTracker::GoogleURL());
+ EXPECT_EQ(GURL("http://www.google.co.uk/"), GetLastPromptedGoogleURL());
+}
diff --git a/chrome/browser/google/google_util.cc b/chrome/browser/google/google_util.cc
new file mode 100644
index 0000000..a9a3439
--- /dev/null
+++ b/chrome/browser/google/google_util.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2006-2008 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/google/google_util.h"
+
+#include <string>
+
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/google/google_url_tracker.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/registry_controlled_domain.h"
+
+namespace {
+
+// A helper method for adding a query param to |url|.
+GURL AppendParam(const GURL& url,
+ const std::string& param_name,
+ const std::string& param_value) {
+ std::string query(url.query());
+ if (!query.empty())
+ query += "&";
+ query += param_name + "=" + param_value;
+ GURL::Replacements repl;
+ repl.SetQueryStr(query);
+ return url.ReplaceComponents(repl);
+}
+
+} // anonymous namespace
+
+namespace google_util {
+
+const char kLinkDoctorBaseURL[] =
+ "http://linkhelp.clients.google.com/tbproxy/lh/fixurl";
+
+GURL AppendGoogleLocaleParam(const GURL& url) {
+ // Google does not yet recognize 'nb' for Norwegian Bokmal, but it uses
+ // 'no' for that.
+ std::string locale = g_browser_process->GetApplicationLocale();
+ if (locale == "nb")
+ locale = "no";
+ return AppendParam(url, "hl", locale);
+}
+
+GURL AppendGoogleTLDParam(const GURL& url) {
+ const std::string google_domain(
+ net::RegistryControlledDomainService::GetDomainAndRegistry(
+ GoogleURLTracker::GoogleURL()));
+ const size_t first_dot = google_domain.find('.');
+ if (first_dot == std::string::npos) {
+ NOTREACHED();
+ return url;
+ }
+ return AppendParam(url, "sd", google_domain.substr(first_dot + 1));
+}
+
+} // namespace google_util
diff --git a/chrome/browser/google/google_util.h b/chrome/browser/google/google_util.h
new file mode 100644
index 0000000..3741e70
--- /dev/null
+++ b/chrome/browser/google/google_util.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2008 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.
+//
+// Some Google related utility functions.
+
+#ifndef CHROME_BROWSER_GOOGLE_GOOGLE_UTIL_H__
+#define CHROME_BROWSER_GOOGLE_GOOGLE_UTIL_H__
+#pragma once
+
+class GURL;
+
+namespace google_util {
+
+extern const char kLinkDoctorBaseURL[];
+
+// Adds the Google locale string to the URL (e.g., hl=en-US). This does not
+// check to see if the param already exists.
+GURL AppendGoogleLocaleParam(const GURL& url);
+
+// Adds the Google TLD string to the URL (e.g., sd=com). This does not
+// check to see if the param already exists.
+GURL AppendGoogleTLDParam(const GURL& url);
+
+} // namespace google_util
+
+#endif // CHROME_BROWSER_GOOGLE_GOOGLE_UTIL_H__