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, &current_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