146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// found in the LICENSE file. 446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 5f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "chrome/browser/ui/views/session_crashed_bubble_view.h" 646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include <vector> 846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/bind.h" 1046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/bind_helpers.h" 11f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/command_line.h" 1246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/metrics/field_trial.h" 1346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/metrics/histogram.h" 1446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/prefs/pref_service.h" 1546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/browser_process.h" 1646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/chrome_notification_types.h" 1746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/metrics/metrics_reporting_state.h" 1846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/sessions/session_restore.h" 1946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/ui/browser_list.h" 2046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/ui/browser_list_observer.h" 2146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/ui/startup/session_crashed_bubble.h" 2246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/ui/startup/startup_browser_creator_impl.h" 2346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h" 2446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/ui/views/frame/browser_view.h" 2546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/browser/ui/views/toolbar/toolbar_view.h" 26f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "chrome/common/chrome_switches.h" 27f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "chrome/common/pref_names.h" 2846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/common/url_constants.h" 2946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/grit/chromium_strings.h" 3046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/grit/generated_resources.h" 3146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/grit/google_chrome_strings.h" 3246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "chrome/installer/util/google_update_settings.h" 33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "content/public/browser/browser_context.h" 34f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "content/public/browser/notification_source.h" 36f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "content/public/browser/web_contents.h" 37f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 3846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/bubble/bubble_frame_view.h" 3946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/controls/button/checkbox.h" 4046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/controls/button/label_button.h" 4146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/controls/label.h" 4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/controls/separator.h" 43f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ui/views/controls/styled_label.h" 4446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/layout/grid_layout.h" 4546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/layout/layout_constants.h" 4646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/views/widget/widget.h" 4746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 4846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)using views::GridLayout; 4946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 5046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)namespace { 5146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 5246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Fixed width of the column holding the description label of the bubble. 5346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const int kWidthOfDescriptionText = 320; 5446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 5546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Distance between checkbox and the text to the right of it. 5646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const int kCheckboxTextDistance = 4; 5746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 5846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// The color of the text and background of the sub panel to offer UMA optin. 5946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// These values match the BookmarkSyncPromoView colors. 6046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const SkColor kBackgroundColor = SkColorSetRGB(245, 245, 245); 6146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const SkColor kTextColor = SkColorSetRGB(102, 102, 102); 6246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 6346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// The Finch study name and group name that enables session crashed bubble UI. 6446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const char kEnableBubbleUIFinchName[] = "EnableSessionCrashedBubbleUI"; 6546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)const char kEnableBubbleUIGroupEnabled[] = "Enabled"; 6646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 6746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)enum SessionCrashedBubbleHistogramValue { 6846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_SHOWN, 6946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_ERROR, 7046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_RESTORED, 7146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_ALREADY_UMA_OPTIN, 7246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_UMA_OPTIN, 7346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_HELP, 7446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_IGNORED, 7546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_OPTIN_BAR_SHOWN, 7646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) SESSION_CRASHED_BUBBLE_MAX, 7746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}; 7846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 7946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void RecordBubbleHistogramValue(SessionCrashedBubbleHistogramValue value) { 8046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) UMA_HISTOGRAM_ENUMERATION( 8146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) "SessionCrashed.Bubble", value, SESSION_CRASHED_BUBBLE_MAX); 8246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)} 8346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 8446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Whether or not the bubble UI should be used. 8546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)bool IsBubbleUIEnabled() { 8646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) const base::CommandLine& command_line = *CommandLine::ForCurrentProcess(); 8746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if (command_line.HasSwitch(switches::kDisableSessionCrashedBubble)) 8846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return false; 8946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if (command_line.HasSwitch(switches::kEnableSessionCrashedBubble)) 9046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return true; 9146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) const std::string group_name = base::FieldTrialList::FindFullName( 9246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) kEnableBubbleUIFinchName); 9346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return group_name == kEnableBubbleUIGroupEnabled; 9446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)} 95f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 96f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)} // namespace 9746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 9846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// A helper class that listens to browser removal event. 99f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)class SessionCrashedBubbleView::BrowserRemovalObserver 10046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) : public chrome::BrowserListObserver { 101f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) public: 10246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) explicit BrowserRemovalObserver(Browser* browser) : browser_(browser) { 10346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) DCHECK(browser_); 10446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) BrowserList::AddObserver(this); 105f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) } 10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 10746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) virtual ~BrowserRemovalObserver() { 10846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) BrowserList::RemoveObserver(this); 109f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) } 11046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 11146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // Overridden from chrome::BrowserListObserver. 11246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) virtual void OnBrowserRemoved(Browser* browser) OVERRIDE { 11346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if (browser == browser_) 11446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) browser_ = NULL; 115f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) } 11646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 11746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) Browser* browser() const { return browser_; } 11846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 11946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) private: 120f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) Browser* browser_; 12146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 12246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(BrowserRemovalObserver); 12346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}; 124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 12546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// static 126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void SessionCrashedBubbleView::Show(Browser* browser) { 12746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 128f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (browser->profile()->IsOffTheRecord()) 12946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) return; 13046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 13146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // Observes browser removal event and will be deallocated in ShowForReal. 132f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) scoped_ptr<BrowserRemovalObserver> browser_observer( 13346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) new BrowserRemovalObserver(browser)); 13446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 13546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Stats collection only applies to Google Chrome builds. 13646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#if defined(GOOGLE_CHROME_BUILD) 137 // Schedule a task to run GoogleUpdateSettings::GetCollectStatsConsent() on 138 // FILE thread, since it does IO. Then, call 139 // SessionCrashedBubbleView::ShowForReal with the result. 140 content::BrowserThread::PostTaskAndReplyWithResult( 141 content::BrowserThread::FILE, 142 FROM_HERE, 143 base::Bind(&GoogleUpdateSettings::GetCollectStatsConsent), 144 base::Bind(&SessionCrashedBubbleView::ShowForReal, 145 base::Passed(&browser_observer))); 146#else 147 SessionCrashedBubbleView::ShowForReal(browser_observer.Pass(), false); 148#endif // defined(GOOGLE_CHROME_BUILD) 149} 150 151// static 152void SessionCrashedBubbleView::ShowForReal( 153 scoped_ptr<BrowserRemovalObserver> browser_observer, 154 bool uma_opted_in_already) { 155 // Determine whether or not the UMA opt-in option should be offered. It is 156 // offered only when it is a Google chrome build, user hasn't opted in yet, 157 // and the preference is modifiable by the user. 158 bool offer_uma_optin = false; 159 160#if defined(GOOGLE_CHROME_BUILD) 161 if (!uma_opted_in_already) { 162 offer_uma_optin = g_browser_process->local_state()->FindPreference( 163 prefs::kMetricsReportingEnabled)->IsUserModifiable(); 164 } 165#endif // defined(GOOGLE_CHROME_BUILD) 166 167 Browser* browser = browser_observer->browser(); 168 169 if (!browser) { 170 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_ERROR); 171 return; 172 } 173 174 views::View* anchor_view = 175 BrowserView::GetBrowserViewForBrowser(browser)->toolbar()->app_menu(); 176 content::WebContents* web_contents = 177 browser->tab_strip_model()->GetActiveWebContents(); 178 179 if (!web_contents) { 180 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_ERROR); 181 return; 182 } 183 184 SessionCrashedBubbleView* crash_bubble = 185 new SessionCrashedBubbleView(anchor_view, browser, web_contents, 186 offer_uma_optin); 187 views::BubbleDelegateView::CreateBubble(crash_bubble)->Show(); 188 189 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_SHOWN); 190 if (uma_opted_in_already) 191 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_ALREADY_UMA_OPTIN); 192} 193 194SessionCrashedBubbleView::SessionCrashedBubbleView( 195 views::View* anchor_view, 196 Browser* browser, 197 content::WebContents* web_contents, 198 bool offer_uma_optin) 199 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), 200 content::WebContentsObserver(web_contents), 201 browser_(browser), 202 web_contents_(web_contents), 203 restore_button_(NULL), 204 uma_option_(NULL), 205 offer_uma_optin_(offer_uma_optin), 206 started_navigation_(false), 207 restored_(false) { 208 set_close_on_deactivate(false); 209 registrar_.Add( 210 this, 211 chrome::NOTIFICATION_TAB_CLOSING, 212 content::Source<content::NavigationController>(&( 213 web_contents->GetController()))); 214 browser->tab_strip_model()->AddObserver(this); 215} 216 217SessionCrashedBubbleView::~SessionCrashedBubbleView() { 218 browser_->tab_strip_model()->RemoveObserver(this); 219} 220 221views::View* SessionCrashedBubbleView::GetInitiallyFocusedView() { 222 return restore_button_; 223} 224 225base::string16 SessionCrashedBubbleView::GetWindowTitle() const { 226 return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_TITLE); 227} 228 229bool SessionCrashedBubbleView::ShouldShowWindowTitle() const { 230 return true; 231} 232 233bool SessionCrashedBubbleView::ShouldShowCloseButton() const { 234 return true; 235} 236 237void SessionCrashedBubbleView::OnWidgetDestroying(views::Widget* widget) { 238 if (!restored_) 239 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_IGNORED); 240 BubbleDelegateView::OnWidgetDestroying(widget); 241} 242 243void SessionCrashedBubbleView::Init() { 244 // Description text label. 245 views::Label* text_label = new views::Label( 246 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE)); 247 text_label->SetMultiLine(true); 248 text_label->SetLineHeight(20); 249 text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 250 text_label->SizeToFit(kWidthOfDescriptionText); 251 252 // Restore button. 253 restore_button_ = new views::LabelButton( 254 this, l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON)); 255 restore_button_->SetStyle(views::Button::STYLE_BUTTON); 256 restore_button_->SetIsDefault(true); 257 258 GridLayout* layout = new GridLayout(this); 259 SetLayoutManager(layout); 260 261 // Text row. 262 const int kTextColumnSetId = 0; 263 views::ColumnSet* cs = layout->AddColumnSet(kTextColumnSetId); 264 cs->AddPaddingColumn(0, GetBubbleFrameView()->GetTitleInsets().left()); 265 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, 266 GridLayout::FIXED, kWidthOfDescriptionText, 0); 267 cs->AddPaddingColumn(0, GetBubbleFrameView()->GetTitleInsets().left()); 268 269 // Restore button row. 270 const int kButtonColumnSetId = 1; 271 cs = layout->AddColumnSet(kButtonColumnSetId); 272 cs->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 1, 273 GridLayout::USE_PREF, 0, 0); 274 cs->AddPaddingColumn(0, GetBubbleFrameView()->GetTitleInsets().left()); 275 276 layout->StartRow(0, kTextColumnSetId); 277 layout->AddView(text_label); 278 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 279 280 layout->StartRow(0, kButtonColumnSetId); 281 layout->AddView(restore_button_); 282 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); 283 284 int bottom_margin = 1; 285 286 // Metrics reporting option. 287 if (offer_uma_optin_) { 288 const int kUMAOptionColumnSetId = 2; 289 cs = layout->AddColumnSet(kUMAOptionColumnSetId); 290 cs->AddColumn( 291 GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); 292 layout->StartRow(1, kUMAOptionColumnSetId); 293 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 294 layout->StartRow(1, kUMAOptionColumnSetId); 295 layout->AddView(CreateUMAOptinView()); 296 297 // Since the UMA optin row has a different background than the default 298 // background color of bubbles, the bottom margin has to be 0 to make sure 299 // the background extends to the bottom edge of the bubble. 300 bottom_margin = 0; 301 302 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_OPTIN_BAR_SHOWN); 303 } 304 305 set_margins(gfx::Insets(1, 0, bottom_margin, 0)); 306 Layout(); 307} 308 309views::View* SessionCrashedBubbleView::CreateUMAOptinView() { 310 // Checkbox for metric reporting setting. 311 // Since the text to the right of the checkbox can't be a simple string (needs 312 // a hyperlink in it), this checkbox contains an empty string as its label, 313 // and the real text will be added as a separate view. 314 uma_option_ = new views::Checkbox(base::string16()); 315 uma_option_->SetChecked(false); 316 317 // The text to the right of the checkbox. 318 size_t offset; 319 base::string16 link_text = 320 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_UMA_LINK_TEXT); 321 base::string16 uma_text = l10n_util::GetStringFUTF16( 322 IDS_SESSION_CRASHED_VIEW_UMA_OPTIN, 323 link_text, 324 &offset); 325 views::StyledLabel* uma_label = new views::StyledLabel(uma_text, this); 326 views::StyledLabel::RangeStyleInfo link_style = 327 views::StyledLabel::RangeStyleInfo::CreateForLink(); 328 link_style.font_style = gfx::Font::NORMAL; 329 uma_label->AddStyleRange(gfx::Range(offset, offset + link_text.length()), 330 link_style); 331 views::StyledLabel::RangeStyleInfo uma_style; 332 uma_style.color = kTextColor; 333 gfx::Range before_link_range(0, offset); 334 if (!before_link_range.is_empty()) 335 uma_label->AddStyleRange(before_link_range, uma_style); 336 gfx::Range after_link_range(offset + link_text.length(), uma_text.length()); 337 if (!after_link_range.is_empty()) 338 uma_label->AddStyleRange(after_link_range, uma_style); 339 340 // Create a view to hold the checkbox and the text. 341 views::View* uma_view = new views::View(); 342 GridLayout* uma_layout = new GridLayout(uma_view); 343 uma_view->SetLayoutManager(uma_layout); 344 345 uma_view->set_background( 346 views::Background::CreateSolidBackground(kBackgroundColor)); 347 int inset_left = GetBubbleFrameView()->GetTitleInsets().left(); 348 uma_layout->SetInsets(views::kRelatedControlVerticalSpacing, inset_left, 349 views::kRelatedControlVerticalSpacing, inset_left); 350 351 const int kReportColumnSetId = 0; 352 views::ColumnSet* cs = uma_layout->AddColumnSet(kReportColumnSetId); 353 cs->AddColumn(GridLayout::CENTER, GridLayout::LEADING, 0, 354 GridLayout::USE_PREF, 0, 0); 355 cs->AddPaddingColumn(0, kCheckboxTextDistance); 356 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, 357 GridLayout::FIXED, kWidthOfDescriptionText, 0); 358 359 uma_layout->StartRow(0, kReportColumnSetId); 360 uma_layout->AddView(uma_option_); 361 uma_layout->AddView(uma_label); 362 363 return uma_view; 364} 365 366void SessionCrashedBubbleView::ButtonPressed(views::Button* sender, 367 const ui::Event& event) { 368 DCHECK_EQ(sender, restore_button_); 369 RestorePreviousSession(sender); 370} 371 372void SessionCrashedBubbleView::StyledLabelLinkClicked(const gfx::Range& range, 373 int event_flags) { 374 browser_->OpenURL(content::OpenURLParams( 375 GURL("https://support.google.com/chrome/answer/96817"), 376 content::Referrer(), 377 NEW_FOREGROUND_TAB, 378 ui::PAGE_TRANSITION_LINK, 379 false)); 380 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_HELP); 381} 382 383void SessionCrashedBubbleView::DidStartNavigationToPendingEntry( 384 const GURL& url, 385 content::NavigationController::ReloadType reload_type) { 386 started_navigation_ = true; 387} 388 389void SessionCrashedBubbleView::DidFinishLoad( 390 content::RenderFrameHost* render_frame_host, 391 const GURL& validated_url) { 392 if (started_navigation_) 393 CloseBubble(); 394} 395 396void SessionCrashedBubbleView::WasShown() { 397 GetWidget()->Show(); 398} 399 400void SessionCrashedBubbleView::WasHidden() { 401 GetWidget()->Hide(); 402} 403 404void SessionCrashedBubbleView::Observe( 405 int type, 406 const content::NotificationSource& source, 407 const content::NotificationDetails& details) { 408 if (type == chrome::NOTIFICATION_TAB_CLOSING) 409 CloseBubble(); 410} 411 412void SessionCrashedBubbleView::TabDetachedAt(content::WebContents* contents, 413 int index) { 414 if (web_contents_ == contents) 415 CloseBubble(); 416} 417 418void SessionCrashedBubbleView::RestorePreviousSession(views::Button* sender) { 419 SessionRestore::RestoreSessionAfterCrash(browser_); 420 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_RESTORED); 421 restored_ = true; 422 423 // Record user's choice for opting in to UMA. 424 // There's no opting-out choice in the crash restore bubble. 425 if (uma_option_ && uma_option_->checked()) { 426 InitiateMetricsReportingChange(true, OnMetricsReportingCallbackType()); 427 RecordBubbleHistogramValue(SESSION_CRASHED_BUBBLE_UMA_OPTIN); 428 } 429 CloseBubble(); 430} 431 432void SessionCrashedBubbleView::CloseBubble() { 433 GetWidget()->Close(); 434} 435 436bool ShowSessionCrashedBubble(Browser* browser) { 437 if (IsBubbleUIEnabled()) { 438 SessionCrashedBubbleView::Show(browser); 439 return true; 440 } 441 return false; 442} 443