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',