OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/views/session_crashed_bubble_view.h" | 5 #include "chrome/browser/ui/views/session_crashed_bubble_view.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "base/metrics/field_trial.h" | 12 #include "base/metrics/field_trial.h" |
| 13 #include "base/metrics/histogram.h" |
13 #include "base/prefs/pref_service.h" | 14 #include "base/prefs/pref_service.h" |
14 #include "chrome/browser/browser_process.h" | 15 #include "chrome/browser/browser_process.h" |
15 #include "chrome/browser/chrome_notification_types.h" | 16 #include "chrome/browser/chrome_notification_types.h" |
16 #include "chrome/browser/sessions/session_restore.h" | 17 #include "chrome/browser/sessions/session_restore.h" |
17 #include "chrome/browser/ui/browser_list.h" | 18 #include "chrome/browser/ui/browser_list.h" |
18 #include "chrome/browser/ui/browser_list_observer.h" | 19 #include "chrome/browser/ui/browser_list_observer.h" |
19 #include "chrome/browser/ui/options/options_util.h" | 20 #include "chrome/browser/ui/options/options_util.h" |
20 #include "chrome/browser/ui/startup/session_crashed_bubble.h" | 21 #include "chrome/browser/ui/startup/session_crashed_bubble.h" |
21 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h" | 22 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h" |
22 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 23 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
58 | 59 |
59 // The color of the text and background of the sub panel to offer UMA optin. | 60 // The color of the text and background of the sub panel to offer UMA optin. |
60 // These values match the BookmarkSyncPromoView colors. | 61 // These values match the BookmarkSyncPromoView colors. |
61 const SkColor kBackgroundColor = SkColorSetRGB(245, 245, 245); | 62 const SkColor kBackgroundColor = SkColorSetRGB(245, 245, 245); |
62 const SkColor kTextColor = SkColorSetRGB(102, 102, 102); | 63 const SkColor kTextColor = SkColorSetRGB(102, 102, 102); |
63 | 64 |
64 // The Finch study name and group name that enables session crashed bubble UI. | 65 // The Finch study name and group name that enables session crashed bubble UI. |
65 const char kEnableBubbleUIFinchName[] = "EnableSessionCrashedBubbleUI"; | 66 const char kEnableBubbleUIFinchName[] = "EnableSessionCrashedBubbleUI"; |
66 const char kEnableBubbleUIGroupEnabled[] = "Enabled"; | 67 const char kEnableBubbleUIGroupEnabled[] = "Enabled"; |
67 | 68 |
| 69 enum SessionCrashedBubbleHistogramValue { |
| 70 SESSION_CRASHED_BUBBLE_SHOWN, |
| 71 SESSION_CRASHED_BUBBLE_ERROR, |
| 72 SESSION_CRASHED_BUBBLE_RESTORED, |
| 73 SESSION_CRASHED_BUBBLE_ALREADY_UMA_OPTIN, |
| 74 SESSION_CRASHED_BUBBLE_UMA_OPTIN, |
| 75 SESSION_CRASHED_BUBBLE_HELP, |
| 76 SESSION_CRASHED_BUBBLE_IGNORED, |
| 77 SESSION_CRASHED_BUBBLE_MAX, |
| 78 }; |
| 79 |
| 80 void RecordBubbleHistogramValue(SessionCrashedBubbleHistogramValue value) { |
| 81 UMA_HISTOGRAM_ENUMERATION( |
| 82 "SessionCrashed.Bubble", value, SESSION_CRASHED_BUBBLE_MAX); |
| 83 } |
| 84 |
68 // Whether or not the bubble UI should be used. | 85 // Whether or not the bubble UI should be used. |
69 bool IsBubbleUIEnabled() { | 86 bool IsBubbleUIEnabled() { |
70 const base::CommandLine& command_line = *CommandLine::ForCurrentProcess(); | 87 const base::CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
71 if (command_line.HasSwitch(switches::kDisableSessionCrashedBubble)) | 88 if (command_line.HasSwitch(switches::kDisableSessionCrashedBubble)) |
72 return false; | 89 return false; |
73 if (command_line.HasSwitch(switches::kEnableSessionCrashedBubble)) | 90 if (command_line.HasSwitch(switches::kEnableSessionCrashedBubble)) |
74 return true; | 91 return true; |
75 const std::string group_name = base::FieldTrialList::FindFullName( | 92 const std::string group_name = base::FieldTrialList::FindFullName( |
76 kEnableBubbleUIFinchName); | 93 kEnableBubbleUIFinchName); |
77 return group_name == kEnableBubbleUIGroupEnabled; | 94 return group_name == kEnableBubbleUIGroupEnabled; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 128 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
112 if (browser->profile()->IsOffTheRecord()) | 129 if (browser->profile()->IsOffTheRecord()) |
113 return; | 130 return; |
114 | 131 |
115 // Observes browser removal event and will be deallocated in ShowForReal. | 132 // Observes browser removal event and will be deallocated in ShowForReal. |
116 scoped_ptr<BrowserRemovalObserver> browser_observer( | 133 scoped_ptr<BrowserRemovalObserver> browser_observer( |
117 new BrowserRemovalObserver(browser)); | 134 new BrowserRemovalObserver(browser)); |
118 | 135 |
119 // Stats collection only applies to Google Chrome builds. | 136 // Stats collection only applies to Google Chrome builds. |
120 #if defined(GOOGLE_CHROME_BUILD) | 137 #if defined(GOOGLE_CHROME_BUILD) |
121 // Schedule a task to run ShouldOfferMetricsReporting() on FILE thread, since | 138 // Schedule a task to run GoogleUpdateSettings::GetCollectStatsConsent() on |
122 // GoogleUpdateSettings::GetCollectStatsConsent() does IO. Then, call | 139 // FILE thread, since it does IO. Then, call |
123 // SessionCrashedBubbleView::ShowForReal with the result. | 140 // SessionCrashedBubbleView::ShowForReal with the result. |
124 content::BrowserThread::PostTaskAndReplyWithResult( | 141 content::BrowserThread::PostTaskAndReplyWithResult( |
125 content::BrowserThread::FILE, | 142 content::BrowserThread::FILE, |
126 FROM_HERE, | 143 FROM_HERE, |
127 base::Bind(&GoogleUpdateSettings::GetCollectStatsConsent), | 144 base::Bind(&GoogleUpdateSettings::GetCollectStatsConsent), |
128 base::Bind(&SessionCrashedBubbleView::ShowForReal, | 145 base::Bind(&SessionCrashedBubbleView::ShowForReal, |
129 base::Passed(&browser_observer))); | 146 base::Passed(&browser_observer))); |
130 #else | 147 #else |
131 SessionCrashedBubbleView::ShowForReal(browser_observer.Pass(), false); | 148 SessionCrashedBubbleView::ShowForReal(browser_observer.Pass(), false); |
132 #endif // defined(GOOGLE_CHROME_BUILD) | 149 #endif // defined(GOOGLE_CHROME_BUILD) |
133 } | 150 } |
134 | 151 |
135 // static | 152 // static |
136 void SessionCrashedBubbleView::ShowForReal( | 153 void SessionCrashedBubbleView::ShowForReal( |
137 scoped_ptr<BrowserRemovalObserver> browser_observer, | 154 scoped_ptr<BrowserRemovalObserver> browser_observer, |
138 bool uma_opted_in_already) { | 155 bool uma_opted_in_already) { |
139 // Determine whether or not the uma opt-in option should be offered. It is | 156 // Determine whether or not the uma opt-in option should be offered. It is |
140 // offered only when it is a Google chrome build, user hasn't opted in yet, | 157 // offered only when it is a Google chrome build, user hasn't opted in yet, |
141 // and the preference is modifiable by the user. | 158 // and the preference is modifiable by the user. |
142 bool offer_uma_optin = false; | 159 bool offer_uma_optin = false; |
143 | 160 |
144 #if defined(GOOGLE_CHROME_BUILD) | 161 #if defined(GOOGLE_CHROME_BUILD) |
145 if (!uma_opted_in_already) | 162 if (uma_opted_in_already) { |
| 163 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_ALREADY_UMA_OPTIN); |
| 164 } else { |
146 offer_uma_optin = g_browser_process->local_state()->FindPreference( | 165 offer_uma_optin = g_browser_process->local_state()->FindPreference( |
147 prefs::kMetricsReportingEnabled)->IsUserModifiable(); | 166 prefs::kMetricsReportingEnabled)->IsUserModifiable(); |
| 167 } |
148 #endif // defined(GOOGLE_CHROME_BUILD) | 168 #endif // defined(GOOGLE_CHROME_BUILD) |
149 | 169 |
150 Browser* browser = browser_observer->browser(); | 170 Browser* browser = browser_observer->browser(); |
151 | 171 |
152 if (!browser) | 172 if (!browser) { |
| 173 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_ERROR); |
153 return; | 174 return; |
| 175 } |
154 | 176 |
155 views::View* anchor_view = | 177 views::View* anchor_view = |
156 BrowserView::GetBrowserViewForBrowser(browser)->toolbar()->app_menu(); | 178 BrowserView::GetBrowserViewForBrowser(browser)->toolbar()->app_menu(); |
157 content::WebContents* web_contents = | 179 content::WebContents* web_contents = |
158 browser->tab_strip_model()->GetActiveWebContents(); | 180 browser->tab_strip_model()->GetActiveWebContents(); |
159 | 181 |
160 if (!web_contents) | 182 if (!web_contents) { |
| 183 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_ERROR); |
161 return; | 184 return; |
| 185 } |
162 | 186 |
163 SessionCrashedBubbleView* crash_bubble = | 187 SessionCrashedBubbleView* crash_bubble = |
164 new SessionCrashedBubbleView(anchor_view, browser, web_contents, | 188 new SessionCrashedBubbleView(anchor_view, browser, web_contents, |
165 offer_uma_optin); | 189 offer_uma_optin); |
166 views::BubbleDelegateView::CreateBubble(crash_bubble)->Show(); | 190 views::BubbleDelegateView::CreateBubble(crash_bubble)->Show(); |
| 191 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_SHOWN); |
167 } | 192 } |
168 | 193 |
169 SessionCrashedBubbleView::SessionCrashedBubbleView( | 194 SessionCrashedBubbleView::SessionCrashedBubbleView( |
170 views::View* anchor_view, | 195 views::View* anchor_view, |
171 Browser* browser, | 196 Browser* browser, |
172 content::WebContents* web_contents, | 197 content::WebContents* web_contents, |
173 bool offer_uma_optin) | 198 bool offer_uma_optin) |
174 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), | 199 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), |
175 content::WebContentsObserver(web_contents), | 200 content::WebContentsObserver(web_contents), |
176 browser_(browser), | 201 browser_(browser), |
177 web_contents_(web_contents), | 202 web_contents_(web_contents), |
178 restore_button_(NULL), | 203 restore_button_(NULL), |
179 uma_option_(NULL), | 204 uma_option_(NULL), |
180 offer_uma_optin_(offer_uma_optin), | 205 offer_uma_optin_(offer_uma_optin), |
181 started_navigation_(false) { | 206 started_navigation_(false), |
| 207 restored_(false) { |
182 set_close_on_deactivate(false); | 208 set_close_on_deactivate(false); |
183 registrar_.Add( | 209 registrar_.Add( |
184 this, | 210 this, |
185 chrome::NOTIFICATION_TAB_CLOSING, | 211 chrome::NOTIFICATION_TAB_CLOSING, |
186 content::Source<content::NavigationController>(&( | 212 content::Source<content::NavigationController>(&( |
187 web_contents->GetController()))); | 213 web_contents->GetController()))); |
188 browser->tab_strip_model()->AddObserver(this); | 214 browser->tab_strip_model()->AddObserver(this); |
189 } | 215 } |
190 | 216 |
191 SessionCrashedBubbleView::~SessionCrashedBubbleView() { | 217 SessionCrashedBubbleView::~SessionCrashedBubbleView() { |
192 browser_->tab_strip_model()->RemoveObserver(this); | 218 browser_->tab_strip_model()->RemoveObserver(this); |
193 } | 219 } |
194 | 220 |
195 views::View* SessionCrashedBubbleView::GetInitiallyFocusedView() { | 221 views::View* SessionCrashedBubbleView::GetInitiallyFocusedView() { |
196 return restore_button_; | 222 return restore_button_; |
197 } | 223 } |
198 | 224 |
199 base::string16 SessionCrashedBubbleView::GetWindowTitle() const { | 225 base::string16 SessionCrashedBubbleView::GetWindowTitle() const { |
200 return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_TITLE); | 226 return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_TITLE); |
201 } | 227 } |
202 | 228 |
203 bool SessionCrashedBubbleView::ShouldShowWindowTitle() const { | 229 bool SessionCrashedBubbleView::ShouldShowWindowTitle() const { |
204 return true; | 230 return true; |
205 } | 231 } |
206 | 232 |
207 bool SessionCrashedBubbleView::ShouldShowCloseButton() const { | 233 bool SessionCrashedBubbleView::ShouldShowCloseButton() const { |
208 return true; | 234 return true; |
209 } | 235 } |
210 | 236 |
| 237 void SessionCrashedBubbleView::OnWidgetDestroying(views::Widget* widget) { |
| 238 if (!restored_) |
| 239 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_IGNORED); |
| 240 BubbleDelegateView::OnWidgetDestroying(widget); |
| 241 } |
| 242 |
211 void SessionCrashedBubbleView::Init() { | 243 void SessionCrashedBubbleView::Init() { |
212 // Description text label. | 244 // Description text label. |
213 views::Label* text_label = new views::Label( | 245 views::Label* text_label = new views::Label( |
214 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE)); | 246 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE)); |
215 text_label->SetMultiLine(true); | 247 text_label->SetMultiLine(true); |
216 text_label->SetLineHeight(20); | 248 text_label->SetLineHeight(20); |
217 text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | 249 text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); |
218 text_label->SizeToFit(kWidthOfDescriptionText); | 250 text_label->SizeToFit(kWidthOfDescriptionText); |
219 | 251 |
220 // Restore button. | 252 // Restore button. |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
330 } | 362 } |
331 | 363 |
332 void SessionCrashedBubbleView::StyledLabelLinkClicked(const gfx::Range& range, | 364 void SessionCrashedBubbleView::StyledLabelLinkClicked(const gfx::Range& range, |
333 int event_flags) { | 365 int event_flags) { |
334 browser_->OpenURL(content::OpenURLParams( | 366 browser_->OpenURL(content::OpenURLParams( |
335 GURL("https://support.google.com/chrome/answer/96817"), | 367 GURL("https://support.google.com/chrome/answer/96817"), |
336 content::Referrer(), | 368 content::Referrer(), |
337 NEW_FOREGROUND_TAB, | 369 NEW_FOREGROUND_TAB, |
338 content::PAGE_TRANSITION_LINK, | 370 content::PAGE_TRANSITION_LINK, |
339 false)); | 371 false)); |
| 372 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_HELP); |
340 } | 373 } |
341 | 374 |
342 void SessionCrashedBubbleView::DidStartNavigationToPendingEntry( | 375 void SessionCrashedBubbleView::DidStartNavigationToPendingEntry( |
343 const GURL& url, | 376 const GURL& url, |
344 content::NavigationController::ReloadType reload_type) { | 377 content::NavigationController::ReloadType reload_type) { |
345 started_navigation_ = true; | 378 started_navigation_ = true; |
346 } | 379 } |
347 | 380 |
348 void SessionCrashedBubbleView::DidFinishLoad( | 381 void SessionCrashedBubbleView::DidFinishLoad( |
349 int64 frame_id, | 382 int64 frame_id, |
(...skipping 21 matching lines...) Expand all Loading... |
371 } | 404 } |
372 | 405 |
373 void SessionCrashedBubbleView::TabDetachedAt(content::WebContents* contents, | 406 void SessionCrashedBubbleView::TabDetachedAt(content::WebContents* contents, |
374 int index) { | 407 int index) { |
375 if (web_contents_ == contents) | 408 if (web_contents_ == contents) |
376 CloseBubble(); | 409 CloseBubble(); |
377 } | 410 } |
378 | 411 |
379 void SessionCrashedBubbleView::RestorePreviousSession(views::Button* sender) { | 412 void SessionCrashedBubbleView::RestorePreviousSession(views::Button* sender) { |
380 SessionRestore::RestoreSessionAfterCrash(browser_); | 413 SessionRestore::RestoreSessionAfterCrash(browser_); |
| 414 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_RESTORED); |
| 415 restored_ = true; |
381 | 416 |
382 // Record user's choice for opting in to UMA. | 417 // Record user's choice for opting in to UMA. |
383 // There's no opting-out choice in the crash restore bubble. | 418 // There's no opting-out choice in the crash restore bubble. |
384 if (uma_option_ && uma_option_->checked()) { | 419 if (uma_option_ && uma_option_->checked()) { |
385 // TODO: Clean up function ResolveMetricsReportingEnabled so that user pref | 420 // TODO: Clean up function ResolveMetricsReportingEnabled so that user pref |
386 // is stored automatically. | 421 // is stored automatically. |
387 OptionsUtil::ResolveMetricsReportingEnabled(true); | 422 OptionsUtil::ResolveMetricsReportingEnabled(true); |
388 g_browser_process->local_state()->SetBoolean( | 423 g_browser_process->local_state()->SetBoolean( |
389 prefs::kMetricsReportingEnabled, true); | 424 prefs::kMetricsReportingEnabled, true); |
| 425 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_UMA_OPTIN); |
390 } | 426 } |
391 CloseBubble(); | 427 CloseBubble(); |
392 } | 428 } |
393 | 429 |
394 void SessionCrashedBubbleView::CloseBubble() { | 430 void SessionCrashedBubbleView::CloseBubble() { |
395 GetWidget()->Close(); | 431 GetWidget()->Close(); |
396 } | 432 } |
397 | 433 |
398 bool ShowSessionCrashedBubble(Browser* browser) { | 434 bool ShowSessionCrashedBubble(Browser* browser) { |
399 if (IsBubbleUIEnabled()) { | 435 if (IsBubbleUIEnabled()) { |
400 SessionCrashedBubbleView::Show(browser); | 436 SessionCrashedBubbleView::Show(browser); |
401 return true; | 437 return true; |
402 } | 438 } |
403 return false; | 439 return false; |
404 } | 440 } |
OLD | NEW |