MacViews: Use Mac's "Constrained Window Button" style for Button::STYLE_BUTTON LabelB...
[chromium-blink-merge.git] / chrome / browser / ui / views / sad_tab_view.cc
blob2fcf79f95eefc322fb5b89cedd858ba746e1087f
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.
5 #include "chrome/browser/ui/views/sad_tab_view.h"
7 #include <string>
9 #include "base/metrics/histogram.h"
10 #include "chrome/browser/ui/browser_finder.h"
11 #include "chrome/browser/ui/chrome_pages.h"
12 #include "chrome/common/url_constants.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "components/feedback/feedback_util.h"
15 #include "content/public/browser/navigation_controller.h"
16 #include "content/public/browser/web_contents.h"
17 #include "grit/components_strings.h"
18 #include "grit/theme_resources.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/views/background.h"
22 #include "ui/views/controls/button/blue_button.h"
23 #include "ui/views/controls/image_view.h"
24 #include "ui/views/controls/label.h"
25 #include "ui/views/controls/link.h"
26 #include "ui/views/layout/grid_layout.h"
27 #include "ui/views/layout/layout_constants.h"
28 #include "ui/views/widget/widget.h"
30 #if defined(OS_CHROMEOS)
31 #include "chrome/browser/memory/oom_memory_details.h"
32 #endif
34 using content::OpenURLParams;
35 using content::WebContents;
37 namespace {
39 const int kMaxContentWidth = 600;
40 const int kMinColumnWidth = 120;
41 const char kCategoryTagCrash[] = "Crash";
42 const int kCrashesBeforeFeedbackIsDisplayed = 1;
44 void RecordKillCreated() {
45 static int killed = 0;
46 killed++;
47 UMA_HISTOGRAM_CUSTOM_COUNTS(
48 "Tabs.SadTab.KillCreated", killed, 1, 1000, 50);
51 void RecordKillDisplayed() {
52 static int killed = 0;
53 killed++;
54 UMA_HISTOGRAM_CUSTOM_COUNTS(
55 "Tabs.SadTab.KillDisplayed", killed, 1, 1000, 50);
58 #if defined(OS_CHROMEOS)
59 void RecordKillCreatedOOM() {
60 static int oom_killed = 0;
61 oom_killed++;
62 UMA_HISTOGRAM_CUSTOM_COUNTS(
63 "Tabs.SadTab.KillCreated.OOM", oom_killed, 1, 1000, 50);
66 void RecordKillDisplayedOOM() {
67 static int oom_killed = 0;
68 oom_killed++;
69 UMA_HISTOGRAM_CUSTOM_COUNTS(
70 "Tabs.SadTab.KillDisplayed.OOM", oom_killed, 1, 1000, 50);
72 #endif
74 } // namespace
76 SadTabView::SadTabView(WebContents* web_contents, chrome::SadTabKind kind)
77 : web_contents_(web_contents),
78 kind_(kind),
79 painted_(false),
80 message_(nullptr),
81 help_link_(nullptr),
82 action_button_(nullptr),
83 title_(nullptr),
84 help_message_(nullptr) {
85 DCHECK(web_contents);
87 // These stats should use the same counting approach and bucket size used for
88 // tab discard events in memory::OomPriorityManager so they can be directly
89 // compared.
90 // TODO(jamescook): Maybe track time between sad tabs?
91 static int total_crashes = 0;
92 total_crashes++;
94 switch (kind_) {
95 case chrome::SAD_TAB_KIND_CRASHED: {
96 static int crashed = 0;
97 crashed++;
98 UMA_HISTOGRAM_CUSTOM_COUNTS(
99 "Tabs.SadTab.CrashCreated", crashed, 1, 1000, 50);
100 break;
102 case chrome::SAD_TAB_KIND_KILLED: {
103 RecordKillCreated();
104 LOG(WARNING) << "Tab Killed: "
105 << web_contents->GetURL().GetOrigin().spec();
106 break;
108 #if defined(OS_CHROMEOS)
109 case chrome::SAD_TAB_KIND_KILLED_BY_OOM: {
110 RecordKillCreated();
111 RecordKillCreatedOOM();
112 const std::string spec = web_contents->GetURL().GetOrigin().spec();
113 memory::OomMemoryDetails::Log(
114 "Tab OOM-Killed Memory details: " + spec + ", ", base::Closure());
115 break;
117 #endif
120 // Set the background color.
121 set_background(
122 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
123 ui::NativeTheme::kColorId_DialogBackground)));
125 views::GridLayout* layout = new views::GridLayout(this);
126 SetLayoutManager(layout);
128 const int column_set_id = 0;
129 views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
130 columns->AddPaddingColumn(1, views::kPanelSubVerticalSpacing);
131 columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0,
132 views::GridLayout::USE_PREF, 0, kMinColumnWidth);
133 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::LEADING, 0,
134 views::GridLayout::USE_PREF, 0, kMinColumnWidth);
135 columns->AddPaddingColumn(1, views::kPanelSubVerticalSpacing);
137 views::ImageView* image = new views::ImageView();
138 image->SetImage(
139 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(IDR_SAD_TAB));
140 layout->AddPaddingRow(1, views::kPanelVerticalSpacing);
141 layout->StartRow(0, column_set_id);
142 layout->AddView(image, 2, 1);
144 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
146 title_ = CreateLabel(l10n_util::GetStringUTF16(IDS_SAD_TAB_TITLE));
147 title_->SetFontList(rb.GetFontList(ui::ResourceBundle::LargeFont));
148 title_->SetMultiLine(true);
149 title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
150 layout->StartRowWithPadding(0, column_set_id, 0,
151 views::kPanelVerticalSpacing);
152 layout->AddView(title_, 2, 1);
154 const SkColor text_color = GetNativeTheme()->GetSystemColor(
155 ui::NativeTheme::kColorId_LabelDisabledColor);
157 int message_id = IDS_SAD_TAB_MESSAGE;
158 #if defined(OS_CHROMEOS)
159 if (kind_ == chrome::SAD_TAB_KIND_KILLED_BY_OOM)
160 message_id = IDS_KILLED_TAB_BY_OOM_MESSAGE;
161 #endif
163 message_ = CreateLabel(l10n_util::GetStringUTF16(message_id));
165 message_->SetMultiLine(true);
166 message_->SetEnabledColor(text_color);
167 message_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
168 message_->SetLineHeight(views::kPanelSubVerticalSpacing);
170 layout->StartRowWithPadding(0, column_set_id, 0, views::kPanelVertMargin);
171 layout->AddView(message_, 2, 1, views::GridLayout::LEADING,
172 views::GridLayout::LEADING);
174 if (web_contents_) {
175 // In the cases of multiple crashes in a session the 'Feedback' button
176 // replaces the 'Reload' button as primary action.
177 int button_type = total_crashes > kCrashesBeforeFeedbackIsDisplayed ?
178 SAD_TAB_BUTTON_FEEDBACK : SAD_TAB_BUTTON_RELOAD;
179 action_button_ = new views::BlueButton(this,
180 l10n_util::GetStringUTF16(button_type == SAD_TAB_BUTTON_FEEDBACK
181 ? IDS_CRASHED_TAB_FEEDBACK_LINK
182 : IDS_SAD_TAB_RELOAD_LABEL));
183 action_button_->set_tag(button_type);
184 help_link_ =
185 CreateLink(l10n_util::GetStringUTF16(IDS_LEARN_MORE), text_color);
186 layout->StartRowWithPadding(0, column_set_id, 0,
187 views::kPanelVerticalSpacing);
188 layout->AddView(help_link_, 1, 1, views::GridLayout::LEADING,
189 views::GridLayout::CENTER);
190 layout->AddView(action_button_, 1, 1, views::GridLayout::TRAILING,
191 views::GridLayout::LEADING);
193 layout->AddPaddingRow(2, views::kPanelSubVerticalSpacing);
196 SadTabView::~SadTabView() {}
198 void SadTabView::LinkClicked(views::Link* source, int event_flags) {
199 DCHECK(web_contents_);
200 OpenURLParams params(GURL(chrome::kCrashReasonURL), content::Referrer(),
201 CURRENT_TAB, ui::PAGE_TRANSITION_LINK, false);
202 web_contents_->OpenURL(params);
205 void SadTabView::ButtonPressed(views::Button* sender,
206 const ui::Event& event) {
207 DCHECK(web_contents_);
208 DCHECK_EQ(action_button_, sender);
210 if (action_button_->tag() == SAD_TAB_BUTTON_FEEDBACK) {
211 chrome::ShowFeedbackPage(
212 chrome::FindBrowserWithWebContents(web_contents_),
213 l10n_util::GetStringUTF8(kind_ == chrome::SAD_TAB_KIND_CRASHED ?
214 IDS_CRASHED_TAB_FEEDBACK_MESSAGE : IDS_KILLED_TAB_FEEDBACK_MESSAGE),
215 std::string(kCategoryTagCrash));
216 } else {
217 web_contents_->GetController().Reload(true);
221 void SadTabView::Layout() {
222 // Specify the maximum message width explicitly.
223 const int max_width =
224 std::min(width() - views::kPanelSubVerticalSpacing * 2, kMaxContentWidth);
225 message_->SizeToFit(max_width);
226 title_->SizeToFit(max_width);
228 if (help_message_ != nullptr)
229 help_message_->SizeToFit(max_width);
231 View::Layout();
234 void SadTabView::OnPaint(gfx::Canvas* canvas) {
235 if (!painted_) {
236 // These stats should use the same counting approach and bucket size used
237 // for tab discard events in memory::OomPriorityManager so they can be
238 // directly compared.
239 switch (kind_) {
240 case chrome::SAD_TAB_KIND_CRASHED: {
241 static int crashed = 0;
242 UMA_HISTOGRAM_CUSTOM_COUNTS(
243 "Tabs.SadTab.CrashDisplayed", ++crashed, 1, 1000, 50);
244 break;
246 case chrome::SAD_TAB_KIND_KILLED:
247 RecordKillDisplayed();
248 break;
249 #if defined(OS_CHROMEOS)
250 case chrome::SAD_TAB_KIND_KILLED_BY_OOM:
251 RecordKillDisplayed();
252 RecordKillDisplayedOOM();
253 break;
254 #endif
256 painted_ = true;
258 View::OnPaint(canvas);
261 void SadTabView::Show() {
262 views::Widget::InitParams sad_tab_params(
263 views::Widget::InitParams::TYPE_CONTROL);
265 // It is not possible to create a native_widget_win that has no parent in
266 // and later re-parent it.
267 // TODO(avi): This is a cheat. Can this be made cleaner?
268 sad_tab_params.parent = web_contents_->GetNativeView();
270 set_owned_by_client();
272 views::Widget* sad_tab = new views::Widget;
273 sad_tab->Init(sad_tab_params);
274 sad_tab->SetContentsView(this);
276 views::Widget::ReparentNativeView(sad_tab->GetNativeView(),
277 web_contents_->GetNativeView());
278 gfx::Rect bounds = web_contents_->GetContainerBounds();
279 sad_tab->SetBounds(gfx::Rect(bounds.size()));
282 void SadTabView::Close() {
283 if (GetWidget())
284 GetWidget()->Close();
287 views::Label* SadTabView::CreateLabel(const base::string16& text) {
288 views::Label* label = new views::Label(text);
289 label->SetBackgroundColor(background()->get_color());
290 return label;
293 views::Link* SadTabView::CreateLink(const base::string16& text,
294 const SkColor& color) {
295 views::Link* link = new views::Link(text);
296 link->SetBackgroundColor(background()->get_color());
297 link->SetEnabledColor(color);
298 link->set_listener(this);
299 return link;
302 namespace chrome {
304 SadTab* SadTab::Create(content::WebContents* web_contents,
305 SadTabKind kind) {
306 return new SadTabView(web_contents, kind);
309 } // namespace chrome