[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / views / session_crashed_bubble_view.cc
blobf5fcd30c60efc3476b75fa34f866513a8975e66c
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"
7 #include <vector>
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"
40 using views::GridLayout;
42 namespace {
44 // Fixed width of the column holding the description label of the bubble.
45 const int kWidthOfDescriptionText = 320;
47 // Distance between checkbox and the text to the right of it.
48 const int kCheckboxTextDistance = 4;
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;
55 // The color of the background of the sub panel to offer UMA optin.
56 const SkColor kLightGrayBackgroundColor = 0xFFF0F0F0;
57 const SkColor kWhiteBackgroundColor = 0xFFFFFFFF;
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)
72 } // namespace
74 // static
75 void SessionCrashedBubbleView::Show(Browser* browser) {
76 if (browser->profile()->IsOffTheRecord())
77 return;
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();
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);
109 SessionCrashedBubbleView::~SessionCrashedBubbleView() {
110 browser_->tab_strip_model()->RemoveObserver(this);
113 views::View* SessionCrashedBubbleView::GetInitiallyFocusedView() {
114 return restore_button_;
117 void SessionCrashedBubbleView::Init() {
118 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
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));
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));
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);
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));
152 GridLayout* layout = new GridLayout(this);
153 SetLayoutManager(layout);
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);
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);
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);
181 layout->AddPaddingRow(0, kMarginHeight);
182 layout->StartRow(0, kTitleColumnSetId);
183 layout->AddView(title_label);
184 layout->AddView(close_);
185 layout->AddPaddingRow(0, kMarginHeight);
187 layout->StartRow(0, kTextColumnSetId);
188 layout->AddView(text_label);
189 layout->AddPaddingRow(0, kMarginHeight);
191 layout->StartRow(0, kButtonColumnSetId);
192 layout->AddView(restore_button_);
193 layout->AddPaddingRow(0, kMarginHeight);
195 // Metrics reporting option.
196 if (ShouldOfferMetricsReporting())
197 CreateUmaOptinView(layout);
199 set_color(kWhiteBackgroundColor);
200 set_margins(gfx::Insets());
201 Layout();
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);
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);
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));
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);
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);
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);
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();
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));
291 void SessionCrashedBubbleView::DidStartNavigationToPendingEntry(
292 const GURL& url,
293 content::NavigationController::ReloadType reload_type) {
294 started_navigation_ = true;
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();
306 void SessionCrashedBubbleView::WasShown() {
307 GetWidget()->Show();
310 void SessionCrashedBubbleView::WasHidden() {
311 GetWidget()->Hide();
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();
322 void SessionCrashedBubbleView::TabDetachedAt(content::WebContents* contents,
323 int index) {
324 if (web_contents_ == contents)
325 CloseBubble();
328 void SessionCrashedBubbleView::RestorePreviousSession(views::Button* sender) {
329 SessionRestore::RestoreSessionAfterCrash(browser_);
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 // TODO: Clean up function ResolveMetricsReportingEnabled so that user pref
335 // is stored automatically.
336 OptionsUtil::ResolveMetricsReportingEnabled(true);
337 g_browser_process->local_state()->SetBoolean(
338 prefs::kMetricsReportingEnabled, true);
340 CloseBubble();
343 void SessionCrashedBubbleView::CloseBubble() {
344 GetWidget()->Close();
347 bool ShowSessionCrashedBubble(Browser* browser) {
348 SessionCrashedBubbleView::Show(browser);
349 return true;