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"
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"
34 using content::OpenURLParams
;
35 using content::WebContents
;
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;
47 UMA_HISTOGRAM_CUSTOM_COUNTS(
48 "Tabs.SadTab.KillCreated", killed
, 1, 1000, 50);
51 void RecordKillDisplayed() {
52 static int killed
= 0;
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;
62 UMA_HISTOGRAM_CUSTOM_COUNTS(
63 "Tabs.SadTab.KillCreated.OOM", oom_killed
, 1, 1000, 50);
66 void RecordKillDisplayedOOM() {
67 static int oom_killed
= 0;
69 UMA_HISTOGRAM_CUSTOM_COUNTS(
70 "Tabs.SadTab.KillDisplayed.OOM", oom_killed
, 1, 1000, 50);
76 int SadTabView::total_crashes_
= 0;
78 SadTabView::SadTabView(WebContents
* web_contents
, chrome::SadTabKind kind
)
79 : web_contents_(web_contents
),
84 action_button_(nullptr),
86 help_message_(nullptr) {
89 // These stats should use the same counting approach and bucket size used for
90 // tab discard events in memory::OomPriorityManager so they can be directly
92 // TODO(jamescook): Maybe track time between sad tabs?
96 case chrome::SAD_TAB_KIND_CRASHED
: {
97 static int crashed
= 0;
99 UMA_HISTOGRAM_CUSTOM_COUNTS(
100 "Tabs.SadTab.CrashCreated", crashed
, 1, 1000, 50);
103 case chrome::SAD_TAB_KIND_KILLED
: {
105 LOG(WARNING
) << "Tab Killed: "
106 << web_contents
->GetURL().GetOrigin().spec();
109 #if defined(OS_CHROMEOS)
110 case chrome::SAD_TAB_KIND_KILLED_BY_OOM
: {
112 RecordKillCreatedOOM();
113 const std::string spec
= web_contents
->GetURL().GetOrigin().spec();
114 memory::OomMemoryDetails::Log(
115 "Tab OOM-Killed Memory details: " + spec
+ ", ", base::Closure());
121 // Set the background color.
123 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
124 ui::NativeTheme::kColorId_DialogBackground
)));
126 views::GridLayout
* layout
= new views::GridLayout(this);
127 SetLayoutManager(layout
);
129 const int column_set_id
= 0;
130 views::ColumnSet
* columns
= layout
->AddColumnSet(column_set_id
);
131 columns
->AddPaddingColumn(1, views::kPanelSubVerticalSpacing
);
132 columns
->AddColumn(views::GridLayout::LEADING
, views::GridLayout::LEADING
, 0,
133 views::GridLayout::USE_PREF
, 0, kMinColumnWidth
);
134 columns
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
, 0,
135 views::GridLayout::USE_PREF
, 0, kMinColumnWidth
);
136 columns
->AddPaddingColumn(1, views::kPanelSubVerticalSpacing
);
138 views::ImageView
* image
= new views::ImageView();
140 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(IDR_SAD_TAB
));
141 layout
->AddPaddingRow(1, views::kPanelVerticalSpacing
);
142 layout
->StartRow(0, column_set_id
);
143 layout
->AddView(image
, 2, 1);
145 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
147 title_
= CreateLabel(l10n_util::GetStringUTF16(IDS_SAD_TAB_TITLE
));
148 title_
->SetFontList(rb
.GetFontList(ui::ResourceBundle::LargeFont
));
149 title_
->SetMultiLine(true);
150 title_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
151 layout
->StartRowWithPadding(0, column_set_id
, 0,
152 views::kPanelVerticalSpacing
);
153 layout
->AddView(title_
, 2, 1);
155 const SkColor text_color
= GetNativeTheme()->GetSystemColor(
156 ui::NativeTheme::kColorId_LabelDisabledColor
);
158 int message_id
= IDS_SAD_TAB_MESSAGE
;
159 #if defined(OS_CHROMEOS)
160 if (kind_
== chrome::SAD_TAB_KIND_KILLED_BY_OOM
)
161 message_id
= IDS_KILLED_TAB_BY_OOM_MESSAGE
;
164 message_
= CreateLabel(l10n_util::GetStringUTF16(message_id
));
166 message_
->SetMultiLine(true);
167 message_
->SetEnabledColor(text_color
);
168 message_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
169 message_
->SetLineHeight(views::kPanelSubVerticalSpacing
);
171 layout
->StartRowWithPadding(0, column_set_id
, 0, views::kPanelVertMargin
);
172 layout
->AddView(message_
, 2, 1, views::GridLayout::LEADING
,
173 views::GridLayout::LEADING
);
176 // In the cases of multiple crashes in a session the 'Feedback' button
177 // replaces the 'Reload' button as primary action.
178 int button_type
= total_crashes_
> kCrashesBeforeFeedbackIsDisplayed
?
179 SAD_TAB_BUTTON_FEEDBACK
: SAD_TAB_BUTTON_RELOAD
;
180 action_button_
= new views::BlueButton(this,
181 l10n_util::GetStringUTF16(button_type
== SAD_TAB_BUTTON_FEEDBACK
182 ? IDS_CRASHED_TAB_FEEDBACK_LINK
183 : IDS_SAD_TAB_RELOAD_LABEL
));
184 action_button_
->set_tag(button_type
);
186 CreateLink(l10n_util::GetStringUTF16(IDS_LEARN_MORE
), text_color
);
187 layout
->StartRowWithPadding(0, column_set_id
, 0,
188 views::kPanelVerticalSpacing
);
189 layout
->AddView(help_link_
, 1, 1, views::GridLayout::LEADING
,
190 views::GridLayout::CENTER
);
191 layout
->AddView(action_button_
, 1, 1, views::GridLayout::TRAILING
,
192 views::GridLayout::LEADING
);
194 layout
->AddPaddingRow(2, views::kPanelSubVerticalSpacing
);
197 SadTabView::~SadTabView() {}
199 void SadTabView::LinkClicked(views::Link
* source
, int event_flags
) {
200 DCHECK(web_contents_
);
201 OpenURLParams
params(GURL(total_crashes_
> kCrashesBeforeFeedbackIsDisplayed
?
202 chrome::kCrashReasonFeedbackDisplayedURL
:
203 chrome::kCrashReasonURL
), content::Referrer(),
204 CURRENT_TAB
, ui::PAGE_TRANSITION_LINK
, false);
205 web_contents_
->OpenURL(params
);
208 void SadTabView::ButtonPressed(views::Button
* sender
,
209 const ui::Event
& event
) {
210 DCHECK(web_contents_
);
211 DCHECK_EQ(action_button_
, sender
);
213 if (action_button_
->tag() == SAD_TAB_BUTTON_FEEDBACK
) {
214 chrome::ShowFeedbackPage(
215 chrome::FindBrowserWithWebContents(web_contents_
),
216 l10n_util::GetStringUTF8(kind_
== chrome::SAD_TAB_KIND_CRASHED
?
217 IDS_CRASHED_TAB_FEEDBACK_MESSAGE
: IDS_KILLED_TAB_FEEDBACK_MESSAGE
),
218 std::string(kCategoryTagCrash
));
220 web_contents_
->GetController().Reload(true);
224 void SadTabView::Layout() {
225 // Specify the maximum message width explicitly.
226 const int max_width
=
227 std::min(width() - views::kPanelSubVerticalSpacing
* 2, kMaxContentWidth
);
228 message_
->SizeToFit(max_width
);
229 title_
->SizeToFit(max_width
);
231 if (help_message_
!= nullptr)
232 help_message_
->SizeToFit(max_width
);
237 void SadTabView::OnPaint(gfx::Canvas
* canvas
) {
239 // These stats should use the same counting approach and bucket size used
240 // for tab discard events in memory::OomPriorityManager so they can be
241 // directly compared.
243 case chrome::SAD_TAB_KIND_CRASHED
: {
244 static int crashed
= 0;
245 UMA_HISTOGRAM_CUSTOM_COUNTS(
246 "Tabs.SadTab.CrashDisplayed", ++crashed
, 1, 1000, 50);
249 case chrome::SAD_TAB_KIND_KILLED
:
250 RecordKillDisplayed();
252 #if defined(OS_CHROMEOS)
253 case chrome::SAD_TAB_KIND_KILLED_BY_OOM
:
254 RecordKillDisplayed();
255 RecordKillDisplayedOOM();
261 View::OnPaint(canvas
);
264 void SadTabView::Show() {
265 views::Widget::InitParams
sad_tab_params(
266 views::Widget::InitParams::TYPE_CONTROL
);
268 // It is not possible to create a native_widget_win that has no parent in
269 // and later re-parent it.
270 // TODO(avi): This is a cheat. Can this be made cleaner?
271 sad_tab_params
.parent
= web_contents_
->GetNativeView();
273 set_owned_by_client();
275 views::Widget
* sad_tab
= new views::Widget
;
276 sad_tab
->Init(sad_tab_params
);
277 sad_tab
->SetContentsView(this);
279 views::Widget::ReparentNativeView(sad_tab
->GetNativeView(),
280 web_contents_
->GetNativeView());
281 gfx::Rect bounds
= web_contents_
->GetContainerBounds();
282 sad_tab
->SetBounds(gfx::Rect(bounds
.size()));
285 void SadTabView::Close() {
287 GetWidget()->Close();
290 views::Label
* SadTabView::CreateLabel(const base::string16
& text
) {
291 views::Label
* label
= new views::Label(text
);
292 label
->SetBackgroundColor(background()->get_color());
296 views::Link
* SadTabView::CreateLink(const base::string16
& text
,
297 const SkColor
& color
) {
298 views::Link
* link
= new views::Link(text
);
299 link
->SetBackgroundColor(background()->get_color());
300 link
->SetEnabledColor(color
);
301 link
->set_listener(this);
307 SadTab
* SadTab::Create(content::WebContents
* web_contents
,
309 return new SadTabView(web_contents
, kind
);
312 } // namespace chrome