Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / session_crashed_bubble_view.cc
1 // Copyright 2014 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/session_crashed_bubble_view.h"
6
7 #include <vector>
8
9 #include "base/prefs/pref_service.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/sessions/session_restore.h"
13 #include "chrome/browser/ui/options/options_util.h"
14 #include "chrome/browser/ui/startup/session_crashed_bubble.h"
15 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/browser/ui/views/frame/browser_view.h"
18 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/common/url_constants.h"
21 #include "chrome/installer/util/google_update_settings.h"
22 #include "content/public/browser/browser_context.h"
23 #include "content/public/browser/notification_source.h"
24 #include "content/public/browser/web_contents.h"
25 #include "grit/chromium_strings.h"
26 #include "grit/generated_resources.h"
27 #include "grit/google_chrome_strings.h"
28 #include "grit/ui_resources.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/resource/resource_bundle.h"
31 #include "ui/views/controls/button/checkbox.h"
32 #include "ui/views/controls/button/label_button.h"
33 #include "ui/views/controls/label.h"
34 #include "ui/views/controls/separator.h"
35 #include "ui/views/controls/styled_label.h"
36 #include "ui/views/layout/grid_layout.h"
37 #include "ui/views/layout/layout_constants.h"
38 #include "ui/views/widget/widget.h"
39
40 using views::GridLayout;
41
42 namespace {
43
44 // Fixed width of the column holding the description label of the bubble.
45 const int kWidthOfDescriptionText = 320;
46
47 // Distance between checkbox and the text to the right of it.
48 const int kCheckboxTextDistance = 4;
49
50 // Margins width for the top rows to compensate for the bottom panel for which
51 // we don't want any margin.
52 const int kMarginWidth = 12;
53 const int kMarginHeight = kMarginWidth;
54
55 // The color of the background of the sub panel to offer UMA optin.
56 const SkColor kLightGrayBackgroundColor = 0xFFF0F0F0;
57 const SkColor kWhiteBackgroundColor = 0xFFFFFFFF;
58
59 bool ShouldOfferMetricsReporting() {
60 // Stats collection only applies to Google Chrome builds.
61 #if defined(GOOGLE_CHROME_BUILD)
62   // Only show metrics reporting option if user didn't already consent to it.
63   if (GoogleUpdateSettings::GetCollectStatsConsent())
64     return false;
65   return g_browser_process->local_state()->FindPreference(
66       prefs::kMetricsReportingEnabled)->IsUserModifiable();
67 #else
68   return false;
69 #endif  // defined(GOOGLE_CHROME_BUILD)
70 }
71
72 }  // namespace
73
74 // static
75 void SessionCrashedBubbleView::Show(Browser* browser) {
76   if (browser->profile()->IsOffTheRecord())
77     return;
78
79   views::View* anchor_view =
80       BrowserView::GetBrowserViewForBrowser(browser)->toolbar()->app_menu();
81   content::WebContents* web_contents =
82       browser->tab_strip_model()->GetActiveWebContents();
83   SessionCrashedBubbleView* crash_bubble =
84       new SessionCrashedBubbleView(anchor_view, browser, web_contents);
85   views::BubbleDelegateView::CreateBubble(crash_bubble)->Show();
86 }
87
88 SessionCrashedBubbleView::SessionCrashedBubbleView(
89     views::View* anchor_view,
90     Browser* browser,
91     content::WebContents* web_contents)
92     : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
93       content::WebContentsObserver(web_contents),
94       browser_(browser),
95       web_contents_(web_contents),
96       restore_button_(NULL),
97       close_(NULL),
98       uma_option_(NULL),
99       started_navigation_(false) {
100   set_close_on_deactivate(false);
101   registrar_.Add(
102       this,
103       chrome::NOTIFICATION_TAB_CLOSING,
104       content::Source<content::NavigationController>(&(
105           web_contents->GetController())));
106   browser->tab_strip_model()->AddObserver(this);
107 }
108
109 SessionCrashedBubbleView::~SessionCrashedBubbleView() {
110   browser_->tab_strip_model()->RemoveObserver(this);
111 }
112
113 views::View* SessionCrashedBubbleView::GetInitiallyFocusedView() {
114   return restore_button_;
115 }
116
117 void SessionCrashedBubbleView::Init() {
118   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
119
120   // Close button.
121   close_ = new views::LabelButton(this, base::string16());
122   close_->SetImage(views::CustomButton::STATE_NORMAL,
123                    *rb->GetImageNamed(IDR_CLOSE_2).ToImageSkia());
124   close_->SetImage(views::CustomButton::STATE_HOVERED,
125                    *rb->GetImageNamed(IDR_CLOSE_2_H).ToImageSkia());
126   close_->SetImage(views::CustomButton::STATE_PRESSED,
127                    *rb->GetImageNamed(IDR_CLOSE_2_P).ToImageSkia());
128   close_->SetSize(close_->GetPreferredSize());
129   close_->SetBorder(views::Border::CreateEmptyBorder(0, 0, 0, 0));
130
131   // Bubble title label.
132   views::Label* title_label = new views::Label(
133       l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_TITLE));
134   title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
135   title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::BoldFont));
136
137   // Description text label.
138   views::Label* text_label = new views::Label(
139       l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE));
140   text_label->SetMultiLine(true);
141   text_label->SetLineHeight(20);
142   text_label->SetEnabledColor(SK_ColorDKGRAY);
143   text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
144
145   // Restore button.
146   restore_button_ = new views::LabelButton(
147       this, l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON));
148   restore_button_->SetStyle(views::Button::STYLE_BUTTON);
149   restore_button_->SetIsDefault(true);
150   restore_button_->SetFontList(rb->GetFontList(ui::ResourceBundle::BoldFont));
151
152   GridLayout* layout = new GridLayout(this);
153   SetLayoutManager(layout);
154
155   // Title and close button row.
156   const int kTitleColumnSetId = 0;
157   views::ColumnSet* cs = layout->AddColumnSet(kTitleColumnSetId);
158   cs->AddPaddingColumn(0, kMarginWidth);
159   cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
160                 GridLayout::USE_PREF, 0, 0);
161   cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
162   cs->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 0,
163                 GridLayout::USE_PREF, 0, 0);
164
165   // Text row.
166   const int kTextColumnSetId = 1;
167   cs = layout->AddColumnSet(kTextColumnSetId);
168   cs->AddPaddingColumn(0, kMarginWidth);
169   cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
170                 GridLayout::FIXED, kWidthOfDescriptionText, 0);
171
172   // Restore button row
173   const int kButtonColumnSetId = 2;
174   cs = layout->AddColumnSet(kButtonColumnSetId);
175   cs->AddPaddingColumn(0, kMarginWidth);
176   cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
177   cs->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
178                 GridLayout::USE_PREF, 0, 0);
179   cs->AddPaddingColumn(0, kMarginWidth);
180
181   layout->AddPaddingRow(0, kMarginHeight);
182   layout->StartRow(0, kTitleColumnSetId);
183   layout->AddView(title_label);
184   layout->AddView(close_);
185   layout->AddPaddingRow(0, kMarginHeight);
186
187   layout->StartRow(0, kTextColumnSetId);
188   layout->AddView(text_label);
189   layout->AddPaddingRow(0, kMarginHeight);
190
191   layout->StartRow(0, kButtonColumnSetId);
192   layout->AddView(restore_button_);
193   layout->AddPaddingRow(0, kMarginHeight);
194
195   // Metrics reporting option.
196   if (ShouldOfferMetricsReporting())
197     CreateUmaOptinView(layout);
198
199   set_color(kWhiteBackgroundColor);
200   set_margins(gfx::Insets());
201   Layout();
202 }
203
204 void SessionCrashedBubbleView::CreateUmaOptinView(GridLayout* layout) {
205   // Checkbox for metric reporting setting.
206   // Since the text to the right of the checkbox can't be a simple string (needs
207   // a hyperlink in it), this checkbox contains an empty string as its label,
208   // and the real text will be added as a separate view.
209   uma_option_ = new views::Checkbox(base::string16());
210   uma_option_->SetTextColor(views::Button::STATE_NORMAL, SK_ColorGRAY);
211   uma_option_->SetChecked(false);
212   uma_option_->set_background(
213       views::Background::CreateSolidBackground(kLightGrayBackgroundColor));
214   uma_option_->set_listener(this);
215
216   // The text to the right of the checkbox.
217   size_t offset;
218   base::string16 link_text =
219       l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_UMA_LINK_TEXT);
220   base::string16 uma_text = l10n_util::GetStringFUTF16(
221       IDS_SESSION_CRASHED_VIEW_UMA_OPTIN,
222       link_text,
223       &offset);
224   views::StyledLabel* uma_label = new views::StyledLabel(uma_text, this);
225   uma_label->set_background(
226       views::Background::CreateSolidBackground(kLightGrayBackgroundColor));
227   views::StyledLabel::RangeStyleInfo link_style =
228       views::StyledLabel::RangeStyleInfo::CreateForLink();
229   link_style.font_style = gfx::Font::NORMAL;
230   uma_label->AddStyleRange(gfx::Range(offset, offset + link_text.length()),
231                            link_style);
232   views::StyledLabel::RangeStyleInfo uma_style;
233   uma_style.color = SK_ColorGRAY;
234   gfx::Range before_link_range(0, offset);
235   if (!before_link_range.is_empty())
236     uma_label->AddStyleRange(before_link_range, uma_style);
237   gfx::Range after_link_range(offset + link_text.length(), uma_text.length());
238   if (!after_link_range.is_empty())
239     uma_label->AddStyleRange(after_link_range, uma_style);
240
241   // We use a border instead of padding so that the background color reach
242   // the edges of the bubble.
243   uma_option_->SetBorder(
244       views::Border::CreateSolidSidedBorder(0, kMarginWidth, 0, 0,
245                                             kLightGrayBackgroundColor));
246   uma_label->SetBorder(
247       views::Border::CreateSolidSidedBorder(
248           kMarginHeight, kCheckboxTextDistance, kMarginHeight, kMarginWidth,
249           kLightGrayBackgroundColor));
250
251   // Separator.
252   const int kSeparatorColumnSetId = 3;
253   views::ColumnSet* cs = layout->AddColumnSet(kSeparatorColumnSetId);
254   cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
255                 GridLayout::FIXED, kWidthOfDescriptionText + kMarginWidth, 0);
256
257   // Reporting row.
258   const int kReportColumnSetId = 4;
259   cs = layout->AddColumnSet(kReportColumnSetId);
260   cs->AddColumn(GridLayout::CENTER, GridLayout::FILL, 0,
261                 GridLayout::USE_PREF, 0, 0);
262   cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
263                 GridLayout::FIXED, kWidthOfDescriptionText, 0);
264
265   layout->StartRow(0, kSeparatorColumnSetId);
266   layout->AddView(new views::Separator(views::Separator::HORIZONTAL));
267   layout->StartRow(0, kReportColumnSetId);
268   layout->AddView(uma_option_);
269   layout->AddView(uma_label);
270 }
271
272 void SessionCrashedBubbleView::ButtonPressed(views::Button* sender,
273                                              const ui::Event& event) {
274   DCHECK(sender);
275   if (sender == restore_button_)
276     RestorePreviousSession(sender);
277   else if (sender == close_)
278     CloseBubble();
279 }
280
281 void SessionCrashedBubbleView::StyledLabelLinkClicked(const gfx::Range& range,
282                                                       int event_flags) {
283   browser_->OpenURL(content::OpenURLParams(
284       GURL("https://support.google.com/chrome/answer/96817"),
285       content::Referrer(),
286       NEW_FOREGROUND_TAB,
287       content::PAGE_TRANSITION_LINK,
288       false));
289 }
290
291 void SessionCrashedBubbleView::DidStartNavigationToPendingEntry(
292       const GURL& url,
293       content::NavigationController::ReloadType reload_type) {
294   started_navigation_ = true;
295 }
296
297 void SessionCrashedBubbleView::DidFinishLoad(
298       int64 frame_id,
299       const GURL& validated_url,
300       bool is_main_frame,
301       content::RenderViewHost* render_view_host) {
302   if (started_navigation_)
303     CloseBubble();
304 }
305
306 void SessionCrashedBubbleView::WasShown() {
307   GetWidget()->Show();
308 }
309
310 void SessionCrashedBubbleView::WasHidden() {
311   GetWidget()->Hide();
312 }
313
314 void SessionCrashedBubbleView::Observe(
315     int type,
316     const content::NotificationSource& source,
317     const content::NotificationDetails& details) {
318   if (type == chrome::NOTIFICATION_TAB_CLOSING)
319     CloseBubble();
320 }
321
322 void SessionCrashedBubbleView::TabDetachedAt(content::WebContents* contents,
323                                              int index) {
324   if (web_contents_ == contents)
325     CloseBubble();
326 }
327
328 void SessionCrashedBubbleView::RestorePreviousSession(views::Button* sender) {
329   SessionRestore::RestoreSessionAfterCrash(browser_);
330
331   // Record user's choice for opting in to UMA.
332   // There's no opting-out choice in the crash restore bubble.
333   if (uma_option_ && uma_option_->checked())
334     OptionsUtil::ResolveMetricsReportingEnabled(true);
335   CloseBubble();
336 }
337
338 void SessionCrashedBubbleView::CloseBubble() {
339   GetWidget()->Close();
340 }
341
342 bool ShowSessionCrashedBubble(Browser* browser) {
343   SessionCrashedBubbleView::Show(browser);
344   return true;
345 }