Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / google / google_update_win.cc
blobadeb33c8f729d951267472dfaec3b6f3f0bc6457
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/metrics/histogram.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/path_service.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/threading/thread.h"
19 #include "base/win/scoped_comptr.h"
20 #include "base/win/windows_version.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "chrome/installer/util/browser_distribution.h"
23 #include "chrome/installer/util/google_update_settings.h"
24 #include "chrome/installer/util/helper.h"
25 #include "chrome/installer/util/install_util.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "google_update/google_update_idl.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/win/atl_module.h"
30 #include "ui/views/widget/widget.h"
32 using content::BrowserThread;
34 namespace {
36 // Check if the currently running instance can be updated by Google Update.
37 // Returns GOOGLE_UPDATE_NO_ERROR only if the instance running is a Google
38 // Chrome distribution installed in a standard location.
39 GoogleUpdateErrorCode CanUpdateCurrentChrome(
40 const base::FilePath& chrome_exe_path) {
41 #if !defined(GOOGLE_CHROME_BUILD)
42 return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY;
43 #else
44 // TODO(tommi): Check if using the default distribution is always the right
45 // thing to do.
46 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
47 base::FilePath user_exe_path = installer::GetChromeInstallPath(false, dist);
48 base::FilePath machine_exe_path = installer::GetChromeInstallPath(true, dist);
49 if (!base::FilePath::CompareEqualIgnoreCase(chrome_exe_path.value(),
50 user_exe_path.value()) &&
51 !base::FilePath::CompareEqualIgnoreCase(chrome_exe_path.value(),
52 machine_exe_path.value())) {
53 LOG(ERROR) << L"Google Update cannot update Chrome installed in a "
54 << L"non-standard location: " << chrome_exe_path.value().c_str()
55 << L". The standard location is: "
56 << user_exe_path.value().c_str()
57 << L" or " << machine_exe_path.value().c_str() << L".";
58 return CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY;
61 base::string16 app_guid = installer::GetAppGuidForUpdates(
62 !InstallUtil::IsPerUserInstall(chrome_exe_path.value().c_str()));
63 DCHECK(!app_guid.empty());
65 GoogleUpdateSettings::UpdatePolicy update_policy =
66 GoogleUpdateSettings::GetAppUpdatePolicy(app_guid, NULL);
68 if (update_policy == GoogleUpdateSettings::UPDATES_DISABLED)
69 return GOOGLE_UPDATE_DISABLED_BY_POLICY;
71 if (update_policy == GoogleUpdateSettings::AUTO_UPDATES_ONLY)
72 return GOOGLE_UPDATE_DISABLED_BY_POLICY_AUTO_ONLY;
74 return GOOGLE_UPDATE_NO_ERROR;
75 #endif
78 // Creates an instance of a COM Local Server class using either plain vanilla
79 // CoCreateInstance, or using the Elevation moniker if running on Vista.
80 // hwnd must refer to a foregound window in order to get the UAC prompt
81 // showing up in the foreground if running on Vista. It can also be NULL if
82 // background UAC prompts are desired.
83 HRESULT CoCreateInstanceAsAdmin(REFCLSID class_id, REFIID interface_id,
84 HWND hwnd, void** interface_ptr) {
85 if (!interface_ptr)
86 return E_POINTER;
88 // For Vista, need to instantiate the COM server via the elevation
89 // moniker. This ensures that the UAC dialog shows up.
90 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
91 wchar_t class_id_as_string[MAX_PATH] = {0};
92 StringFromGUID2(class_id, class_id_as_string,
93 arraysize(class_id_as_string));
95 base::string16 elevation_moniker_name =
96 base::StringPrintf(L"Elevation:Administrator!new:%ls",
97 class_id_as_string);
99 BIND_OPTS3 bind_opts;
100 memset(&bind_opts, 0, sizeof(bind_opts));
101 bind_opts.cbStruct = sizeof(bind_opts);
102 bind_opts.dwClassContext = CLSCTX_LOCAL_SERVER;
103 bind_opts.hwnd = hwnd;
105 return CoGetObject(elevation_moniker_name.c_str(), &bind_opts,
106 interface_id, reinterpret_cast<void**>(interface_ptr));
109 return CoCreateInstance(class_id, NULL, CLSCTX_LOCAL_SERVER,
110 interface_id,
111 reinterpret_cast<void**>(interface_ptr));
115 } // namespace
117 // The GoogleUpdateJobObserver COM class is responsible for receiving status
118 // reports from google Update. It keeps track of the progress as GoogleUpdate
119 // notifies this observer and ends the message loop that is spinning in once
120 // GoogleUpdate reports that it is done.
121 class GoogleUpdateJobObserver
122 : public CComObjectRootEx<CComSingleThreadModel>,
123 public IJobObserver {
124 public:
125 BEGIN_COM_MAP(GoogleUpdateJobObserver)
126 COM_INTERFACE_ENTRY(IJobObserver)
127 END_COM_MAP()
129 GoogleUpdateJobObserver()
130 : result_(UPGRADE_ERROR) {
132 virtual ~GoogleUpdateJobObserver() {}
134 // Notifications from Google Update:
135 STDMETHOD(OnShow)() {
136 return S_OK;
138 STDMETHOD(OnCheckingForUpdate)() {
139 result_ = UPGRADE_CHECK_STARTED;
140 return S_OK;
142 STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) {
143 result_ = UPGRADE_IS_AVAILABLE;
144 new_version_ = version_string;
145 return S_OK;
147 STDMETHOD(OnWaitingToDownload)() {
148 return S_OK;
150 STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) {
151 return S_OK;
153 STDMETHOD(OnWaitingToInstall)() {
154 return S_OK;
156 STDMETHOD(OnInstalling)() {
157 result_ = UPGRADE_STARTED;
158 return S_OK;
160 STDMETHOD(OnPause)() {
161 return S_OK;
163 STDMETHOD(OnComplete)(LegacyCompletionCodes code, const TCHAR* text) {
164 switch (code) {
165 case COMPLETION_CODE_SUCCESS_CLOSE_UI:
166 case COMPLETION_CODE_SUCCESS: {
167 if (result_ == UPGRADE_STARTED)
168 result_ = UPGRADE_SUCCESSFUL;
169 else if (result_ == UPGRADE_CHECK_STARTED)
170 result_ = UPGRADE_ALREADY_UP_TO_DATE;
171 break;
173 case COMPLETION_CODE_ERROR:
174 error_message_ = text;
175 default: {
176 NOTREACHED();
177 result_ = UPGRADE_ERROR;
178 break;
182 event_sink_ = NULL;
184 // No longer need to spin the message loop that started spinning in
185 // InitiateGoogleUpdateCheck.
186 base::MessageLoop::current()->Quit();
187 return S_OK;
189 STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) {
190 event_sink_ = event_sink;
191 return S_OK;
194 // Returns the results of the update operation.
195 STDMETHOD(GetResult)(GoogleUpdateUpgradeResult* result) {
196 // Intermediary steps should never be reported to the client.
197 DCHECK(result_ != UPGRADE_STARTED && result_ != UPGRADE_CHECK_STARTED);
199 *result = result_;
200 return S_OK;
203 // Returns which version Google Update found on the server (if a more
204 // recent version was found). Otherwise, this will be blank.
205 STDMETHOD(GetVersionInfo)(base::string16* version_string) {
206 *version_string = new_version_;
207 return S_OK;
210 // Returns the Google Update supplied error string that describes the error
211 // that occurred during the update check/upgrade.
212 STDMETHOD(GetErrorMessage)(base::string16* error_message) {
213 *error_message = error_message_;
214 return S_OK;
217 private:
218 // The status/result of the Google Update operation.
219 GoogleUpdateUpgradeResult result_;
221 // The version string Google Update found.
222 base::string16 new_version_;
224 // An error message, if any.
225 base::string16 error_message_;
227 // Allows us control the upgrade process to a small degree. After OnComplete
228 // has been called, this object can not be used.
229 base::win::ScopedComPtr<IProgressWndEvents> event_sink_;
232 ////////////////////////////////////////////////////////////////////////////////
233 // GoogleUpdate, public:
235 GoogleUpdate::GoogleUpdate()
236 : listener_(NULL) {
239 GoogleUpdate::~GoogleUpdate() {
242 void GoogleUpdate::CheckForUpdate(bool install_if_newer, HWND window) {
243 // Need to shunt this request over to InitiateGoogleUpdateCheck and have
244 // it run in the file thread.
245 BrowserThread::PostTask(
246 BrowserThread::FILE, FROM_HERE,
247 base::Bind(&GoogleUpdate::InitiateGoogleUpdateCheck, this,
248 install_if_newer, window, base::MessageLoop::current()));
251 ////////////////////////////////////////////////////////////////////////////////
252 // GoogleUpdate, private:
254 void GoogleUpdate::InitiateGoogleUpdateCheck(bool install_if_newer,
255 HWND window,
256 base::MessageLoop* main_loop) {
257 base::FilePath chrome_exe;
258 if (!PathService::Get(base::DIR_EXE, &chrome_exe))
259 NOTREACHED();
261 GoogleUpdateErrorCode error_code = CanUpdateCurrentChrome(chrome_exe);
262 if (error_code != GOOGLE_UPDATE_NO_ERROR) {
263 main_loop->PostTask(
264 FROM_HERE,
265 base::Bind(&GoogleUpdate::ReportResults, this,
266 UPGRADE_ERROR, error_code, base::string16()));
267 return;
270 // Make sure ATL is initialized in this module.
271 ui::win::CreateATLModuleIfNeeded();
273 CComObject<GoogleUpdateJobObserver>* job_observer;
274 HRESULT hr =
275 CComObject<GoogleUpdateJobObserver>::CreateInstance(&job_observer);
276 if (hr != S_OK) {
277 // Most of the error messages come straight from Google Update. This one is
278 // deemed worthy enough to also warrant its own error.
279 GoogleUpdateErrorCode error = GOOGLE_UPDATE_JOB_SERVER_CREATION_FAILED;
280 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
281 ReportFailure(
282 hr, error,
283 l10n_util::GetStringFUTF16(IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
284 error_code),
285 main_loop);
286 return;
289 base::win::ScopedComPtr<IJobObserver> job_holder(job_observer);
291 base::win::ScopedComPtr<IGoogleUpdate> on_demand;
293 bool system_level = false;
295 if (InstallUtil::IsPerUserInstall(chrome_exe.value().c_str())) {
296 hr = on_demand.CreateInstance(CLSID_OnDemandUserAppsClass);
297 } else {
298 // The Update operation needs Admin privileges for writing
299 // to %ProgramFiles%. On Vista, need to elevate before instantiating
300 // the updater instance.
301 if (!install_if_newer) {
302 hr = on_demand.CreateInstance(CLSID_OnDemandMachineAppsClass);
303 } else {
304 hr = CoCreateInstanceAsAdmin(CLSID_OnDemandMachineAppsClass,
305 IID_IGoogleUpdate, window,
306 reinterpret_cast<void**>(on_demand.Receive()));
308 system_level = true;
311 if (hr != S_OK) {
312 GoogleUpdateErrorCode error = GOOGLE_UPDATE_ONDEMAND_CLASS_NOT_FOUND;
313 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
314 if (system_level)
315 error_code += L" -- system level";
316 ReportFailure(hr, error,
317 l10n_util::GetStringFUTF16(
318 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
319 error_code),
320 main_loop);
321 return;
324 base::string16 app_guid = installer::GetAppGuidForUpdates(system_level);
325 DCHECK(!app_guid.empty());
327 if (!install_if_newer)
328 hr = on_demand->CheckForUpdate(app_guid.c_str(), job_observer);
329 else
330 hr = on_demand->Update(app_guid.c_str(), job_observer);
332 if (hr != S_OK) {
333 GoogleUpdateErrorCode error = GOOGLE_UPDATE_ONDEMAND_CLASS_REPORTED_ERROR;
334 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
335 ReportFailure(hr, error,
336 l10n_util::GetStringFUTF16(
337 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
338 error_code),
339 main_loop);
340 return;
343 // Need to spin the message loop while Google Update is running so that it
344 // can report back to us through GoogleUpdateJobObserver. This message loop
345 // will terminate once Google Update sends us the completion status
346 // (success/error). See OnComplete().
347 base::MessageLoop::current()->Run();
349 GoogleUpdateUpgradeResult results;
350 hr = job_observer->GetResult(&results);
352 if (hr != S_OK) {
353 GoogleUpdateErrorCode error = GOOGLE_UPDATE_GET_RESULT_CALL_FAILED;
354 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
355 ReportFailure(hr, error,
356 l10n_util::GetStringFUTF16(
357 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
358 error_code),
359 main_loop);
360 return;
363 if (results == UPGRADE_ERROR) {
364 base::string16 error_message;
365 job_observer->GetErrorMessage(&error_message);
366 ReportFailure(hr, GOOGLE_UPDATE_ERROR_UPDATING, error_message, main_loop);
367 return;
370 hr = job_observer->GetVersionInfo(&version_available_);
371 if (hr != S_OK) {
372 GoogleUpdateErrorCode error = GOOGLE_UPDATE_GET_VERSION_INFO_FAILED;
373 base::string16 error_code = base::StringPrintf(L"%d: 0x%x", error, hr);
374 ReportFailure(hr, error,
375 l10n_util::GetStringFUTF16(
376 IDS_ABOUT_BOX_ERROR_UPDATE_CHECK_FAILED,
377 error_code),
378 main_loop);
379 return;
382 main_loop->PostTask(
383 FROM_HERE,
384 base::Bind(&GoogleUpdate::ReportResults, this,
385 results, GOOGLE_UPDATE_NO_ERROR, base::string16()));
386 job_holder = NULL;
387 on_demand = NULL;
390 void GoogleUpdate::ReportResults(GoogleUpdateUpgradeResult results,
391 GoogleUpdateErrorCode error_code,
392 const base::string16& error_message) {
393 // If there is an error, then error code must not be blank, and vice versa.
394 DCHECK(results == UPGRADE_ERROR ? error_code != GOOGLE_UPDATE_NO_ERROR :
395 error_code == GOOGLE_UPDATE_NO_ERROR);
396 UMA_HISTOGRAM_ENUMERATION(
397 "GoogleUpdate.UpgradeResult", results, NUM_UPGRADE_RESULTS);
398 if (results == UPGRADE_ERROR) {
399 UMA_HISTOGRAM_ENUMERATION(
400 "GoogleUpdate.UpdateErrorCode", error_code, NUM_ERROR_CODES);
402 if (listener_) {
403 listener_->OnReportResults(
404 results, error_code, error_message, version_available_);
408 bool GoogleUpdate::ReportFailure(HRESULT hr,
409 GoogleUpdateErrorCode error_code,
410 const base::string16& error_message,
411 base::MessageLoop* main_loop) {
412 DLOG(ERROR) << "Communication with Google Update failed: " << hr
413 << " error: " << error_code
414 << ", message: " << error_message.c_str();
415 UMA_HISTOGRAM_SPARSE_SLOWLY("GoogleUpdate.ErrorHresult", hr);
416 main_loop->PostTask(
417 FROM_HERE,
418 base::Bind(&GoogleUpdate::ReportResults, this,
419 UPGRADE_ERROR, error_code, error_message));
420 return false;