Commit 26cabf35 authored by sdy's avatar sdy Committed by Commit bot

Bring the feedback button to the Mac sad tab

The Views implementation of SadTab supports two big things that were
missing in the Mac implementation:

1. The reload button turns into a feedback button on repeated crashes.
2. Histograms for tracking different kinds of crashes.

This moves that logic up into the base SadTab, so that both
implementations can use it.

It also adds new histograms to count when a sad tab with a reload button
or feedback button is displayed, and clicks on the button or help link.

Other changes:

- HISTOGRAM_ENUMERATION_WITH_FLAG uses a new base::underlying_value()
  function to cast scoped enums to their underlying integral values.
  (Unlike classic enums, they don't implicitly convert).

- The Show() and Close() methods of SadTab are gone. Show() was always
  called right after construction, and Close() was always called right
  before destruction, so I merged them with the ctor+dtor.

- SadTabController is gone.

- SadTabView no longer gets a reference to the WebContents; SadTabCocoa
  is now responsible for sizing it and adding it to the view hierarchy.

- The help link text no longer selects when you right click it.

BUG=623690
TEST=Trigger sad tabs on each platform either with chrome://crash or by
killing processes in the task manager. Check that the first sad tab
shows a reload button and subsequent sad tabs show a feedback button.
Otherwise, the look and behavior of the sad tab should be the same.

Review-Url: https://codereview.chromium.org/2261793002
Cr-Commit-Position: refs/heads/master@{#417606}
parent 7212ed1d
......@@ -2260,8 +2260,7 @@ split_static_library("ui") {
"cocoa/status_icons/status_tray_mac.mm",
"cocoa/tab_contents/chrome_web_contents_view_delegate_mac.h",
"cocoa/tab_contents/chrome_web_contents_view_delegate_mac.mm",
"cocoa/tab_contents/sad_tab_controller.h",
"cocoa/tab_contents/sad_tab_controller.mm",
"cocoa/tab_contents/sad_tab_mac.mm",
"cocoa/tab_contents/sad_tab_view_cocoa.h",
"cocoa/tab_contents/sad_tab_view_cocoa.mm",
"cocoa/tab_contents/web_drag_bookmark_handler_mac.h",
......
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_SAD_TAB_CONTROLLER_H_
#define CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_SAD_TAB_CONTROLLER_H_
#include "base/compiler_specific.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "chrome/browser/ui/sad_tab.h"
#import <Cocoa/Cocoa.h>
@class SadTabController;
@class SadTabView;
namespace chrome {
class SadTabCocoa : public SadTab {
public:
explicit SadTabCocoa(content::WebContents* web_contents);
~SadTabCocoa() override;
private:
// Overridden from SadTab:
void Show() override;
void Close() override;
base::scoped_nsobject<SadTabController> sad_tab_controller_;
content::WebContents* web_contents_;
DISALLOW_COPY_AND_ASSIGN(SadTabCocoa);
};
} // namespace chrome
// A controller class that manages the SadTabView (aka "Aw Snap" or crash page).
@interface SadTabController : NSViewController
// Designated initializer.
- (id)initWithWebContents:(content::WebContents*)webContents;
// Returns a weak reference to the WebContents whose WebContentsView created
// this SadTabController.
- (content::WebContents*)webContents;
@end
#endif // CHROME_BROWSER_UI_COCOA_TAB_CONTENTS_SAD_TAB_CONTROLLER_H_
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.h"
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
using content::OpenURLParams;
using content::Referrer;
namespace chrome {
SadTab* SadTab::Create(content::WebContents* web_contents, SadTabKind kind) {
return new SadTabCocoa(web_contents);
}
SadTabCocoa::SadTabCocoa(content::WebContents* web_contents)
: web_contents_(web_contents) {
}
SadTabCocoa::~SadTabCocoa() {
}
void SadTabCocoa::Show() {
sad_tab_controller_.reset(
[[SadTabController alloc] initWithWebContents:web_contents_]);
}
void SadTabCocoa::Close() {
[[sad_tab_controller_ view] removeFromSuperview];
}
} // namespace chrome
@interface SadTabController ()<SadTabViewDelegate>
@end
@implementation SadTabController {
content::WebContents* webContents_;
SadTabView* sadTabView_;
}
- (id)initWithWebContents:(content::WebContents*)webContents {
if ((self = [super init])) {
DCHECK(webContents);
webContents_ = webContents;
if (webContents_) { // NULL in unit_tests.
NSView* ns_view = webContents_->GetNativeView();
[[self view] setAutoresizingMask:
(NSViewWidthSizable | NSViewHeightSizable)];
[ns_view addSubview:[self view]];
[[self view] setFrame:[ns_view bounds]];
}
}
return self;
}
- (void)dealloc {
sadTabView_.delegate = nil;
[super dealloc];
}
- (void)loadView {
sadTabView_ = [[SadTabView new] autorelease];
sadTabView_.delegate = self;
[sadTabView_ setTitle:IDS_SAD_TAB_TITLE];
[sadTabView_ setMessage:IDS_SAD_TAB_MESSAGE];
[sadTabView_ setButtonTitle:IDS_SAD_TAB_RELOAD_LABEL];
[sadTabView_ setHelpLinkTitle:IDS_SAD_TAB_LEARN_MORE_LINK
URL:@(chrome::kCrashReasonURL)];
self.view = sadTabView_;
}
- (content::WebContents*)webContents {
return webContents_;
}
- (void)sadTabViewButtonClicked:(SadTabView*)sadTabView {
webContents_->GetController().Reload(true);
}
- (void)sadTabView:(SadTabView*)sadTabView
helpLinkClickedWithURL:(NSString*)url {
OpenURLParams params(GURL(url.UTF8String), Referrer(),
WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_LINK, false);
webContents_->OpenURL(params);
}
@end
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/debug/debugger.h"
#import "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.h"
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#import "ui/base/cocoa/controls/hyperlink_text_view.h"
@interface SadTabView (ExposedForTesting)
// Implementation is below.
- (HyperlinkTextView*)helpTextView;
@end
@implementation SadTabView (ExposedForTesting)
- (HyperlinkTextView*)helpTextView {
NSView* containerView = [[self subviews] lastObject];
for (NSView* view in [containerView subviews]) {
if (auto textView = base::mac::ObjCCast<HyperlinkTextView>(view))
return textView;
}
return nil;
}
@end
namespace {
class ContentsDelegate : public content::WebContentsDelegate {
public:
content::WebContents* OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) override {
opened_url_ = params.url;
return nullptr;
}
const GURL& OpenedURL() { return opened_url_; }
private:
GURL opened_url_;
};
class SadTabControllerTest : public ChromeRenderViewHostTestHarness {
public:
SadTabControllerTest() : test_window_(nil) {}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
web_contents()->SetDelegate(&contents_delegate_);
// Inherting from ChromeRenderViewHostTestHarness means we can't inherit
// from from CocoaTest, so do a bootstrap and create test window.
CocoaTest::BootstrapCocoa();
test_window_ = [[CocoaTestHelperWindow alloc] init];
if (base::debug::BeingDebugged()) {
[test_window_ orderFront:nil];
} else {
[test_window_ orderBack:nil];
}
}
void TearDown() override {
[test_window_ close];
test_window_ = nil;
ChromeRenderViewHostTestHarness::TearDown();
}
// Creates the controller and adds its view to contents, caller has ownership.
SadTabController* CreateController() {
SadTabController* controller =
[[SadTabController alloc] initWithWebContents:web_contents()];
EXPECT_TRUE(controller);
NSView* view = [controller view];
EXPECT_TRUE(view);
NSView* contentView = [test_window_ contentView];
[contentView addSubview:view];
return controller;
}
HyperlinkTextView* GetHelpTextView(SadTabController* controller) {
SadTabView* view = static_cast<SadTabView*>([controller view]);
return ([view helpTextView]);
}
ContentsDelegate contents_delegate_;
CocoaTestHelperWindow* test_window_;
};
TEST_F(SadTabControllerTest, ClickOnLink) {
base::scoped_nsobject<SadTabController> controller(CreateController());
HyperlinkTextView* help = GetHelpTextView(controller);
EXPECT_TRUE(help);
EXPECT_TRUE(contents_delegate_.OpenedURL().is_empty());
[help clickedOnLink:@(chrome::kCrashReasonURL) atIndex:0];
EXPECT_EQ(contents_delegate_.OpenedURL(), GURL(chrome::kCrashReasonURL));
}
} // namespace
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h"
#include "chrome/browser/ui/sad_tab.h"
#include "content/public/browser/web_contents.h"
namespace {
class SadTabCocoa : public chrome::SadTab {
public:
SadTabCocoa(content::WebContents* web_contents, chrome::SadTabKind kind)
: SadTab(web_contents, kind) {
NSView* web_contents_view = web_contents->GetNativeView();
sad_tab_view_ =
[[SadTabView alloc] initWithFrame:web_contents_view.bounds sadTab:this];
[web_contents_view addSubview:sad_tab_view_];
[sad_tab_view_ release];
}
~SadTabCocoa() override { [sad_tab_view_ removeFromSuperview]; }
private:
// Owned by web_contents
SadTabView* sad_tab_view_;
};
} // namespace
chrome::SadTab* chrome::SadTab::Create(content::WebContents* web_contents,
SadTabKind kind) {
return new SadTabCocoa(web_contents, kind);
}
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h"
#import "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "ui/base/cocoa/controls/hyperlink_text_view.h"
namespace {
template <typename T>
T* FindSadTabSubview(NSView* sadTabView) {
NSView* containerView = sadTabView.subviews.firstObject;
for (NSView* view in [containerView subviews]) {
if (auto subview = base::mac::ObjCCast<T>(view))
return subview;
}
return nil;
}
class SadTabViewTest : public CocoaTest {
public:
SadTabViewTest() {
base::scoped_nsobject<SadTabView> view([SadTabView new]);
view_ = view;
[[test_window() contentView] addSubview:view_];
}
SadTabView* view_; // Weak. Owned by the view hierarchy.
};
TEST_VIEW(SadTabViewTest, view_);
TEST(SadTabViewBehaviorTest, ClickOnLinks) {
using chrome::SadTab;
using Action = SadTab::Action;
class MockSadTab : public chrome::SadTab {
public:
MockSadTab() : SadTab(nullptr, chrome::SAD_TAB_KIND_CRASHED) {}
MOCK_METHOD0(RecordFirstPaint, void());
MOCK_METHOD1(PerformAction, void(Action));
};
MockSadTab sadTab;
base::scoped_nsobject<SadTabView> view(
[[SadTabView alloc] initWithFrame:NSZeroRect sadTab:&sadTab]);
EXPECT_CALL(sadTab, RecordFirstPaint());
EXPECT_CALL(sadTab, PerformAction(testing::TypedEq<Action>(Action::BUTTON)));
EXPECT_CALL(sadTab,
PerformAction(testing::TypedEq<Action>(Action::HELP_LINK)));
[view displayIfNeeded];
[FindSadTabSubview<NSButton>(view) performClick:nil];
[FindSadTabSubview<HyperlinkTextView>(view) clickedOnLink:[NSNull null]
atIndex:0];
}
} // namespace
......@@ -7,25 +7,12 @@
#import <Cocoa/Cocoa.h>
#import "ui/base/cocoa/controls/hyperlink_text_view.h"
@class SadTabView;
@protocol SadTabViewDelegate
- (void)sadTabViewButtonClicked:(SadTabView*)sadTabView;
- (void)sadTabView:(SadTabView*)sadTabView
helpLinkClickedWithURL:(NSString*)url;
@end
#include "chrome/browser/ui/sad_tab.h"
// A view that displays the "sad tab" (aka crash page).
@interface SadTabView : NSView
@property(nonatomic, assign) id<SadTabViewDelegate> delegate;
- (void)setTitle:(int)title;
- (void)setMessage:(int)message;
- (void)setButtonTitle:(int)buttonTitle;
- (void)setHelpLinkTitle:(int)helpLinkTitle URL:(NSString*)url;
- (instancetype)initWithFrame:(NSRect)frame sadTab:(chrome::SadTab*)sadTab;
@end
......
......@@ -8,6 +8,7 @@
#include "components/grit/components_scaled_resources.h"
#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
#import "ui/base/cocoa/controls/blue_label_button.h"
#import "ui/base/cocoa/controls/hyperlink_text_view.h"
#import "ui/base/cocoa/nscolor_additions.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/resource/resource_bundle.h"
......@@ -30,8 +31,9 @@ const CGFloat kTabMargin = 13;
const CGFloat kMaxTopMargin = 130;
NSTextField* MakeLabelTextField(NSRect frame) {
// Not a scoped_nsobject for easy property access.
NSTextField* ret = [[[NSTextField alloc] initWithFrame:frame] autorelease];
ret.autoresizingMask = NSViewWidthSizable | NSViewMaxYMargin;
ret.autoresizingMask = NSViewWidthSizable;
ret.editable = NO;
ret.drawsBackground = NO;
ret.bezeled = NO;
......@@ -54,17 +56,16 @@ NSTextField* MakeLabelTextField(NSRect frame) {
@implementation SadTabView {
NSView* container_;
NSImageView* icon_;
NSTextField* title_;
NSTextField* message_;
HyperlinkTextView* help_;
NSButton* button_;
chrome::SadTab* sadTab_;
}
@synthesize delegate = delegate_;
- (instancetype)initWithFrame:(NSRect)frame {
- (instancetype)initWithFrame:(NSRect)frame sadTab:(chrome::SadTab*)sadTab {
if ((self = [super initWithFrame:frame])) {
sadTab_ = sadTab;
self.wantsLayer = YES;
self.layer.backgroundColor =
[NSColor colorWithCalibratedWhite:245.0f / 255.0f alpha:1.0].cr_CGColor;
......@@ -73,51 +74,85 @@ NSTextField* MakeLabelTextField(NSRect frame) {
NSImage* iconImage = ResourceBundle::GetSharedInstance()
.GetNativeImageNamed(IDR_CRASH_SAD_TAB)
.ToNSImage();
icon_ = [[NSImageView new] autorelease];
icon_.image = iconImage;
icon_.frameSize = iconImage.size;
icon_.autoresizingMask = NSViewMaxXMargin | NSViewMaxYMargin;
[container_ addSubview:icon_];
CGFloat width = self.bounds.size.width;
title_ = MakeLabelTextField(
NSMakeRect(0, NSMaxY(icon_.frame) + kIconTitleSpacing, width, 0));
title_.font = [NSFont systemFontOfSize:24];
title_.textColor =
NSImageView* icon = [[NSImageView new] autorelease];
icon.image = iconImage;
icon.frameSize = iconImage.size;
[container_ addSubview:icon];
NSTextField* title = MakeLabelTextField(
NSMakeRect(0, NSMaxY(icon.frame) + kIconTitleSpacing, 0, 0));
title.font = [NSFont systemFontOfSize:24];
title.textColor =
[NSColor colorWithCalibratedWhite:38.0f / 255.0f alpha:1.0];
[title_ sizeToFit];
[container_ addSubview:title_];
message_ =
MakeLabelTextField(NSMakeRect(0, NSMaxY(title_.frame), width, 0));
base::mac::ObjCCast<NSCell>(message_.cell).wraps = YES;
message_.font = [NSFont systemFontOfSize:14];
message_.textColor =
NSMutableParagraphStyle* titleStyle =
[[NSMutableParagraphStyle new] autorelease];
titleStyle.lineSpacing = 6;
title.attributedStringValue = [[[NSAttributedString alloc]
initWithString:l10n_util::GetNSString(sadTab->GetTitle())
attributes:@{NSParagraphStyleAttributeName : titleStyle}]
autorelease];
[title sizeToFit];
[container_ addSubview:title];
NSFont* messageFont = [NSFont systemFontOfSize:14];
NSColor* messageColor =
[NSColor colorWithCalibratedWhite:81.0f / 255.0f alpha:1.0];
message_ = MakeLabelTextField(NSMakeRect(0, NSMaxY(title.frame), 0, 0));
message_.frameOrigin =
NSMakePoint(0, NSMaxY(title.frame) + kTitleMessageSpacing);
base::mac::ObjCCast<NSCell>(message_.cell).wraps = YES;
message_.font = messageFont;
message_.textColor = messageColor;
message_.stringValue = l10n_util::GetNSString(sadTab->GetMessage());
[container_ addSubview:message_];
NSString* helpLinkTitle =
l10n_util::GetNSString(sadTab->GetHelpLinkTitle());
help_ = [[[HyperlinkTextView alloc]
initWithFrame:NSMakeRect(0, 0, 1, message_.font.pointSize + 4)]
autorelease];
help_.delegate = self;
help_.autoresizingMask = NSViewWidthSizable | NSViewMaxYMargin;
help_.autoresizingMask = NSViewWidthSizable;
help_.textContainer.lineFragmentPadding = 2; // To align with message_.
[help_ setMessage:helpLinkTitle
withFont:messageFont
messageColor:messageColor];
[help_ addLinkRange:NSMakeRange(0, helpLinkTitle.length)
withURL:@(sadTab->GetHelpLinkURL())
linkColor:messageColor];
[help_ sizeToFit];
[container_ addSubview:help_];
button_ = [[BlueLabelButton new] autorelease];
button_.target = self;
button_.action = @selector(buttonClicked);
button_.title = l10n_util::GetNSString(sadTab->GetButtonTitle());
[button_ sizeToFit];
[container_ addSubview:button_];
[self addSubview:container_];
[self resizeSubviewsWithOldSize:self.bounds.size];
}
return self;
}
- (BOOL)isOpaque {
return YES;
}
- (BOOL)isFlipped {
return YES;
}
- (void)updateLayer {
// Currently, updateLayer is only called once. If that changes, a DCHECK in
// SadTab::RecordFirstPaint will pipe up and we should add a guard here.
sadTab_->RecordFirstPaint();
}
- (void)resizeSubviewsWithOldSize:(NSSize)oldSize {
[super resizeSubviewsWithOldSize:oldSize];
......@@ -131,8 +166,6 @@ NSTextField* MakeLabelTextField(NSRect frame) {
// |message_| can wrap to variable number of lines.
[GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:message_];
message_.frameOrigin =
NSMakePoint(0, NSMaxY(title_.frame) + kTitleMessageSpacing);
help_.frameOrigin =
NSMakePoint(0, NSMaxY(message_.frame) + kMessageLinkSpacing);
......@@ -151,45 +184,22 @@ NSTextField* MakeLabelTextField(NSRect frame) {
size.height - containerSize.height - kTabMargin)));
}
- (void)setTitle:(int)title {
NSMutableParagraphStyle* style = [[NSMutableParagraphStyle new] autorelease];
style.lineSpacing = 6;
title_.attributedStringValue = [[[NSAttributedString alloc]
initWithString:l10n_util::GetNSString(title)
attributes:@{NSParagraphStyleAttributeName : style}] autorelease];
}
- (void)setMessage:(int)message {
message_.stringValue = l10n_util::GetNSString(message);
}
- (void)setButtonTitle:(int)buttonTitle {
button_.title = l10n_util::GetNSString(buttonTitle);
[button_ sizeToFit];
}
- (void)setHelpLinkTitle:(int)helpLinkTitle URL:(NSString*)url {
NSString* title = l10n_util::GetNSString(helpLinkTitle);
[help_ setMessage:title
withFont:message_.font
messageColor:message_.textColor];
[help_ addLinkRange:NSMakeRange(0, title.length)
withURL:url
linkColor:message_.textColor];
[help_ sizeToFit];
}
- (void)buttonClicked {
[delegate_ sadTabViewButtonClicked:self];
sadTab_->PerformAction(chrome::SadTab::Action::BUTTON);
}
// Called when someone clicks on the embedded link.
- (BOOL)textView:(NSTextView*)textView
clickedOnLink:(id)link
atIndex:(NSUInteger)charIndex {
[delegate_ sadTabView:self helpLinkClickedWithURL:(NSString*)link];
sadTab_->PerformAction(chrome::SadTab::Action::HELP_LINK);
return YES;
}
- (NSRange)textView:(NSTextView*)textView
willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange
toCharacterRange:(NSRange)newSelectedCharRange {
return NSMakeRange(0, 0);
}
@end
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_view_cocoa.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
namespace {
class SadTabViewTest : public CocoaTest {
public:
SadTabViewTest() {
NSRect content_frame = [[test_window() contentView] frame];
base::scoped_nsobject<SadTabView> view(
[[SadTabView alloc] initWithFrame:content_frame]);
view_ = view.get();
[[test_window() contentView] addSubview:view_];
}
SadTabView* view_; // Weak. Owned by the view hierarchy.
};
TEST_VIEW(SadTabViewTest, view_);
} // namespace
......@@ -2,20 +2,210 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "build/build_config.h"
#include "chrome/browser/ui/sad_tab.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/net/referrer.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/feedback/feedback_util.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/memory/oom_memory_details.h"
#endif
namespace {
// These stats should use the same counting approach and bucket size as tab
// discard events in memory::OomPriorityManager so they can be directly
// compared.
// This macro uses a static counter to track how many times it's hit in a
// session. See Tabs.SadTab.CrashCreated in histograms.xml for details.
#define UMA_SAD_TAB_COUNTER(histogram_name) \
{ \
static int count = 0; \
++count; \
UMA_HISTOGRAM_COUNTS_1000(histogram_name, count); \
}
// This enum backs an UMA histogram, so it should be treated as append-only.
enum SadTabEvent {
DISPLAYED,
BUTTON_CLICKED,
HELP_LINK_CLICKED,
MAX_SAD_TAB_EVENT
};
void RecordEvent(bool feedback, SadTabEvent event) {
if (feedback) {
UMA_HISTOGRAM_ENUMERATION("Tabs.SadTab.Feedback.Event", event,
SadTabEvent::MAX_SAD_TAB_EVENT);
} else {
UMA_HISTOGRAM_ENUMERATION("Tabs.SadTab.Reload.Event", event,
SadTabEvent::MAX_SAD_TAB_EVENT);
}
}
const char kCategoryTagCrash[] = "Crash";
bool ShouldShowFeedbackButton() {
#if defined(GOOGLE_CHROME_BUILD)
const int kCrashesBeforeFeedbackIsDisplayed = 1;
static int total_crashes = 0;
return ++total_crashes > kCrashesBeforeFeedbackIsDisplayed;
#else
return false;
#endif
}
} // namespace
namespace chrome {
// static
bool SadTab::ShouldShow(base::TerminationStatus status) {
return (status == base::TERMINATION_STATUS_ABNORMAL_TERMINATION ||
status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED ||
switch (status) {
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
#if defined(OS_CHROMEOS)
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
#endif
case base::TERMINATION_STATUS_PROCESS_CRASHED:
case base::TERMINATION_STATUS_OOM:
return true;
case base::TERMINATION_STATUS_NORMAL_TERMINATION:
case base::TERMINATION_STATUS_STILL_RUNNING:
#if defined(OS_ANDROID)
case base::TERMINATION_STATUS_OOM_PROTECTED:
#endif
case base::TERMINATION_STATUS_LAUNCH_FAILED:
case base::TERMINATION_STATUS_MAX_ENUM:
return false;
}
NOTREACHED();
return false;
}
int SadTab::GetTitle() {
return IDS_SAD_TAB_TITLE;
}
int SadTab::GetMessage() {
switch (kind_) {
#if defined(OS_CHROMEOS)
case chrome::SAD_TAB_KIND_KILLED_BY_OOM:
return IDS_KILLED_TAB_BY_OOM_MESSAGE;
#endif
case chrome::SAD_TAB_KIND_OOM:
return IDS_SAD_TAB_OOM_MESSAGE;
case chrome::SAD_TAB_KIND_CRASHED:
case chrome::SAD_TAB_KIND_KILLED:
return IDS_SAD_TAB_MESSAGE;
}
NOTREACHED();
return 0;
}
int SadTab::GetButtonTitle() {
return show_feedback_button_ ? IDS_CRASHED_TAB_FEEDBACK_LINK
: IDS_SAD_TAB_RELOAD_LABEL;
}
int SadTab::GetHelpLinkTitle() {
return IDS_SAD_TAB_LEARN_MORE_LINK;
}
const char* SadTab::GetHelpLinkURL() {
return show_feedback_button_ ? chrome::kCrashReasonFeedbackDisplayedURL
: chrome::kCrashReasonURL;
}
void SadTab::RecordFirstPaint() {
DCHECK(!recorded_paint_);
recorded_paint_ = true;
switch (kind_) {
case chrome::SAD_TAB_KIND_CRASHED:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.CrashDisplayed");
break;
case chrome::SAD_TAB_KIND_OOM:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.OomDisplayed");
break;
#if defined(OS_CHROMEOS)
case chrome::SAD_TAB_KIND_KILLED_BY_OOM:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.KillDisplayed.OOM");
#endif
// Fallthrough
case chrome::SAD_TAB_KIND_KILLED:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.KillDisplayed");
break;
}
RecordEvent(show_feedback_button_, SadTabEvent::DISPLAYED);
}
void SadTab::PerformAction(SadTab::Action action) {
DCHECK(recorded_paint_);
switch (action) {
case Action::BUTTON:
RecordEvent(show_feedback_button_, SadTabEvent::BUTTON_CLICKED);
if (show_feedback_button_) {
chrome::ShowFeedbackPage(
chrome::FindBrowserWithWebContents(web_contents_),
l10n_util::GetStringUTF8(kind_ == chrome::SAD_TAB_KIND_CRASHED
? IDS_CRASHED_TAB_FEEDBACK_MESSAGE
: IDS_KILLED_TAB_FEEDBACK_MESSAGE),
std::string(kCategoryTagCrash));
} else {
web_contents_->GetController().Reload(true);
}
break;
case Action::HELP_LINK:
RecordEvent(show_feedback_button_, SadTabEvent::HELP_LINK_CLICKED);
content::OpenURLParams params(GURL(GetHelpLinkURL()), content::Referrer(),
WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_LINK, false);
web_contents_->OpenURL(params);
break;
}
}
SadTab::SadTab(content::WebContents* web_contents, SadTabKind kind)
: web_contents_(web_contents),
kind_(kind),
show_feedback_button_(ShouldShowFeedbackButton()),
recorded_paint_(false) {
switch (kind) {
case chrome::SAD_TAB_KIND_CRASHED:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.CrashCreated");
break;
case chrome::SAD_TAB_KIND_OOM:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.OomCreated");
break;
#if defined(OS_CHROMEOS)
status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM ||
case chrome::SAD_TAB_KIND_KILLED_BY_OOM:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.KillCreated.OOM");
{
const std::string spec = web_contents->GetURL().GetOrigin().spec();
memory::OomMemoryDetails::Log(
"Tab OOM-Killed Memory details: " + spec + ", ", base::Closure());
}
#endif
status == base::TERMINATION_STATUS_PROCESS_CRASHED ||
status == base::TERMINATION_STATUS_OOM);
// Fall through
case chrome::SAD_TAB_KIND_KILLED:
UMA_SAD_TAB_COUNTER("Tabs.SadTab.KillCreated");
LOG(WARNING) << "Tab Killed: "
<< web_contents->GetURL().GetOrigin().spec();
break;
}
}
} // namespace chrome
......@@ -17,6 +17,11 @@ namespace chrome {
// Cross-platform interface to show the Sad tab UI.
class SadTab {
public:
enum class Action {
BUTTON,
HELP_LINK,
};
// Factory function to create the platform specific implementations.
static SadTab* Create(content::WebContents* web_contents, SadTabKind kind);
......@@ -25,11 +30,32 @@ class SadTab {
virtual ~SadTab() {}
// Shows the Sad tab.
virtual void Show() = 0;
// These functions return resource string IDs for UI text. They may be
// different for each sad tab. (Right now, the first sad tab in a session
// suggests reloading and subsequent ones suggest sending feedback.)
int GetTitle();
int GetMessage();
int GetButtonTitle();
int GetHelpLinkTitle();
// Returns the target of the "Learn more" link. Use it for the context menu
// and to show the URL on hover, but call PerformAction() for regular clicks.
const char* GetHelpLinkURL();
// Virtual for testing.
virtual void RecordFirstPaint();
virtual void PerformAction(Action);
protected:
SadTab(content::WebContents* web_contents, SadTabKind kind);
private:
content::WebContents* web_contents_;
SadTabKind kind_;
bool show_feedback_button_;
bool recorded_paint_;
// Closes the Sad tab.
virtual void Close() = 0;
DISALLOW_COPY_AND_ASSIGN(SadTab);
};
} // namespace chrome
......
......@@ -41,10 +41,7 @@ SadTabHelper::SadTabHelper(content::WebContents* web_contents)
}
void SadTabHelper::RenderViewReady() {
if (sad_tab_) {
sad_tab_->Close();
sad_tab_.reset();
}
sad_tab_.reset();
}
void SadTabHelper::RenderProcessGone(base::TerminationStatus status) {
......@@ -64,5 +61,4 @@ void SadTabHelper::RenderProcessGone(base::TerminationStatus status) {
void SadTabHelper::InstallSadTab(base::TerminationStatus status) {
sad_tab_.reset(chrome::SadTab::Create(
web_contents(), SadTabKindFromTerminationStatus(status)));
sad_tab_->Show();
}
This diff is collapsed.
......@@ -5,8 +5,6 @@
#ifndef CHROME_BROWSER_UI_VIEWS_SAD_TAB_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_SAD_TAB_VIEW_H_
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "chrome/browser/ui/sad_tab.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/link_listener.h"
......@@ -36,12 +34,6 @@ class SadTabView : public chrome::SadTab,
public views::LinkListener,
public views::ButtonListener {
public:
// Tag to denote which type of action button is displayed.
enum ButtonTag {
SAD_TAB_BUTTON_FEEDBACK,
SAD_TAB_BUTTON_RELOAD,
};
SadTabView(content::WebContents* web_contents, chrome::SadTabKind kind);
~SadTabView() override;
......@@ -59,24 +51,15 @@ class SadTabView : public chrome::SadTab,
void OnPaint(gfx::Canvas* canvas) override;
private:
// Overridden from chrome::SadTab:
void Show() override;
void Close() override;
views::Label* CreateLabel(const base::string16& text);
views::Link* CreateLink(const base::string16& text, const SkColor& color);
content::WebContents* web_contents_;
chrome::SadTabKind kind_;
bool painted_;
bool painted_ = false;
views::Label* message_;
views::Link* help_link_;
views::LabelButton* action_button_;
views::Label* title_;
views::StyledLabel* help_message_;
static int total_crashes_;
DISALLOW_COPY_AND_ASSIGN(SadTabView);
};
#endif // CHROME_BROWSER_UI_VIEWS_SAD_TAB_VIEW_H__
......@@ -4505,8 +4505,7 @@ test("unit_tests") {
"../browser/ui/cocoa/styled_text_field_test_helper.h",
"../browser/ui/cocoa/styled_text_field_test_helper.mm",
"../browser/ui/cocoa/styled_text_field_unittest.mm",
"../browser/ui/cocoa/tab_contents/sad_tab_controller_unittest.mm",
"../browser/ui/cocoa/tab_contents/sad_tab_view_cocoa_unittest.mm",
"../browser/ui/cocoa/tab_contents/sad_tab_mac_unittest.mm",
"../browser/ui/cocoa/tabs/alert_indicator_button_cocoa_unittest.mm",
"../browser/ui/cocoa/tabs/tab_controller_unittest.mm",
"../browser/ui/cocoa/tabs/tab_strip_controller_unittest.mm",
......
......@@ -61773,6 +61773,15 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary>
</histogram>
<histogram name="Tabs.SadTab.Feedback.Event" enum="SadTabEvent">
<owner>sdy@chromium.org</owner>
<summary>
Counts of events from the style of sad tab which has a feedback button as
its primary action. Currently, events include being displayed (actually
visible in a window), and the actions a user can take on the page.
</summary>
</histogram>
<histogram name="Tabs.SadTab.KillCreated" units="tabs">
<owner>jamescook@chromium.org</owner>
<summary>
......@@ -61846,6 +61855,14 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary>
</histogram>
<histogram name="Tabs.SadTab.Reload.Event" enum="SadTabEvent">
<owner>sdy@chromium.org</owner>
<summary>
Counts of events from the style of sad tab which has a reload button as its
primary action. Compare to Tabs.SadTab.Feedback.Event.
</summary>
</histogram>
<histogram name="Tabs.SadTab.ReloadCount" units="tabs">
<owner>jamescook@chromium.org</owner>
<summary>
......@@ -92834,6 +92851,12 @@ To add a new entry, add it with any value and run test to compute valid value.
<int value="1" label="Tabbed Mode"/>
</enum>
<enum name="SadTabEvent" type="int">
<int value="0" label="Displayed"/>
<int value="1" label="Button clicked"/>
<int value="2" label="Learn more clicked"/>
</enum>
<enum name="SadTabKind" type="int">
<int value="0" label="Crash (Aw, Snap!)"/>
<int value="1" label="Kill (He's dead, Jim!)"/>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment