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.
5 #include "chrome/browser/ui/views/session_crashed_bubble_view.h"
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/ui_resources.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/base/resource/resource_bundle.h"
30 #include "ui/views/controls/button/checkbox.h"
31 #include "ui/views/controls/button/label_button.h"
32 #include "ui/views/controls/label.h"
33 #include "ui/views/controls/link.h"
34 #include "ui/views/controls/separator.h"
35 #include "ui/views/layout/grid_layout.h"
36 #include "ui/views/layout/layout_constants.h"
37 #include "ui/views/widget/widget.h"
39 using views::GridLayout
;
43 // Fixed width of the column holding the description label of the bubble.
44 const int kWidthOfDescriptionText
= 320;
46 // Margins width for the top rows to compensate for the bottom panel for which
47 // we don't want any margin.
48 const int kMarginWidth
= 12;
49 const int kMarginHeight
= kMarginWidth
;
51 // The color of the background of the sub panel to offer UMA optin.
52 const SkColor kLightGrayBackgroundColor
= 0xFFF0F0F0;
53 const SkColor kWhiteBackgroundColor
= 0xFFFFFFFF;
55 bool ShouldOfferMetricsReporting() {
56 // Stats collection only applies to Google Chrome builds.
57 #if defined(GOOGLE_CHROME_BUILD)
58 // Only show metrics reporting option if user didn't already consent to it.
59 if (GoogleUpdateSettings::GetCollectStatsConsent())
61 return g_browser_process
->local_state()->FindPreference(
62 prefs::kMetricsReportingEnabled
)->IsUserModifiable();
65 #endif // defined(GOOGLE_CHROME_BUILD)
71 void SessionCrashedBubbleView::Show(Browser
* browser
) {
72 if (browser
->profile()->IsOffTheRecord())
75 views::View
* anchor_view
=
76 BrowserView::GetBrowserViewForBrowser(browser
)->toolbar()->app_menu();
77 content::WebContents
* web_contents
=
78 browser
->tab_strip_model()->GetActiveWebContents();
79 SessionCrashedBubbleView
* crash_bubble
=
80 new SessionCrashedBubbleView(anchor_view
, browser
, web_contents
);
81 views::BubbleDelegateView::CreateBubble(crash_bubble
)->Show();
84 SessionCrashedBubbleView::SessionCrashedBubbleView(
85 views::View
* anchor_view
,
87 content::WebContents
* web_contents
)
88 : BubbleDelegateView(anchor_view
, views::BubbleBorder::TOP_RIGHT
),
89 content::WebContentsObserver(web_contents
),
91 web_contents_(web_contents
),
92 restore_button_(NULL
),
95 started_navigation_(false) {
96 set_close_on_deactivate(false);
99 chrome::NOTIFICATION_TAB_CLOSING
,
100 content::Source
<content::NavigationController
>(&(
101 web_contents
->GetController())));
102 browser
->tab_strip_model()->AddObserver(this);
105 SessionCrashedBubbleView::~SessionCrashedBubbleView() {
106 browser_
->tab_strip_model()->RemoveObserver(this);
109 views::View
* SessionCrashedBubbleView::GetInitiallyFocusedView() {
110 return restore_button_
;
113 void SessionCrashedBubbleView::Init() {
114 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
117 close_
= new views::LabelButton(this, base::string16());
118 close_
->SetImage(views::CustomButton::STATE_NORMAL
,
119 *rb
->GetImageNamed(IDR_CLOSE_2
).ToImageSkia());
120 close_
->SetImage(views::CustomButton::STATE_HOVERED
,
121 *rb
->GetImageNamed(IDR_CLOSE_2_H
).ToImageSkia());
122 close_
->SetImage(views::CustomButton::STATE_PRESSED
,
123 *rb
->GetImageNamed(IDR_CLOSE_2_P
).ToImageSkia());
124 close_
->SetSize(close_
->GetPreferredSize());
125 close_
->SetBorder(views::Border::CreateEmptyBorder(0, 0, 0, 0));
127 // Bubble title label.
128 views::Label
* title_label
= new views::Label(
129 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_TITLE
));
130 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
131 title_label
->SetFontList(rb
->GetFontList(ui::ResourceBundle::BoldFont
));
133 // Description text label.
134 views::Label
* text_label
= new views::Label(
135 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE
));
136 text_label
->SetMultiLine(true);
137 text_label
->SetLineHeight(20);
138 text_label
->SetEnabledColor(SK_ColorDKGRAY
);
139 text_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
142 views::Link
* learn_more_link
= NULL
;
143 if (ShouldOfferMetricsReporting()) {
144 learn_more_link
= new views::Link(
145 l10n_util::GetStringUTF16(IDS_LEARN_MORE
));
146 learn_more_link
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
147 learn_more_link
->set_listener(this);
148 learn_more_link
->SetUnderline(false);
152 restore_button_
= new views::LabelButton(
153 this, l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON
));
154 restore_button_
->SetStyle(views::Button::STYLE_BUTTON
);
155 restore_button_
->SetIsDefault(true);
156 restore_button_
->SetFontList(rb
->GetFontList(ui::ResourceBundle::BoldFont
));
158 GridLayout
* layout
= new GridLayout(this);
159 SetLayoutManager(layout
);
161 // Title and close button row.
162 const int kTitleColumnSetId
= 0;
163 views::ColumnSet
* cs
= layout
->AddColumnSet(kTitleColumnSetId
);
164 cs
->AddPaddingColumn(0, kMarginWidth
);
165 cs
->AddColumn(GridLayout::LEADING
, GridLayout::TRAILING
, 0,
166 GridLayout::USE_PREF
, 0, 0);
167 cs
->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing
);
168 cs
->AddColumn(GridLayout::TRAILING
, GridLayout::LEADING
, 0,
169 GridLayout::USE_PREF
, 0, 0);
172 const int kTextColumnSetId
= 1;
173 cs
= layout
->AddColumnSet(kTextColumnSetId
);
174 cs
->AddPaddingColumn(0, kMarginWidth
);
175 cs
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 0,
176 GridLayout::FIXED
, kWidthOfDescriptionText
, 0);
178 // Learn more link and restore button row
179 const int kLinkAndButtonColumnSetId
= 2;
180 cs
= layout
->AddColumnSet(kLinkAndButtonColumnSetId
);
181 cs
->AddPaddingColumn(0, kMarginWidth
);
182 if (learn_more_link
) {
183 cs
->AddColumn(GridLayout::LEADING
, GridLayout::CENTER
, 0,
184 GridLayout::USE_PREF
, 0, 0);
186 cs
->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing
);
187 cs
->AddColumn(GridLayout::TRAILING
, GridLayout::CENTER
, 0,
188 GridLayout::USE_PREF
, 0, 0);
189 cs
->AddPaddingColumn(0, kMarginWidth
);
191 layout
->AddPaddingRow(0, kMarginHeight
);
192 layout
->StartRow(0, kTitleColumnSetId
);
193 layout
->AddView(title_label
);
194 layout
->AddView(close_
);
195 layout
->AddPaddingRow(0, kMarginHeight
);
197 layout
->StartRow(0, kTextColumnSetId
);
198 layout
->AddView(text_label
);
199 layout
->AddPaddingRow(0, kMarginHeight
);
201 layout
->StartRow(0, kLinkAndButtonColumnSetId
);
203 layout
->AddView(learn_more_link
);
204 layout
->AddView(restore_button_
);
205 layout
->AddPaddingRow(0, kMarginHeight
);
207 // Metrics reporting option.
209 CreateUmaOptinView(layout
);
211 set_color(kWhiteBackgroundColor
);
212 set_margins(gfx::Insets());
216 void SessionCrashedBubbleView::CreateUmaOptinView(GridLayout
* layout
) {
217 // Checkbox for metric reporting setting.
218 uma_option_
= new views::Checkbox(
219 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_UMA_OPTIN
));
220 uma_option_
->SetTextColor(views::Button::STATE_NORMAL
, SK_ColorGRAY
);
221 uma_option_
->SetChecked(false);
222 uma_option_
->SetTextMultiLine(true);
223 uma_option_
->set_background(
224 views::Background::CreateSolidBackground(kLightGrayBackgroundColor
));
225 uma_option_
->set_listener(this);
226 // We use a border instead of padding so that the background color reach
227 // the edges of the bubble.
228 uma_option_
->SetBorder(
229 views::Border::CreateSolidSidedBorder(
230 kMarginHeight
, kMarginWidth
, kMarginHeight
, kMarginWidth
,
231 kLightGrayBackgroundColor
));
234 const int kSeparatorColumnSetId
= 3;
235 views::ColumnSet
* cs
= layout
->AddColumnSet(kSeparatorColumnSetId
);
236 cs
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 0,
237 GridLayout::FIXED
, kWidthOfDescriptionText
+ kMarginWidth
, 0);
240 const int kReportColumnSetId
= 4;
241 cs
= layout
->AddColumnSet(kReportColumnSetId
);
242 cs
->AddColumn(GridLayout::FILL
, GridLayout::FILL
, 0,
243 GridLayout::FIXED
, kWidthOfDescriptionText
+ kMarginWidth
, 0);
245 layout
->StartRow(0, kSeparatorColumnSetId
);
246 layout
->AddView(new views::Separator(views::Separator::HORIZONTAL
));
247 layout
->StartRow(0, kReportColumnSetId
);
248 layout
->AddView(uma_option_
);
251 void SessionCrashedBubbleView::ButtonPressed(views::Button
* sender
,
252 const ui::Event
& event
) {
254 if (sender
== restore_button_
)
255 RestorePreviousSession(sender
);
256 else if (sender
== close_
)
260 void SessionCrashedBubbleView::LinkClicked(views::Link
* source
,
262 browser_
->OpenURL(content::OpenURLParams(
263 GURL("https://support.google.com/chrome/answer/96817"),
266 content::PAGE_TRANSITION_LINK
,
270 void SessionCrashedBubbleView::DidStartNavigationToPendingEntry(
272 content::NavigationController::ReloadType reload_type
) {
273 started_navigation_
= true;
276 void SessionCrashedBubbleView::DidFinishLoad(
278 const GURL
& validated_url
,
280 content::RenderViewHost
* render_view_host
) {
281 if (started_navigation_
)
285 void SessionCrashedBubbleView::WasShown() {
289 void SessionCrashedBubbleView::WasHidden() {
293 void SessionCrashedBubbleView::Observe(
295 const content::NotificationSource
& source
,
296 const content::NotificationDetails
& details
) {
297 if (type
== chrome::NOTIFICATION_TAB_CLOSING
)
301 void SessionCrashedBubbleView::TabDetachedAt(content::WebContents
* contents
,
303 if (web_contents_
== contents
)
307 void SessionCrashedBubbleView::RestorePreviousSession(views::Button
* sender
) {
308 SessionRestore::RestoreSessionAfterCrash(browser_
);
310 // Record user's choice for opting in to UMA.
311 // There's no opting-out choice in the crash restore bubble.
312 if (uma_option_
&& uma_option_
->checked())
313 OptionsUtil::ResolveMetricsReportingEnabled(true);
317 void SessionCrashedBubbleView::CloseBubble() {
318 GetWidget()->Close();
321 bool ShowSessionCrashedBubble(Browser
* browser
) {
322 SessionCrashedBubbleView::Show(browser
);