session_crashed_bubble_view.cc revision 010d83a9304c5a91596085d917d248abff47903a
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/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/separator.h" 34#include "ui/views/layout/grid_layout.h" 35#include "ui/views/layout/layout_constants.h" 36#include "ui/views/widget/widget.h" 37 38using views::GridLayout; 39 40namespace { 41 42// Fixed width of the column holding the description label of the bubble. 43const int kWidthOfDescriptionText = 320; 44 45// Margins width for the top rows to compensate for the bottom panel for which 46// we don't want any margin. 47const int kMarginWidth = 12; 48const int kMarginHeight = kMarginWidth; 49 50// The color of the background of the sub panel to offer UMA optin. 51const SkColor kLightGrayBackgroundColor = 0xFFF0F0F0; 52const SkColor kWhiteBackgroundColor = 0xFFFFFFFF; 53 54bool ShouldOfferMetricsReporting() { 55// Stats collection only applies to Google Chrome builds. 56#if defined(GOOGLE_CHROME_BUILD) 57 // Only show metrics reporting option if user didn't already consent to it. 58 if (GoogleUpdateSettings::GetCollectStatsConsent()) 59 return false; 60 return g_browser_process->local_state()->FindPreference( 61 prefs::kMetricsReportingEnabled)->IsUserModifiable(); 62#else 63 return false; 64#endif // defined(GOOGLE_CHROME_BUILD) 65} 66 67} // namespace 68 69// static 70void SessionCrashedBubbleView::Show(Browser* browser) { 71 if (browser->profile()->IsOffTheRecord()) 72 return; 73 74 views::View* anchor_view = 75 BrowserView::GetBrowserViewForBrowser(browser)->toolbar()->app_menu(); 76 content::WebContents* web_contents = 77 browser->tab_strip_model()->GetActiveWebContents(); 78 SessionCrashedBubbleView* crash_bubble = 79 new SessionCrashedBubbleView(anchor_view, browser, web_contents); 80 views::BubbleDelegateView::CreateBubble(crash_bubble)->Show(); 81} 82 83SessionCrashedBubbleView::SessionCrashedBubbleView( 84 views::View* anchor_view, 85 Browser* browser, 86 content::WebContents* web_contents) 87 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), 88 content::WebContentsObserver(web_contents), 89 browser_(browser), 90 web_contents_(web_contents), 91 restore_button_(NULL), 92 close_(NULL), 93 uma_option_(NULL), 94 started_navigation_(false) { 95 set_close_on_deactivate(false); 96 registrar_.Add( 97 this, 98 chrome::NOTIFICATION_TAB_CLOSING, 99 content::Source<content::NavigationController>(&( 100 web_contents->GetController()))); 101 browser->tab_strip_model()->AddObserver(this); 102} 103 104SessionCrashedBubbleView::~SessionCrashedBubbleView() { 105 browser_->tab_strip_model()->RemoveObserver(this); 106} 107 108views::View* SessionCrashedBubbleView::GetInitiallyFocusedView() { 109 return restore_button_; 110} 111 112void SessionCrashedBubbleView::Init() { 113 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 114 115 // Close button. 116 close_ = new views::LabelButton(this, base::string16()); 117 close_->SetImage(views::CustomButton::STATE_NORMAL, 118 *rb->GetImageNamed(IDR_CLOSE_2).ToImageSkia()); 119 close_->SetImage(views::CustomButton::STATE_HOVERED, 120 *rb->GetImageNamed(IDR_CLOSE_2_H).ToImageSkia()); 121 close_->SetImage(views::CustomButton::STATE_PRESSED, 122 *rb->GetImageNamed(IDR_CLOSE_2_P).ToImageSkia()); 123 close_->SetSize(close_->GetPreferredSize()); 124 close_->SetBorder(views::Border::CreateEmptyBorder(0, 0, 0, 0)); 125 126 // Bubble title label. 127 views::Label* title_label = new views::Label( 128 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_TITLE)); 129 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 130 title_label->SetFontList(rb->GetFontList(ui::ResourceBundle::BoldFont)); 131 132 // Description text label. 133 views::Label* text_label = new views::Label( 134 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE)); 135 text_label->SetMultiLine(true); 136 text_label->SetLineHeight(20); 137 text_label->SetEnabledColor(SK_ColorDKGRAY); 138 text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 139 140 // Restore button. 141 restore_button_ = new views::LabelButton( 142 this, l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON)); 143 restore_button_->SetStyle(views::Button::STYLE_BUTTON); 144 restore_button_->SetIsDefault(true); 145 restore_button_->SetFontList(rb->GetFontList(ui::ResourceBundle::BoldFont)); 146 147 GridLayout* layout = new GridLayout(this); 148 SetLayoutManager(layout); 149 150 // Title and close button row. 151 const int kTitleColumnSetId = 0; 152 views::ColumnSet* cs = layout->AddColumnSet(kTitleColumnSetId); 153 cs->AddPaddingColumn(0, kMarginWidth); 154 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, 155 GridLayout::USE_PREF, 0, 0); 156 cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing); 157 cs->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 0, 158 GridLayout::USE_PREF, 0, 0); 159 160 // Text row. 161 const int kTextColumnSetId = 1; 162 cs = layout->AddColumnSet(kTextColumnSetId); 163 cs->AddPaddingColumn(0, kMarginWidth); 164 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, 165 GridLayout::FIXED, kWidthOfDescriptionText, 0); 166 167 // Restore button row 168 const int kButtonColumnSetId = 2; 169 cs = layout->AddColumnSet(kButtonColumnSetId); 170 cs->AddPaddingColumn(0, kMarginWidth); 171 cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing); 172 cs->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0, 173 GridLayout::USE_PREF, 0, 0); 174 cs->AddPaddingColumn(0, kMarginWidth); 175 176 layout->AddPaddingRow(0, kMarginHeight); 177 layout->StartRow(0, kTitleColumnSetId); 178 layout->AddView(title_label); 179 layout->AddView(close_); 180 layout->AddPaddingRow(0, kMarginHeight); 181 182 layout->StartRow(0, kTextColumnSetId); 183 layout->AddView(text_label); 184 layout->AddPaddingRow(0, kMarginHeight); 185 186 layout->StartRow(0, kButtonColumnSetId); 187 layout->AddView(restore_button_); 188 layout->AddPaddingRow(0, kMarginHeight); 189 190 // Metrics reporting option. 191 if (ShouldOfferMetricsReporting()) 192 CreateUmaOptinView(layout); 193 194 set_color(kWhiteBackgroundColor); 195 set_margins(gfx::Insets()); 196 Layout(); 197} 198 199void SessionCrashedBubbleView::CreateUmaOptinView(GridLayout* layout) { 200 // Checkbox for metric reporting setting. 201 uma_option_ = new views::Checkbox( 202 l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_UMA_OPTIN)); 203 uma_option_->SetTextColor(views::Button::STATE_NORMAL, SK_ColorGRAY); 204 uma_option_->SetChecked(false); 205 uma_option_->SetTextMultiLine(true); 206 uma_option_->set_background( 207 views::Background::CreateSolidBackground(kLightGrayBackgroundColor)); 208 uma_option_->set_listener(this); 209 // We use a border instead of padding so that the background color reach 210 // the edges of the bubble. 211 uma_option_->SetBorder( 212 views::Border::CreateSolidSidedBorder( 213 kMarginHeight, kMarginWidth, kMarginHeight, kMarginWidth, 214 kLightGrayBackgroundColor)); 215 216 // Separator. 217 const int kSeparatorColumnSetId = 3; 218 views::ColumnSet* cs = layout->AddColumnSet(kSeparatorColumnSetId); 219 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, 220 GridLayout::FIXED, kWidthOfDescriptionText + kMarginWidth, 0); 221 222 // Reporting row. 223 const int kReportColumnSetId = 4; 224 cs = layout->AddColumnSet(kReportColumnSetId); 225 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, 226 GridLayout::FIXED, kWidthOfDescriptionText + kMarginWidth, 0); 227 228 layout->StartRow(0, kSeparatorColumnSetId); 229 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); 230 layout->StartRow(0, kReportColumnSetId); 231 layout->AddView(uma_option_); 232} 233 234void SessionCrashedBubbleView::ButtonPressed(views::Button* sender, 235 const ui::Event& event) { 236 DCHECK(sender); 237 if (sender == restore_button_) 238 RestorePreviousSession(sender); 239 else if (sender == close_) 240 CloseBubble(); 241} 242 243void SessionCrashedBubbleView::DidStartNavigationToPendingEntry( 244 const GURL& url, 245 content::NavigationController::ReloadType reload_type) { 246 started_navigation_ = true; 247} 248 249void SessionCrashedBubbleView::DidFinishLoad( 250 int64 frame_id, 251 const GURL& validated_url, 252 bool is_main_frame, 253 content::RenderViewHost* render_view_host) { 254 if (started_navigation_) 255 CloseBubble(); 256} 257 258void SessionCrashedBubbleView::WasShown() { 259 GetWidget()->Show(); 260} 261 262void SessionCrashedBubbleView::WasHidden() { 263 GetWidget()->Hide(); 264} 265 266void SessionCrashedBubbleView::Observe( 267 int type, 268 const content::NotificationSource& source, 269 const content::NotificationDetails& details) { 270 if (type == chrome::NOTIFICATION_TAB_CLOSING) 271 CloseBubble(); 272} 273 274void SessionCrashedBubbleView::TabDetachedAt(content::WebContents* contents, 275 int index) { 276 if (web_contents_ == contents) 277 CloseBubble(); 278} 279 280void SessionCrashedBubbleView::RestorePreviousSession(views::Button* sender) { 281 SessionRestore::RestoreSessionAfterCrash(browser_); 282 283 // Record user's choice for opting in to UMA. 284 // There's no opting-out choice in the crash restore bubble. 285 if (uma_option_ && uma_option_->checked()) 286 OptionsUtil::ResolveMetricsReportingEnabled(true); 287 CloseBubble(); 288} 289 290void SessionCrashedBubbleView::CloseBubble() { 291 GetWidget()->Close(); 292} 293 294bool ShowSessionCrashedBubble(Browser* browser) { 295 SessionCrashedBubbleView::Show(browser); 296 return true; 297} 298