diff options
Diffstat (limited to 'chrome/browser/google_update.cc')
-rw-r--r-- | chrome/browser/google_update.cc | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/chrome/browser/google_update.cc b/chrome/browser/google_update.cc new file mode 100644 index 0000000..dca2e9d --- /dev/null +++ b/chrome/browser/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_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; +} |