Change chrome://crash (sad tab page) "Learn more" link to:
"If you're seeing this frequently, try these suggestions."
Link "these suggestions" to the "Learn more" help article.
Update Views, GTK, and Cocoa implementations.
chrome://kill (unused) changes on Mac, but not on Views nor GTK.
Not wrapping this line on overflow for Views and GTK seems acceptable.
Strings: Add sad tab help message and embedded link resources.
Views: Rewrite the SadTabView to use ImageView, GridLayout, etc.
GTK: Add new prefix and suffix Label Widgets to an hbox with the link.
Cocoa Support: Create HyperlinkTextView for non-selectable text & link.
Cocoa IB: Replace SadTab.xib NSButton with a NSTextfield placeholder.
Cocoa: Replace the placeholder with a customized HyperlinkTextView.
BUG=80428
TEST=chrome://crash and chrome://kill on all platforms.
Review URL: http://codereview.chromium.org/7610011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99393 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 263043f..bcf8d6a1 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7415,6 +7415,12 @@
<message name="IDS_SAD_TAB_MESSAGE" desc="The message displayed on the sad tab page.">
Something went wrong while displaying this webpage. To continue, reload or go to another page.
</message>
+ <message name="IDS_SAD_TAB_HELP_MESSAGE" desc="The help message displayed on the sad tab page, with IDS_SAD_TAB_HELP_LINK embedded as a link to help.">
+ If you're seeing this frequently, try <ph name="HELP_LINK">$1<ex>these suggestions</ex></ph>.
+ </message>
+ <message name="IDS_SAD_TAB_HELP_LINK" desc="The link text displayed on the sad tab page pointing the users to a help article.">
+ these suggestions
+ </message>
<!-- Killed Tab Strings -->
<!-- TODO(gspencer): These are temporary values until real
diff --git a/chrome/app/nibs/SadTab.xib b/chrome/app/nibs/SadTab.xib
index 8f60903..e609c588 100644
--- a/chrome/app/nibs/SadTab.xib
+++ b/chrome/app/nibs/SadTab.xib
@@ -2,9 +2,9 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1050</int>
- <string key="IBDocument.SystemVersion">10F2108</string>
+ <string key="IBDocument.SystemVersion">10J869</string>
<string key="IBDocument.InterfaceBuilderVersion">823</string>
- <string key="IBDocument.AppKitVersion">1038.29</string>
+ <string key="IBDocument.AppKitVersion">1038.35</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
@@ -98,6 +98,26 @@
</object>
</object>
</object>
+ <object class="NSTextField" id="796295772">
+ <reference key="NSNextResponder" ref="1005"/>
+ <int key="NSvFlags">303</int>
+ <string key="NSFrame">{{14, 4}, {68, 17}}</string>
+ <reference key="NSSuperview" ref="1005"/>
+ <bool key="NSEnabled">YES</bool>
+ <object class="NSTextFieldCell" key="NSCell" id="755651743">
+ <int key="NSCellFlags">67239424</int>
+ <int key="NSCellFlags2">138412032</int>
+ <string key="NSContents">Help text</string>
+ <object class="NSFont" key="NSSupport" id="413085122">
+ <string key="NSName">LucidaGrande</string>
+ <double key="NSSize">13</double>
+ <int key="NSfFlags">16</int>
+ </object>
+ <reference key="NSControlView" ref="796295772"/>
+ <reference key="NSBackgroundColor" ref="78971029"/>
+ <reference key="NSTextColor" ref="7040190"/>
+ </object>
+ </object>
<object class="NSTextField" id="685519067">
<reference key="NSNextResponder" ref="1005"/>
<int key="NSvFlags">301</int>
@@ -108,36 +128,12 @@
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">138412032</int>
<string key="NSContents">^IDS_SAD_TAB_MESSAGE</string>
- <object class="NSFont" key="NSSupport">
- <string key="NSName">LucidaGrande</string>
- <double key="NSSize">13</double>
- <int key="NSfFlags">16</int>
- </object>
+ <reference key="NSSupport" ref="413085122"/>
<reference key="NSControlView" ref="685519067"/>
<reference key="NSBackgroundColor" ref="78971029"/>
<reference key="NSTextColor" ref="7040190"/>
</object>
</object>
- <object class="NSButton" id="437803890">
- <reference key="NSNextResponder" ref="1005"/>
- <int key="NSvFlags">301</int>
- <string key="NSFrame">{{27, 0}, {42, 32}}</string>
- <reference key="NSSuperview" ref="1005"/>
- <bool key="NSEnabled">YES</bool>
- <object class="NSButtonCell" key="NSCell" id="968909108">
- <int key="NSCellFlags">67239424</int>
- <int key="NSCellFlags2">134217728</int>
- <string key="NSContents">^IDS_LEARN_MORE</string>
- <reference key="NSSupport" ref="958586153"/>
- <reference key="NSControlView" ref="437803890"/>
- <int key="NSButtonFlags">-2046672641</int>
- <int key="NSButtonFlags2">134</int>
- <string key="NSAlternateContents"/>
- <string key="NSKeyEquivalent"/>
- <int key="NSPeriodicDelay">200</int>
- <int key="NSPeriodicInterval">25</int>
- </object>
- </object>
</object>
<string key="NSFrameSize">{97, 95}</string>
<reference key="NSSuperview"/>
@@ -178,14 +174,6 @@
<int key="connectionID">22</int>
</object>
<object class="IBConnectionRecord">
- <object class="IBActionConnection" key="connection">
- <string key="label">openLearnMoreAboutCrashLink:</string>
- <reference key="source" ref="1001"/>
- <reference key="destination" ref="437803890"/>
- </object>
- <int key="connectionID">37</int>
- </object>
- <object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">image_</string>
<reference key="source" ref="1005"/>
@@ -195,22 +183,6 @@
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
- <string key="label">linkButton_</string>
- <reference key="source" ref="1005"/>
- <reference key="destination" ref="437803890"/>
- </object>
- <int key="connectionID">41</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBOutletConnection" key="connection">
- <string key="label">linkCell_</string>
- <reference key="source" ref="1005"/>
- <reference key="destination" ref="968909108"/>
- </object>
- <int key="connectionID">42</int>
- </object>
- <object class="IBConnectionRecord">
- <object class="IBOutletConnection" key="connection">
<string key="label">message_</string>
<reference key="source" ref="1005"/>
<reference key="destination" ref="685519067"/>
@@ -225,6 +197,22 @@
</object>
<int key="connectionID">44</int>
</object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">helpPlaceholder_</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="796295772"/>
+ </object>
+ <int key="connectionID">50</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">controller_</string>
+ <reference key="source" ref="1005"/>
+ <reference key="destination" ref="1001"/>
+ </object>
+ <int key="connectionID">51</int>
+ </object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
@@ -262,8 +250,8 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="685519067"/>
<reference ref="261676766"/>
- <reference ref="437803890"/>
<reference ref="48169842"/>
+ <reference ref="796295772"/>
</object>
<reference key="parent" ref="1002"/>
</object>
@@ -320,18 +308,18 @@
<reference key="parent" ref="685519067"/>
</object>
<object class="IBObjectRecord">
- <int key="objectID">27</int>
- <reference key="object" ref="437803890"/>
+ <int key="objectID">47</int>
+ <reference key="object" ref="796295772"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
- <reference ref="968909108"/>
+ <reference ref="755651743"/>
</object>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
- <int key="objectID">28</int>
- <reference key="object" ref="968909108"/>
- <reference key="parent" ref="437803890"/>
+ <int key="objectID">48</int>
+ <reference key="object" ref="755651743"/>
+ <reference key="parent" ref="796295772"/>
</object>
</object>
</object>
@@ -345,30 +333,40 @@
<string>1.WindowOrigin</string>
<string>1.editorWindowContentRectSynchronizationRect</string>
<string>24.IBPluginDependency</string>
+ <string>24.IBViewBoundsToFrameTransform</string>
<string>25.IBPluginDependency</string>
+ <string>25.IBViewBoundsToFrameTransform</string>
<string>26.IBPluginDependency</string>
- <string>27.IBPluginDependency</string>
- <string>28.CustomClassName</string>
- <string>28.IBPluginDependency</string>
<string>29.IBPluginDependency</string>
<string>30.IBPluginDependency</string>
<string>31.IBPluginDependency</string>
+ <string>47.IBPluginDependency</string>
+ <string>47.IBViewBoundsToFrameTransform</string>
+ <string>48.IBPluginDependency</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>{{333, 671}, {97, 95}}</string>
+ <string>{{305, 671}, {97, 95}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{628, 654}</string>
<string>{{217, 442}, {480, 272}}</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABBYAAAwjAAAA</bytes>
+ </object>
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABBMAAAwooAAA</bytes>
+ </object>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>HyperlinkButtonCell</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
- <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <object class="NSAffineTransform">
+ <bytes key="NSTransformStruct">P4AAAL+AAABBmAAAwhwAAA</bytes>
+ </object>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
</object>
@@ -388,7 +386,7 @@
</object>
</object>
<nil key="sourceID"/>
- <int key="maxID">44</int>
+ <int key="maxID">51</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -504,18 +502,17 @@
</object>
</object>
<object class="IBPartialClassDescription">
- <string key="className">HyperlinkButtonCell</string>
- <string key="superclassName">NSButtonCell</string>
+ <string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
- <string key="minorKey">browser/ui/cocoa/hyperlink_button_cell.h</string>
+ <string key="minorKey">../third_party/GTM/Foundation/GTMNSObject+KeyValueObserving.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
- <string key="minorKey">../third_party/GTM/Foundation/GTMNSObject+KeyValueObserving.h</string>
+ <string key="minorKey">browser/renderer_host/accelerated_plugin_view_mac.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
@@ -543,6 +540,13 @@
<string key="className">NSView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">browser/ui/cocoa/nsview_additions.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
<string key="minorKey">browser/ui/cocoa/view_id_util.h</string>
</object>
</object>
@@ -572,17 +576,17 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
+ <string>controller_</string>
+ <string>helpPlaceholder_</string>
<string>image_</string>
- <string>linkButton_</string>
- <string>linkCell_</string>
<string>message_</string>
<string>title_</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
+ <string>SadTabController</string>
+ <string>NSTextField</string>
<string>NSImageView</string>
- <string>NSButton</string>
- <string>HyperlinkButtonCell</string>
<string>NSTextField</string>
<string>NSTextField</string>
</object>
@@ -591,27 +595,27 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
+ <string>controller_</string>
+ <string>helpPlaceholder_</string>
<string>image_</string>
- <string>linkButton_</string>
- <string>linkCell_</string>
<string>message_</string>
<string>title_</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
+ <string key="name">controller_</string>
+ <string key="candidateClassName">SadTabController</string>
+ </object>
+ <object class="IBToOneOutletInfo">
+ <string key="name">helpPlaceholder_</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ <object class="IBToOneOutletInfo">
<string key="name">image_</string>
<string key="candidateClassName">NSImageView</string>
</object>
<object class="IBToOneOutletInfo">
- <string key="name">linkButton_</string>
- <string key="candidateClassName">NSButton</string>
- </object>
- <object class="IBToOneOutletInfo">
- <string key="name">linkCell_</string>
- <string key="candidateClassName">HyperlinkButtonCell</string>
- </object>
- <object class="IBToOneOutletInfo">
<string key="name">message_</string>
<string key="candidateClassName">NSTextField</string>
</object>
@@ -674,22 +678,6 @@
</object>
</object>
<object class="IBPartialClassDescription">
- <string key="className">NSButton</string>
- <string key="superclassName">NSControl</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSButton.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
- <string key="className">NSButtonCell</string>
- <string key="superclassName">NSActionCell</string>
- <object class="IBClassDescriptionSource" key="sourceIdentifier">
- <string key="majorKey">IBFrameworkSource</string>
- <string key="minorKey">AppKit.framework/Headers/NSButtonCell.h</string>
- </object>
- </object>
- <object class="IBPartialClassDescription">
<string key="className">NSCell</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
diff --git a/chrome/browser/ui/cocoa/hyperlink_text_view.h b/chrome/browser/ui/cocoa/hyperlink_text_view.h
new file mode 100644
index 0000000..a9ba8d33
--- /dev/null
+++ b/chrome/browser/ui/cocoa/hyperlink_text_view.h
@@ -0,0 +1,20 @@
+// 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 <Cocoa/Cocoa.h>
+
+// HyperlinkTextView is an NSTextView subclass for unselectable, linkable text.
+// This subclass doesn't show the text caret or IBeamCursor, whereas the base
+// class NSTextView displays both with full keyboard accessibility enabled.
+@interface HyperlinkTextView : NSTextView
+// Convenience function that sets the |HyperlinkTextView| contents to the
+// specified |message| with a hypertext style |link| inserted at |linkOffset|.
+// Uses the supplied |font|, |messageColor|, and |linkColor|.
+- (void)setMessageAndLink:(NSString*)message
+ withLink:(NSString*)link
+ atOffset:(NSUInteger)linkOffset
+ font:(NSFont*)font
+ messageColor:(NSColor*)messageColor
+ linkColor:(NSColor*)linkColor;
+@end
diff --git a/chrome/browser/ui/cocoa/hyperlink_text_view.mm b/chrome/browser/ui/cocoa/hyperlink_text_view.mm
new file mode 100644
index 0000000..ea92f4c
--- /dev/null
+++ b/chrome/browser/ui/cocoa/hyperlink_text_view.mm
@@ -0,0 +1,131 @@
+// 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/hyperlink_text_view.h"
+
+#include "base/memory/scoped_nsobject.h"
+
+// The baseline shift for text in the NSTextView.
+const float kTextBaselineShift = -1.0;
+
+@interface HyperlinkTextView(Private)
+// Initialize the NSTextView properties for this subclass.
+- (void)configureTextView;
+
+// Change the current IBeamCursor to an arrowCursor.
+- (void)fixupCursor;
+@end
+
+@implementation HyperlinkTextView
+
+- (id)initWithCoder:(NSCoder*)decoder {
+ if ((self = [super initWithCoder:decoder]))
+ [self configureTextView];
+ return self;
+}
+
+- (id)initWithFrame:(NSRect)frameRect {
+ if ((self = [super initWithFrame:frameRect]))
+ [self configureTextView];
+ return self;
+}
+
+// Never draw the insertion point (otherwise, it shows up without any user
+// action if full keyboard accessibility is enabled).
+- (BOOL)shouldDrawInsertionPoint {
+ return NO;
+}
+
+- (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange
+ granularity:(NSSelectionGranularity)granularity {
+ // Do not allow selections.
+ return NSMakeRange(0, 0);
+}
+
+// Convince NSTextView to not show an I-Beam cursor when the cursor is over the
+// text view but not over actual text.
+//
+// http://www.mail-archive.com/[email protected]/msg10791.html
+// "NSTextView sets the cursor over itself dynamically, based on considerations
+// including the text under the cursor. It does so in -mouseEntered:,
+// -mouseMoved:, and -cursorUpdate:, so those would be points to consider
+// overriding."
+- (void)mouseMoved:(NSEvent*)e {
+ [super mouseMoved:e];
+ [self fixupCursor];
+}
+
+- (void)mouseEntered:(NSEvent*)e {
+ [super mouseEntered:e];
+ [self fixupCursor];
+}
+
+- (void)cursorUpdate:(NSEvent*)e {
+ [super cursorUpdate:e];
+ [self fixupCursor];
+}
+
+- (void)configureTextView {
+ [self setEditable:NO];
+ [self setDrawsBackground:NO];
+ [self setHorizontallyResizable:NO];
+ [self setVerticallyResizable:NO];
+}
+
+- (void)fixupCursor {
+ if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]])
+ [[NSCursor arrowCursor] set];
+}
+
+- (void)setMessageAndLink:(NSString*)message
+ withLink:(NSString*)link
+ atOffset:(NSUInteger)linkOffset
+ font:(NSFont*)font
+ messageColor:(NSColor*)messageColor
+ linkColor:(NSColor*)linkColor {
+ // Create an attributes dictionary for the message and link.
+ NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
+ [attributes setObject:messageColor
+ forKey:NSForegroundColorAttributeName];
+ [attributes setObject:[NSCursor arrowCursor]
+ forKey:NSCursorAttributeName];
+ [attributes setObject:font
+ forKey:NSFontAttributeName];
+ [attributes setObject:[NSNumber numberWithFloat:kTextBaselineShift]
+ forKey:NSBaselineOffsetAttributeName];
+
+ // Create the attributed string for the message.
+ scoped_nsobject<NSMutableAttributedString> attributedMessage(
+ [[NSMutableAttributedString alloc] initWithString:message
+ attributes:attributes]);
+
+ if ([link length] != 0) {
+ // Add additional attributes to style the link text appropriately as
+ // well as linkify it.
+ [attributes setObject:linkColor
+ forKey:NSForegroundColorAttributeName];
+ [attributes setObject:[NSNumber numberWithBool:YES]
+ forKey:NSUnderlineStyleAttributeName];
+ [attributes setObject:[NSCursor pointingHandCursor]
+ forKey:NSCursorAttributeName];
+ [attributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle]
+ forKey:NSUnderlineStyleAttributeName];
+ [attributes setObject:[NSString string] // dummy value
+ forKey:NSLinkAttributeName];
+
+ // Insert the link into the message at the appropriate offset.
+ scoped_nsobject<NSAttributedString> attributedLink(
+ [[NSAttributedString alloc] initWithString:link
+ attributes:attributes]);
+ [attributedMessage.get() insertAttributedString:attributedLink.get()
+ atIndex:linkOffset];
+ // Ensure the TextView doesn't override the link style.
+ [self setLinkTextAttributes:attributes];
+ }
+
+ // Update the text view with the new text.
+ [[self textStorage] setAttributedString:attributedMessage];
+}
+
+@end
diff --git a/chrome/browser/ui/cocoa/hyperlink_text_view_unittest.mm b/chrome/browser/ui/cocoa/hyperlink_text_view_unittest.mm
new file mode 100644
index 0000000..b5903b2
--- /dev/null
+++ b/chrome/browser/ui/cocoa/hyperlink_text_view_unittest.mm
@@ -0,0 +1,33 @@
+// 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.
+
+#include "base/memory/scoped_nsobject.h"
+#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
+#import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
+
+namespace {
+
+class HyperlinkTextViewTest : public CocoaTest {
+ public:
+ HyperlinkTextViewTest() {
+ NSRect frame = NSMakeRect(0, 0, 50, 50);
+ scoped_nsobject<HyperlinkTextView> view(
+ [[HyperlinkTextView alloc] initWithFrame:frame]);
+ view_ = view.get();
+ [[test_window() contentView] addSubview:view_];
+ }
+
+ HyperlinkTextView* view_;
+};
+
+TEST_VIEW(HyperlinkTextViewTest, view_);
+
+TEST_F(HyperlinkTextViewTest, TestViewConfiguration) {
+ EXPECT_FALSE([view_ isEditable]);
+ EXPECT_FALSE([view_ drawsBackground]);
+ EXPECT_FALSE([view_ isHorizontallyResizable]);
+ EXPECT_FALSE([view_ isVerticallyResizable]);
+}
+
+} // namespace
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.h b/chrome/browser/ui/cocoa/infobars/infobar_controller.h
index 8b858895..b5d080d0 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_controller.h
+++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.h
@@ -77,9 +77,6 @@
// infobar closes.
- (void)infobarWillClose;
-// Sets the info bar message to the specified |message|.
-- (void)setLabelToMessage:(NSString*)message;
-
// Removes the OK and Cancel buttons and resizes the textfield to use the
// space.
- (void)removeButtons;
diff --git a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
index f8640f8..eabf4a9 100644
--- a/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
+++ b/chrome/browser/ui/cocoa/infobars/infobar_controller.mm
@@ -10,15 +10,16 @@
#include "chrome/browser/infobars/infobar_tab_helper.h"
#include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
#include "chrome/browser/tab_contents/link_infobar_delegate.h"
-#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#import "chrome/browser/ui/cocoa/animatable_view.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
#include "chrome/browser/ui/cocoa/event_utils.h"
+#import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
#include "chrome/browser/ui/cocoa/infobars/infobar.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_gradient_view.h"
#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
#include "ui/gfx/image/image.h"
#include "webkit/glue/window_open_disposition.h"
@@ -27,61 +28,8 @@
// Durations set to match the default SlideAnimation duration.
const float kAnimateOpenDuration = 0.12;
const float kAnimateCloseDuration = 0.12;
-
-// The baseline shift for text in the NSTextView.
-const float kTextBaselineShift = -1.0;
}
-// This simple subclass of |NSTextView| just doesn't show the (text) cursor
-// (|NSTextView| displays the cursor with full keyboard accessibility enabled).
-@interface InfoBarTextView : NSTextView
-- (void)fixupCursor;
-@end
-
-@implementation InfoBarTextView
-
-// Never draw the insertion point (otherwise, it shows up without any user
-// action if full keyboard accessibility is enabled).
-- (BOOL)shouldDrawInsertionPoint {
- return NO;
-}
-
-- (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange
- granularity:(NSSelectionGranularity)granularity {
- // Do not allow selections.
- return NSMakeRange(0, 0);
-}
-
-// Convince NSTextView to not show an I-Beam cursor when the cursor is over the
-// text view but not over actual text.
-//
-// http://www.mail-archive.com/[email protected]/msg10791.html
-// "NSTextView sets the cursor over itself dynamically, based on considerations
-// including the text under the cursor. It does so in -mouseEntered:,
-// -mouseMoved:, and -cursorUpdate:, so those would be points to consider
-// overriding."
-- (void)mouseMoved:(NSEvent*)e {
- [super mouseMoved:e];
- [self fixupCursor];
-}
-
-- (void)mouseEntered:(NSEvent*)e {
- [super mouseEntered:e];
- [self fixupCursor];
-}
-
-- (void)cursorUpdate:(NSEvent*)e {
- [super cursorUpdate:e];
- [self fixupCursor];
-}
-
-- (void)fixupCursor {
- if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]])
- [[NSCursor arrowCursor] set];
-}
-
-@end
-
@interface InfoBarController (PrivateMethods)
// Sets |label_| based on |labelPlaceholder_|, sets |labelPlaceholder_| to nil.
- (void)initializeLabel;
@@ -95,12 +43,6 @@
// infobar from its container, if necessary.
- (void)cleanUpAfterAnimation:(BOOL)finished;
-// Sets the info bar message to the specified |message|, with a hypertext
-// style link. |link| will be inserted into message at |linkOffset|.
-- (void)setLabelToMessage:(NSString*)message
- withLink:(NSString*)link
- atOffset:(NSUInteger)linkOffset;
-
// Returns the point, in gradient view coordinates, at which the apex of the
// infobar tip should be drawn.
- (NSPoint)pointForTipApex;
@@ -229,22 +171,6 @@
// Default implementation does nothing.
}
-- (void)setLabelToMessage:(NSString*)message {
- NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
- NSFont* font = [NSFont labelFontOfSize:
- [NSFont systemFontSizeForControlSize:NSRegularControlSize]];
- [attributes setObject:font
- forKey:NSFontAttributeName];
- [attributes setObject:[NSCursor arrowCursor]
- forKey:NSCursorAttributeName];
- [attributes setObject:[NSNumber numberWithFloat:kTextBaselineShift]
- forKey:NSBaselineOffsetAttributeName];
- scoped_nsobject<NSAttributedString> attributedString(
- [[NSAttributedString alloc] initWithString:message
- attributes:attributes]);
- [[label_.get() textStorage] setAttributedString:attributedString];
-}
-
- (void)removeButtons {
// Extend the label all the way across.
NSRect labelFrame = [label_.get() frame];
@@ -263,17 +189,13 @@
// The former doesn't show links in a nice way, but the latter can't be added
// in IB without a containing scroll view, so create the NSTextView
// programmatically.
- label_.reset([[InfoBarTextView alloc]
+ label_.reset([[HyperlinkTextView alloc]
initWithFrame:[labelPlaceholder_ frame]]);
[label_.get() setAutoresizingMask:[labelPlaceholder_ autoresizingMask]];
[[labelPlaceholder_ superview]
replaceSubview:labelPlaceholder_ with:label_.get()];
labelPlaceholder_ = nil; // Now released.
[label_.get() setDelegate:self];
- [label_.get() setEditable:NO];
- [label_.get() setDrawsBackground:NO];
- [label_.get() setHorizontallyResizable:NO];
- [label_.get() setVerticallyResizable:NO];
}
- (void)removeSelf {
@@ -314,62 +236,6 @@
[self cleanUpAfterAnimation:YES];
}
-// TODO(joth): This method factors out some common functionality between the
-// various derived infobar classes, however the class hierarchy itself could
-// use refactoring to reduce this duplication. http://crbug.com/38924
-- (void)setLabelToMessage:(NSString*)message
- withLink:(NSString*)link
- atOffset:(NSUInteger)linkOffset {
- if (linkOffset == std::wstring::npos) {
- // linkOffset == std::wstring::npos means the link should be right-aligned,
- // which is not supported on Mac (http://crbug.com/47728).
- NOTIMPLEMENTED();
- linkOffset = [message length];
- }
- // Create an attributes dictionary for the entire message. We have
- // to explicitly set the control's font. We also override the cursor to give
- // us the normal cursor rather than the text insertion cursor.
- NSMutableDictionary* linkAttributes = [NSMutableDictionary dictionary];
- [linkAttributes setObject:[NSCursor arrowCursor]
- forKey:NSCursorAttributeName];
- NSFont* font = [NSFont labelFontOfSize:
- [NSFont systemFontSizeForControlSize:NSRegularControlSize]];
- [linkAttributes setObject:font
- forKey:NSFontAttributeName];
-
- // Create the attributed string for the main message text.
- scoped_nsobject<NSMutableAttributedString> infoText(
- [[NSMutableAttributedString alloc] initWithString:message]);
- [infoText.get() addAttributes:linkAttributes
- range:NSMakeRange(0, [infoText.get() length])];
- // Add additional attributes to style the link text appropriately as
- // well as linkify it.
- [linkAttributes setObject:[NSColor blueColor]
- forKey:NSForegroundColorAttributeName];
- [linkAttributes setObject:[NSNumber numberWithBool:YES]
- forKey:NSUnderlineStyleAttributeName];
- [linkAttributes setObject:[NSCursor pointingHandCursor]
- forKey:NSCursorAttributeName];
- [linkAttributes setObject:[NSNumber numberWithInt:NSSingleUnderlineStyle]
- forKey:NSUnderlineStyleAttributeName];
- [linkAttributes setObject:[NSString string] // dummy value
- forKey:NSLinkAttributeName];
-
- // Insert the link text into the string at the appropriate offset.
- scoped_nsobject<NSAttributedString> attributedString(
- [[NSAttributedString alloc] initWithString:link
- attributes:linkAttributes]);
- [infoText.get() insertAttributedString:attributedString.get()
- atIndex:linkOffset];
- // The entire text needs a baseline shift.
- [infoText addAttribute:NSBaselineOffsetAttributeName
- value:[NSNumber numberWithDouble:kTextBaselineShift]
- range:NSMakeRange(0, [infoText length])];
-
- // Update the label view with the new text.
- [[label_.get() textStorage] setAttributedString:infoText];
-}
-
- (NSPoint)pointForTipApex {
BrowserWindowController* windowController =
[containerController_ browserWindowController];
@@ -406,9 +272,16 @@
DCHECK(delegate);
size_t offset = std::wstring::npos;
string16 message = delegate->GetMessageTextWithOffset(&offset);
- [self setLabelToMessage:base::SysUTF16ToNSString(message)
- withLink:base::SysUTF16ToNSString(delegate->GetLinkText())
- atOffset:offset];
+ string16 link = delegate->GetLinkText();
+ NSFont* font = [NSFont labelFontOfSize:
+ [NSFont systemFontSizeForControlSize:NSRegularControlSize]];
+ HyperlinkTextView* view = (HyperlinkTextView*)label_.get();
+ [view setMessageAndLink:base::SysUTF16ToNSString(message)
+ withLink:base::SysUTF16ToNSString(link)
+ atOffset:offset
+ font:font
+ messageColor:[NSColor blackColor]
+ linkColor:[NSColor blueColor]];
}
// Called when someone clicks on the link in the infobar. This method
@@ -525,20 +398,19 @@
// Set the text and link.
NSString* message = base::SysUTF16ToNSString(delegate->GetMessageText());
string16 link = delegate->GetLinkText();
- if (link.empty()) {
- // Simple case: no link, so just set the message directly.
- [self setLabelToMessage:message];
- } else {
- // Inserting the link unintentionally causes the text to have a slightly
- // different result to the simple case above: text is truncated on word
- // boundaries (if needed) rather than elided with ellipses.
-
+ if (!link.empty()) {
// Add spacing between the label and the link.
message = [message stringByAppendingString:@" "];
- [self setLabelToMessage:message
- withLink:base::SysUTF16ToNSString(link)
- atOffset:[message length]];
}
+ NSFont* font = [NSFont labelFontOfSize:
+ [NSFont systemFontSizeForControlSize:NSRegularControlSize]];
+ HyperlinkTextView* view = (HyperlinkTextView*)label_.get();
+ [view setMessageAndLink:message
+ withLink:base::SysUTF16ToNSString(link)
+ atOffset:[message length]
+ font:font
+ messageColor:[NSColor blackColor]
+ linkColor:[NSColor blueColor]];
}
// Called when someone clicks on the link in the infobar. This method
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.mm b/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.mm
index 3b11c14..c01a945 100644
--- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// 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.
@@ -27,7 +27,7 @@
// If tab_contents_ is nil, ask view to remove link.
if (!tabContents_) {
SadTabView* sad_view = static_cast<SadTabView*>([self view]);
- [sad_view removeLinkButton];
+ [sad_view removeHelpText];
}
}
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller_unittest.mm b/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller_unittest.mm
index 16b54a1..06d62f68 100644
--- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_controller_unittest.mm
@@ -5,6 +5,7 @@
#include "base/debug/debugger.h"
#include "base/memory/scoped_nsobject.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
+#import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.h"
#import "chrome/browser/ui/cocoa/tab_contents/sad_tab_view.h"
#include "chrome/test/base/testing_profile.h"
@@ -13,12 +14,12 @@
@interface SadTabView (ExposedForTesting)
// Implementation is below.
-- (NSButton*)linkButton;
+- (HyperlinkTextView*)helpTextView;
@end
@implementation SadTabView (ExposedForTesting)
-- (NSButton*)linkButton {
- return linkButton_;
+- (HyperlinkTextView*)helpTextView {
+ return help_.get();
}
@end
@@ -62,9 +63,9 @@
return controller;
}
- NSButton* GetLinkButton(SadTabController* controller) {
+ HyperlinkTextView* GetHelpTextView(SadTabController* controller) {
SadTabView* view = static_cast<SadTabView*>([controller view]);
- return ([view linkButton]);
+ return ([view helpTextView]);
}
static bool link_clicked_;
@@ -77,24 +78,24 @@
TEST_F(SadTabControllerTest, WithTabContents) {
scoped_nsobject<SadTabController> controller(CreateController());
EXPECT_TRUE(controller);
- NSButton* link = GetLinkButton(controller);
- EXPECT_TRUE(link);
+ HyperlinkTextView* help = GetHelpTextView(controller);
+ EXPECT_TRUE(help);
}
TEST_F(SadTabControllerTest, WithoutTabContents) {
DeleteContents();
scoped_nsobject<SadTabController> controller(CreateController());
EXPECT_TRUE(controller);
- NSButton* link = GetLinkButton(controller);
- EXPECT_FALSE(link);
+ HyperlinkTextView* help = GetHelpTextView(controller);
+ EXPECT_FALSE(help);
}
TEST_F(SadTabControllerTest, ClickOnLink) {
scoped_nsobject<SadTabController> controller(CreateController());
- NSButton* link = GetLinkButton(controller);
- EXPECT_TRUE(link);
+ HyperlinkTextView* help = GetHelpTextView(controller);
+ EXPECT_TRUE(help);
EXPECT_FALSE(link_clicked_);
- [link performClick:link];
+ [help clickedOnLink:nil atIndex:0];
EXPECT_TRUE(link_clicked_);
}
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.h b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.h
index c97083c..4470c2f1 100644
--- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.h
+++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.h
@@ -11,7 +11,8 @@
#import <Cocoa/Cocoa.h>
-@class HyperlinkButtonCell;
+@class SadTabController;
+@class HyperlinkTextView;
// A view that displays the "sad tab" (aka crash page).
@interface SadTabView : BaseView {
@@ -19,17 +20,28 @@
IBOutlet NSImageView* image_;
IBOutlet NSTextField* title_;
IBOutlet NSTextField* message_;
- IBOutlet NSButton* linkButton_;
- IBOutlet HyperlinkButtonCell* linkCell_;
+ IBOutlet NSTextField* helpPlaceholder_;
scoped_nsobject<NSColor> backgroundColor_;
NSSize messageSize_;
+
+ // Text fields don't work as well with embedded links as text views, but
+ // text views cannot conveniently be created in IB. The xib file contains
+ // a text field |helpPlaceholder_| that's replaced by this text view |help_|
+ // in -awakeFromNib.
+ scoped_nsobject<HyperlinkTextView> help_;
+
+ // A weak reference to the parent controller.
+ IBOutlet SadTabController* controller_;
}
// Designated initializer is -initWithFrame: .
-// Called by SadTabController to remove link button.
-- (void)removeLinkButton;
+// Called by SadTabController to remove the help text and link.
+- (void)removeHelpText;
+
+// Sets |help_| based on |helpPlaceholder_|, sets |helpPlaceholder_| to nil.
+- (void)initializeHelpText;
@end
diff --git a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.mm b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.mm
index 53ffcd9..dffe008 100644
--- a/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/sad_tab_view.mm
@@ -1,13 +1,20 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// 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.
#include "chrome/browser/ui/cocoa/tab_contents/sad_tab_view.h"
#include "base/logging.h"
+#include "base/sys_string_conversions.h"
#import "chrome/browser/ui/cocoa/hyperlink_button_cell.h"
+#import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
+#include "chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.h"
+#include "chrome/common/url_constants.h"
+#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"
@@ -39,11 +46,8 @@
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]];
- }
+ DCHECK(controller_);
+ [self initializeHelpText];
// Initialize background color.
NSColor* backgroundColor = [[NSColor colorWithCalibratedRed:(35.0f/255.0f)
@@ -106,23 +110,63 @@
titleY - kTitleMessageSpacing - NSHeight(messageFrame);
[message_ setFrame:messageFrame];
- if (linkButton_) {
+ // Set new frame for help text and link.
+ if (help_) {
if (callSizeToFit)
- [linkButton_ sizeToFit];
+ [help_.get() sizeToFit];
+ CGFloat helpHeight = [help_.get() frame].size.height;
+ [help_.get() setFrameSize:NSMakeSize(maxWidth, helpHeight)];
// 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)];
+ NSRect helpFrame = [help_.get() frame];
+ CGFloat helpX = (maxWidth - NSWidth(helpFrame)) / 2;
+ CGFloat helpY =
+ NSMinY(messageFrame) - kMessageLinkSpacing - NSHeight(helpFrame);
+ [help_.get() setFrameOrigin:NSMakePoint(helpX, helpY)];
}
}
-- (void)removeLinkButton {
- if (linkButton_) {
- [linkButton_ removeFromSuperview];
- linkButton_ = nil;
+- (void)removeHelpText {
+ if (help_.get()) {
+ [help_.get() removeFromSuperview];
+ help_.reset(nil);
}
}
+- (void)initializeHelpText {
+ // Replace the help placeholder NSTextField with the real help NSTextView.
+ // The former doesn't show links in a nice way, but the latter can't be added
+ // in IB without a containing scroll view, so create the NSTextView
+ // programmatically. Taken from -[InfoBarController initializeLabel].
+ help_.reset(
+ [[HyperlinkTextView alloc] initWithFrame:[helpPlaceholder_ frame]]);
+ [help_.get() setAutoresizingMask:[helpPlaceholder_ autoresizingMask]];
+ [[helpPlaceholder_ superview]
+ replaceSubview:helpPlaceholder_ with:help_.get()];
+ helpPlaceholder_ = nil; // Now released.
+ [help_.get() setDelegate:self];
+ [help_.get() setAlignment:NSCenterTextAlignment];
+
+ // Get the help text and link.
+ size_t linkOffset = 0;
+ NSString* helpMessage(base::SysUTF16ToNSString(l10n_util::GetStringFUTF16(
+ IDS_SAD_TAB_HELP_MESSAGE, string16(), &linkOffset)));
+ NSString* helpLink = l10n_util::GetNSString(IDS_SAD_TAB_HELP_LINK);
+ NSFont* font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
+ [help_.get() setMessageAndLink:helpMessage
+ withLink:helpLink
+ atOffset:linkOffset
+ font:font
+ messageColor:[NSColor whiteColor]
+ linkColor:[NSColor whiteColor]];
+}
+
+// Called when someone clicks on the embedded link.
+- (BOOL) textView:(NSTextView*)textView
+ clickedOnLink:(id)link
+ atIndex:(NSUInteger)charIndex {
+ if (controller_)
+ [controller_ openLearnMoreAboutCrashLink:nil];
+ return YES;
+}
+
@end
diff --git a/chrome/browser/ui/gtk/sad_tab_gtk.cc b/chrome/browser/ui/gtk/sad_tab_gtk.cc
index 2fc2081..2e1eb6cc 100644
--- a/chrome/browser/ui/gtk/sad_tab_gtk.cc
+++ b/chrome/browser/ui/gtk/sad_tab_gtk.cc
@@ -4,8 +4,7 @@
#include "chrome/browser/ui/gtk/sad_tab_gtk.h"
-#include <string>
-
+#include "base/utf_string_conversions.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
#include "chrome/common/url_constants.h"
@@ -109,15 +108,42 @@
gtk_box_pack_start(GTK_BOX(vbox), spacer, FALSE, FALSE, 0);
if (tab_contents_ != NULL) {
- // Add the learn-more link and center-align it in an alignment.
- GtkWidget* link = gtk_chrome_link_button_new(
- l10n_util::GetStringUTF8(IDS_LEARN_MORE).c_str());
+ // Create the help link and alignment.
+ std::string link_text(l10n_util::GetStringUTF8(
+ kind == CRASHED ? IDS_SAD_TAB_HELP_LINK : IDS_LEARN_MORE));
+ GtkWidget* link = gtk_chrome_link_button_new(link_text.c_str());
gtk_chrome_link_button_set_normal_color(GTK_CHROME_LINK_BUTTON(link),
&ui::kGdkWhite);
g_signal_connect(link, "clicked", G_CALLBACK(OnLinkButtonClickThunk), this);
- GtkWidget* link_alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
- gtk_container_add(GTK_CONTAINER(link_alignment), link);
- gtk_box_pack_start(GTK_BOX(vbox), link_alignment, FALSE, FALSE, 0);
+ GtkWidget* help_alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
+
+ if (kind == CRASHED) {
+ // Use a horizontal box to contain the help text and link.
+ GtkWidget* help_hbox = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(vbox), help_hbox);
+
+ size_t offset = 0;
+ string16 help_text(l10n_util::GetStringFUTF16(IDS_SAD_TAB_HELP_MESSAGE,
+ string16(), &offset));
+ std::string help_prefix_text(UTF16ToUTF8(help_text.substr(0, offset)));
+ std::string help_suffix_text(UTF16ToUTF8(help_text.substr(offset)));
+
+ GtkWidget* help_prefix = MakeWhiteMarkupLabel(
+ "<span style=\"normal\">%s</span>", help_prefix_text);
+ GtkWidget* help_suffix = MakeWhiteMarkupLabel(
+ "<span style=\"normal\">%s</span>", help_suffix_text);
+
+ // Add the help link and text to the horizontal box.
+ gtk_box_pack_start(GTK_BOX(help_hbox), help_prefix, FALSE, FALSE, 0);
+ GtkWidget* link_alignment = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
+ gtk_container_add(GTK_CONTAINER(link_alignment), link);
+ gtk_box_pack_start(GTK_BOX(help_hbox), link_alignment, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(help_hbox), help_suffix, FALSE, FALSE, 0);
+ } else {
+ // Add just the help link to a centered alignment.
+ gtk_container_add(GTK_CONTAINER(help_alignment), link);
+ }
+ gtk_box_pack_start(GTK_BOX(vbox), help_alignment, FALSE, FALSE, 0);
}
gtk_widget_show_all(event_box_.get());
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc
index b63879c..9ed5e67 100644
--- a/chrome/browser/ui/views/sad_tab_view.cc
+++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -12,36 +12,21 @@
#include "chrome/browser/userfeedback/proto/extension.pb.h"
#include "chrome/common/url_constants.h"
#include "content/browser/tab_contents/tab_contents.h"
-#include "content/browser/tab_contents/tab_contents_delegate.h"
#include "grit/generated_resources.h"
-#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
-#include "third_party/skia/include/effects/SkGradientShader.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/canvas_skia.h"
#include "ui/gfx/font.h"
-#include "ui/gfx/size.h"
-#include "ui/gfx/skia_util.h"
+#include "views/controls/image_view.h"
+#include "views/controls/label.h"
#include "views/controls/link.h"
+#include "views/layout/grid_layout.h"
-static const int kSadTabOffset = -64;
-static const int kIconTitleSpacing = 20;
-static const int kTitleMessageSpacing = 15;
-static const int kMessageBottomMargin = 20;
+static const int kPadding = 20;
static const float kMessageSize = 0.65f;
-static const SkColor kTitleColor = SK_ColorWHITE;
-static const SkColor kMessageColor = SK_ColorWHITE;
-static const SkColor kLinkColor = SK_ColorWHITE;
-static const SkColor kCrashBackgroundColor = SkColorSetRGB(35, 48, 64);
-static const SkColor kCrashBackgroundEndColor = SkColorSetRGB(35, 48, 64);
-// TODO(gspencer): update these colors when the UI team has picked
-// official versions. See http://crosbug.com/10711.
-static const SkColor kKillBackgroundColor = SkColorSetRGB(57, 48, 88);
-static const SkColor kKillBackgroundEndColor = SkColorSetRGB(57, 48, 88);
-static const int kMessageFlags = gfx::Canvas::MULTI_LINE |
- gfx::Canvas::NO_ELLIPSIS | gfx::Canvas::TEXT_ALIGN_CENTER;
+static const SkColor kTextColor = SK_ColorWHITE;
+static const SkColor kCrashColor = SkColorSetRGB(35, 48, 64);
+static const SkColor kKillColor = SkColorSetRGB(57, 48, 88);
// Font size correction.
#if defined(CROS_FONTS_USING_BCI)
@@ -54,138 +39,26 @@
SadTabView::SadTabView(TabContents* tab_contents, Kind kind)
: tab_contents_(tab_contents),
- learn_more_link_(NULL),
- feedback_link_(NULL),
kind_(kind),
- painted_(false) {
+ painted_(false),
+ message_(NULL),
+ help_link_(NULL),
+ feedback_link_(NULL) {
DCHECK(tab_contents);
// Sometimes the user will never see this tab, so keep track of the total
// number of creation events to compare to display events.
- UMA_HISTOGRAM_COUNTS("SadTab.Created", kind);
+ UMA_HISTOGRAM_COUNTS("SadTab.Created", kind_);
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- title_font_ = new gfx::Font(
- rb.GetFont(ResourceBundle::BaseFont).DeriveFont(kTitleFontSizeDelta,
- gfx::Font::BOLD));
- message_font_ = new gfx::Font(
- rb.GetFont(ResourceBundle::BaseFont).DeriveFont(kMessageFontSizeDelta));
- sad_tab_bitmap_ = rb.GetBitmapNamed(
- kind == CRASHED ? IDR_SAD_TAB : IDR_KILLED_TAB);
-
- title_ = l10n_util::GetStringUTF16(
- kind == CRASHED ? IDS_SAD_TAB_TITLE : IDS_KILLED_TAB_TITLE);
- title_width_ = title_font_->GetStringWidth(title_);
- message_ = l10n_util::GetStringUTF16(
- kind == CRASHED ? IDS_SAD_TAB_MESSAGE : IDS_KILLED_TAB_MESSAGE);
-
- if (tab_contents != NULL) {
- learn_more_link_ =
- new views::Link(UTF16ToWide(l10n_util::GetStringUTF16(IDS_LEARN_MORE)));
- learn_more_link_->SetFont(*message_font_);
- learn_more_link_->SetNormalColor(kLinkColor);
- learn_more_link_->set_listener(this);
- AddChildView(learn_more_link_);
-
- if (kind == KILLED) {
- feedback_link_ = new views::Link(
- UTF16ToWide(l10n_util::GetStringUTF16(IDS_KILLED_TAB_FEEDBACK_LINK)));
- feedback_link_->SetFont(*message_font_);
- feedback_link_->SetNormalColor(kLinkColor);
- feedback_link_->set_listener(this);
- AddChildView(feedback_link_);
- }
- }
+ // Set the background color.
+ set_background(views::Background::CreateSolidBackground(
+ (kind_ == CRASHED) ? kCrashColor : kKillColor));
}
SadTabView::~SadTabView() {}
-void SadTabView::OnPaint(gfx::Canvas* canvas) {
- if (!painted_) {
- // User actually saw the error, keep track for user experience stats.
- UMA_HISTOGRAM_COUNTS("SadTab.Displayed", kind_);
- painted_ = true;
- }
- SkPaint paint;
- SkSafeUnref(paint.setShader(
- gfx::CreateGradientShader(
- 0,
- height(),
- kind_ == CRASHED ? kCrashBackgroundColor : kKillBackgroundColor,
- kind_ == CRASHED ?
- kCrashBackgroundEndColor : kKillBackgroundEndColor)));
- paint.setStyle(SkPaint::kFill_Style);
- canvas->AsCanvasSkia()->drawRectCoords(
- 0, 0, SkIntToScalar(width()), SkIntToScalar(height()), paint);
-
- canvas->DrawBitmapInt(*sad_tab_bitmap_, icon_bounds_.x(), icon_bounds_.y());
-
- canvas->DrawStringInt(title_, *title_font_, kTitleColor,
- title_bounds_.x(), title_bounds_.y(),
- title_bounds_.width(), title_bounds_.height(),
- gfx::Canvas::TEXT_ALIGN_CENTER);
-
- canvas->DrawStringInt(message_, *message_font_,
- kMessageColor, message_bounds_.x(), message_bounds_.y(),
- message_bounds_.width(), message_bounds_.height(),
- kMessageFlags);
-
- if (learn_more_link_ != NULL) {
- learn_more_link_->SetBounds(
- learn_more_bounds_.x(), learn_more_bounds_.y(),
- learn_more_bounds_.width(), learn_more_bounds_.height());
- }
- if (feedback_link_ != NULL) {
- feedback_link_->SetBounds(
- feedback_bounds_.x(), feedback_bounds_.y(),
- feedback_bounds_.width(), feedback_bounds_.height());
- }
-}
-
-void SadTabView::Layout() {
- int icon_width = sad_tab_bitmap_->width();
- int icon_height = sad_tab_bitmap_->height();
- int icon_x = (width() - icon_width) / 2;
- int icon_y = ((height() - icon_height) / 2) + kSadTabOffset;
- icon_bounds_.SetRect(icon_x, icon_y, icon_width, icon_height);
-
- int title_x = (width() - title_width_) / 2;
- int title_y = icon_bounds_.bottom() + kIconTitleSpacing;
- int title_height = title_font_->GetHeight();
- title_bounds_.SetRect(title_x, title_y, title_width_, title_height);
-
- int message_width = static_cast<int>(width() * kMessageSize);
- int message_height = 0;
- gfx::CanvasSkia::SizeStringInt(message_,
- *message_font_, &message_width,
- &message_height, kMessageFlags);
- int message_x = (width() - message_width) / 2;
- int message_y = title_bounds_.bottom() + kTitleMessageSpacing;
- message_bounds_.SetRect(message_x, message_y, message_width, message_height);
- int bottom = message_bounds_.bottom();
-
- if (learn_more_link_ != NULL) {
- gfx::Size sz = learn_more_link_->GetPreferredSize();
- gfx::Insets insets = learn_more_link_->GetInsets();
- learn_more_bounds_.SetRect((width() - sz.width()) / 2,
- bottom + kTitleMessageSpacing - insets.top(),
- sz.width(),
- sz.height());
- bottom = learn_more_bounds_.bottom();
- }
-
- if (feedback_link_ != NULL) {
- gfx::Size sz = feedback_link_->GetPreferredSize();
- gfx::Insets insets = feedback_link_->GetInsets();
- feedback_bounds_.SetRect((width() - sz.width()) / 2,
- bottom + kTitleMessageSpacing - insets.top(),
- sz.width(),
- sz.height());
- }
-}
-
void SadTabView::LinkClicked(views::Link* source, int event_flags) {
- if (tab_contents_ != NULL && source == learn_more_link_) {
+ if (tab_contents_ != NULL && source == help_link_) {
GURL help_url =
google_util::AppendGoogleLocaleParam(GURL(kind_ == CRASHED ?
chrome::kCrashReasonURL :
@@ -198,3 +71,107 @@
userfeedback::ChromeOsData_ChromeOsCategory_CRASH);
}
}
+
+void SadTabView::Layout() {
+ // Specify the maximum message width explicitly.
+ message_->SizeToFit(static_cast<int>(width() * kMessageSize));
+ View::Layout();
+}
+
+void SadTabView::ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child) {
+ if (child != this || !is_add)
+ return;
+
+ views::GridLayout* layout = views::GridLayout::CreatePanel(this);
+ SetLayoutManager(layout);
+
+ const int column_set_id = 0;
+ views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
+ columns->AddPaddingColumn(1, kPadding);
+ columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
+ 0, views::GridLayout::USE_PREF, 0, 0);
+ columns->AddPaddingColumn(1, kPadding);
+
+ views::ImageView* image = new views::ImageView();
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ image->SetImage(
+ rb.GetBitmapNamed((kind_ == CRASHED) ? IDR_SAD_TAB : IDR_KILLED_TAB));
+ layout->StartRowWithPadding(0, column_set_id, 1, kPadding);
+ layout->AddView(image);
+
+ views::Label* title = new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
+ (kind_ == CRASHED) ? IDS_SAD_TAB_TITLE : IDS_KILLED_TAB_TITLE)));
+ const gfx::Font& base_font = rb.GetFont(ResourceBundle::BaseFont);
+ title->SetFont(base_font.DeriveFont(kTitleFontSizeDelta, gfx::Font::BOLD));
+ title->SetColor(kTextColor);
+ layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
+ layout->AddView(title);
+
+ message_ = new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(
+ (kind_ == CRASHED) ? IDS_SAD_TAB_MESSAGE : IDS_KILLED_TAB_MESSAGE)));
+ message_->SetFont(base_font.DeriveFont(kMessageFontSizeDelta));
+ message_->SetColor(kTextColor);
+ message_->SetMultiLine(true);
+ layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
+ layout->AddView(message_);
+
+ if (tab_contents_) {
+ std::wstring help_link(UTF16ToWide(l10n_util::GetStringUTF16(
+ (kind_ == CRASHED) ? IDS_SAD_TAB_HELP_LINK : IDS_LEARN_MORE)));
+ help_link_ = new views::Link(help_link);
+ help_link_->SetFont(base_font.DeriveFont(kMessageFontSizeDelta));
+ help_link_->SetNormalColor(kTextColor);
+ help_link_->set_listener(this);
+
+ if (kind_ == CRASHED) {
+ size_t offset = 0;
+ string16 help_text(l10n_util::GetStringFUTF16(IDS_SAD_TAB_HELP_MESSAGE,
+ string16(), &offset));
+ views::Label* help_prefix =
+ new views::Label(UTF16ToWide(help_text.substr(0, offset)));
+ help_prefix->SetFont(base_font.DeriveFont(kMessageFontSizeDelta));
+ help_prefix->SetColor(kTextColor);
+ views::Label* help_suffix =
+ new views::Label(UTF16ToWide(help_text.substr(offset)));
+ help_suffix->SetFont(base_font.DeriveFont(kMessageFontSizeDelta));
+ help_suffix->SetColor(kTextColor);
+
+ const int help_column_set_id = 1;
+ views::ColumnSet* help_columns = layout->AddColumnSet(help_column_set_id);
+ help_columns->AddPaddingColumn(1, kPadding);
+ // Center three middle columns for the help's [prefix][link][suffix].
+ for (size_t column = 0; column < 3; column++)
+ help_columns->AddColumn(views::GridLayout::CENTER,
+ views::GridLayout::CENTER, 0, views::GridLayout::USE_PREF, 0, 0);
+ help_columns->AddPaddingColumn(1, kPadding);
+
+ layout->StartRowWithPadding(0, help_column_set_id, 0, kPadding);
+ layout->AddView(help_prefix);
+ layout->AddView(help_link_);
+ layout->AddView(help_suffix);
+ } else {
+ layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
+ layout->AddView(help_link_);
+
+ feedback_link_ = new views::Link(UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_KILLED_TAB_FEEDBACK_LINK)));
+ feedback_link_->SetFont(base_font.DeriveFont(kMessageFontSizeDelta));
+ feedback_link_->SetNormalColor(kTextColor);
+ feedback_link_->set_listener(this);
+ layout->StartRowWithPadding(0, column_set_id, 0, kPadding);
+ layout->AddView(feedback_link_);
+ }
+ }
+ layout->AddPaddingRow(1, kPadding);
+}
+
+void SadTabView::OnPaint(gfx::Canvas* canvas) {
+ if (!painted_) {
+ // User actually saw the error, keep track for user experience stats.
+ UMA_HISTOGRAM_COUNTS("SadTab.Displayed", kind_);
+ painted_ = true;
+ }
+ View::OnPaint(canvas);
+}
diff --git a/chrome/browser/ui/views/sad_tab_view.h b/chrome/browser/ui/views/sad_tab_view.h
index 573e58c..11a0271e 100644
--- a/chrome/browser/ui/views/sad_tab_view.h
+++ b/chrome/browser/ui/views/sad_tab_view.h
@@ -7,18 +7,21 @@
#pragma once
#include "base/basictypes.h"
-#include "base/string16.h"
-#include "ui/gfx/rect.h"
+#include "base/memory/scoped_ptr.h"
#include "views/controls/link_listener.h"
#include "views/view.h"
-class SkBitmap;
class TabContents;
namespace gfx {
class Font;
}
+namespace views {
+class ImageView;
+class Label;
+}
+
///////////////////////////////////////////////////////////////////////////////
//
// SadTabView
@@ -41,35 +44,25 @@
virtual ~SadTabView();
// Overridden from views::View:
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
virtual void Layout() OVERRIDE;
// Overridden from views::LinkListener:
virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
+ protected:
+ // Overridden from views::View:
+ virtual void ViewHierarchyChanged(bool is_add,
+ views::View* parent,
+ views::View* child) OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
private:
- // Assorted resources for display.
- SkBitmap* sad_tab_bitmap_;
- gfx::Font* title_font_;
- gfx::Font* message_font_;
- string16 title_;
- string16 message_;
- int title_width_;
-
TabContents* tab_contents_;
- views::Link* learn_more_link_;
- views::Link* feedback_link_;
-
- // Regions within the display for different components, populated by
- // Layout().
- gfx::Rect icon_bounds_;
- gfx::Rect title_bounds_;
- gfx::Rect message_bounds_;
- gfx::Rect learn_more_bounds_;
- gfx::Rect feedback_bounds_;
-
Kind kind_;
bool painted_;
+ views::Label* message_;
+ views::Link* help_link_;
+ views::Link* feedback_link_;
DISALLOW_COPY_AND_ASSIGN(SadTabView);
};
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index e939c75..2830f8c 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2520,6 +2520,8 @@
'browser/ui/cocoa/hung_renderer_controller.mm',
'browser/ui/cocoa/hyperlink_button_cell.h',
'browser/ui/cocoa/hyperlink_button_cell.mm',
+ 'browser/ui/cocoa/hyperlink_text_view.h',
+ 'browser/ui/cocoa/hyperlink_text_view.mm',
'browser/ui/cocoa/image_button_cell.h',
'browser/ui/cocoa/image_button_cell.mm',
'browser/ui/cocoa/image_utils.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index f631ce6..3372429 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1790,6 +1790,7 @@
'browser/ui/cocoa/html_dialog_window_controller_unittest.mm',
'browser/ui/cocoa/hung_renderer_controller_unittest.mm',
'browser/ui/cocoa/hyperlink_button_cell_unittest.mm',
+ 'browser/ui/cocoa/hyperlink_text_view_unittest.mm',
'browser/ui/cocoa/image_button_cell_unittest.mm',
'browser/ui/cocoa/image_utils_unittest.mm',
'browser/ui/cocoa/info_bubble_view_unittest.mm',