Componentize AccountReconcilor.
[chromium-blink-merge.git] / chrome / browser / google / google_update_win.cc
blob6631eca74253bc6424b60fdbefa116e980b840c4
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/google/google_update_win.h"
7 #include <atlbase.h>
8 #include <atlcom.h>
10 #include "base/bind.h"
11 #include "base/files/file_path.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/path_service.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/threading/thread.h"
17 #include "base/win/scoped_comptr.h"
18 #include "base/win/windows_version.h"
19 #include "chrome/installer/util/browser_distribution.h"
20 #include "chrome/installer/util/google_update_settings.h"
21 #include "chrome/installer/util/helper.h"
22 #include "chrome/installer/util/install_util.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "google_update/google_update_idl.h"
25 #include "grit/generated_resources.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/win/atl_module.h"
28 #include "ui/views/widget/widget.h"
30 using content::BrowserThread;
32 namespace {
34 // Check if the currently running instance can be updated by Google Update.
35 // Returns GOOGLE_UPDATE_NO_ERROR only if the instance running is a Google
36 // Chrome distribution installed in a standard location.
37 GoogleUpdateErrorCode CanUpdateCurrentChrome(
38 const base::FilePath& chrome_exe_path) {
39 #if !defined(GOOGLE_CHROME_BUILD)
40 return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY;
41 #else
42 // TODO(tommi): Check if using the default distribution is always the right
43 // thing to do.
44 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
45 base::FilePath user_exe_path = installer::GetChromeInstallPath(false, dist);
46 base::FilePath machine_exe_path = installer::GetChromeInstallPath(true, dist);
47 if (!base::FilePath::CompareEqualIgnoreCase(chrome_exe_path.value(),
48 user_exe_path.value()) &&
49 !base::FilePath::CompareEqualIgnoreCase(chrome_exe_path.value(),
50 machine_exe_path.value())) {
51 LOG(ERROR) << L"Google Update cannot update Chrome installed in a "
52 << L"non-standard location: " << chrome_exe_path.value().c_str()
53 << L". The standard location is: "
54 << user_exe_path.value().c_str()
55 << L" or " << machine_exe_path.value().c_str() << L".";
56 return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY;
59 base::string16 app_guid = installer::GetAppGuidForUpdates(
60 !InstallUtil::IsPerUserInstall(chrome_exe_path.value().c_str()));
61 DCHECK(!app_guid.empty());
63 GoogleUpdateSettings::UpdatePolicy update_policy =
64 GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, NULL);
66 if (update_policy == GoogleUpdateSettings::UPDATES_DISABLED)
67 return GOOGLE_UPDATE_DISABLED_BY_POLICY;
69 if (update_policy == GoogleUpdateSettings::AUTO_UPDATES_ONLY)
70 return GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY;
72 return GOOGLE_UPDATE_NO_ERROR;
73 #endif
76 // Creates an instance of a COM Local Server class using either plain vanilla
77 // CoCreateInstance, or using the Elevation moniker if running on Vista.
78 // hwnd must refer to a foregound window in order to get the UAC prompt
79 // showing up in the foreground if running on Vista. It can also be NULL if
80 // background UAC prompts are desired.
81 HRESULT CoCreateInstanceAsAdmin(REFCLSID class_id, REFIID interface_id,
82 HWND hwnd, void** interface_ptr) {
83 if (!interface_ptr)
84 return E_POINTER;
86 // For Vista, need to instantiate the COM server via the elevation
87 // moniker. This ensures that the UAC dialog shows up.
88 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
89 wchar_t class_id_as_string[MAX_PATH] = {0};
90 StringFromGUID2(class_id, class_id_as_string,
91 arraysize(class_id_as_string));
93 base::string16 elevation_moniker_name =
94 base::StringPrintf(L"Elevation:Administrator!new:%ls",
95 class_id_as_string);
97 BIND_OPTS3 bind_opts;
98 memset(&bind_opts, 0, sizeof(bind_opts));
99 bind_opts.cbStruct = sizeof(bind_opts);
100 bind_opts.dwClassContext = CLSCTX_LOCAL_SERVER;
101 bind_opts.hwnd = hwnd;
103 return CoGetObject(elevation_moniker_name.c_str(), &bind_opts,
104 interface_id, reinterpret_cast<void**>(interface_ptr));
107 return CoCreateInstance(class_id, NULL, CLSCTX_LOCAL_SERVER,
108 interface_id,
109 reinterpret_cast<void**>(interface_ptr));
113 } // namespace
115 // The GoogleUpdateJobObserver COM class is responsible for receiving status
116 // reports from google Update. It keeps track of the progress as GoogleUpdate
117 // notifies this observer and ends the message loop that is spinning in once
118 // GoogleUpdate reports that it is done.
119 class GoogleUpdateJobObserver
120 : public CComObjectRootEx<CComSingleThreadModel>,
121 public IJobObserver {
122 public:
123 BEGIN_COM_MAP(GoogleUpdateJobObserver)
124 COM_INTERFACE_ENTRY(IJobObserver)
125 END_COM_MAP()
127 GoogleUpdateJobObserver()
128 : result_(UPGRADE_ERROR) {
130 virtual ~GoogleUpdateJobObserver() {}
132 // Notifications from Google Update:
133 STDMETHOD(OnShow)() {
134 return S_OK;
136 STDMETHOD(OnCheckingForUpdate)() {
137 result_ = UPGRADE_CHECK_STARTED;
138 return S_OK;
140 STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) {
141 result_ = UPGRADE_IS_AVAILABLE;
142 new_version_ = version_string;
143 return S_OK;
145 STDMETHOD(OnWaitingToDownload)() {
146 return S_OK;
148 STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) {
149 return S_OK;
151 STDMETHOD(OnWaitingToInstall)() {
152 return S_OK;
154 STDMETHOD(OnInstalling)() {
155 result_ = UPGRADE_STARTED;
156 return S_OK;
158 STDMETHOD(OnPause)() {
159 return S_OK;
161 STDMETHOD(OnComplete)(LegacyCompletionCodes code, const TCHAR* text) {
162 switch (code) {
163 case COMPLETION_CODE_SUCCESS_CLOSE_UI:
164 case COMPLETION_CODE_SUCCESS: {
165 if (result_ == UPGRADE_STARTED)
166 result_ = UPGRADE_SUCCESSFUL;
167 else if (result_ == UPGRADE_CHECK_STARTED)
168 result_ = UPGRADE_ALREADY_UP_TO_DATE;
169 break;
171 case COMPLETION_CODE_ERROR:
172 error_message_ = text;
173 default: {
174 NOTREACHED();
175 result_ = UPGRADE_ERROR;
176 break;
180 event_sink_ = NULL;
182 // No longer need to spin the message loop that started spinning in
183 // InitiateGoogleUpdateCheck.
184 base::MessageLoop::current()->Quit();
185 return S_OK;
187 STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) {
188 event_sink_ = event_sink;
189 return S_OK;
192 // Returns the results of the update operation.
193 STDMETHOD(GetResult)(GoogleUpdateUpgradeResult* result) {
194 // Intermediary steps should never be reported to the client.
195 DCHECK(result_ != UPGRADE_STARTED && result_ != UPGRADE_CHECK_STARTED);
197 *result = result_;
198 return S_OK;
201 // Returns which version Google Update found on the server (if a more
202 // recent version was found). Otherwise, this will be blank.
203 STDMETHOD(GetVersionInfo)(base::string16* version_string) {
204 *version_string = new_version_;
205 return S_OK;
208 // Returns the Google Update supplied error string that describes the error
209 // that occurred during the update check/upgrade.
210 STDMETHOD(GetErrorMessage)(base::string16* error_message) {
211 *error_message = error_message_;
212 return S_OK;
215 private:
216 // The status/result of the Google Update operation.
217 GoogleUpdateUpgradeResult result_;
219 // The version string Google Update found.
220 base::string16 new_version_;
222 // An error message, if any.
223 base::string16 error_message_;
225 // Allows us control the upgrade process to a small degree. After OnComplete
226 // has been called, this object can not be used.
227 base::win::ScopedComPtr<IProgressWndEvents> event_sink_;
230 ////////////////////////////////////////////////////////////////////////////////
231 // GoogleUpdate, public:
233 GoogleUpdate::GoogleUpdate()
234 : listener_(NULL) {
237 GoogleUpdate::~GoogleUpdate() {
240 void GoogleUpdate::CheckForUpdate(bool install_if_newer, HWND window) {
241 // Need to shunt this request over to InitiateGoogleUpdateCheck and have
242 // it run in the file thread.
243 BrowserThread::PostTask(
244 BrowserThread::FILE, FROM_HERE,
245 base::Bind(&GoogleUpdate::InitiateGoogleUpdateCheck, this,
246 install_if_newer, window, base::MessageLoop::current()));
249 ////////////////////////////////////////////////////////////////////////////////
250 // GoogleUpdate, private:
252 void GoogleUpdate::InitiateGoogleUpdateCheck(bool install_if_newer,
253 HWND window,
254 base::MessageLoop* main_loop) {
255 base::FilePath chrome_exe;
256 if (!PathService::Get(base::DIR_EXE, &chrome_exe))
257 NOTREACHED();
259 GoogleUpdateErrorCode error_code = CanUpdateCurrentChrome(chrome_exe);
260 if (error_code != GOOGLE_UPDATE_NO_ERROR) {
261 main_loop->PostTask(
262 FROM_HERE,
263 base::Bind(&GoogleUpdate::ReportResults, this,
264 UPGRADE_ERROR, error_code, base::string16()));
265 return;
268 // Make sure ATL is initialized in this module.
269 ui::win::CreateATLModuleIfNeeded();
271 CComObject<GoogleUpdateJobObserver>* job_observer;
272 HRESULT hr =
273 CComObject<GoogleUpdateJobObserver>::CreateInstance(&job_observer);
274 if (hr != S_OK) {
275 // Most of the error messages come straight from Google Update. This one is
276 // deemed worthy enough to also warrant its own error.
277 GoogleUpdateErrorCode error = GOOGLE_UPDATE_JOB_SERVER_CREATION_FAILED;
278 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
279 ReportFailure(
280 hr, error,
281 l10n_util::GetStringFUTF16(IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
282 error_code),
283 main_loop);
284 return;
287 base::win::ScopedComPtr<IJobObserver> job_holder(job_observer);
289 base::win::ScopedComPtr<IGoogleUpdate> on_demand;
291 bool system_level = false;
293 if (InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) {
294 hr = on_demand.CreateInstance(CLSID_OnDemandUserAppsClass);
295 } else {
296 // The Update operation needs Admin privileges for writing
297 // to %ProgramFiles%. On Vista, need to elevate before instantiating
298 // the updater instance.
299 if (!install_if_newer) {
300 hr = on_demand.CreateInstance(CLSID_OnDemandMachineAppsClass);
301 } else {
302 hr = CoCreateInstanceAsAdmin(CLSID_OnDemandMachineAppsClass,
303 IID_IGoogleUpdate, window,
304 reinterpret_cast<void**>(on_demand.Receive()));
306 system_level = true;
309 if (hr != S_OK) {
310 GoogleUpdateErrorCode error = GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND;
311 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
312 if (system_level)
313 error_code += L" -- system level";
314 ReportFailure(hr, error,
315 l10n_util::GetStringFUTF16(
316 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
317 error_code),
318 main_loop);
319 return;
322 base::string16 app_guid = installer::GetAppGuidForUpdates(system_level);
323 DCHECK(!app_guid.empty());
325 if (!install_if_newer)
326 hr = on_demand->CheckForUpdate(app_guid.c_str(), job_observer);
327 else
328 hr = on_demand->Update(app_guid.c_str(), job_observer);
330 if (hr != S_OK) {
331 GoogleUpdateErrorCode error = GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR;
332 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
333 ReportFailure(hr, error,
334 l10n_util::GetStringFUTF16(
335 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
336 error_code),
337 main_loop);
338 return;
341 // Need to spin the message loop while Google Update is running so that it
342 // can report back to us through GoogleUpdateJobObserver. This message loop
343 // will terminate once Google Update sends us the completion status
344 // (success/error). See OnComplete().
345 base::MessageLoop::current()->Run();
347 GoogleUpdateUpgradeResult results;
348 hr = job_observer->GetResult(&results);
350 if (hr != S_OK) {
351 GoogleUpdateErrorCode error = GOOGLE_UPDATE_GET_RESULT_CALL_FAILED;
352 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
353 ReportFailure(hr, error,
354 l10n_util::GetStringFUTF16(
355 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
356 error_code),
357 main_loop);
358 return;
361 if (results == UPGRADE_ERROR) {
362 base::string16 error_message;
363 job_observer->GetErrorMessage(&error_message);
364 ReportFailure(hr, GOOGLE_UPDATE_ERROR_UPDATING, error_message, main_loop);
365 return;
368 hr = job_observer->GetVersionInfo(&version_available_);
369 if (hr != S_OK) {
370 GoogleUpdateErrorCode error = GOOGLE_UPDATE_GET_VERSION_INFO_FAILED;
371 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
372 ReportFailure(hr, error,
373 l10n_util::GetStringFUTF16(
374 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
375 error_code),
376 main_loop);
377 return;
380 main_loop->PostTask(
381 FROM_HERE,
382 base::Bind(&GoogleUpdate::ReportResults, this,
383 results, GOOGLE_UPDATE_NO_ERROR, base::string16()));
384 job_holder = NULL;
385 on_demand = NULL;
388 void GoogleUpdate::ReportResults(GoogleUpdateUpgradeResult results,
389 GoogleUpdateErrorCode error_code,
390 const base::string16& error_message) {
391 // If there is an error, then error code must not be blank, and vice versa.
392 DCHECK(results == UPGRADE_ERROR ? error_code != GOOGLE_UPDATE_NO_ERROR :
393 error_code == GOOGLE_UPDATE_NO_ERROR);
394 if (listener_) {
395 listener_->OnReportResults(
396 results, error_code, error_message, version_available_);
400 bool GoogleUpdate::ReportFailure(HRESULT hr,
401 GoogleUpdateErrorCode error_code,
402 const base::string16& error_message,
403 base::MessageLoop* main_loop) {
404 NOTREACHED() << "Communication with Google Update failed: " << hr
405 << " error: " << error_code
406 << ", message: " << error_message.c_str();
407 main_loop->PostTask(
408 FROM_HERE,
409 base::Bind(&GoogleUpdate::ReportResults, this,
410 UPGRADE_ERROR, error_code, error_message));
411 return false;