diff options
author | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-30 22:22:49 +0000 |
---|---|---|
committer | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-30 22:22:49 +0000 |
commit | f7578f5d14dbd1c2931b7f574ead27cc978ab73a (patch) | |
tree | ddb316a54abc01a6219e4d129062390af5759be9 /chrome/browser/google | |
parent | 439764b703a5edd48aa878b86fbd07a117b6a3cc (diff) | |
download | chromium_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.cc | 331 | ||||
-rw-r--r-- | chrome/browser/google/google_update.h | 144 | ||||
-rw-r--r-- | chrome/browser/google/google_update_settings_posix.cc | 84 | ||||
-rw-r--r-- | chrome/browser/google/google_update_settings_unittest.cc | 35 | ||||
-rw-r--r-- | chrome/browser/google/google_url_tracker.cc | 348 | ||||
-rw-r--r-- | chrome/browser/google/google_url_tracker.h | 162 | ||||
-rw-r--r-- | chrome/browser/google/google_url_tracker_unittest.cc | 458 | ||||
-rw-r--r-- | chrome/browser/google/google_util.cc | 58 | ||||
-rw-r--r-- | chrome/browser/google/google_util.h | 27 |
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__ |