summaryrefslogtreecommitdiffstats
path: root/chrome/browser/google_update.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/google_update.cc')
-rw-r--r--chrome/browser/google_update.cc331
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;
+}