Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / critical_notification_bubble_view.cc
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.
4
5 #include "chrome/browser/ui/views/critical_notification_bubble_view.h"
6
7 #include "base/prefs/pref_service.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/lifetime/application_lifetime.h"
12 #include "chrome/browser/upgrade_detector.h"
13 #include "chrome/common/pref_names.h"
14 #include "content/public/browser/user_metrics.h"
15 #include "grit/chromium_strings.h"
16 #include "grit/generated_resources.h"
17 #include "grit/locale_settings.h"
18 #include "grit/theme_resources.h"
19 #include "ui/accessibility/ax_view_state.h"
20 #include "ui/base/accelerators/accelerator.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/views/controls/button/label_button.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/label.h"
26 #include "ui/views/layout/grid_layout.h"
27 #include "ui/views/layout/layout_constants.h"
28 #include "ui/views/widget/widget.h"
29
30 using base::UserMetricsAction;
31
32 namespace {
33
34 // Layout constants.
35 const int kInset = 2;
36 const int kImageHeadlinePadding = 4;
37 const int kHeadlineMessagePadding = 4;
38 const int kMessageBubblePadding = 11;
39
40 // How long to give the user until auto-restart if no action is taken. The code
41 // assumes this to be less than a minute.
42 const int kCountdownDuration = 30;  // Seconds.
43
44 // How often to refresh the bubble UI to update the counter. As long as the
45 // countdown is in seconds, this should be 1000 or lower.
46 const int kRefreshBubbleEvery = 1000;  // Millisecond.
47
48 }  // namespace
49
50 ////////////////////////////////////////////////////////////////////////////////
51 // CriticalNotificationBubbleView
52
53 CriticalNotificationBubbleView::CriticalNotificationBubbleView(
54     views::View* anchor_view)
55     : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
56       headline_(NULL),
57       restart_button_(NULL),
58       dismiss_button_(NULL) {
59   set_close_on_deactivate(false);
60 }
61
62 CriticalNotificationBubbleView::~CriticalNotificationBubbleView() {
63 }
64
65 int CriticalNotificationBubbleView::GetRemainingTime() {
66   base::TimeDelta time_lapsed = base::Time::Now() - bubble_created_;
67   return kCountdownDuration - time_lapsed.InSeconds();
68 }
69
70 void CriticalNotificationBubbleView::UpdateBubbleHeadline(int seconds) {
71   if (seconds > 0) {
72     headline_->SetText(
73         l10n_util::GetStringFUTF16(IDS_CRITICAL_NOTIFICATION_HEADLINE,
74             l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
75             base::IntToString16(seconds)));
76   } else {
77     headline_->SetText(
78         l10n_util::GetStringFUTF16(IDS_CRITICAL_NOTIFICATION_HEADLINE_ALTERNATE,
79             l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
80   }
81 }
82
83 void CriticalNotificationBubbleView::OnCountdown() {
84   UpgradeDetector* upgrade_detector = UpgradeDetector::GetInstance();
85   if (upgrade_detector->critical_update_acknowledged()) {
86     // The user has already interacted with the bubble and chosen a path.
87     GetWidget()->Close();
88     return;
89   }
90
91   int seconds = GetRemainingTime();
92   if (seconds <= 0) {
93     // Time's up!
94     upgrade_detector->acknowledge_critical_update();
95
96     content::RecordAction(
97         UserMetricsAction("CriticalNotification_AutoRestart"));
98     refresh_timer_.Stop();
99     chrome::AttemptRestart();
100   }
101
102   // Update the counter. It may seem counter-intuitive to update the message
103   // after we attempt restart, but remember that shutdown may be aborted by
104   // an onbeforeunload handler, leaving the bubble up when the browser should
105   // have restarted (giving the user another chance).
106   UpdateBubbleHeadline(seconds);
107   SchedulePaint();
108 }
109
110 void CriticalNotificationBubbleView::ButtonPressed(
111     views::Button* sender, const ui::Event& event) {
112   // Let other bubbles know we have an answer from the user.
113   UpgradeDetector::GetInstance()->acknowledge_critical_update();
114
115   if (sender == restart_button_) {
116     content::RecordAction(UserMetricsAction("CriticalNotification_Restart"));
117     chrome::AttemptRestart();
118   } else if (sender == dismiss_button_) {
119     content::RecordAction(UserMetricsAction("CriticalNotification_Ignore"));
120     // If the counter reaches 0, we set a restart flag that must be cleared if
121     // the user selects, for example, "Stay on this page" during an
122     // onbeforeunload handler.
123     PrefService* prefs = g_browser_process->local_state();
124     if (prefs->HasPrefPath(prefs::kRestartLastSessionOnShutdown))
125       prefs->ClearPref(prefs::kRestartLastSessionOnShutdown);
126   } else {
127     NOTREACHED();
128   }
129
130   GetWidget()->Close();
131 }
132
133 void CriticalNotificationBubbleView::WindowClosing() {
134   refresh_timer_.Stop();
135 }
136
137 void CriticalNotificationBubbleView::GetAccessibleState(
138     ui::AXViewState* state) {
139   state->role = ui::AX_ROLE_ALERT;
140 }
141
142 void CriticalNotificationBubbleView::ViewHierarchyChanged(
143     const ViewHierarchyChangedDetails& details) {
144   if (details.is_add && details.child == this)
145     NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
146 }
147
148 bool CriticalNotificationBubbleView::AcceleratorPressed(
149     const ui::Accelerator& accelerator) {
150   if (accelerator.key_code() == ui::VKEY_ESCAPE)
151     UpgradeDetector::GetInstance()->acknowledge_critical_update();
152   return BubbleDelegateView::AcceleratorPressed(accelerator);
153 }
154
155 void CriticalNotificationBubbleView::Init() {
156   bubble_created_ = base::Time::Now();
157
158   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
159
160   views::GridLayout* layout = views::GridLayout::CreatePanel(this);
161   layout->SetInsets(0, kInset, kInset, kInset);
162   SetLayoutManager(layout);
163
164   const int top_column_set_id = 0;
165   views::ColumnSet* top_columns = layout->AddColumnSet(top_column_set_id);
166   top_columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
167                          0, views::GridLayout::USE_PREF, 0, 0);
168   top_columns->AddPaddingColumn(0, kImageHeadlinePadding);
169   top_columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
170                          0, views::GridLayout::USE_PREF, 0, 0);
171   top_columns->AddPaddingColumn(1, 0);
172   layout->StartRow(0, top_column_set_id);
173
174   views::ImageView* image = new views::ImageView();
175   image->SetImage(rb.GetImageSkiaNamed(IDR_UPDATE_MENU_SEVERITY_HIGH));
176   layout->AddView(image);
177
178   headline_ = new views::Label();
179   headline_->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
180   UpdateBubbleHeadline(GetRemainingTime());
181   layout->AddView(headline_);
182
183   const int middle_column_set_id = 1;
184   views::ColumnSet* middle_column = layout->AddColumnSet(middle_column_set_id);
185   middle_column->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
186                            0, views::GridLayout::USE_PREF, 0, 0);
187   layout->StartRowWithPadding(0, middle_column_set_id,
188                               0, kHeadlineMessagePadding);
189
190   views::Label* message = new views::Label();
191   message->SetMultiLine(true);
192   message->SetHorizontalAlignment(gfx::ALIGN_LEFT);
193   message->SetText(l10n_util::GetStringFUTF16(IDS_CRITICAL_NOTIFICATION_TEXT,
194       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
195   message->SizeToFit(views::Widget::GetLocalizedContentsWidth(
196       IDS_CRUCIAL_NOTIFICATION_BUBBLE_WIDTH_CHARS));
197   layout->AddView(message);
198
199   const int bottom_column_set_id = 2;
200   views::ColumnSet* bottom_columns = layout->AddColumnSet(bottom_column_set_id);
201   bottom_columns->AddPaddingColumn(1, 0);
202   bottom_columns->AddColumn(views::GridLayout::CENTER,
203       views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
204   bottom_columns->AddPaddingColumn(0, views::kRelatedButtonHSpacing);
205   bottom_columns->AddColumn(views::GridLayout::CENTER,
206       views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
207   layout->StartRowWithPadding(0, bottom_column_set_id,
208                               0, kMessageBubblePadding);
209
210   restart_button_ = new views::LabelButton(this,
211       l10n_util::GetStringUTF16(IDS_CRITICAL_NOTIFICATION_RESTART));
212   restart_button_->SetStyle(views::Button::STYLE_BUTTON);
213   restart_button_->SetIsDefault(true);
214   layout->AddView(restart_button_);
215   dismiss_button_ = new views::LabelButton(this,
216       l10n_util::GetStringUTF16(IDS_CRITICAL_NOTIFICATION_DISMISS));
217   dismiss_button_->SetStyle(views::Button::STYLE_BUTTON);
218   layout->AddView(dismiss_button_);
219
220   refresh_timer_.Start(FROM_HERE,
221       base::TimeDelta::FromMilliseconds(kRefreshBubbleEvery),
222       this, &CriticalNotificationBubbleView::OnCountdown);
223
224   content::RecordAction(UserMetricsAction("CriticalNotificationShown"));
225 }