summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r--chrome/browser/cocoa/browser_window_controller.mm17
-rw-r--r--chrome/browser/cocoa/sad_tab_controller.h32
-rw-r--r--chrome/browser/cocoa/sad_tab_controller.mm48
-rw-r--r--chrome/browser/cocoa/sad_tab_controller_unittest.mm101
-rw-r--r--chrome/browser/cocoa/sad_tab_view.h15
-rw-r--r--chrome/browser/cocoa/sad_tab_view.mm168
6 files changed, 318 insertions, 63 deletions
diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm
index 43416bd..3af0816 100644
--- a/chrome/browser/cocoa/browser_window_controller.mm
+++ b/chrome/browser/cocoa/browser_window_controller.mm
@@ -36,6 +36,7 @@
#include "chrome/browser/cocoa/find_bar_bridge.h"
#import "chrome/browser/cocoa/fullscreen_window.h"
#import "chrome/browser/cocoa/infobar_container_controller.h"
+#import "chrome/browser/cocoa/sad_tab_controller.h"
#import "chrome/browser/cocoa/status_bubble_mac.h"
#import "chrome/browser/cocoa/tab_strip_model_observer_bridge.h"
#import "chrome/browser/cocoa/tab_strip_view.h"
@@ -48,6 +49,7 @@
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#import "third_party/GTM/AppKit/GTMTheme.h"
@@ -1337,6 +1339,21 @@ willPositionSheet:(NSWindow*)sheet
}
}
+// Handle the openLearnMoreAboutCrashLink: action from SadTabController when
+// "Learn more" link in "Aw snap" page (i.e. crash page or sad tab) is
+// clicked. Decoupling the action from its target makes unitestting possible.
+- (void)openLearnMoreAboutCrashLink:(id)sender {
+ if ([sender isKindOfClass:[SadTabController class]]) {
+ SadTabController* sad_tab = static_cast<SadTabController*>(sender);
+ TabContents* tab_contents = [sad_tab tabContents];
+ if (tab_contents) {
+ std::string linkUrl = l10n_util::GetStringUTF8(IDS_CRASH_REASON_URL);
+ tab_contents->OpenURL(GURL(linkUrl), GURL(), CURRENT_TAB,
+ PageTransition::LINK);
+ }
+ }
+}
+
// Delegate method called when window did move. (See below for why we don't use
// |-windowWillMove:|, which is called less frequently than |-windowDidMove|
// instead.)
diff --git a/chrome/browser/cocoa/sad_tab_controller.h b/chrome/browser/cocoa/sad_tab_controller.h
new file mode 100644
index 0000000..148729a
--- /dev/null
+++ b/chrome/browser/cocoa/sad_tab_controller.h
@@ -0,0 +1,32 @@
+// 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.
+
+#ifndef CHROME_BROWSER_COCOA_SAD_TAB_CONTROLLER_H_
+#define CHROME_BROWSER_COCOA_SAD_TAB_CONTROLLER_H_
+
+#import <Cocoa/Cocoa.h>
+
+class TabContents;
+
+// A controller class that manages the SadTabView (aka "Aw Snap" or crash page).
+@interface SadTabController : NSViewController {
+ @private
+ TabContents* tabContents_; // Weak reference.
+}
+
+// Designated initializer is initWithTabContents.
+- (id)initWithTabContents:(TabContents*)someTabContents
+ superview:(NSView*)superview;
+
+// This action just calls the NSApp sendAction to get it into the standard
+// Cocoa action processing.
+- (IBAction)openLearnMoreAboutCrashLink:(id)sender;
+
+// Returns a weak reference to the TabContents whose TabContentsView created
+// this SadTabController.
+- (TabContents*)tabContents;
+
+@end
+
+#endif // CHROME_BROWSER_COCOA_SAD_TAB_CONTROLLER_H_
diff --git a/chrome/browser/cocoa/sad_tab_controller.mm b/chrome/browser/cocoa/sad_tab_controller.mm
new file mode 100644
index 0000000..c0c832a
--- /dev/null
+++ b/chrome/browser/cocoa/sad_tab_controller.mm
@@ -0,0 +1,48 @@
+// 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.
+
+#include "chrome/browser/cocoa/sad_tab_controller.h"
+
+#include "base/mac_util.h"
+#import "chrome/browser/cocoa/sad_tab_view.h"
+
+@implementation SadTabController
+
+- (id)initWithTabContents:(TabContents*)someTabContents
+ superview:(NSView*)superview {
+ if ((self = [super initWithNibName:@"SadTab"
+ bundle:mac_util::MainAppBundle()])) {
+ tabContents_ = someTabContents;
+
+ NSView* view = [self view];
+ [superview addSubview:view];
+ [view setFrame:[superview bounds]];
+ }
+
+ return self;
+}
+
+- (void)awakeFromNib {
+ // If tab_contents_ is nil, ask view to remove link.
+ if (!tabContents_) {
+ SadTabView* sad_view = static_cast<SadTabView*>([self view]);
+ [sad_view removeLinkButton];
+ }
+}
+
+- (void)dealloc {
+ [[self view] removeFromSuperview];
+ [super dealloc];
+}
+
+- (TabContents*)tabContents {
+ return tabContents_;
+}
+
+- (void)openLearnMoreAboutCrashLink:(id)sender {
+ // Send the action up through the responder chain.
+ [NSApp sendAction:@selector(openLearnMoreAboutCrashLink:) to:nil from:self];
+}
+
+@end
diff --git a/chrome/browser/cocoa/sad_tab_controller_unittest.mm b/chrome/browser/cocoa/sad_tab_controller_unittest.mm
new file mode 100644
index 0000000..9395cbb
--- /dev/null
+++ b/chrome/browser/cocoa/sad_tab_controller_unittest.mm
@@ -0,0 +1,101 @@
+// 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.
+
+#include "base/scoped_nsobject.h"
+#include "chrome/browser/cocoa/browser_test_helper.h"
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#import "chrome/browser/cocoa/sad_tab_controller.h"
+#import "chrome/browser/cocoa/sad_tab_view.h"
+#include "chrome/browser/renderer_host/site_instance.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+
+@interface SadTabView (ExposedForTesting)
+// Implementation is below.
+- (NSButton*)linkButton;
+@end
+
+@implementation SadTabView (ExposedForTesting)
+- (NSButton*)linkButton {
+ return linkButton_;
+}
+@end
+
+namespace {
+
+class SadTabControllerTest : public CocoaTest {
+ public:
+ SadTabControllerTest() {
+ link_clicked_ = false;
+ }
+
+ TabContents* CreateTabContents() {
+ SiteInstance* instance =
+ SiteInstance::CreateSiteInstance(browser_helper_.profile());
+ TabContents* tab_contents = new TabContents(browser_helper_.profile(),
+ instance, MSG_ROUTING_NONE, NULL);
+ return tab_contents;
+ }
+
+ // Creates the controller and adds its view to contents, caller has ownership.
+ SadTabController* CreateController(TabContents* tab_contents) {
+ NSView* contentView = [test_window() contentView];
+ SadTabController* controller =
+ [[SadTabController alloc] initWithTabContents:tab_contents
+ superview:contentView];
+ EXPECT_TRUE(controller);
+ NSView* view = [controller view];
+ EXPECT_TRUE(view);
+
+ return controller;
+ }
+
+ NSButton* GetLinkButton(SadTabController* controller) {
+ SadTabView* view = static_cast<SadTabView*>([controller view]);
+ return ([view linkButton]);
+ }
+
+ BrowserTestHelper browser_helper_;
+ static bool link_clicked_;
+};
+
+TEST_F(SadTabControllerTest, TestWithTabContents) {
+ scoped_ptr<TabContents> tab_contents(CreateTabContents());
+ scoped_nsobject<SadTabController>
+ controller(CreateController(tab_contents.get()));
+ EXPECT_TRUE(controller);
+ NSButton* link = GetLinkButton(controller);
+ EXPECT_TRUE(link);
+}
+
+TEST_F(SadTabControllerTest, TestWithoutTabContents) {
+ scoped_nsobject<SadTabController> controller(CreateController(NULL));
+ EXPECT_TRUE(controller);
+ NSButton* link = GetLinkButton(controller);
+ EXPECT_FALSE(link);
+}
+
+TEST_F(SadTabControllerTest, TestClickOnLink) {
+ scoped_ptr<TabContents> tab_contents(CreateTabContents());
+ scoped_nsobject<SadTabController>
+ controller(CreateController(tab_contents.get()));
+ NSButton* link = GetLinkButton(controller);
+ EXPECT_TRUE(link);
+ EXPECT_FALSE(link_clicked_);
+ [link performClick:link];
+ EXPECT_TRUE(link_clicked_);
+}
+
+} // namespace
+
+@implementation NSApplication (SadTabControllerUnitTest)
+// Add handler for the openLearnMoreAboutCrashLink: action to NSApp for testing
+// purposes. Normally this would be sent up the responder tree correctly, but
+// since tests run in the background, key window and main window are never set
+// on NSApplication. Adding it to NSApplication directly removes the need for
+// worrying about what the current window with focus is.
+- (void)openLearnMoreAboutCrashLink:(id)sender {
+ SadTabControllerTest::link_clicked_ = true;
+}
+
+@end
diff --git a/chrome/browser/cocoa/sad_tab_view.h b/chrome/browser/cocoa/sad_tab_view.h
index d003536..f89e679 100644
--- a/chrome/browser/cocoa/sad_tab_view.h
+++ b/chrome/browser/cocoa/sad_tab_view.h
@@ -5,18 +5,31 @@
#ifndef CHROME_BROWSER_COCOA_SAD_TAB_VIEW_H_
#define CHROME_BROWSER_COCOA_SAD_TAB_VIEW_H_
+#include "base/scoped_nsobject.h"
#include "chrome/browser/cocoa/base_view.h"
#import <Cocoa/Cocoa.h>
-// A view that displays the "sad tab".
+@class HyperlinkButtonCell;
+// A view that displays the "sad tab" (aka crash page).
@interface SadTabView : BaseView {
@private
+ IBOutlet NSImageView* image_;
+ IBOutlet NSTextField* title_;
+ IBOutlet NSTextField* message_;
+ IBOutlet NSButton* linkButton_;
+ IBOutlet HyperlinkButtonCell* linkCell_;
+
+ scoped_nsobject<NSColor> backgroundColor_;
+ NSSize messageSize_;
}
// Designated initializer is -initWithFrame: .
+// Called by SadTabController to remove link button.
+- (void)removeLinkButton;
+
@end
#endif // CHROME_BROWSER_COCOA_SAD_TAB_VIEW_H_
diff --git a/chrome/browser/cocoa/sad_tab_view.mm b/chrome/browser/cocoa/sad_tab_view.mm
index 1143025..ae018f5 100644
--- a/chrome/browser/cocoa/sad_tab_view.mm
+++ b/chrome/browser/cocoa/sad_tab_view.mm
@@ -4,78 +4,122 @@
#include "chrome/browser/cocoa/sad_tab_view.h"
-#include "app/l10n_util_mac.h"
#include "app/resource_bundle.h"
-#include "base/sys_string_conversions.h"
-#include "grit/generated_resources.h"
+#include "base/logging.h"
+#import "chrome/browser/cocoa/hyperlink_button_cell.h"
#include "grit/theme_resources.h"
-
-static const int kSadTabOffset = -64;
-static const int kIconTitleSpacing = 20;
-static const int kTitleMessageSpacing = 15;
+#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
+
+// Offset above vertical middle of page where contents of page start.
+static const CGFloat kSadTabOffset = -64;
+// Padding between icon and title.
+static const CGFloat kIconTitleSpacing = 20;
+// Padding between title and message.
+static const CGFloat kTitleMessageSpacing = 15;
+// Padding between message and link.
+static const CGFloat kMessageLinkSpacing = 15;
+// Paddings on left and right of page.
+static const CGFloat kTabHorzMargin = 13;
@implementation SadTabView
-- (void)drawRect:(NSRect)dirtyRect {
+- (void)awakeFromNib {
+ // Load resource for image and set it.
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- NSImage* sadTabImage = rb.GetNSImageNamed(IDR_SAD_TAB);
- DCHECK(sadTabImage);
- NSString* title = l10n_util::GetNSStringWithFixup(IDS_SAD_TAB_TITLE);
- NSString* message = l10n_util::GetNSStringWithFixup(IDS_SAD_TAB_MESSAGE);
-
- NSColor* textColor = [NSColor whiteColor];
- NSColor* backgroundColor = [NSColor colorWithCalibratedRed:(35.0f/255.0f)
- green:(48.0f/255.0f)
- blue:(64.0f/255.0f)
- alpha:1.0];
-
- // Layout
+ NSImage* image = rb.GetNSImageNamed(IDR_SAD_TAB);
+ DCHECK(image);
+ [image_ setImage:image];
+
+ // Set font for title.
NSFont* titleFont = [NSFont boldSystemFontOfSize:[NSFont systemFontSize]];
+ [title_ setFont:titleFont];
+
+ // Set font for message.
NSFont* messageFont = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
+ [message_ setFont:messageFont];
+
+ // If necessary, set font and color for link.
+ if (linkButton_) {
+ [linkButton_ setFont:messageFont];
+ [linkCell_ setTextColor:[NSColor whiteColor]];
+ }
+
+ // Initialize background color.
+ NSColor* backgroundColor = [[NSColor colorWithCalibratedRed:(35.0f/255.0f)
+ green:(48.0f/255.0f)
+ blue:(64.0f/255.0f)
+ alpha:1.0] retain];
+ backgroundColor_.reset(backgroundColor);
+}
- NSDictionary* titleAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
- titleFont, NSFontAttributeName,
- textColor, NSForegroundColorAttributeName,
- nil];
- NSDictionary* messageAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
- messageFont, NSFontAttributeName,
- textColor, NSForegroundColorAttributeName,
- nil];
-
- NSAttributedString* titleString =
- [[[NSAttributedString alloc] initWithString:title
- attributes:titleAttrs] autorelease];
- NSAttributedString* messageString =
- [[[NSAttributedString alloc] initWithString:message
- attributes:messageAttrs] autorelease];
-
- NSRect viewBounds = [self bounds];
-
- NSSize sadTabImageSize = [sadTabImage size];
- CGFloat iconWidth = sadTabImageSize.width;
- CGFloat iconHeight = sadTabImageSize.height;
- CGFloat iconX = (viewBounds.size.width - iconWidth) / 2;
+- (void)drawRect:(NSRect)dirtyRect {
+ // Paint background.
+ [backgroundColor_ set];
+ NSRectFill(dirtyRect);
+}
+
+- (void)resizeSubviewsWithOldSize:(NSSize)oldSize {
+ NSRect newBounds = [self bounds];
+ CGFloat maxWidth = NSWidth(newBounds) - (kTabHorzMargin * 2);
+ BOOL callSizeToFit = (messageSize_.width == 0);
+
+ // Set new frame origin for image.
+ NSRect iconFrame = [image_ frame];
+ CGFloat iconX = (maxWidth - NSWidth(iconFrame)) / 2;
CGFloat iconY =
- ((viewBounds.size.height - iconHeight) / 2) - kSadTabOffset;
-
- NSSize titleSize = [titleString size];
- CGFloat titleX = (viewBounds.size.width - titleSize.width) / 2;
- CGFloat titleY = iconY - kIconTitleSpacing - titleSize.height;
-
- NSSize messageSize = [messageString size];
- CGFloat messageX = (viewBounds.size.width - messageSize.width) / 2;
- CGFloat messageY = titleY - kTitleMessageSpacing - messageSize.height;
-
- // Paint
- [backgroundColor set];
- NSRectFill(viewBounds);
-
- [sadTabImage drawAtPoint:NSMakePoint(iconX, iconY)
- fromRect:NSZeroRect
- operation:NSCompositeSourceOver
- fraction:1.0f];
- [titleString drawAtPoint:NSMakePoint(titleX, titleY)];
- [messageString drawAtPoint:NSMakePoint(messageX, messageY)];
+ MIN(((NSHeight(newBounds) - NSHeight(iconFrame)) / 2) - kSadTabOffset,
+ NSHeight(newBounds) - NSHeight(iconFrame));
+ [image_ setFrameOrigin:NSMakePoint(iconX, iconY)];
+
+ // Set new frame origin for title.
+ if (callSizeToFit)
+ [title_ sizeToFit];
+ NSRect titleFrame = [title_ frame];
+ CGFloat titleX = (maxWidth - NSWidth(titleFrame)) / 2;
+ CGFloat titleY = iconY - kIconTitleSpacing - NSHeight(titleFrame);
+ [title_ setFrameOrigin:NSMakePoint(titleX, titleY)];
+
+ // Set new frame for message, wrapping or unwrapping the text if necessary.
+ if (callSizeToFit) {
+ [message_ sizeToFit];
+ messageSize_ = [message_ frame].size;
+ }
+ NSRect messageFrame = [message_ frame];
+ if (messageSize_.width > maxWidth) { // Need to wrap message.
+ [message_ setFrameSize:NSMakeSize(maxWidth, messageSize_.height)];
+ CGFloat heightChange =
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:message_];
+ messageFrame.size.width = maxWidth;
+ messageFrame.size.height = messageSize_.height + heightChange;
+ messageFrame.origin.x = kTabHorzMargin;
+ } else {
+ if (!callSizeToFit) {
+ [message_ sizeToFit];
+ messageFrame = [message_ frame];
+ }
+ messageFrame.origin.x = (maxWidth - NSWidth(messageFrame)) / 2;
+ }
+ messageFrame.origin.y =
+ titleY - kTitleMessageSpacing - NSHeight(messageFrame);
+ [message_ setFrame:messageFrame];
+
+ if (linkButton_) {
+ if (callSizeToFit)
+ [linkButton_ sizeToFit];
+ // Set new frame origin for link.
+ NSRect linkFrame = [linkButton_ frame];
+ CGFloat linkX = (maxWidth - NSWidth(linkFrame)) / 2;
+ CGFloat linkY =
+ NSMinY(messageFrame) - kMessageLinkSpacing - NSHeight(linkFrame);
+ [linkButton_ setFrameOrigin:NSMakePoint(linkX, linkY)];
+ }
+}
+
+- (void)removeLinkButton {
+ if (linkButton_) {
+ [linkButton_ removeFromSuperview];
+ linkButton_ = nil;
+ }
}
@end