diff options
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 17 | ||||
-rw-r--r-- | chrome/browser/cocoa/sad_tab_controller.h | 32 | ||||
-rw-r--r-- | chrome/browser/cocoa/sad_tab_controller.mm | 48 | ||||
-rw-r--r-- | chrome/browser/cocoa/sad_tab_controller_unittest.mm | 101 | ||||
-rw-r--r-- | chrome/browser/cocoa/sad_tab_view.h | 15 | ||||
-rw-r--r-- | chrome/browser/cocoa/sad_tab_view.mm | 168 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_mac.h | 4 | ||||
-rw-r--r-- | chrome/browser/tab_contents/tab_contents_view_mac.mm | 22 |
8 files changed, 330 insertions, 77 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 diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.h b/chrome/browser/tab_contents/tab_contents_view_mac.h index c3801a7..1e8e174b 100644 --- a/chrome/browser/tab_contents/tab_contents_view_mac.h +++ b/chrome/browser/tab_contents/tab_contents_view_mac.h @@ -19,7 +19,7 @@ class FilePath; class FindBarMac; @class FocusTracker; -@class SadTabView; +@class SadTabController; class TabContentsViewMac; @class WebDragSource; @class WebDropTarget; @@ -101,7 +101,7 @@ class TabContentsViewMac : public TabContentsView, // Used to render the sad tab. This will be non-NULL only when the sad tab is // visible. - scoped_nsobject<SadTabView> sad_tab_; + scoped_nsobject<SadTabController> sad_tab_; // The page content's intrinsic width. int preferred_width_; diff --git a/chrome/browser/tab_contents/tab_contents_view_mac.mm b/chrome/browser/tab_contents/tab_contents_view_mac.mm index 14b16c6..8c1f3d6 100644 --- a/chrome/browser/tab_contents/tab_contents_view_mac.mm +++ b/chrome/browser/tab_contents/tab_contents_view_mac.mm @@ -14,7 +14,7 @@ #import "chrome/browser/cocoa/chrome_browser_window.h" #import "chrome/browser/cocoa/browser_window_controller.h" #include "chrome/browser/global_keyboard_shortcuts_mac.h" -#include "chrome/browser/cocoa/sad_tab_view.h" +#include "chrome/browser/cocoa/sad_tab_controller.h" #import "chrome/browser/cocoa/web_drag_source.h" #import "chrome/browser/cocoa/web_drop_target.h" #include "chrome/browser/renderer_host/render_view_host_factory.h" @@ -158,13 +158,14 @@ void TabContentsViewMac::SetPageTitle(const std::wstring& title) { void TabContentsViewMac::OnTabCrashed() { if (!sad_tab_.get()) { - SadTabView* view = [[SadTabView alloc] initWithFrame:NSZeroRect]; - sad_tab_.reset(view); - - // Set as the dominant child. - [cocoa_view_.get() addSubview:view]; - [view setFrame:[cocoa_view_.get() bounds]]; - [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + TabContents* contents = tab_contents(); + DCHECK(contents); + if (contents) { + SadTabController* sad_tab = + [[SadTabController alloc] initWithTabContents:contents + superview:cocoa_view_]; + sad_tab_.reset(sad_tab); + } } } @@ -295,10 +296,7 @@ void TabContentsViewMac::Observe(NotificationType type, const NotificationDetails& details) { switch (type.value) { case NotificationType::TAB_CONTENTS_CONNECTED: { - if (sad_tab_.get()) { - [sad_tab_.get() removeFromSuperview]; - sad_tab_.reset(); - } + sad_tab_.reset(); break; } default: |