1 // Copyright (c) 2012 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/sad_tab_view.h" 6 7 #include <string> 8 9 #include "base/metrics/field_trial.h" 10 #include "base/metrics/histogram.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/browser_finder.h" 14 #include "chrome/browser/ui/chrome_pages.h" 15 #include "chrome/common/url_constants.h" 16 #include "chrome/grit/generated_resources.h" 17 #include "components/feedback/feedback_util.h" 18 #include "content/public/browser/navigation_controller.h" 19 #include "content/public/browser/web_contents.h" 20 #include "grit/components_strings.h" 21 #include "grit/theme_resources.h" 22 #include "ui/base/l10n/l10n_util.h" 23 #include "ui/base/resource/resource_bundle.h" 24 #include "ui/views/background.h" 25 #include "ui/views/controls/button/label_button.h" 26 #include "ui/views/controls/button/label_button_border.h" 27 #include "ui/views/controls/image_view.h" 28 #include "ui/views/controls/label.h" 29 #include "ui/views/controls/link.h" 30 #include "ui/views/layout/grid_layout.h" 31 #include "ui/views/widget/widget.h" 32 33 using content::OpenURLParams; 34 using content::WebContents; 35 36 namespace { 37 38 const int kPadding = 20; 39 const float kMessageSize = 0.65f; 40 const SkColor kTextColor = SK_ColorWHITE; 41 const SkColor kCrashColor = SkColorSetRGB(35, 48, 64); 42 const SkColor kKillColor = SkColorSetRGB(57, 48, 88); 43 44 const char kCategoryTagCrash[] = "Crash"; 45 46 } // namespace 47 48 SadTabView::SadTabView(WebContents* web_contents, chrome::SadTabKind kind) 49 : web_contents_(web_contents), 50 kind_(kind), 51 painted_(false), 52 message_(NULL), 53 help_link_(NULL), 54 feedback_link_(NULL), 55 reload_button_(NULL) { 56 DCHECK(web_contents); 57 58 // Sometimes the user will never see this tab, so keep track of the total 59 // number of creation events to compare to display events. 60 // TODO(jamescook): Remove this after R20 stable. Keep it for now so we can 61 // compare R20 to earlier versions. 62 UMA_HISTOGRAM_COUNTS("SadTab.Created", kind_); 63 64 // These stats should use the same counting approach and bucket size used for 65 // tab discard events in chromeos::OomPriorityManager so they can be 66 // directly compared. 67 // TODO(jamescook): Maybe track time between sad tabs? 68 switch (kind_) { 69 case chrome::SAD_TAB_KIND_CRASHED: { 70 static int crashed = 0; 71 crashed++; 72 UMA_HISTOGRAM_CUSTOM_COUNTS( 73 "Tabs.SadTab.CrashCreated", crashed, 1, 1000, 50); 74 break; 75 } 76 case chrome::SAD_TAB_KIND_KILLED: { 77 static int killed = 0; 78 killed++; 79 UMA_HISTOGRAM_CUSTOM_COUNTS( 80 "Tabs.SadTab.KillCreated", killed, 1, 1000, 50); 81 break; 82 } 83 default: 84 NOTREACHED(); 85 } 86 87 // Set the background color. 88 set_background(views::Background::CreateSolidBackground( 89 (kind_ == chrome::SAD_TAB_KIND_CRASHED) ? kCrashColor : kKillColor)); 90 91 views::GridLayout* layout = new views::GridLayout(this); 92 SetLayoutManager(layout); 93 94 const int column_set_id = 0; 95 views::ColumnSet* columns = layout->AddColumnSet(column_set_id); 96 columns->AddPaddingColumn(1, kPadding); 97 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER, 98 0, views::GridLayout::USE_PREF, 0, 0); 99 columns->AddPaddingColumn(1, kPadding); 100 101 views::ImageView* image = new views::ImageView(); 102 image->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 103 (kind_ == chrome::SAD_TAB_KIND_CRASHED) ? IDR_SAD_TAB : IDR_KILLED_TAB)); 104 layout->StartRowWithPadding(0, column_set_id, 1, kPadding); 105 layout->AddView(image); 106 107 views::Label* title = CreateLabel(l10n_util::GetStringUTF16( 108 (kind_ == chrome::SAD_TAB_KIND_CRASHED) ? 109 IDS_SAD_TAB_TITLE : IDS_KILLED_TAB_TITLE)); 110 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 111 title->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont)); 112 layout->StartRowWithPadding(0, column_set_id, 0, kPadding); 113 layout->AddView(title); 114 115 message_ = CreateLabel(l10n_util::GetStringUTF16( 116 (kind_ == chrome::SAD_TAB_KIND_CRASHED) ? 117 IDS_SAD_TAB_MESSAGE : IDS_KILLED_TAB_MESSAGE)); 118 message_->SetMultiLine(true); 119 layout->StartRowWithPadding(0, column_set_id, 0, kPadding); 120 layout->AddView(message_); 121 122 if (web_contents_) { 123 layout->StartRowWithPadding(0, column_set_id, 0, kPadding); 124 reload_button_ = new views::LabelButton( 125 this, 126 l10n_util::GetStringUTF16(IDS_SAD_TAB_RELOAD_LABEL)); 127 reload_button_->SetStyle(views::Button::STYLE_BUTTON); 128 // Always render the reload button with chrome style borders; never rely on 129 // native styles. 130 reload_button_->SetBorder(scoped_ptr<views::Border>( 131 new views::LabelButtonBorder(reload_button_->style()))); 132 layout->AddView(reload_button_); 133 134 help_link_ = CreateLink(l10n_util::GetStringUTF16( 135 (kind_ == chrome::SAD_TAB_KIND_CRASHED) ? 136 IDS_SAD_TAB_HELP_LINK : IDS_LEARN_MORE)); 137 138 if (kind_ == chrome::SAD_TAB_KIND_CRASHED) { 139 size_t offset = 0; 140 base::string16 help_text( 141 l10n_util::GetStringFUTF16(IDS_SAD_TAB_HELP_MESSAGE, 142 base::string16(), &offset)); 143 views::Label* help_prefix = CreateLabel(help_text.substr(0, offset)); 144 views::Label* help_suffix = CreateLabel(help_text.substr(offset)); 145 146 const int help_column_set_id = 1; 147 views::ColumnSet* help_columns = layout->AddColumnSet(help_column_set_id); 148 help_columns->AddPaddingColumn(1, kPadding); 149 // Center three middle columns for the help's [prefix][link][suffix]. 150 for (size_t column = 0; column < 3; column++) 151 help_columns->AddColumn(views::GridLayout::CENTER, 152 views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0); 153 help_columns->AddPaddingColumn(1, kPadding); 154 155 layout->StartRowWithPadding(0, help_column_set_id, 0, kPadding); 156 layout->AddView(help_prefix); 157 layout->AddView(help_link_); 158 layout->AddView(help_suffix); 159 } else { 160 layout->StartRowWithPadding(0, column_set_id, 0, kPadding); 161 layout->AddView(help_link_); 162 163 feedback_link_ = CreateLink( 164 l10n_util::GetStringUTF16(IDS_KILLED_TAB_FEEDBACK_LINK)); 165 layout->StartRowWithPadding(0, column_set_id, 0, kPadding); 166 layout->AddView(feedback_link_); 167 } 168 } 169 layout->AddPaddingRow(1, kPadding); 170 } 171 172 SadTabView::~SadTabView() {} 173 174 void SadTabView::LinkClicked(views::Link* source, int event_flags) { 175 DCHECK(web_contents_); 176 if (source == help_link_) { 177 GURL help_url((kind_ == chrome::SAD_TAB_KIND_CRASHED) ? 178 chrome::kCrashReasonURL : chrome::kKillReasonURL); 179 OpenURLParams params( 180 help_url, content::Referrer(), CURRENT_TAB, 181 ui::PAGE_TRANSITION_LINK, false); 182 web_contents_->OpenURL(params); 183 } else if (source == feedback_link_) { 184 chrome::ShowFeedbackPage( 185 chrome::FindBrowserWithWebContents(web_contents_), 186 l10n_util::GetStringUTF8(IDS_KILLED_TAB_FEEDBACK_MESSAGE), 187 std::string(kCategoryTagCrash)); 188 } 189 } 190 191 void SadTabView::ButtonPressed(views::Button* sender, 192 const ui::Event& event) { 193 DCHECK(web_contents_); 194 DCHECK_EQ(reload_button_, sender); 195 web_contents_->GetController().Reload(true); 196 } 197 198 void SadTabView::Layout() { 199 // Specify the maximum message width explicitly. 200 message_->SizeToFit(static_cast<int>(width() * kMessageSize)); 201 View::Layout(); 202 } 203 204 void SadTabView::OnPaint(gfx::Canvas* canvas) { 205 if (!painted_) { 206 // User actually saw the error, keep track for user experience stats. 207 // TODO(jamescook): Remove this after R20 stable. Keep it for now so we can 208 // compare R20 to earlier versions. 209 UMA_HISTOGRAM_COUNTS("SadTab.Displayed", kind_); 210 211 // These stats should use the same counting approach and bucket size used 212 // for tab discard events in chromeos::OomPriorityManager so they 213 // can be directly compared. 214 switch (kind_) { 215 case chrome::SAD_TAB_KIND_CRASHED: { 216 static int crashed = 0; 217 UMA_HISTOGRAM_CUSTOM_COUNTS( 218 "Tabs.SadTab.CrashDisplayed", ++crashed, 1, 1000, 50); 219 break; 220 } 221 case chrome::SAD_TAB_KIND_KILLED: { 222 static int killed = 0; 223 UMA_HISTOGRAM_CUSTOM_COUNTS( 224 "Tabs.SadTab.KillDisplayed", ++killed, 1, 1000, 50); 225 break; 226 } 227 default: 228 NOTREACHED(); 229 } 230 painted_ = true; 231 } 232 View::OnPaint(canvas); 233 } 234 235 void SadTabView::Show() { 236 views::Widget::InitParams sad_tab_params( 237 views::Widget::InitParams::TYPE_CONTROL); 238 239 // It is not possible to create a native_widget_win that has no parent in 240 // and later re-parent it. 241 // TODO(avi): This is a cheat. Can this be made cleaner? 242 sad_tab_params.parent = web_contents_->GetNativeView(); 243 244 set_owned_by_client(); 245 246 views::Widget* sad_tab = new views::Widget; 247 sad_tab->Init(sad_tab_params); 248 sad_tab->SetContentsView(this); 249 250 views::Widget::ReparentNativeView(sad_tab->GetNativeView(), 251 web_contents_->GetNativeView()); 252 gfx::Rect bounds = web_contents_->GetContainerBounds(); 253 sad_tab->SetBounds(gfx::Rect(bounds.size())); 254 } 255 256 void SadTabView::Close() { 257 if (GetWidget()) 258 GetWidget()->Close(); 259 } 260 261 views::Label* SadTabView::CreateLabel(const base::string16& text) { 262 views::Label* label = new views::Label(text); 263 label->SetBackgroundColor(background()->get_color()); 264 label->SetEnabledColor(kTextColor); 265 return label; 266 } 267 268 views::Link* SadTabView::CreateLink(const base::string16& text) { 269 views::Link* link = new views::Link(text); 270 link->SetBackgroundColor(background()->get_color()); 271 link->SetEnabledColor(kTextColor); 272 link->set_listener(this); 273 return link; 274 } 275 276 namespace chrome { 277 278 SadTab* SadTab::Create(content::WebContents* web_contents, 279 SadTabKind kind) { 280 return new SadTabView(web_contents, kind); 281 } 282 283 } // namespace chrome 284