1 // Copyright 2019 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.
4
5 #include "chrome/updater/win/ui/progress_wnd.h"
6
7 #include <algorithm>
8
9 #include "base/check_op.h"
10 #include "base/i18n/message_formatter.h"
11 #include "base/notreached.h"
12 #include "base/process/launch.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/time/time.h"
17 #include "chrome/updater/constants.h"
18 #include "chrome/updater/util.h"
19 #include "chrome/updater/win/ui/constants.h"
20 #include "chrome/updater/win/ui/ui_ctls.h"
21 #include "chrome/updater/win/ui/util.h"
22 #include "chrome/updater/win/util.h"
23
24 namespace updater {
25 namespace ui {
26
27 namespace {
28
29 // TODO(crbug.com/1065588): remove this symbol.
30 const char kChromeAppId[] = "{8A69D345-D564-463C-AFF1-A69D9E530F96}";
31
32 // The current UI shows to the user only one completion type, even though
33 // there could be multiple applications in a bundle, where each application
34 // could have a different completion type. The following array lists the
35 // completion codes from low priority to high priority. The completion type
36 // with highest priority will be shown to the user.
37 constexpr CompletionCodes kCompletionCodesActionPriority[] = {
38 CompletionCodes::COMPLETION_CODE_EXIT_SILENTLY,
39 CompletionCodes::COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND,
40 CompletionCodes::COMPLETION_CODE_SUCCESS,
41 CompletionCodes::COMPLETION_CODE_LAUNCH_COMMAND,
42 CompletionCodes::COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
43 CompletionCodes::COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
44 CompletionCodes::COMPLETION_CODE_RESTART_BROWSER,
45 CompletionCodes::COMPLETION_CODE_RESTART_ALL_BROWSERS,
46 CompletionCodes::COMPLETION_CODE_REBOOT_NOTICE_ONLY,
47 CompletionCodes::COMPLETION_CODE_REBOOT,
48 CompletionCodes::COMPLETION_CODE_ERROR,
49 CompletionCodes::COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL,
50 };
51
52 // |kCompletionCodesActionPriority| must have all the values in enumeration
53 // CompletionCodes. The enumeration value starts from 1 so the array size
54 // should match the last value in the enumeration.
55 static_assert(
56 base::size(kCompletionCodesActionPriority) ==
57 static_cast<size_t>(
58 CompletionCodes::COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL),
59 "completion code is missing");
60
GetPriority(CompletionCodes code)61 int GetPriority(CompletionCodes code) {
62 for (size_t i = 0; i < base::size(kCompletionCodesActionPriority); ++i) {
63 if (kCompletionCodesActionPriority[i] == code)
64 return i;
65 }
66
67 NOTREACHED();
68 return -1;
69 }
70
71 // Returns true if all apps are cancelled or if the range is empty.
AreAllAppsCanceled(const std::vector<AppCompletionInfo> & apps_info)72 bool AreAllAppsCanceled(const std::vector<AppCompletionInfo>& apps_info) {
73 return std::all_of(
74 apps_info.begin(), apps_info.end(),
75 [](const AppCompletionInfo& app_info) { return app_info.is_canceled; });
76 }
77
78 } // namespace
79
InstallStoppedWnd(WTL::CMessageLoop * message_loop,HWND parent)80 InstallStoppedWnd::InstallStoppedWnd(WTL::CMessageLoop* message_loop,
81 HWND parent)
82 : message_loop_(message_loop), parent_(parent) {
83 DCHECK(message_loop);
84 DCHECK(::IsWindow(parent));
85 }
86
~InstallStoppedWnd()87 InstallStoppedWnd::~InstallStoppedWnd() {
88 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
89 if (IsWindow())
90 CloseWindow();
91 }
92
PreTranslateMessage(MSG * msg)93 BOOL InstallStoppedWnd::PreTranslateMessage(MSG* msg) {
94 return CWindow::IsDialogMessage(msg);
95 }
96
CloseWindow()97 HRESULT InstallStoppedWnd::CloseWindow() {
98 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
99 DCHECK(IsWindow());
100 ::EnableWindow(parent_, true);
101 return DestroyWindow() ? S_OK : HRESULTFromLastError();
102 }
103
OnInitDialog(UINT,WPARAM,LPARAM,BOOL & handled)104 LRESULT InstallStoppedWnd::OnInitDialog(UINT, WPARAM, LPARAM, BOOL& handled) {
105 // Simulates the modal behavior by disabling its parent window. The parent
106 // window must be enabled before this window is destroyed.
107 ::EnableWindow(parent_, false);
108
109 message_loop_->AddMessageFilter(this);
110
111 default_font_.CreatePointFont(90, kDialogFont);
112 SendMessageToDescendants(
113 WM_SETFONT, reinterpret_cast<WPARAM>(static_cast<HFONT>(default_font_)),
114 0);
115
116 CreateOwnerDrawTitleBar(m_hWnd, GetDlgItem(IDC_TITLE_BAR_SPACER), kBkColor);
117 SetCustomDlgColors(kTextColor, kBkColor);
118
119 EnableFlatButtons(m_hWnd);
120
121 handled = true;
122 return 1;
123 }
124
OnClickButton(WORD,WORD id,HWND,BOOL & handled)125 LRESULT InstallStoppedWnd::OnClickButton(WORD, WORD id, HWND, BOOL& handled) {
126 DCHECK(id == IDOK || id == IDCANCEL);
127 ::PostMessage(parent_, WM_INSTALL_STOPPED, id, 0);
128 handled = true;
129 return 0;
130 }
131
OnDestroy(UINT,WPARAM,LPARAM,BOOL & handled)132 LRESULT InstallStoppedWnd::OnDestroy(UINT, WPARAM, LPARAM, BOOL& handled) {
133 message_loop_->RemoveMessageFilter(this);
134 handled = true;
135 return 0;
136 }
137
ProgressWnd(WTL::CMessageLoop * message_loop,HWND parent)138 ProgressWnd::ProgressWnd(WTL::CMessageLoop* message_loop, HWND parent)
139 : CompleteWnd(IDD_PROGRESS,
140 ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS,
141 message_loop,
142 parent) {}
143
~ProgressWnd()144 ProgressWnd::~ProgressWnd() {
145 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
146 DCHECK(!IsWindow());
147 cur_state_ = States::STATE_END;
148 }
149
SetEventSink(ProgressWndEvents * events)150 void ProgressWnd::SetEventSink(ProgressWndEvents* events) {
151 events_sink_ = events;
152 CompleteWnd::SetEventSink(events_sink_);
153 }
154
OnInitDialog(UINT message,WPARAM w_param,LPARAM l_param,BOOL & handled)155 LRESULT ProgressWnd::OnInitDialog(UINT message,
156 WPARAM w_param,
157 LPARAM l_param,
158 BOOL& handled) {
159 // TODO(sorin): remove this when https://crbug.com/1010653 is fixed.
160 HideWindowChildren(*this);
161
162 InitializeDialog();
163
164 SetMarqueeMode(true);
165
166 base::string16 state_text;
167 ui::LoadString(IDS_INITIALIZING, &state_text);
168 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, state_text.c_str());
169 ChangeControlState();
170
171 handled = true;
172 return 1; // Let the system set the focus.
173 }
174
175 // If closing is disabled, then it does not close the window.
176 // If in a completion state, then the window is closed.
177 // Otherwise, the InstallStoppedWnd is displayed and the window is closed only
178 // if the user chooses cancel.
MaybeCloseWindow()179 bool ProgressWnd::MaybeCloseWindow() {
180 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
181 if (!is_close_enabled())
182 return false;
183
184 if (cur_state_ != States::STATE_COMPLETE_SUCCESS &&
185 cur_state_ != States::STATE_COMPLETE_ERROR &&
186 cur_state_ != States::STATE_COMPLETE_RESTART_BROWSER &&
187 cur_state_ != States::STATE_COMPLETE_RESTART_ALL_BROWSERS &&
188 cur_state_ != States::STATE_COMPLETE_REBOOT) {
189 // The UI is not in final state: ask the user to proceed with closing it.
190 // A modal dialog opens up and sends a message back to this window to
191 // communicate the user decision.
192 install_stopped_wnd_ =
193 std::make_unique<InstallStoppedWnd>(message_loop(), *this);
194 HWND hwnd = install_stopped_wnd_->Create(*this);
195 if (hwnd) {
196 base::string16 title;
197 ui::LoadString(IDS_INSTALLATION_STOPPED_WINDOW_TITLE, &title);
198 install_stopped_wnd_->SetWindowText(title.c_str());
199
200 base::string16 button_text;
201 ui::LoadString(IDS_RESUME_INSTALLATION, &button_text);
202 install_stopped_wnd_->SetDlgItemText(IDOK, button_text.c_str());
203
204 ui::LoadString(IDS_CANCEL_INSTALLATION, &button_text);
205 install_stopped_wnd_->SetDlgItemText(IDCANCEL, button_text.c_str());
206
207 base::string16 text;
208 ui::LoadString(IDS_INSTALL_STOPPED, &text);
209 install_stopped_wnd_->SetDlgItemText(IDC_INSTALL_STOPPED_TEXT,
210 text.c_str());
211
212 install_stopped_wnd_->CenterWindow(*this);
213 install_stopped_wnd_->ShowWindow(SW_SHOWDEFAULT);
214 return false;
215 }
216 }
217
218 CloseWindow();
219 return true;
220 }
221
OnClickedButton(WORD notify_code,WORD id,HWND wnd_ctl,BOOL & handled)222 LRESULT ProgressWnd::OnClickedButton(WORD notify_code,
223 WORD id,
224 HWND wnd_ctl,
225 BOOL& handled) {
226 DCHECK(id == IDC_BUTTON1 || id == IDC_BUTTON2 || id == IDC_CLOSE);
227 DCHECK(events_sink_);
228
229 switch (id) {
230 case IDC_BUTTON1:
231 switch (cur_state_) {
232 case States::STATE_COMPLETE_RESTART_BROWSER:
233 events_sink_->DoRestartBrowser(false, post_install_urls_);
234 break;
235 case States::STATE_COMPLETE_RESTART_ALL_BROWSERS:
236 events_sink_->DoRestartBrowser(true, post_install_urls_);
237 break;
238 case States::STATE_COMPLETE_REBOOT:
239 events_sink_->DoReboot();
240 break;
241 default:
242 NOTREACHED();
243 }
244 break;
245 case IDC_BUTTON2:
246 switch (cur_state_) {
247 case States::STATE_COMPLETE_RESTART_BROWSER:
248 case States::STATE_COMPLETE_RESTART_ALL_BROWSERS:
249 case States::STATE_COMPLETE_REBOOT:
250 break;
251 default:
252 NOTREACHED();
253 }
254 break;
255 case IDC_CLOSE:
256 switch (cur_state_) {
257 case States::STATE_COMPLETE_SUCCESS:
258 case States::STATE_COMPLETE_ERROR:
259 return CompleteWnd::OnClickedButton(notify_code, id, wnd_ctl,
260 handled);
261 break;
262 default:
263 NOTREACHED();
264 }
265 break;
266 default:
267 NOTREACHED();
268 }
269
270 handled = true;
271 CloseWindow();
272
273 return 0;
274 }
275
OnInstallStopped(UINT msg,WPARAM wparam,LPARAM,BOOL & handled)276 LRESULT ProgressWnd::OnInstallStopped(UINT msg,
277 WPARAM wparam,
278 LPARAM,
279 BOOL& handled) {
280 install_stopped_wnd_.reset();
281
282 DCHECK(msg == WM_INSTALL_STOPPED);
283 DCHECK(wparam == IDOK || wparam == IDCANCEL);
284 switch (wparam) {
285 case IDOK:
286 break;
287 case IDCANCEL:
288 HandleCancelRequest();
289 break;
290 default:
291 NOTREACHED();
292 break;
293 }
294
295 handled = true;
296 return 0;
297 }
298
HandleCancelRequest()299 void ProgressWnd::HandleCancelRequest() {
300 base::string16 s;
301 ui::LoadString(IDS_CANCELING, &s);
302 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, s.c_str());
303
304 if (is_canceled_)
305 return;
306 is_canceled_ = true;
307 if (events_sink_)
308 events_sink_->DoCancel();
309 }
310
OnCheckingForUpdate()311 void ProgressWnd::OnCheckingForUpdate() {
312 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
313 if (!IsWindow())
314 return;
315
316 cur_state_ = States::STATE_CHECKING_FOR_UPDATE;
317
318 base::string16 s;
319 ui::LoadString(IDS_WAITING_TO_CONNECT, &s);
320 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, s.c_str());
321
322 ChangeControlState();
323 }
324
OnUpdateAvailable(const base::string16 & app_id,const base::string16 & app_name,const base::string16 & version_string)325 void ProgressWnd::OnUpdateAvailable(const base::string16& app_id,
326 const base::string16& app_name,
327 const base::string16& version_string) {
328 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
329
330 if (base::EqualsCaseInsensitiveASCII(app_id,
331 base::ASCIIToUTF16(kChromeAppId))) {
332 HBITMAP app_bitmap = reinterpret_cast<HBITMAP>(
333 ::LoadImage(GetCurrentModuleHandle(), MAKEINTRESOURCE(IDB_CHROME),
334 IMAGE_BITMAP, 0, 0, LR_SHARED));
335 DCHECK(app_bitmap);
336 SendDlgItemMessage(IDC_APP_BITMAP, STM_SETIMAGE, IMAGE_BITMAP,
337 reinterpret_cast<LPARAM>(app_bitmap));
338 }
339
340 if (!IsWindow())
341 return;
342 }
343
OnWaitingToDownload(const base::string16 & app_id,const base::string16 & app_name)344 void ProgressWnd::OnWaitingToDownload(const base::string16& app_id,
345 const base::string16& app_name) {
346 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
347 if (!IsWindow())
348 return;
349
350 cur_state_ = States::STATE_WAITING_TO_DOWNLOAD;
351
352 base::string16 s;
353 ui::LoadString(IDS_WAITING_TO_DOWNLOAD, &s);
354 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, s.c_str());
355
356 ChangeControlState();
357 }
358
359 // May be called repeatedly during download.
OnDownloading(const base::string16 & app_id,const base::string16 & app_name,int time_remaining_ms,int pos)360 void ProgressWnd::OnDownloading(const base::string16& app_id,
361 const base::string16& app_name,
362 int time_remaining_ms,
363 int pos) {
364 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
365 if (!IsWindow())
366 return;
367
368 DCHECK(0 <= pos && pos <= 100);
369
370 cur_state_ = States::STATE_DOWNLOADING;
371
372 base::string16 s;
373
374 // TODO(sorin): should use base::TimeDelta, https://crbug.com/1016921
375 int time_remaining_sec = CeilingDivide(time_remaining_ms, kMsPerSec);
376 if (time_remaining_ms < 0) {
377 ui::LoadString(IDS_DOWNLOADING, &s);
378 } else if (time_remaining_ms == 0) {
379 ui::LoadString(IDS_DOWNLOADING_COMPLETED, &s);
380 } else if (time_remaining_sec < kSecPerMin) {
381 // Less than one minute remaining.
382 ui::LoadString(IDS_DOWNLOADING_SHORT, &s);
383 s = base::i18n::MessageFormatter::FormatWithNumberedArgs(
384 s, time_remaining_sec);
385 } else if (time_remaining_sec < kSecondsPerHour) {
386 // Less than one hour remaining.
387 int time_remaining_minute = CeilingDivide(time_remaining_sec, kSecPerMin);
388 ui::LoadString(IDS_DOWNLOADING_LONG, &s);
389 s = base::i18n::MessageFormatter::FormatWithNumberedArgs(
390 s, time_remaining_minute);
391 } else {
392 int time_remaining_hour =
393 CeilingDivide(time_remaining_sec, kSecondsPerHour);
394 ui::LoadString(IDS_DOWNLOADING_VERY_LONG, &s);
395 s = base::i18n::MessageFormatter::FormatWithNumberedArgs(
396 s, time_remaining_hour);
397 }
398
399 // Reduces flicker by only updating the control if the text has changed.
400 base::string16 current_text;
401 ui::GetDlgItemText(*this, IDC_INSTALLER_STATE_TEXT, ¤t_text);
402 if (s != current_text)
403 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, s.c_str());
404
405 SetMarqueeMode(pos == 0);
406 if (pos > 0)
407 SendDlgItemMessage(IDC_PROGRESS, PBM_SETPOS, pos, 0);
408
409 ChangeControlState();
410 }
411
OnWaitingRetryDownload(const base::string16 & app_id,const base::string16 & app_name,const base::Time & next_retry_time)412 void ProgressWnd::OnWaitingRetryDownload(const base::string16& app_id,
413 const base::string16& app_name,
414 const base::Time& next_retry_time) {
415 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
416 if (!IsWindow())
417 return;
418
419 // Display the next retry time interval if |next_retry_time| is in the future.
420 const auto retry_time_in_sec =
421 (next_retry_time - base::Time::NowFromSystemTime()).InSeconds();
422 if (retry_time_in_sec > 0) {
423 base::string16 s;
424 ui::LoadString(IDS_DOWNLOAD_RETRY, &s);
425 s = base::i18n::MessageFormatter::FormatWithNumberedArgs(
426 s, 1 + retry_time_in_sec);
427 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, s.c_str());
428 ChangeControlState();
429 }
430 }
431
OnWaitingToInstall(const base::string16 & app_id,const base::string16 & app_name,bool * can_start_install)432 void ProgressWnd::OnWaitingToInstall(const base::string16& app_id,
433 const base::string16& app_name,
434 bool* can_start_install) {
435 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
436 DCHECK(can_start_install);
437 if (!IsWindow())
438 return;
439
440 if (States::STATE_WAITING_TO_INSTALL != cur_state_) {
441 cur_state_ = States::STATE_WAITING_TO_INSTALL;
442 base::string16 s;
443 ui::LoadString(IDS_WAITING_TO_INSTALL, &s);
444 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, s.c_str());
445 ChangeControlState();
446 }
447
448 *can_start_install = !IsInstallStoppedWindowPresent();
449 }
450
451 // May be called repeatedly during install.
OnInstalling(const base::string16 & app_id,const base::string16 & app_name,int time_remaining_ms,int pos)452 void ProgressWnd::OnInstalling(const base::string16& app_id,
453 const base::string16& app_name,
454 int time_remaining_ms,
455 int pos) {
456 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
457 if (!IsWindow())
458 return;
459
460 if (States::STATE_INSTALLING != cur_state_) {
461 cur_state_ = States::STATE_INSTALLING;
462 base::string16 s;
463 ui::LoadString(IDS_INSTALLING, &s);
464 SetDlgItemText(IDC_INSTALLER_STATE_TEXT, s.c_str());
465 ChangeControlState();
466 }
467
468 SetMarqueeMode(pos <= 0);
469 if (pos > 0)
470 SendDlgItemMessage(IDC_PROGRESS, PBM_SETPOS, pos, 0);
471 }
472
OnPause()473 void ProgressWnd::OnPause() {
474 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
475 if (!IsWindow())
476 return;
477
478 cur_state_ = States::STATE_PAUSED;
479 ChangeControlState();
480 }
481
DeterminePostInstallUrls(const ObserverCompletionInfo & info)482 void ProgressWnd::DeterminePostInstallUrls(const ObserverCompletionInfo& info) {
483 DCHECK(post_install_urls_.empty());
484 post_install_urls_.clear();
485
486 for (size_t i = 0; i < info.apps_info.size(); ++i) {
487 const AppCompletionInfo& app_info = info.apps_info[i];
488 if (!app_info.post_install_url.empty() &&
489 (app_info.completion_code ==
490 CompletionCodes::COMPLETION_CODE_RESTART_ALL_BROWSERS ||
491 app_info.completion_code ==
492 CompletionCodes::COMPLETION_CODE_RESTART_BROWSER)) {
493 post_install_urls_.push_back(app_info.post_install_url);
494 }
495 }
496 DCHECK(!post_install_urls_.empty());
497 }
498
GetBundleOverallCompletionCode(const ObserverCompletionInfo & info) const499 CompletionCodes ProgressWnd::GetBundleOverallCompletionCode(
500 const ObserverCompletionInfo& info) const {
501 if (info.completion_code == CompletionCodes::COMPLETION_CODE_ERROR ||
502 info.completion_code ==
503 CompletionCodes::COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL) {
504 return info.completion_code;
505 }
506
507 DCHECK(info.completion_code == CompletionCodes::COMPLETION_CODE_SUCCESS);
508
509 CompletionCodes overall_completion_code = kCompletionCodesActionPriority[0];
510 for (size_t i = 0; i < info.apps_info.size(); ++i) {
511 if (GetPriority(overall_completion_code) <
512 GetPriority(info.apps_info[i].completion_code)) {
513 overall_completion_code = info.apps_info[i].completion_code;
514 }
515 }
516
517 return overall_completion_code;
518 }
519
OnComplete(const ObserverCompletionInfo & observer_info)520 void ProgressWnd::OnComplete(const ObserverCompletionInfo& observer_info) {
521 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
522
523 if (!CompleteWnd::OnComplete())
524 return;
525
526 CloseInstallStoppedWindow();
527
528 bool launch_commands_succeeded = LaunchCmdLines(observer_info);
529
530 using MessageFormatter = base::i18n::MessageFormatter;
531
532 base::string16 s;
533 CompletionCodes overall_completion_code =
534 GetBundleOverallCompletionCode(observer_info);
535 switch (overall_completion_code) {
536 case CompletionCodes::COMPLETION_CODE_SUCCESS:
537 case CompletionCodes::COMPLETION_CODE_LAUNCH_COMMAND:
538 case CompletionCodes::COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL:
539 cur_state_ = States::STATE_COMPLETE_SUCCESS;
540 CompleteWnd::DisplayCompletionDialog(true, observer_info.completion_text,
541 observer_info.help_url);
542 break;
543 case CompletionCodes::COMPLETION_CODE_ERROR:
544 if (AreAllAppsCanceled(observer_info.apps_info)) {
545 CloseWindow();
546 return;
547 }
548 cur_state_ = States::STATE_COMPLETE_ERROR;
549 CompleteWnd::DisplayCompletionDialog(false, observer_info.completion_text,
550 observer_info.help_url);
551 break;
552 case CompletionCodes::COMPLETION_CODE_RESTART_ALL_BROWSERS:
553 cur_state_ = States::STATE_COMPLETE_RESTART_ALL_BROWSERS;
554 ui::LoadString(IDS_RESTART_NOW, &s);
555 SetDlgItemText(IDC_BUTTON1, s.c_str());
556 ui::LoadString(IDS_RESTART_LATER, &s);
557 SetDlgItemText(IDC_BUTTON2, s.c_str());
558 ui::LoadString(IDS_TEXT_RESTART_ALL_BROWSERS, &s);
559 SetDlgItemText(
560 IDC_COMPLETE_TEXT,
561 MessageFormatter::FormatWithNumberedArgs(s, bundle_name()).c_str());
562 DeterminePostInstallUrls(observer_info);
563 break;
564 case CompletionCodes::COMPLETION_CODE_RESTART_BROWSER:
565 cur_state_ = States::STATE_COMPLETE_RESTART_BROWSER;
566 ui::LoadString(IDS_RESTART_NOW, &s);
567 SetDlgItemText(IDC_BUTTON1, s.c_str());
568 ui::LoadString(IDS_RESTART_LATER, &s);
569 SetDlgItemText(IDC_BUTTON2, s.c_str());
570 ui::LoadString(IDS_TEXT_RESTART_BROWSER, &s);
571 SetDlgItemText(
572 IDC_COMPLETE_TEXT,
573 MessageFormatter::FormatWithNumberedArgs(s, bundle_name()).c_str());
574 SetDlgItemText(IDC_COMPLETE_TEXT, s.c_str());
575 DeterminePostInstallUrls(observer_info);
576 break;
577 case CompletionCodes::COMPLETION_CODE_REBOOT:
578 cur_state_ = States::STATE_COMPLETE_REBOOT;
579 ui::LoadString(IDS_RESTART_NOW, &s);
580 SetDlgItemText(IDC_BUTTON1, s.c_str());
581 ui::LoadString(IDS_RESTART_LATER, &s);
582 SetDlgItemText(IDC_BUTTON2, s.c_str());
583 ui::LoadString(IDS_TEXT_RESTART_COMPUTER, &s);
584 SetDlgItemText(
585 IDC_COMPLETE_TEXT,
586 MessageFormatter::FormatWithNumberedArgs(s, bundle_name()).c_str());
587 SetDlgItemText(IDC_COMPLETE_TEXT, s.c_str());
588 break;
589 case CompletionCodes::COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY:
590 cur_state_ = States::STATE_COMPLETE_SUCCESS;
591 ui::LoadString(IDS_TEXT_RESTART_ALL_BROWSERS, &s);
592 CompleteWnd::DisplayCompletionDialog(
593 true, MessageFormatter::FormatWithNumberedArgs(s, bundle_name()),
594 observer_info.help_url);
595 break;
596 case CompletionCodes::COMPLETION_CODE_REBOOT_NOTICE_ONLY:
597 cur_state_ = States::STATE_COMPLETE_SUCCESS;
598 ui::LoadString(IDS_TEXT_RESTART_COMPUTER, &s);
599 CompleteWnd::DisplayCompletionDialog(
600 true, MessageFormatter::FormatWithNumberedArgs(s, bundle_name()),
601 observer_info.help_url);
602 break;
603 case CompletionCodes::COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY:
604 cur_state_ = States::STATE_COMPLETE_SUCCESS;
605 ui::LoadString(IDS_TEXT_RESTART_BROWSER, &s);
606 CompleteWnd::DisplayCompletionDialog(
607 true, MessageFormatter::FormatWithNumberedArgs(s, bundle_name()),
608 observer_info.help_url);
609 break;
610 case CompletionCodes::COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND:
611 cur_state_ = States::STATE_COMPLETE_SUCCESS;
612 if (launch_commands_succeeded) {
613 CloseWindow();
614 return;
615 }
616 CompleteWnd::DisplayCompletionDialog(true, observer_info.completion_text,
617 observer_info.help_url);
618 break;
619 case CompletionCodes::COMPLETION_CODE_EXIT_SILENTLY:
620 cur_state_ = States::STATE_COMPLETE_SUCCESS;
621 CloseWindow();
622 return;
623 default:
624 NOTREACHED();
625 break;
626 }
627
628 ChangeControlState();
629 }
630
LaunchCmdLine(const AppCompletionInfo & app_info)631 HRESULT ProgressWnd::LaunchCmdLine(const AppCompletionInfo& app_info) {
632 if (app_info.post_install_launch_command_line.empty())
633 return S_OK;
634
635 if (app_info.completion_code !=
636 CompletionCodes::COMPLETION_CODE_LAUNCH_COMMAND &&
637 app_info.completion_code !=
638 CompletionCodes::COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND) {
639 return S_OK;
640 }
641
642 DCHECK(SUCCEEDED(app_info.error_code));
643 DCHECK(!app_info.is_noupdate);
644
645 auto process =
646 base::LaunchProcess(app_info.post_install_launch_command_line, {});
647 return process.IsValid() ? S_OK : HRESULTFromLastError();
648 }
649
LaunchCmdLines(const ObserverCompletionInfo & info)650 bool ProgressWnd::LaunchCmdLines(const ObserverCompletionInfo& info) {
651 bool result = true;
652
653 for (size_t i = 0; i < info.apps_info.size(); ++i) {
654 const AppCompletionInfo& app_info = info.apps_info[i];
655 if (FAILED(app_info.error_code)) {
656 continue;
657 }
658 result &= SUCCEEDED(LaunchCmdLine(app_info));
659 }
660
661 return result;
662 }
663
ChangeControlState()664 HRESULT ProgressWnd::ChangeControlState() {
665 for (const auto& ctl : ctls_)
666 SetControlAttributes(ctl.id, ctl.attr[static_cast<size_t>(cur_state_)]);
667 return S_OK;
668 }
669
SetMarqueeMode(bool is_marquee)670 HRESULT ProgressWnd::SetMarqueeMode(bool is_marquee) {
671 CWindow progress_bar = GetDlgItem(IDC_PROGRESS);
672 LONG_PTR style = progress_bar.GetWindowLongPtr(GWL_STYLE);
673 if (is_marquee)
674 style |= PBS_MARQUEE;
675 else
676 style &= ~PBS_MARQUEE;
677 progress_bar.SetWindowLongPtr(GWL_STYLE, style);
678 progress_bar.SendMessage(PBM_SETMARQUEE, !!is_marquee, 0);
679
680 return S_OK;
681 }
682
IsInstallStoppedWindowPresent()683 bool ProgressWnd::IsInstallStoppedWindowPresent() {
684 return install_stopped_wnd_.get() && install_stopped_wnd_->IsWindow();
685 }
686
CloseInstallStoppedWindow()687 bool ProgressWnd::CloseInstallStoppedWindow() {
688 if (IsInstallStoppedWindowPresent()) {
689 install_stopped_wnd_->CloseWindow();
690 install_stopped_wnd_.reset();
691 return true;
692 }
693 return false;
694 }
695
696 } // namespace ui
697 } // namespace updater
698