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 "ui/base/l10n/l10n_util.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/paint_vector_icon.h"
21 #include "ui/gfx/vector_icons_public.h"
22 #include "ui/native_theme/common_theme.h"
23 #include "ui/native_theme/native_theme.h"
24 #include "ui/views/background.h"
25 #include "ui/views/controls/button/blue_button.h"
26 #include "ui/views/controls/image_view.h"
27 #include "ui/views/controls/label.h"
28 #include "ui/views/controls/link.h"
29 #include "ui/views/layout/grid_layout.h"
30 #include "ui/views/layout/layout_constants.h"
31 #include "ui/views/widget/widget.h"
33 #if defined(OS_CHROMEOS)
34 #include "chrome/browser/memory/oom_memory_details.h"
37 using content::OpenURLParams
;
38 using content::WebContents
;
42 const int kMaxContentWidth
= 600;
43 const int kMinColumnWidth
= 120;
44 const char kCategoryTagCrash
[] = "Crash";
45 const int kCrashesBeforeFeedbackIsDisplayed
= 1;
47 void RecordKillCreated() {
48 static int killed
= 0;
50 UMA_HISTOGRAM_CUSTOM_COUNTS(
51 "Tabs.SadTab.KillCreated", killed
, 1, 1000, 50);
54 void RecordKillDisplayed() {
55 static int killed
= 0;
57 UMA_HISTOGRAM_CUSTOM_COUNTS(
58 "Tabs.SadTab.KillDisplayed", killed
, 1, 1000, 50);
61 #if defined(OS_CHROMEOS)
62 void RecordKillCreatedOOM() {
63 static int oom_killed
= 0;
65 UMA_HISTOGRAM_CUSTOM_COUNTS(
66 "Tabs.SadTab.KillCreated.OOM", oom_killed
, 1, 1000, 50);
69 void RecordKillDisplayedOOM() {
70 static int oom_killed
= 0;
72 UMA_HISTOGRAM_CUSTOM_COUNTS(
73 "Tabs.SadTab.KillDisplayed.OOM", oom_killed
, 1, 1000, 50);
79 int SadTabView::total_crashes_
= 0;
81 SadTabView::SadTabView(WebContents
* web_contents
, chrome::SadTabKind kind
)
82 : web_contents_(web_contents
),
87 action_button_(nullptr),
89 help_message_(nullptr) {
92 // These stats should use the same counting approach and bucket size used for
93 // tab discard events in memory::OomPriorityManager so they can be directly
95 // TODO(jamescook): Maybe track time between sad tabs?
99 case chrome::SAD_TAB_KIND_CRASHED
: {
100 static int crashed
= 0;
102 UMA_HISTOGRAM_CUSTOM_COUNTS(
103 "Tabs.SadTab.CrashCreated", crashed
, 1, 1000, 50);
106 case chrome::SAD_TAB_KIND_KILLED
: {
108 LOG(WARNING
) << "Tab Killed: "
109 << web_contents
->GetURL().GetOrigin().spec();
112 #if defined(OS_CHROMEOS)
113 case chrome::SAD_TAB_KIND_KILLED_BY_OOM
: {
115 RecordKillCreatedOOM();
116 const std::string spec
= web_contents
->GetURL().GetOrigin().spec();
117 memory::OomMemoryDetails::Log(
118 "Tab OOM-Killed Memory details: " + spec
+ ", ", base::Closure());
124 // Set the background color.
126 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
127 ui::NativeTheme::kColorId_DialogBackground
)));
129 views::GridLayout
* layout
= new views::GridLayout(this);
130 SetLayoutManager(layout
);
132 const int column_set_id
= 0;
133 views::ColumnSet
* columns
= layout
->AddColumnSet(column_set_id
);
134 columns
->AddPaddingColumn(1, views::kPanelSubVerticalSpacing
);
135 columns
->AddColumn(views::GridLayout::LEADING
, views::GridLayout::LEADING
, 0,
136 views::GridLayout::USE_PREF
, 0, kMinColumnWidth
);
137 columns
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
, 0,
138 views::GridLayout::USE_PREF
, 0, kMinColumnWidth
);
139 columns
->AddPaddingColumn(1, views::kPanelSubVerticalSpacing
);
141 views::ImageView
* image
= new views::ImageView();
144 ui::CommonThemeGetSystemColor(ui::NativeTheme::kColorId_ChromeIconGrey
,
147 gfx::CreateVectorIcon(gfx::VectorIconId::CRASHED_TAB
, 48, icon_color
));
148 layout
->AddPaddingRow(1, views::kPanelVerticalSpacing
);
149 layout
->StartRow(0, column_set_id
);
150 layout
->AddView(image
, 2, 1);
152 title_
= CreateLabel(l10n_util::GetStringUTF16(IDS_SAD_TAB_TITLE
));
153 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
154 title_
->SetFontList(rb
.GetFontList(ui::ResourceBundle::LargeFont
));
155 title_
->SetMultiLine(true);
156 title_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
157 layout
->StartRowWithPadding(0, column_set_id
, 0,
158 views::kPanelVerticalSpacing
);
159 layout
->AddView(title_
, 2, 1);
161 const SkColor text_color
= GetNativeTheme()->GetSystemColor(
162 ui::NativeTheme::kColorId_LabelDisabledColor
);
164 int message_id
= IDS_SAD_TAB_MESSAGE
;
165 #if defined(OS_CHROMEOS)
166 if (kind_
== chrome::SAD_TAB_KIND_KILLED_BY_OOM
)
167 message_id
= IDS_KILLED_TAB_BY_OOM_MESSAGE
;
170 message_
= CreateLabel(l10n_util::GetStringUTF16(message_id
));
172 message_
->SetMultiLine(true);
173 message_
->SetEnabledColor(text_color
);
174 message_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
175 message_
->SetLineHeight(views::kPanelSubVerticalSpacing
);
177 layout
->StartRowWithPadding(0, column_set_id
, 0, views::kPanelVertMargin
);
178 layout
->AddView(message_
, 2, 1, views::GridLayout::LEADING
,
179 views::GridLayout::LEADING
);
182 // In the cases of multiple crashes in a session the 'Feedback' button
183 // replaces the 'Reload' button as primary action.
184 int button_type
= total_crashes_
> kCrashesBeforeFeedbackIsDisplayed
?
185 SAD_TAB_BUTTON_FEEDBACK
: SAD_TAB_BUTTON_RELOAD
;
186 action_button_
= new views::BlueButton(this,
187 l10n_util::GetStringUTF16(button_type
== SAD_TAB_BUTTON_FEEDBACK
188 ? IDS_CRASHED_TAB_FEEDBACK_LINK
189 : IDS_SAD_TAB_RELOAD_LABEL
));
190 action_button_
->set_tag(button_type
);
192 CreateLink(l10n_util::GetStringUTF16(IDS_LEARN_MORE
), text_color
);
193 layout
->StartRowWithPadding(0, column_set_id
, 0,
194 views::kPanelVerticalSpacing
);
195 layout
->AddView(help_link_
, 1, 1, views::GridLayout::LEADING
,
196 views::GridLayout::CENTER
);
197 layout
->AddView(action_button_
, 1, 1, views::GridLayout::TRAILING
,
198 views::GridLayout::LEADING
);
200 layout
->AddPaddingRow(2, views::kPanelSubVerticalSpacing
);
203 SadTabView::~SadTabView() {}
205 void SadTabView::LinkClicked(views::Link
* source
, int event_flags
) {
206 DCHECK(web_contents_
);
207 OpenURLParams
params(GURL(total_crashes_
> kCrashesBeforeFeedbackIsDisplayed
?
208 chrome::kCrashReasonFeedbackDisplayedURL
:
209 chrome::kCrashReasonURL
), content::Referrer(),
210 CURRENT_TAB
, ui::PAGE_TRANSITION_LINK
, false);
211 web_contents_
->OpenURL(params
);
214 void SadTabView::ButtonPressed(views::Button
* sender
,
215 const ui::Event
& event
) {
216 DCHECK(web_contents_
);
217 DCHECK_EQ(action_button_
, sender
);
219 if (action_button_
->tag() == SAD_TAB_BUTTON_FEEDBACK
) {
220 chrome::ShowFeedbackPage(
221 chrome::FindBrowserWithWebContents(web_contents_
),
222 l10n_util::GetStringUTF8(kind_
== chrome::SAD_TAB_KIND_CRASHED
?
223 IDS_CRASHED_TAB_FEEDBACK_MESSAGE
: IDS_KILLED_TAB_FEEDBACK_MESSAGE
),
224 std::string(kCategoryTagCrash
));
226 web_contents_
->GetController().Reload(true);
230 void SadTabView::Layout() {
231 // Specify the maximum message width explicitly.
232 const int max_width
=
233 std::min(width() - views::kPanelSubVerticalSpacing
* 2, kMaxContentWidth
);
234 message_
->SizeToFit(max_width
);
235 title_
->SizeToFit(max_width
);
237 if (help_message_
!= nullptr)
238 help_message_
->SizeToFit(max_width
);
243 void SadTabView::OnPaint(gfx::Canvas
* canvas
) {
245 // These stats should use the same counting approach and bucket size used
246 // for tab discard events in memory::OomPriorityManager so they can be
247 // directly compared.
249 case chrome::SAD_TAB_KIND_CRASHED
: {
250 static int crashed
= 0;
251 UMA_HISTOGRAM_CUSTOM_COUNTS(
252 "Tabs.SadTab.CrashDisplayed", ++crashed
, 1, 1000, 50);
255 case chrome::SAD_TAB_KIND_KILLED
:
256 RecordKillDisplayed();
258 #if defined(OS_CHROMEOS)
259 case chrome::SAD_TAB_KIND_KILLED_BY_OOM
:
260 RecordKillDisplayed();
261 RecordKillDisplayedOOM();
267 View::OnPaint(canvas
);
270 void SadTabView::Show() {
271 views::Widget::InitParams
sad_tab_params(
272 views::Widget::InitParams::TYPE_CONTROL
);
274 // It is not possible to create a native_widget_win that has no parent in
275 // and later re-parent it.
276 // TODO(avi): This is a cheat. Can this be made cleaner?
277 sad_tab_params
.parent
= web_contents_
->GetNativeView();
279 set_owned_by_client();
281 views::Widget
* sad_tab
= new views::Widget
;
282 sad_tab
->Init(sad_tab_params
);
283 sad_tab
->SetContentsView(this);
285 views::Widget::ReparentNativeView(sad_tab
->GetNativeView(),
286 web_contents_
->GetNativeView());
287 gfx::Rect bounds
= web_contents_
->GetContainerBounds();
288 sad_tab
->SetBounds(gfx::Rect(bounds
.size()));
291 void SadTabView::Close() {
293 GetWidget()->Close();
296 views::Label
* SadTabView::CreateLabel(const base::string16
& text
) {
297 views::Label
* label
= new views::Label(text
);
298 label
->SetBackgroundColor(background()->get_color());
302 views::Link
* SadTabView::CreateLink(const base::string16
& text
,
303 const SkColor
& color
) {
304 views::Link
* link
= new views::Link(text
);
305 link
->SetBackgroundColor(background()->get_color());
306 link
->SetEnabledColor(color
);
307 link
->set_listener(this);
313 SadTab
* SadTab::Create(content::WebContents
* web_contents
,
315 return new SadTabView(web_contents
, kind
);
318 } // namespace chrome