Index: chrome/browser/google/google_update_win.cc |
diff --git a/chrome/browser/google/google_update_win.cc b/chrome/browser/google/google_update_win.cc |
index 1e0d1b9c18b4b0b7ff7fbe10602f972a4754041a..a913c02fc63ac986afe6e5dbe47e705e95dfb37a 100644 |
--- a/chrome/browser/google/google_update_win.cc |
+++ b/chrome/browser/google/google_update_win.cc |
@@ -14,34 +14,64 @@ |
#include "base/metrics/histogram.h" |
#include "base/metrics/sparse_histogram.h" |
#include "base/path_service.h" |
+#include "base/sequenced_task_runner_helpers.h" |
+#include "base/single_thread_task_runner.h" |
#include "base/strings/string_util.h" |
#include "base/strings/stringprintf.h" |
-#include "base/task_runner.h" |
+#include "base/strings/utf_string_conversions.h" |
#include "base/thread_task_runner_handle.h" |
+#include "base/time/time.h" |
+#include "base/win/scoped_bstr.h" |
#include "base/win/windows_version.h" |
#include "chrome/grit/generated_resources.h" |
#include "chrome/installer/util/browser_distribution.h" |
-#include "chrome/installer/util/google_update_settings.h" |
#include "chrome/installer/util/helper.h" |
#include "chrome/installer/util/install_util.h" |
-#include "content/public/browser/browser_thread.h" |
#include "ui/base/l10n/l10n_util.h" |
#include "ui/base/win/atl_module.h" |
+#include "ui/gfx/geometry/safe_integer_conversions.h" |
namespace { |
-OnDemandAppsClassFactory* g_google_update_factory = nullptr; |
+// The status of the upgrade. These values are used for a histogram. Do not |
+// reorder. |
+enum GoogleUpdateUpgradeStatus { |
+ // The upgrade has started. DEPRECATED. |
+ // UPGRADE_STARTED = 0, |
+ // A check for upgrade has been initiated. DEPRECATED. |
+ // UPGRADE_CHECK_STARTED = 1, |
+ // An update is available. |
+ UPGRADE_IS_AVAILABLE = 2, |
+ // The upgrade happened successfully. |
+ UPGRADE_SUCCESSFUL = 3, |
+ // No need to upgrade, Chrome is up to date. |
+ UPGRADE_ALREADY_UP_TO_DATE = 4, |
+ // An error occurred. |
+ UPGRADE_ERROR = 5, |
+ NUM_UPGRADE_STATUS |
+}; |
+ |
+GoogleUpdate3ClassFactory* g_google_update_factory = nullptr; |
+ |
+// The time interval, in milliseconds, between polls to Google Update. This |
+// value was chosen unscientificaly during an informal discussion. |
+const int64_t kGoogleUpdatePollIntervalMs = 250; |
+ |
+// Constants from Google Update. |
+const HRESULT GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY = 0x80040813; |
+const HRESULT GOOPDATEINSTALL_E_INSTALLER_FAILED = 0x80040902; |
// Check if the currently running instance can be updated by Google Update. |
// Returns GOOGLE_UPDATE_NO_ERROR only if the instance running is a Google |
// Chrome distribution installed in a standard location. |
GoogleUpdateErrorCode CanUpdateCurrentChrome( |
const base::FilePath& chrome_exe_path, |
- bool system_level) { |
+ bool system_level_install) { |
#if !defined(GOOGLE_CHROME_BUILD) |
return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY; |
#else |
- DCHECK_NE(InstallUtil::IsPerUserInstall(chrome_exe_path), system_level); |
+ DCHECK_NE(InstallUtil::IsPerUserInstall(chrome_exe_path), |
+ system_level_install); |
BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
base::FilePath user_exe_path = installer::GetChromeInstallPath(false, dist); |
base::FilePath machine_exe_path = installer::GetChromeInstallPath(true, dist); |
@@ -52,18 +82,6 @@ GoogleUpdateErrorCode CanUpdateCurrentChrome( |
return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY; |
} |
- base::string16 app_guid = installer::GetAppGuidForUpdates(system_level); |
- DCHECK(!app_guid.empty()); |
- |
- GoogleUpdateSettings::UpdatePolicy update_policy = |
- GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, NULL); |
- |
- if (update_policy == GoogleUpdateSettings::UPDATES_DISABLED) |
- return GOOGLE_UPDATE_DISABLED_BY_POLICY; |
- |
- if (update_policy == GoogleUpdateSettings::AUTO_UPDATES_ONLY) |
- return GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY; |
- |
return GOOGLE_UPDATE_NO_ERROR; |
#endif |
} |
@@ -107,389 +125,582 @@ HRESULT CoCreateInstanceAsAdmin(REFCLSID class_id, |
interface_ptr); |
} |
-HRESULT CreateOnDemandAppsClass( |
- bool system_level, |
- bool install_if_newer, |
+HRESULT CreateGoogleUpdate3WebClass( |
+ bool system_level_install, |
+ bool install_update_if_possible, |
gfx::AcceleratedWidget elevation_window, |
- base::win::ScopedComPtr<IGoogleUpdate>* on_demand) { |
+ base::win::ScopedComPtr<IGoogleUpdate3Web>* google_update) { |
if (g_google_update_factory) |
- return g_google_update_factory->Run(on_demand); |
+ return g_google_update_factory->Run(google_update); |
// For a user-level install, update checks and updates can both be done by a |
- // normal user with the UserAppsClass. |
- if (!system_level) |
- return on_demand->CreateInstance(CLSID_OnDemandUserAppsClass); |
+ // normal user with the UserClass. |
+ if (!system_level_install) |
+ return google_update->CreateInstance(CLSID_GoogleUpdate3WebUserClass); |
// For a system-level install, update checks can be done by a normal user with |
- // the MachineAppsClass. |
- if (!install_if_newer) |
- return on_demand->CreateInstance(CLSID_OnDemandMachineAppsClass); |
+ // the ServiceClass. |
S. Ganesh
2015/05/13 20:21:40
MachineClass
grt (UTC plus 2)
2015/05/13 20:36:38
Nice catch. Will take care of in a follow-on CL.
|
+ if (!install_update_if_possible) |
+ return google_update->CreateInstance(CLSID_GoogleUpdate3WebMachineClass); |
// For a system-level install, an update requires Admin privileges for writing |
- // to %ProgramFiles%. Elevate while instantiating the MachineAppsClass. |
- return CoCreateInstanceAsAdmin(CLSID_OnDemandMachineAppsClass, |
- IID_IGoogleUpdate, elevation_window, |
- on_demand->ReceiveVoid()); |
-} |
- |
- |
-// GoogleUpdateJobObserver ----------------------------------------------------- |
- |
-// The GoogleUpdateJobObserver COM class is responsible for receiving status |
-// reports from google Update. It keeps track of the progress as Google Update |
-// notifies this observer and runs a completion callback 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(); |
- virtual ~GoogleUpdateJobObserver(); |
- |
- // Sets the callback to be invoked when Google Update reports that the job is |
- // done. |
- void set_on_complete_callback(const base::Closure& on_complete_callback) { |
- on_complete_callback_ = on_complete_callback; |
- } |
- |
- // Returns the results of the update operation. |
- GoogleUpdateUpgradeResult result() const { |
- // Intermediary steps should never be reported to the client. |
- DCHECK_NE(UPGRADE_STARTED, result_); |
- DCHECK_NE(UPGRADE_CHECK_STARTED, result_); |
- return result_; |
- } |
- |
- // Returns which version Google Update found on the server (if a more |
- // recent version was found). Otherwise, this will be blank. |
- base::string16 new_version() const { return new_version_; } |
- |
- // Returns the Google Update supplied error string that describes the error |
- // that occurred during the update check/upgrade. |
- base::string16 error_message() const { return error_message_; } |
- |
- private: |
- // IJobObserver: |
- STDMETHOD(OnShow)() override; |
- STDMETHOD(OnCheckingForUpdate)() override; |
- STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) override; |
- STDMETHOD(OnWaitingToDownload)() override; |
- STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) override; |
- STDMETHOD(OnWaitingToInstall)() override; |
- STDMETHOD(OnInstalling)() override; |
- STDMETHOD(OnPause)() override; |
- STDMETHOD(OnComplete)(LegacyCompletionCodes code, const TCHAR* text) override; |
- STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) override; |
- |
- // The task runner associated with the thread in which the job runs. |
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
- |
- // A callback to be run to complete processing; |
- base::Closure on_complete_callback_; |
- |
- // The status/result of the Google Update operation. |
- GoogleUpdateUpgradeResult result_; |
- |
- // The version string Google Update found. |
- base::string16 new_version_; |
- |
- // An error message, if any. |
- base::string16 error_message_; |
- |
- // Allows us control the upgrade process to a small degree. After OnComplete |
- // has been called, this object can not be used. |
- base::win::ScopedComPtr<IProgressWndEvents> event_sink_; |
- |
- DISALLOW_COPY_AND_ASSIGN(GoogleUpdateJobObserver); |
-}; |
- |
-GoogleUpdateJobObserver::GoogleUpdateJobObserver() |
- : task_runner_(base::ThreadTaskRunnerHandle::Get()), |
- result_(UPGRADE_ERROR) { |
-} |
- |
-GoogleUpdateJobObserver::~GoogleUpdateJobObserver() { |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnShow() { |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnCheckingForUpdate() { |
- result_ = UPGRADE_CHECK_STARTED; |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnUpdateAvailable( |
- const TCHAR* version_string) { |
- result_ = UPGRADE_IS_AVAILABLE; |
- new_version_ = version_string; |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnWaitingToDownload() { |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnDownloading(int time_remaining_ms, |
- int pos) { |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnWaitingToInstall() { |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnInstalling() { |
- result_ = UPGRADE_STARTED; |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnPause() { |
- return S_OK; |
-} |
- |
-STDMETHODIMP GoogleUpdateJobObserver::OnComplete(LegacyCompletionCodes code, |
- const TCHAR* text) { |
- if (code == COMPLETION_CODE_ERROR) { |
- error_message_ = text; |
- result_ = UPGRADE_ERROR; |
- } else { |
- // Everything that isn't an error is some form of success. Chrome doesn't |
- // support any of the fancy codes (e.g., COMPLETION_CODE_REBOOT), but they |
- // shouldn't be generated anyway. |
- LOG_IF(DFATAL, (code != COMPLETION_CODE_SUCCESS && |
- code != COMPLETION_CODE_SUCCESS_CLOSE_UI)) |
- << "Unexpected LegacyCompletionCode from IGoogleUpdate: " << code; |
- if (result_ == UPGRADE_STARTED) |
- result_ = UPGRADE_SUCCESSFUL; |
- else if (result_ == UPGRADE_CHECK_STARTED) |
- result_ = UPGRADE_ALREADY_UP_TO_DATE; |
- } |
- |
- event_sink_ = NULL; |
- |
- task_runner_->PostTask(FROM_HERE, on_complete_callback_); |
- return S_OK; |
+ // to %ProgramFiles%. Elevate while instantiating the ServiceClass. |
+ return CoCreateInstanceAsAdmin(CLSID_GoogleUpdate3WebMachineClass, |
+ IID_IGoogleUpdate3Web, elevation_window, |
+ google_update->ReceiveVoid()); |
} |
-STDMETHODIMP GoogleUpdateJobObserver::SetEventSink( |
- IProgressWndEvents* event_sink) { |
- event_sink_ = event_sink; |
- return S_OK; |
-} |
- |
- |
// UpdateCheckDriver ----------------------------------------------------------- |
// A driver that is created and destroyed on the caller's thread and drives |
// Google Update on another. |
class UpdateCheckDriver { |
public: |
- // Runs an update check on |task_runner|, invoking |callback| on the caller's |
- // thread upon completion. |task_runner| must run a TYPE_UI message loop if |
- // the default IGoogleUpdate on-demand COM class is used. |
- static void RunUpdateCheck(const scoped_refptr<base::TaskRunner>& task_runner, |
- bool install_if_newer, |
- gfx::AcceleratedWidget elevation_window, |
- const UpdateCheckCallback& callback); |
+ // Runs an update check on |task_runner|, invoking methods of |delegate| on |
+ // the caller's thread to report progress and final results. |
+ static void RunUpdateCheck( |
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
+ const std::string& locale, |
+ bool install_update_if_possible, |
+ gfx::AcceleratedWidget elevation_window, |
+ const base::WeakPtr<UpdateCheckDelegate>& delegate); |
private: |
friend class base::DeleteHelper<UpdateCheckDriver>; |
- explicit UpdateCheckDriver(const UpdateCheckCallback& callback); |
+ UpdateCheckDriver( |
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
+ const std::string& locale, |
+ bool install_update_if_possible, |
+ gfx::AcceleratedWidget elevation_window, |
+ const base::WeakPtr<UpdateCheckDelegate>& delegate); |
- // Runs the caller's update check callback with the results of the operation. |
+ // Invokes a completion or error method on the caller's delegate, as |
+ // appropriate. |
~UpdateCheckDriver(); |
// Starts an update check. |
- void BeginUpdateCheck(bool install_if_newer, |
- gfx::AcceleratedWidget elevation_window); |
+ void BeginUpdateCheck(); |
- // Helper function for starting an update check. Returns true if the check was |
- // properly started, in which case CompleteUpdateCheck will be invoked upon |
- // completion to return results to the caller on its own thread. |
- bool BeginUpdateCheckInternal(bool install_if_newer, |
- gfx::AcceleratedWidget elevation_window); |
+ // Returns the result of initiating an update check. Sets |error_code| if the |
+ // result is any kind of failure. |
+ HRESULT BeginUpdateCheckInternal(GoogleUpdateErrorCode* error_code); |
- // Invoked when results are in from Google Update. |
- void CompleteUpdateCheck(); |
- |
- // Prepares |results| to return the upgrade error indicated by |error_code| |
- // and |hr|. The string " -- system level" is included in the generated error |
- // message when |system_level| is true. |
+ // Sets status_ to UPGRADE_ERROR, error_code_ to |error_code|, hresult_ to |
+ // |hresult|, installer_exit_code_ to |installer_exit_code|, and |
+ // error_message_ to a composition of all values suitable for display to the |
+ // user. This call should be followed by deletion of the driver, which will |
+ // result in the caller being notified via its delegate. |
void OnUpgradeError(GoogleUpdateErrorCode error_code, |
- HRESULT hr, |
- bool system_level); |
+ HRESULT hresult, |
+ int installer_exit_code, |
+ const base::string16& error_string); |
+ |
+ // Returns true if |current_state| and |state_value| can be obtained from the |
+ // ongoing update check. Otherwise, populates |hresult| with the reason they |
+ // could not be obtained. |
+ bool GetCurrentState(base::win::ScopedComPtr<ICurrentState>* current_state, |
+ CurrentState* state_value, |
+ HRESULT* hresult) const; |
+ |
+ // Returns true if |current_state| and |state_value| constitute an error state |
+ // for the ongoing update check, in which case |error_code| is populated with |
+ // one of GOOGLE_UPDATE_ERROR_UPDATING, GOOGLE_UPDATE_DISABLED_BY_POLICY, or |
+ // GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR. |hresult| is populated with |
+ // the most relevant HRESULT (which may be a value from Google Update; see |
+ // https://code.google.com/p/omaha/source/browse/trunk/base/error.h). In case |
+ // Chrome's installer failed during execution, |installer_exit_code| may be |
+ // populated with its process exit code (see enum installer::InstallStatus in |
+ // chrome/installer/util/util_constants.h); otherwise, it will be -1. |
+ // |error_string| will be populated with a completion message if one is |
+ // provided by Google Update. |
+ bool IsErrorState(const base::win::ScopedComPtr<ICurrentState>& current_state, |
+ CurrentState state_value, |
+ GoogleUpdateErrorCode* error_code, |
+ HRESULT* hresult, |
+ int* installer_exit_code, |
+ base::string16* error_string) const; |
+ |
+ // Returns true if |current_state| and |state_value| constitute a final state |
+ // for the ongoing update check, in which case |upgrade_status| is populated |
+ // with one of UPGRADE_ALREADY_UP_TO_DATE or UPGRADE_IS_AVAILABLE (in case a |
+ // pure check is being performed rather than an update) or UPGRADE_SUCCESSFUL |
+ // (in case an update is being performed). For the UPGRADE_IS_AVAILABLE case, |
+ // |new_version| will be populated with the available version, if provided by |
+ // Google Update. |
+ bool IsFinalState(const base::win::ScopedComPtr<ICurrentState>& current_state, |
+ CurrentState state_value, |
+ GoogleUpdateUpgradeStatus* upgrade_status, |
+ base::string16* new_version) const; |
+ |
+ // Returns true if |current_state| and |state_value| constitute an |
+ // intermediate state for the ongoing update check. |new_version| will be |
+ // populated with the version to be installed if it is provided by Google |
+ // Update for the current state. |progress| will be populated with a number |
+ // between 0 and 100 according to how far Google Update has progressed in the |
+ // download and install process. |
+ bool IsIntermediateState( |
+ const base::win::ScopedComPtr<ICurrentState>& current_state, |
+ CurrentState state_value, |
+ base::string16* new_version, |
+ int* progress) const; |
+ |
+ // Polls Google Update to determine the state of the ongoing check or |
+ // update. If the process has reached a terminal state, this instance will be |
+ // deleted and the caller will be notified of the final status. Otherwise, the |
+ // caller will be notified of the intermediate state (iff it differs from a |
+ // previous notification) and another future poll will be scheduled. |
+ void PollGoogleUpdate(); |
+ |
+ // The task runner on which the update checks runs. |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
- // The caller's task runner, on which |result_callback_| will be run. |
+ // The caller's task runner, on which methods of |delegate_| will be invoked. |
scoped_refptr<base::SingleThreadTaskRunner> result_runner_; |
- // The caller's callback to be run when the update check is compelte. |
- UpdateCheckCallback result_callback_; |
+ // The UI locale. |
+ std::string locale_; |
- // The results of the update check. |
- GoogleUpdateUpgradeResult result_; |
- GoogleUpdateErrorCode error_code_; |
- base::string16 error_message_; |
- base::string16 version_; |
- HRESULT hresult_; |
+ // False to only check for an update; true to also install one if available. |
+ bool install_update_if_possible_; |
+ |
+ // A parent window in case any UX is required (e.g., an elevation prompt). |
+ gfx::AcceleratedWidget elevation_window_; |
- // A direct pointer to the job observer by which the driver is notified of |
- // interesting events from Google Update. |
- CComObject<GoogleUpdateJobObserver>* job_observer_; |
+ // The caller's delegate by which feedback is conveyed. |
+ base::WeakPtr<UpdateCheckDelegate> delegate_; |
- // A scoped pointer to |job_observer_| that holds a reference to it, keeping |
- // it alive. |
- base::win::ScopedComPtr<IJobObserver> job_holder_; |
+ // True if operating on a per-machine installation rather than a per-user one. |
+ bool system_level_install_; |
// The on-demand updater that is doing the work. |
- base::win::ScopedComPtr<IGoogleUpdate> on_demand_; |
+ base::win::ScopedComPtr<IGoogleUpdate3Web> google_update_; |
+ |
+ // An app bundle containing the application being updated. |
+ base::win::ScopedComPtr<IAppBundleWeb> app_bundle_; |
+ |
+ // The application being updated (Chrome, Chrome Binaries, or Chrome SxS). |
+ base::win::ScopedComPtr<IAppWeb> app_; |
+ |
+ // The progress value reported most recently to the caller. |
+ int last_reported_progress_; |
+ |
+ // The results of the update check to be logged via UMA and/or reported to the |
+ // caller. |
+ GoogleUpdateUpgradeStatus status_; |
+ GoogleUpdateErrorCode error_code_; |
+ base::string16 error_message_; |
+ base::string16 new_version_; |
+ HRESULT hresult_; |
+ int installer_exit_code_; |
DISALLOW_COPY_AND_ASSIGN(UpdateCheckDriver); |
}; |
// static |
void UpdateCheckDriver::RunUpdateCheck( |
- const scoped_refptr<base::TaskRunner>& task_runner, |
- bool install_if_newer, |
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
+ const std::string& locale, |
+ bool install_update_if_possible, |
gfx::AcceleratedWidget elevation_window, |
- const UpdateCheckCallback& callback) { |
+ const base::WeakPtr<UpdateCheckDelegate>& delegate) { |
// The driver is owned by itself, and will self-destruct when its work is |
// done. |
- UpdateCheckDriver* driver = new UpdateCheckDriver(callback); |
- task_runner->PostTask( |
- FROM_HERE, |
- base::Bind(&UpdateCheckDriver::BeginUpdateCheck, base::Unretained(driver), |
- install_if_newer, elevation_window)); |
+ UpdateCheckDriver* driver = |
+ new UpdateCheckDriver(task_runner, locale, install_update_if_possible, |
+ elevation_window, delegate); |
+ task_runner->PostTask(FROM_HERE, |
+ base::Bind(&UpdateCheckDriver::BeginUpdateCheck, |
+ base::Unretained(driver))); |
} |
// Runs on the caller's thread. |
-UpdateCheckDriver::UpdateCheckDriver(const UpdateCheckCallback& callback) |
- : result_runner_(base::ThreadTaskRunnerHandle::Get()), |
- result_callback_(callback), |
- result_(UPGRADE_ERROR), |
+UpdateCheckDriver::UpdateCheckDriver( |
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
+ const std::string& locale, |
+ bool install_update_if_possible, |
+ gfx::AcceleratedWidget elevation_window, |
+ const base::WeakPtr<UpdateCheckDelegate>& delegate) |
+ : task_runner_(task_runner), |
+ result_runner_(base::ThreadTaskRunnerHandle::Get()), |
+ locale_(locale), |
+ install_update_if_possible_(install_update_if_possible), |
+ elevation_window_(elevation_window), |
+ delegate_(delegate), |
+ system_level_install_(false), |
+ last_reported_progress_(0), |
+ status_(UPGRADE_ERROR), |
error_code_(GOOGLE_UPDATE_NO_ERROR), |
hresult_(S_OK), |
- job_observer_(nullptr) { |
+ installer_exit_code_(-1) { |
} |
UpdateCheckDriver::~UpdateCheckDriver() { |
DCHECK(result_runner_->BelongsToCurrentThread()); |
// If there is an error, then error_code must not be blank, and vice versa. |
- DCHECK_NE(result_ == UPGRADE_ERROR, error_code_ == GOOGLE_UPDATE_NO_ERROR); |
- UMA_HISTOGRAM_ENUMERATION("GoogleUpdate.UpgradeResult", result_, |
- NUM_UPGRADE_RESULTS); |
- if (result_ == UPGRADE_ERROR) { |
+ DCHECK_NE(status_ == UPGRADE_ERROR, error_code_ == GOOGLE_UPDATE_NO_ERROR); |
+ UMA_HISTOGRAM_ENUMERATION("GoogleUpdate.UpgradeResult", status_, |
+ NUM_UPGRADE_STATUS); |
+ if (status_ == UPGRADE_ERROR) { |
UMA_HISTOGRAM_ENUMERATION("GoogleUpdate.UpdateErrorCode", error_code_, |
NUM_ERROR_CODES); |
- if (hresult_ != S_OK) |
+ if (FAILED(hresult_)) |
UMA_HISTOGRAM_SPARSE_SLOWLY("GoogleUpdate.ErrorHresult", hresult_); |
+ if (installer_exit_code_ != -1) { |
+ UMA_HISTOGRAM_SPARSE_SLOWLY("GoogleUpdate.InstallerExitCode", |
+ installer_exit_code_); |
+ } |
+ } |
+ if (delegate_) { |
+ if (status_ == UPGRADE_ERROR) |
+ delegate_->OnError(error_code_, error_message_, new_version_); |
+ else if (install_update_if_possible_) |
+ delegate_->OnUpgradeComplete(new_version_); |
+ else |
+ delegate_->OnUpdateCheckComplete(new_version_); |
} |
- result_callback_.Run(result_, error_code_, error_message_, version_); |
} |
-void UpdateCheckDriver::BeginUpdateCheck( |
- bool install_if_newer, |
- gfx::AcceleratedWidget elevation_window) { |
- // Return results immediately if the driver is not waiting for Google Update. |
- if (!BeginUpdateCheckInternal(install_if_newer, elevation_window)) |
+void UpdateCheckDriver::BeginUpdateCheck() { |
+ GoogleUpdateErrorCode error_code = GOOGLE_UPDATE_NO_ERROR; |
+ HRESULT hresult = BeginUpdateCheckInternal(&error_code); |
+ if (SUCCEEDED(hresult)) { |
+ // Start polling. |
+ task_runner_->PostTask(FROM_HERE, |
+ base::Bind(&UpdateCheckDriver::PollGoogleUpdate, |
+ base::Unretained(this))); |
+ } else { |
+ // Return results immediately since the driver is not polling Google Update. |
+ OnUpgradeError(error_code, hresult, -1, base::string16()); |
result_runner_->DeleteSoon(FROM_HERE, this); |
+ } |
} |
-bool UpdateCheckDriver::BeginUpdateCheckInternal( |
- bool install_if_newer, |
- gfx::AcceleratedWidget elevation_window) { |
+HRESULT UpdateCheckDriver::BeginUpdateCheckInternal( |
+ GoogleUpdateErrorCode* error_code) { |
base::FilePath chrome_exe; |
if (!PathService::Get(base::DIR_EXE, &chrome_exe)) |
NOTREACHED(); |
- const bool system_level = !InstallUtil::IsPerUserInstall(chrome_exe); |
- |
- // The failures handled here are: |
- // CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY, |
- // GOOGLE_UPDATE_DISABLED_BY_POLICY, and |
- // GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY. |
- error_code_ = CanUpdateCurrentChrome(chrome_exe, system_level); |
- if (error_code_ != GOOGLE_UPDATE_NO_ERROR) { |
- // These failures are handled in the UX with custom messages. |
- result_ = UPGRADE_ERROR; |
- return false; |
- } |
+ system_level_install_ = !InstallUtil::IsPerUserInstall(chrome_exe); |
// Make sure ATL is initialized in this module. |
ui::win::CreateATLModuleIfNeeded(); |
- HRESULT hr = |
- CComObject<GoogleUpdateJobObserver>::CreateInstance(&job_observer_); |
- if (hr != S_OK) { |
- // Most of the error messages come straight from Google Update. This one is |
- // deemed worthy enough to also warrant its own error. |
- OnUpgradeError(GOOGLE_UPDATE_JOB_SERVER_CREATION_FAILED, hr, false); |
- return false; |
+ *error_code = CanUpdateCurrentChrome(chrome_exe, system_level_install_); |
+ if (*error_code != GOOGLE_UPDATE_NO_ERROR) |
+ return E_FAIL; |
+ |
+ HRESULT hresult = CreateGoogleUpdate3WebClass( |
+ system_level_install_, install_update_if_possible_, elevation_window_, |
+ &google_update_); |
+ if (FAILED(hresult)) { |
+ *error_code = GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND; |
+ return hresult; |
} |
- // Hold a reference on the observer for the lifetime of the driver. |
- job_holder_ = job_observer_; |
- job_observer_->set_on_complete_callback(base::Bind( |
- &UpdateCheckDriver::CompleteUpdateCheck, base::Unretained(this))); |
+ // The class was created, so all subsequent errors are reported as: |
+ *error_code = GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR; |
+ base::string16 app_guid = |
+ installer::GetAppGuidForUpdates(system_level_install_); |
+ DCHECK(!app_guid.empty()); |
- hr = CreateOnDemandAppsClass(system_level, install_if_newer, elevation_window, |
- &on_demand_); |
- if (hr != S_OK) { |
- OnUpgradeError(GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND, hr, system_level); |
- return false; |
+ { |
+ base::win::ScopedComPtr<IDispatch> dispatch; |
+ hresult = google_update_->createAppBundleWeb(dispatch.Receive()); |
+ if (FAILED(hresult)) |
+ return hresult; |
+ hresult = dispatch.QueryInterface(app_bundle_.Receive()); |
+ if (FAILED(hresult)) |
+ return hresult; |
} |
- base::string16 app_guid = installer::GetAppGuidForUpdates(system_level); |
- DCHECK(!app_guid.empty()); |
+ if (!locale_.empty()) { |
+ // Ignore the result of this since, while setting the display language is |
+ // nice to have, a failure to do so does not affect the likelihood that the |
+ // update check and/or install will succeed. |
+ app_bundle_->put_displayLanguage( |
+ base::win::ScopedBstr(base::UTF8ToUTF16(locale_).c_str())); |
+ } |
+ |
+ hresult = app_bundle_->initialize(); |
+ if (FAILED(hresult)) |
+ return hresult; |
+ if (elevation_window_) { |
+ // Likewise, a failure to set the parent window need not block an update |
+ // check. |
+ app_bundle_->put_parentHWND(reinterpret_cast<ULONG_PTR>(elevation_window_)); |
+ } |
- if (install_if_newer) |
- hr = on_demand_->Update(app_guid.c_str(), job_observer_); |
- else |
- hr = on_demand_->CheckForUpdate(app_guid.c_str(), job_observer_); |
+ base::win::ScopedComPtr<IDispatch> dispatch; |
+ hresult = |
+ app_bundle_->createInstalledApp(base::win::ScopedBstr(app_guid.c_str())); |
+ if (FAILED(hresult)) |
+ return hresult; |
+ hresult = app_bundle_->get_appWeb(0, dispatch.Receive()); |
+ if (FAILED(hresult)) |
+ return hresult; |
+ hresult = dispatch.QueryInterface(app_.Receive()); |
+ if (FAILED(hresult)) |
+ return hresult; |
+ return app_bundle_->checkForUpdate(); |
+} |
- if (hr != S_OK) { |
- OnUpgradeError(GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR, hr, |
- system_level); |
+bool UpdateCheckDriver::GetCurrentState( |
+ base::win::ScopedComPtr<ICurrentState>* current_state, |
+ CurrentState* state_value, |
+ HRESULT* hresult) const { |
+ base::win::ScopedComPtr<IDispatch> dispatch; |
+ *hresult = app_->get_currentState(dispatch.Receive()); |
+ if (FAILED(*hresult)) |
+ return false; |
+ *hresult = dispatch.QueryInterface(current_state->Receive()); |
+ if (FAILED(*hresult)) |
+ return false; |
+ LONG value = 0; |
+ *hresult = (*current_state)->get_stateValue(&value); |
+ if (FAILED(*hresult)) |
return false; |
+ *state_value = static_cast<CurrentState>(value); |
+ return true; |
+} |
+ |
+bool UpdateCheckDriver::IsErrorState( |
+ const base::win::ScopedComPtr<ICurrentState>& current_state, |
+ CurrentState state_value, |
+ GoogleUpdateErrorCode* error_code, |
+ HRESULT* hresult, |
+ int* installer_exit_code, |
+ base::string16* error_string) const { |
+ if (state_value == STATE_ERROR) { |
+ // In general, errors reported by Google Update fall under this category |
+ // (see special case below). |
+ *error_code = GOOGLE_UPDATE_ERROR_UPDATING; |
+ |
+ // In general, the exit code of Chrome's installer is unknown (see special |
+ // case below). |
+ *installer_exit_code = -1; |
+ |
+ // Report the error_code provided by Google Update if possible, or the |
+ // reason it wasn't possible otherwise. |
+ LONG long_value = 0; |
+ *hresult = current_state->get_errorCode(&long_value); |
+ if (SUCCEEDED(*hresult)) |
+ *hresult = long_value; |
+ |
+ // Special cases: |
+ // - Use a custom error code if Google Update repoted that the update was |
+ // disabled by a Group Policy setting. |
+ // - Extract the exit code of Chrome's installer if Google Update repoted |
+ // that the update failed because of a failure in the installer. |
+ LONG code = 0; |
+ if (*hresult == GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY) { |
+ *error_code = GOOGLE_UPDATE_DISABLED_BY_POLICY; |
+ } else if (*hresult == GOOPDATEINSTALL_E_INSTALLER_FAILED && |
+ SUCCEEDED(current_state->get_installerResultCode(&code))) { |
+ *installer_exit_code = code; |
+ } |
+ |
+ base::win::ScopedBstr message; |
+ if (SUCCEEDED(current_state->get_completionMessage(message.Receive()))) |
+ error_string->assign(message, message.Length()); |
+ |
+ return true; |
+ } |
+ if (state_value == STATE_UPDATE_AVAILABLE && install_update_if_possible_) { |
+ *hresult = app_bundle_->install(); |
S. Ganesh
2015/05/13 20:21:41
It is somewhat unexpected to see IsErrorState() ca
grt (UTC plus 2)
2015/05/13 20:36:38
Understood. The reason it isn't called from PollGo
|
+ if (FAILED(*hresult)) { |
+ // Report a failure to start the install as a general error while trying |
+ // to interact with Google Update. |
+ *error_code = GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR; |
+ *installer_exit_code = -1; |
+ return true; |
+ } |
+ // Return false for handling in IsIntermediateState. |
+ } |
+ return false; |
+} |
+ |
+bool UpdateCheckDriver::IsFinalState( |
+ const base::win::ScopedComPtr<ICurrentState>& current_state, |
+ CurrentState state_value, |
+ GoogleUpdateUpgradeStatus* upgrade_status, |
+ base::string16* new_version) const { |
+ if (state_value == STATE_UPDATE_AVAILABLE && !install_update_if_possible_) { |
+ base::win::ScopedBstr version; |
+ *upgrade_status = UPGRADE_IS_AVAILABLE; |
+ if (SUCCEEDED(current_state->get_availableVersion(version.Receive()))) |
S. Ganesh
2015/05/13 20:21:40
Minor code duplication here and in IsIntermediateS
grt (UTC plus 2)
2015/05/13 20:36:38
It would be possible, but would take some rejigger
|
+ new_version->assign(version, version.Length()); |
+ return true; |
+ } |
+ if (state_value == STATE_INSTALL_COMPLETE) { |
+ DCHECK(install_update_if_possible_); |
+ *upgrade_status = UPGRADE_SUCCESSFUL; |
+ return true; |
+ } |
+ if (state_value == STATE_NO_UPDATE) { |
+ *upgrade_status = UPGRADE_ALREADY_UP_TO_DATE; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool UpdateCheckDriver::IsIntermediateState( |
+ const base::win::ScopedComPtr<ICurrentState>& current_state, |
+ CurrentState state_value, |
+ base::string16* new_version, |
+ int* progress) const { |
+ // ERROR will have been handled in IsErrorState. UPDATE_AVAILABLE, and |
+ // NO_UPDATE will have been handled in IsFinalState if not doing an install, |
+ // as will STATE_INSTALL_COMPLETE when doing an install. All other states |
+ // following UPDATE_AVAILABLE will only happen when an install is to be done. |
+ DCHECK(state_value < STATE_UPDATE_AVAILABLE || install_update_if_possible_); |
+ *progress = 0; |
+ |
+ switch (state_value) { |
+ case STATE_INIT: |
+ case STATE_WAITING_TO_CHECK_FOR_UPDATE: |
+ case STATE_CHECKING_FOR_UPDATE: |
+ // There is no news to report yet. |
+ break; |
+ |
+ case STATE_UPDATE_AVAILABLE: { |
+ base::win::ScopedBstr version; |
+ if (SUCCEEDED(current_state->get_availableVersion(version.Receive()))) |
+ new_version->assign(version, version.Length()); |
+ break; |
+ } |
+ |
+ case STATE_WAITING_TO_DOWNLOAD: |
+ case STATE_RETRYING_DOWNLOAD: |
+ break; |
+ |
+ case STATE_DOWNLOADING: { |
+ ULONG bytes_downloaded = 0; |
+ ULONG total_bytes = 0; |
+ if (SUCCEEDED(current_state->get_bytesDownloaded(&bytes_downloaded)) && |
+ SUCCEEDED(current_state->get_totalBytesToDownload(&total_bytes)) && |
+ total_bytes) { |
+ // 0-50 is downloading. |
+ *progress = gfx::ToFlooredInt((static_cast<double>(bytes_downloaded) / |
+ static_cast<double>(total_bytes)) * |
+ 50.0); |
+ } |
+ break; |
+ } |
+ |
+ case STATE_DOWNLOAD_COMPLETE: |
+ case STATE_EXTRACTING: |
+ case STATE_APPLYING_DIFFERENTIAL_PATCH: |
+ case STATE_READY_TO_INSTALL: |
+ case STATE_WAITING_TO_INSTALL: |
+ *progress = 50; |
+ break; |
+ |
+ case STATE_INSTALLING: { |
+ *progress = 50; |
+ LONG install_progress = 0; |
+ if (SUCCEEDED(current_state->get_installProgress(&install_progress)) && |
+ install_progress >= 0 && install_progress <= 100) { |
+ // 50-100 is installing. |
+ *progress = (50 + install_progress / 2); |
+ } |
+ break; |
+ } |
+ |
+ case STATE_INSTALL_COMPLETE: |
+ case STATE_PAUSED: |
+ case STATE_NO_UPDATE: |
+ case STATE_ERROR: |
+ default: |
+ NOTREACHED(); |
+ UMA_HISTOGRAM_SPARSE_SLOWLY("GoogleUpdate.UnexpectedState", state_value); |
+ return false; |
} |
return true; |
} |
-void UpdateCheckDriver::CompleteUpdateCheck() { |
- result_ = job_observer_->result(); |
- if (result_ == UPGRADE_ERROR) { |
- error_code_ = GOOGLE_UPDATE_ERROR_UPDATING; |
- error_message_ = job_observer_->error_message(); |
- } else { |
- version_ = job_observer_->new_version(); |
+void UpdateCheckDriver::PollGoogleUpdate() { |
+ base::win::ScopedComPtr<ICurrentState> state; |
+ CurrentState state_value = STATE_INIT; |
+ HRESULT hresult = S_OK; |
+ GoogleUpdateErrorCode error_code = GOOGLE_UPDATE_NO_ERROR; |
+ int installer_exit_code = -1; |
+ base::string16 error_string; |
+ GoogleUpdateUpgradeStatus upgrade_status = UPGRADE_ERROR; |
+ base::string16 new_version; |
+ int progress = 0; |
+ |
+ if (!GetCurrentState(&state, &state_value, &hresult)) { |
+ OnUpgradeError(GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR, hresult, -1, |
+ base::string16()); |
+ } else if (IsErrorState(state, state_value, &error_code, &hresult, |
+ &installer_exit_code, &error_string)) { |
+ OnUpgradeError(error_code, hresult, installer_exit_code, error_string); |
+ } else if (IsFinalState(state, state_value, &upgrade_status, &new_version)) { |
+ status_ = upgrade_status; |
+ error_code_ = GOOGLE_UPDATE_NO_ERROR; |
+ error_message_.clear(); |
+ if (!new_version.empty()) |
+ new_version_ = new_version; |
+ hresult_ = S_OK; |
+ installer_exit_code_ = -1; |
+ } else if (IsIntermediateState(state, state_value, &new_version, &progress)) { |
+ bool got_new_version = new_version_.empty() && !new_version.empty(); |
+ if (got_new_version) |
+ new_version_ = new_version; |
+ // Give the caller this status update if it differs from the last one given. |
+ if (got_new_version || progress != last_reported_progress_) { |
+ last_reported_progress_ = progress; |
+ |
+ // It is safe to post this task with an unretained pointer since the task |
+ // is guaranteed to run before a subsequent DeleteSoon is handled. |
+ result_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&UpdateCheckDelegate::OnUpgradeProgress, delegate_, |
+ last_reported_progress_, new_version_)); |
+ } |
+ |
+ // Schedule the next check. |
+ task_runner_->PostDelayedTask( |
+ FROM_HERE, base::Bind(&UpdateCheckDriver::PollGoogleUpdate, |
+ base::Unretained(this)), |
+ base::TimeDelta::FromMilliseconds(kGoogleUpdatePollIntervalMs)); |
+ // Early return for this non-terminal state. |
+ return; |
} |
// Release the reference on the COM objects before bouncing back to the |
// caller's thread. |
- on_demand_.Release(); |
- job_holder_.Release(); |
- job_observer_ = nullptr; |
+ state.Release(); |
+ app_.Release(); |
+ app_bundle_.Release(); |
+ google_update_.Release(); |
result_runner_->DeleteSoon(FROM_HERE, this); |
} |
void UpdateCheckDriver::OnUpgradeError(GoogleUpdateErrorCode error_code, |
- HRESULT hr, |
- bool system_level) { |
- result_ = UPGRADE_ERROR; |
+ HRESULT hresult, |
+ int installer_exit_code, |
+ const base::string16& error_string) { |
+ status_ = UPGRADE_ERROR; |
error_code_ = error_code; |
- hresult_ = hr; |
+ hresult_ = hresult; |
+ installer_exit_code_ = installer_exit_code; |
base::string16 error_msg = |
base::StringPrintf(L"%d: 0x%x", error_code_, hresult_); |
- if (system_level) |
+ if (installer_exit_code_ != -1) |
+ error_msg += base::StringPrintf(L": %d", installer_exit_code_); |
+ if (system_level_install_) |
error_msg += L" -- system level"; |
- error_message_ = l10n_util::GetStringFUTF16( |
- IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED, error_msg); |
+ if (error_string.empty()) { |
+ error_message_ = l10n_util::GetStringFUTF16( |
+ IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED, error_msg); |
+ } else { |
+ error_message_ = l10n_util::GetStringFUTF16( |
+ IDS_ABOUT_BOX_GOOGLE_UPDATE_ERROR, error_string, error_msg); |
+ } |
} |
} // namespace |
@@ -497,25 +708,28 @@ void UpdateCheckDriver::OnUpgradeError(GoogleUpdateErrorCode error_code, |
// Globals --------------------------------------------------------------------- |
-void BeginUpdateCheck(const scoped_refptr<base::TaskRunner>& task_runner, |
- bool install_if_newer, |
- gfx::AcceleratedWidget elevation_window, |
- const UpdateCheckCallback& callback) { |
- UpdateCheckDriver::RunUpdateCheck(task_runner, install_if_newer, |
- elevation_window, callback); |
+void BeginUpdateCheck( |
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
+ const std::string& locale, |
+ bool install_update_if_possible, |
+ gfx::AcceleratedWidget elevation_window, |
+ const base::WeakPtr<UpdateCheckDelegate>& delegate) { |
+ UpdateCheckDriver::RunUpdateCheck(task_runner, locale, |
+ install_update_if_possible, |
+ elevation_window, delegate); |
} |
// Private API exposed for testing. -------------------------------------------- |
void SetGoogleUpdateFactoryForTesting( |
- const OnDemandAppsClassFactory& google_update_factory) { |
+ const GoogleUpdate3ClassFactory& google_update_factory) { |
if (g_google_update_factory) { |
delete g_google_update_factory; |
g_google_update_factory = nullptr; |
} |
if (!google_update_factory.is_null()) { |
g_google_update_factory = |
- new OnDemandAppsClassFactory(google_update_factory); |
+ new GoogleUpdate3ClassFactory(google_update_factory); |
} |
} |