| // Copyright (c) 2012 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/ui/views/critical_notification_bubble_view.h" |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/upgrade_detector.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/grit/chromium_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chrome/grit/locale_settings.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "grit/theme_resources.h" |
| #include "ui/accessibility/ax_view_state.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/views/bubble/bubble_frame_view.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/widget/widget.h" |
| |
| using base::UserMetricsAction; |
| |
| namespace { |
| |
| // How long to give the user until auto-restart if no action is taken. The code |
| // assumes this to be less than a minute. |
| const int kCountdownDuration = 30; // Seconds. |
| |
| // How often to refresh the bubble UI to update the counter. As long as the |
| // countdown is in seconds, this should be 1000 or lower. |
| const int kRefreshBubbleEvery = 1000; // Millisecond. |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // CriticalNotificationBubbleView |
| |
| CriticalNotificationBubbleView::CriticalNotificationBubbleView( |
| views::View* anchor_view) |
| : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT) { |
| set_close_on_deactivate(false); |
| } |
| |
| CriticalNotificationBubbleView::~CriticalNotificationBubbleView() { |
| } |
| |
| int CriticalNotificationBubbleView::GetRemainingTime() const { |
| base::TimeDelta time_lapsed = base::TimeTicks::Now() - bubble_created_; |
| return kCountdownDuration - time_lapsed.InSeconds(); |
| } |
| |
| void CriticalNotificationBubbleView::OnCountdown() { |
| UpgradeDetector* upgrade_detector = UpgradeDetector::GetInstance(); |
| if (upgrade_detector->critical_update_acknowledged()) { |
| // The user has already interacted with the bubble and chosen a path. |
| GetWidget()->Close(); |
| return; |
| } |
| |
| int seconds = GetRemainingTime(); |
| if (seconds <= 0) { |
| // Time's up! |
| upgrade_detector->acknowledge_critical_update(); |
| |
| content::RecordAction( |
| UserMetricsAction("CriticalNotification_AutoRestart")); |
| refresh_timer_.Stop(); |
| chrome::AttemptRestart(); |
| } |
| |
| // Update the counter. It may seem counter-intuitive to update the message |
| // after we attempt restart, but remember that shutdown may be aborted by |
| // an onbeforeunload handler, leaving the bubble up when the browser should |
| // have restarted (giving the user another chance). |
| GetBubbleFrameView()->UpdateWindowTitle(); |
| } |
| |
| base::string16 CriticalNotificationBubbleView::GetWindowTitle() const { |
| int seconds = GetRemainingTime(); |
| return seconds > 0 ? l10n_util::GetPluralStringFUTF16( |
| IDS_CRITICAL_NOTIFICATION_HEADLINE, seconds) |
| : l10n_util::GetStringUTF16( |
| IDS_CRITICAL_NOTIFICATION_HEADLINE_ALTERNATE); |
| } |
| |
| gfx::ImageSkia CriticalNotificationBubbleView::GetWindowIcon() { |
| return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| IDR_UPDATE_MENU_SEVERITY_HIGH); |
| } |
| |
| bool CriticalNotificationBubbleView::ShouldShowWindowIcon() const { |
| return true; |
| } |
| |
| void CriticalNotificationBubbleView::WindowClosing() { |
| refresh_timer_.Stop(); |
| } |
| |
| bool CriticalNotificationBubbleView::Cancel() { |
| UpgradeDetector::GetInstance()->acknowledge_critical_update(); |
| content::RecordAction(UserMetricsAction("CriticalNotification_Ignore")); |
| // If the counter reaches 0, we set a restart flag that must be cleared if |
| // the user selects, for example, "Stay on this page" during an |
| // onbeforeunload handler. |
| PrefService* prefs = g_browser_process->local_state(); |
| if (prefs->HasPrefPath(prefs::kRestartLastSessionOnShutdown)) |
| prefs->ClearPref(prefs::kRestartLastSessionOnShutdown); |
| return true; |
| } |
| |
| bool CriticalNotificationBubbleView::Accept() { |
| UpgradeDetector::GetInstance()->acknowledge_critical_update(); |
| content::RecordAction(UserMetricsAction("CriticalNotification_Restart")); |
| chrome::AttemptRestart(); |
| return true; |
| } |
| |
| base::string16 CriticalNotificationBubbleView::GetDialogButtonLabel( |
| ui::DialogButton button) const { |
| return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_CANCEL |
| ? IDS_CRITICAL_NOTIFICATION_DISMISS |
| : IDS_CRITICAL_NOTIFICATION_RESTART); |
| } |
| |
| void CriticalNotificationBubbleView::Init() { |
| bubble_created_ = base::TimeTicks::Now(); |
| |
| SetLayoutManager(new views::FillLayout()); |
| |
| views::Label* message = new views::Label(); |
| message->SetMultiLine(true); |
| message->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| message->SetText(l10n_util::GetStringUTF16(IDS_CRITICAL_NOTIFICATION_TEXT)); |
| message->SizeToFit(views::Widget::GetLocalizedContentsWidth( |
| IDS_CRUCIAL_NOTIFICATION_BUBBLE_WIDTH_CHARS)); |
| AddChildView(message); |
| |
| refresh_timer_.Start(FROM_HERE, |
| base::TimeDelta::FromMilliseconds(kRefreshBubbleEvery), |
| this, &CriticalNotificationBubbleView::OnCountdown); |
| |
| content::RecordAction(UserMetricsAction("CriticalNotificationShown")); |
| } |
| |
| void CriticalNotificationBubbleView::GetAccessibleState( |
| ui::AXViewState* state) { |
| state->role = ui::AX_ROLE_ALERT; |
| } |
| |
| void CriticalNotificationBubbleView::ViewHierarchyChanged( |
| const ViewHierarchyChangedDetails& details) { |
| if (details.is_add && details.child == this) |
| NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); |
| } |