summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-30 17:32:28 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-30 17:32:28 +0000
commit440cb536972b33d49eb996b9d45aa4a29c65168a (patch)
tree722dfb9eaa966494c4a94f223826e233b5ecb43e /chrome
parent237a2f1d4378c294f6a66c95ecf37093fbaccc11 (diff)
downloadchromium_src-440cb536972b33d49eb996b9d45aa4a29c65168a.zip
chromium_src-440cb536972b33d49eb996b9d45aa4a29c65168a.tar.gz
chromium_src-440cb536972b33d49eb996b9d45aa4a29c65168a.tar.bz2
[Mac] Convert the page info window to a bubble.
BUG=52430,52916 TEST=Enable Page Info Bubble in about:labs and click on the lock icon. Review URL: http://codereview.chromium.org/3461016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61077 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/cocoa/base_bubble_controller.h10
-rw-r--r--chrome/browser/cocoa/base_bubble_controller.mm28
-rw-r--r--chrome/browser/cocoa/location_bar/location_bar_view_mac.h3
-rw-r--r--chrome/browser/cocoa/location_bar/location_bar_view_mac.mm8
-rw-r--r--chrome/browser/cocoa/location_bar/location_icon_decoration.h4
-rw-r--r--chrome/browser/cocoa/location_bar/location_icon_decoration.mm12
-rw-r--r--chrome/browser/cocoa/page_info_bubble_controller.h50
-rw-r--r--chrome/browser/cocoa/page_info_bubble_controller.mm383
-rw-r--r--chrome/browser/cocoa/page_info_bubble_controller_unittest.mm191
-rw-r--r--chrome/browser/cocoa/page_info_window_mac.mm8
-rw-r--r--chrome/browser/labs.cc2
-rw-r--r--chrome/browser/page_info_model.h5
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
14 files changed, 694 insertions, 13 deletions
diff --git a/chrome/browser/cocoa/base_bubble_controller.h b/chrome/browser/cocoa/base_bubble_controller.h
index 51cfa58..b00e49c 100644
--- a/chrome/browser/cocoa/base_bubble_controller.h
+++ b/chrome/browser/cocoa/base_bubble_controller.h
@@ -26,6 +26,9 @@
IBOutlet InfoBubbleView* bubble_; // to set arrow position
}
+@property (nonatomic, readonly) NSWindow* parentWindow;
+@property (nonatomic, readonly) InfoBubbleView* bubble;
+
// Creates a bubble. |nibPath| is just the basename, e.g. @"FirstRunBubble".
// |anchoredAt| is in screen space. You need to call -showWindow: to make the
// bubble visible. It will autorelease itself when the user dismisses the
@@ -46,6 +49,11 @@
offset:(NSPoint)offset;
-@property (nonatomic, readonly) InfoBubbleView* bubble;
+// For subclasses that do not load from a XIB, this will simply set the instance
+// variables appropriately. This will also replace the |-[self window]|'s
+// contentView with an instance of InfoBubbleView.
+- (id)initWithWindow:(NSWindow*)theWindow
+ parentWindow:(NSWindow*)parentWindow
+ anchoredAt:(NSPoint)anchoredAt;
@end
diff --git a/chrome/browser/cocoa/base_bubble_controller.mm b/chrome/browser/cocoa/base_bubble_controller.mm
index b155a4f..9cebc61 100644
--- a/chrome/browser/cocoa/base_bubble_controller.mm
+++ b/chrome/browser/cocoa/base_bubble_controller.mm
@@ -7,12 +7,14 @@
#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/mac_util.h"
+#include "base/scoped_nsobject.h"
#include "base/string_util.h"
#import "chrome/browser/cocoa/info_bubble_view.h"
#include "grit/generated_resources.h"
@implementation BaseBubbleController
+@synthesize parentWindow = parentWindow_;
@synthesize bubble = bubble_;
- (id)initWithWindowNibPath:(NSString*)nibPath
@@ -48,6 +50,32 @@
anchoredAt:anchor];
}
+- (id)initWithWindow:(NSWindow*)theWindow
+ parentWindow:(NSWindow*)parentWindow
+ anchoredAt:(NSPoint)anchoredAt {
+ DCHECK(theWindow);
+ if ((self = [super initWithWindow:theWindow])) {
+ parentWindow_ = parentWindow;
+ anchor_ = anchoredAt;
+ DCHECK(![[self window] delegate]);
+ [theWindow setDelegate:self];
+
+ scoped_nsobject<InfoBubbleView> contentView(
+ [[InfoBubbleView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)]);
+ [theWindow setContentView:contentView.get()];
+ bubble_ = contentView.get();
+
+ // Watch to see if the parent window closes, and if so, close this one.
+ NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(parentWindowWillClose:)
+ name:NSWindowWillCloseNotification
+ object:parentWindow_];
+
+ [self awakeFromNib];
+ }
+ return self;
+}
- (void)awakeFromNib {
// Check all connections have been made in Interface Builder.
diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h
index 0ec9058..7ff8306 100644
--- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.h
+++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.h
@@ -91,6 +91,9 @@ class LocationBarViewMac : public AutocompleteEditController,
// Get the point on the star for the bookmark bubble to aim at.
NSPoint GetBookmarkBubblePoint() const;
+ // Get the point in the security icon at which the page info bubble aims.
+ NSPoint GetPageInfoBubblePoint() const;
+
// Updates the location bar. Resets the bar's permanent text and
// security style, and if |should_restore_state| is true, restores
// saved state from the tab (for tab switching).
diff --git a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm
index d8b8b80..8863841 100644
--- a/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm
+++ b/chrome/browser/cocoa/location_bar/location_bar_view_mac.mm
@@ -409,6 +409,14 @@ NSPoint LocationBarViewMac::GetBookmarkBubblePoint() const {
return [field_ convertPoint:point toView:nil];
}
+NSPoint LocationBarViewMac::GetPageInfoBubblePoint() const {
+ AutocompleteTextFieldCell* cell = [field_ cell];
+ const NSRect frame = [cell frameForDecoration:location_icon_decoration_.get()
+ inFrame:[field_ bounds]];
+ const NSPoint point = location_icon_decoration_->GetBubblePointInFrame(frame);
+ return [field_ convertPoint:point toView:nil];
+}
+
NSImage* LocationBarViewMac::GetKeywordImage(const std::wstring& keyword) {
const TemplateURL* template_url =
profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword);
diff --git a/chrome/browser/cocoa/location_bar/location_icon_decoration.h b/chrome/browser/cocoa/location_bar/location_icon_decoration.h
index 028a0ab..4cba72f 100644
--- a/chrome/browser/cocoa/location_bar/location_icon_decoration.h
+++ b/chrome/browser/cocoa/location_bar/location_icon_decoration.h
@@ -28,6 +28,10 @@ class LocationIconDecoration : public ImageDecoration {
return GetDrawRectInFrame(frame);
}
+ // Get the point where the page info bubble should point within the
+ // decoration's frame, in the |owner_|'s coordinates.
+ NSPoint GetBubblePointInFrame(NSRect frame);
+
// Show the page info panel on click.
virtual bool OnMousePressed(NSRect frame);
virtual bool AcceptsMousePress() { return true; }
diff --git a/chrome/browser/cocoa/location_bar/location_icon_decoration.mm b/chrome/browser/cocoa/location_bar/location_icon_decoration.mm
index 9ec9794..f967af2 100644
--- a/chrome/browser/cocoa/location_bar/location_icon_decoration.mm
+++ b/chrome/browser/cocoa/location_bar/location_icon_decoration.mm
@@ -11,6 +11,12 @@
#include "chrome/browser/tab_contents/tab_contents.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
+// The info-bubble point should look like it points to the point
+// between the star's lower tips. The popup should be where the
+// Omnibox popup ends up (2px below field). Determined via Pixie.app
+// magnification.
+const CGFloat kBubblePointYOffset = 2.0;
+
LocationIconDecoration::LocationIconDecoration(LocationBarViewMac* owner)
: owner_(owner) {
}
@@ -45,6 +51,12 @@ NSPasteboard* LocationIconDecoration::GetDragPasteboard() {
return pboard;
}
+NSPoint LocationIconDecoration::GetBubblePointInFrame(NSRect frame) {
+ const NSRect draw_frame = GetDrawRectInFrame(frame);
+ return NSMakePoint(NSMidX(draw_frame),
+ NSMaxY(draw_frame) - kBubblePointYOffset);
+}
+
bool LocationIconDecoration::OnMousePressed(NSRect frame) {
// Do not show page info if the user has been editing the location
// bar, or the location bar is at the NTP.
diff --git a/chrome/browser/cocoa/page_info_bubble_controller.h b/chrome/browser/cocoa/page_info_bubble_controller.h
new file mode 100644
index 0000000..7d3d9a3
--- /dev/null
+++ b/chrome/browser/cocoa/page_info_bubble_controller.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 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>
+
+#include "base/scoped_nsobject.h"
+#include "base/scoped_ptr.h"
+#import "chrome/browser/cocoa/base_bubble_controller.h"
+#include "chrome/browser/page_info_model.h"
+
+// This NSWindowController subclass manages the InfoBubbleWindow and view that
+// are displayed when the user clicks the security lock icon.
+@interface PageInfoBubbleController : BaseBubbleController {
+ @private
+ // The model that generates the content displayed by the controller.
+ scoped_ptr<PageInfoModel> model_;
+
+ // Thin bridge that pushes model-changed notifications from C++ to Cocoa.
+ scoped_ptr<PageInfoModel::PageInfoModelObserver> bridge_;
+
+ // The certificate ID for the page, 0 if the page is not over HTTPS.
+ int certID_;
+
+ // Reference to the images that are placed within the UI.
+ scoped_nsobject<NSImage> okImage_;
+ scoped_nsobject<NSImage> warningMinorImage_;
+ scoped_nsobject<NSImage> warningMajorImage_;
+ scoped_nsobject<NSImage> errorImage_;
+}
+
+@property (nonatomic, assign) int certID;
+
+// Designated initializer. The new instance will take ownership of |model| and
+// |bridge|. There should be a 1:1 mapping of models to bridges. The
+// controller will release itself when the bubble is closed.
+- (id)initWithPageInfoModel:(PageInfoModel*)model
+ modelObserver:(PageInfoModel::PageInfoModelObserver*)bridge
+ parentWindow:(NSWindow*)parentWindow;
+
+// Shows the certificate display window. Note that this will implicitly close
+// the bubble because the certificate window will become key. The certificate
+// information attaches itself as a sheet to the |parentWindow|.
+- (IBAction)showCertWindow:(id)sender;
+
+@end
+
+@interface PageInfoBubbleController (ExposedForUnitTesting)
+- (void)performLayout;
+@end
diff --git a/chrome/browser/cocoa/page_info_bubble_controller.mm b/chrome/browser/cocoa/page_info_bubble_controller.mm
new file mode 100644
index 0000000..708d0e6
--- /dev/null
+++ b/chrome/browser/cocoa/page_info_bubble_controller.mm
@@ -0,0 +1,383 @@
+// Copyright (c) 2010 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/cocoa/page_info_bubble_controller.h"
+
+#include "app/l10n_util_mac.h"
+#include "app/resource_bundle.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/certificate_viewer.h"
+#import "chrome/browser/cocoa/browser_window_controller.h"
+#import "chrome/browser/cocoa/location_bar/location_bar_view_mac.h"
+#import "chrome/browser/cocoa/info_bubble_view.h"
+#import "chrome/browser/cocoa/info_bubble_window.h"
+#include "chrome/browser/profile.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/x509_certificate.h"
+#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
+
+@interface PageInfoBubbleController (Private)
+- (PageInfoModel*)model;
+- (NSButton*)certificateButtonWithFrame:(NSRect)frame;
+- (NSImage*)statusIconForState:(PageInfoModel::SectionInfoState)state;
+- (void)configureTextFieldAsLabel:(NSTextField*)textField;
+- (CGFloat)addTitleViewForInfo:(const PageInfoModel::SectionInfo&)info
+ toSubviews:(NSMutableArray*)subviews
+ atPoint:(NSPoint)point;
+- (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info
+ toSubviews:(NSMutableArray*)subviews
+ atPoint:(NSPoint)point;
+- (CGFloat)addCertificateButtonToSubviews:(NSMutableArray*)subviews
+ atOffset:(CGFloat)offset;
+- (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info
+ toSubviews:(NSMutableArray*)subviews
+ atOffset:(CGFloat)offset;
+@end
+
+namespace {
+
+// The width of the window, in view coordinates. The height will be determined
+// by the content.
+const NSInteger kWindowWidth = 380;
+
+// Spacing in between sections.
+const NSInteger kVerticalSpacing = 10;
+
+// Padding along on the X-axis between the window frame and content.
+const NSInteger kFramePadding = 20;
+
+// Spacing between the title and description text views.
+const NSInteger kTitleSpacing = 2;
+
+// Spacing between the image and the text.
+const NSInteger kImageSpacing = 10;
+
+// Square size of the image.
+const CGFloat kImageSize = 30;
+
+// The X position of the text fields. Variants for with and without an image.
+const CGFloat kTextXPositionNoImage = kFramePadding;
+const CGFloat kTextXPosition = kTextXPositionNoImage + kImageSize +
+ kImageSpacing;
+
+// Width of the text fields.
+const CGFloat kTextWidth = kWindowWidth - (kImageSize + kImageSpacing +
+ kFramePadding * 2);
+
+// Takes in the bubble's height and the parent window, which should be a
+// BrowserWindow, and gets the proper anchor point for the bubble. The point is
+// in screen coordinates.
+NSPoint AnchorPointFromParentWindow(NSWindow* parent, CGFloat bubbleHeight) {
+ BrowserWindowController* controller = [parent windowController];
+ NSPoint origin = NSZeroPoint;
+ if ([controller isKindOfClass:[BrowserWindowController class]]) {
+ LocationBarViewMac* location_bar = [controller locationBarBridge];
+ if (location_bar) {
+ NSPoint arrowTip = location_bar->GetPageInfoBubblePoint();
+ origin = [parent convertBaseToScreen:arrowTip];
+ origin.y -= bubbleHeight;
+ }
+ }
+ return origin;
+}
+
+// Bridge that listens for change notifications from the model.
+class PageInfoModelBubbleBridge : public PageInfoModel::PageInfoModelObserver {
+ public:
+ PageInfoModelBubbleBridge() : controller_(nil) {}
+
+ // PageInfoModelObserver implementation.
+ virtual void ModelChanged() {
+ [controller_ performLayout];
+ }
+
+ // Sets the controller.
+ void set_controller(PageInfoBubbleController* controller) {
+ controller_ = controller;
+ }
+
+ private:
+ PageInfoBubbleController* controller_; // weak
+};
+
+} // namespace
+
+namespace browser {
+
+void ShowPageInfoBubble(gfx::NativeWindow parent,
+ Profile* profile,
+ const GURL& url,
+ const NavigationEntry::SSLStatus& ssl,
+ bool show_history) {
+ PageInfoModelBubbleBridge* bridge = new PageInfoModelBubbleBridge();
+ PageInfoModel* model =
+ new PageInfoModel(profile, url, ssl, show_history, bridge);
+ PageInfoBubbleController* controller =
+ [[PageInfoBubbleController alloc] initWithPageInfoModel:model
+ modelObserver:bridge
+ parentWindow:parent];
+ bridge->set_controller(controller);
+ [controller setCertID:ssl.cert_id()];
+ [controller showWindow:nil];
+}
+
+} // namespace browser
+
+@implementation PageInfoBubbleController
+
+@synthesize certID = certID_;
+
+- (id)initWithPageInfoModel:(PageInfoModel*)model
+ modelObserver:(PageInfoModel::PageInfoModelObserver*)bridge
+ parentWindow:(NSWindow*)parentWindow {
+ // Use an arbitrary height because it will be changed by the bridge.
+ NSRect contentRect = NSMakeRect(0, 0, kWindowWidth, 50);
+ // Create an empty window into which content is placed.
+ scoped_nsobject<InfoBubbleWindow> window(
+ [[InfoBubbleWindow alloc] initWithContentRect:contentRect
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO]);
+
+ NSPoint anchorPoint = AnchorPointFromParentWindow(parentWindow, 0);
+ if ((self = [super initWithWindow:window.get()
+ parentWindow:parentWindow
+ anchoredAt:anchorPoint])) {
+ model_.reset(model);
+ bridge_.reset(bridge);
+
+ // Load the image refs.
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ okImage_.reset([rb.GetNSImageNamed(IDR_PAGEINFO_GOOD) retain]);
+ DCHECK_GE(kImageSize, [okImage_ size].width);
+ DCHECK_GE(kImageSize, [okImage_ size].height);
+ warningMinorImage_.reset(
+ [rb.GetNSImageNamed(IDR_PAGEINFO_WARNING_MINOR) retain]);
+ DCHECK_GE(kImageSize, [warningMinorImage_ size].width);
+ DCHECK_GE(kImageSize, [warningMinorImage_ size].height);
+ warningMajorImage_.reset(
+ [rb.GetNSImageNamed(IDR_PAGEINFO_WARNING_MAJOR) retain]);
+ DCHECK_GE(kImageSize, [warningMajorImage_ size].width);
+ DCHECK_GE(kImageSize, [warningMajorImage_ size].height);
+ errorImage_.reset([rb.GetNSImageNamed(IDR_PAGEINFO_BAD) retain]);
+ DCHECK_GE(kImageSize, [errorImage_ size].width);
+ DCHECK_GE(kImageSize, [errorImage_ size].height);
+
+ [[self bubble] setArrowLocation:info_bubble::kTopLeft];
+
+ [self performLayout];
+ }
+ return self;
+}
+
+- (PageInfoModel*)model {
+ return model_.get();
+}
+
+- (IBAction)showCertWindow:(id)sender {
+ DCHECK(certID_ != 0);
+ ShowCertificateViewerByID([self parentWindow], certID_);
+}
+
+// This will create the subviews for the page info window. The general layout
+// is 2 or 3 boxed and titled sections, each of which has a status image to
+// provide visual feedback and a description that explains it. The description
+// text is usually only 1 or 2 lines, but can be much longer. At the bottom of
+// the window is a button to view the SSL certificate, which is disabled if
+// not using HTTPS.
+- (void)performLayout {
+ // |offset| is the Y position that should be drawn at next.
+ CGFloat offset = kVerticalSpacing;
+
+ // Keep the new subviews in an array that gets replaced at the end.
+ NSMutableArray* subviews = [NSMutableArray array];
+
+ // Build the window from bottom-up because Cocoa's coordinate origin is the
+ // lower left.
+ for (int i = model_->GetSectionCount() - 1; i >= 0; --i) {
+ PageInfoModel::SectionInfo info = model_->GetSectionInfo(i);
+
+ // Only certain sections have images. This affects the X position.
+ BOOL hasImage = info.type == PageInfoModel::SECTION_INFO_IDENTITY ||
+ info.type == PageInfoModel::SECTION_INFO_CONNECTION;
+ CGFloat xPosition = (hasImage ? kTextXPosition : kTextXPositionNoImage);
+
+ if (info.type == PageInfoModel::SECTION_INFO_IDENTITY) {
+ offset += [self addCertificateButtonToSubviews:subviews
+ atOffset:offset];
+ }
+
+ // Create the description of the state.
+ offset += [self addDescriptionViewForInfo:info
+ toSubviews:subviews
+ atPoint:NSMakePoint(xPosition, offset)];
+
+ // Add the title.
+ offset += kTitleSpacing;
+ offset += [self addTitleViewForInfo:info
+ toSubviews:subviews
+ atPoint:NSMakePoint(xPosition, offset)];
+
+ // Insert the image subview for sections that are appropriate.
+ if (hasImage) {
+ [self addImageViewForInfo:info toSubviews:subviews atOffset:offset];
+ }
+
+ // Add the separators.
+ offset += kVerticalSpacing;
+ if (i != 0) {
+ scoped_nsobject<NSBox> spacer(
+ [[NSBox alloc] initWithFrame:NSMakeRect(0, offset, kWindowWidth, 1)]);
+ [spacer setBoxType:NSBoxSeparator];
+ [spacer setBorderType:NSLineBorder];
+ [subviews addObject:spacer.get()];
+ offset += kVerticalSpacing;
+ }
+ }
+
+ // Replace the window's content.
+ [[[self window] contentView] setSubviews:subviews];
+
+ offset += kFramePadding;
+
+ // TODO(rsesek): Remove constant value to account for http://crbug.com/57306.
+ NSRect windowFrame = NSMakeRect(0, 0, kWindowWidth, 50);
+ windowFrame.size.height += offset;
+ windowFrame.size = [[[self window] contentView] convertSize:windowFrame.size
+ toView:nil];
+ // Just setting |size| will cause the window to grow upwards. Shift the
+ // origin up by grow amount, which causes the window to grow downwards.
+ windowFrame.origin = AnchorPointFromParentWindow([self parentWindow],
+ NSHeight(windowFrame));
+
+ // Resize the window. Only animate if the window is visible, otherwise it
+ // could be "growing" while it's opening, looking awkward.
+ [[self window] setFrame:windowFrame
+ display:YES
+ animate:[[self window] isVisible]];
+}
+
+// Creates the button with a given |frame| that, when clicked, will show the
+// SSL certificate information.
+- (NSButton*)certificateButtonWithFrame:(NSRect)frame {
+ NSButton* certButton = [[[NSButton alloc] initWithFrame:frame] autorelease];
+ [certButton setTitle:
+ l10n_util::GetNSStringWithFixup(IDS_PAGEINFO_CERT_INFO_BUTTON)];
+ [certButton setButtonType:NSMomentaryPushInButton];
+ [certButton setBezelStyle:NSRoundRectBezelStyle];
+ [certButton setTarget:self];
+ [certButton setAction:@selector(showCertWindow:)];
+ return certButton;
+}
+
+// Returns a weak reference to the NSImage instance to used, or nil if none, for
+// the specified info |state|.
+- (NSImage*)statusIconForState:(PageInfoModel::SectionInfoState)state {
+ switch (state) {
+ case PageInfoModel::SECTION_STATE_OK:
+ return okImage_.get();
+ case PageInfoModel::SECTION_STATE_WARNING_MINOR:
+ return warningMinorImage_.get();
+ case PageInfoModel::SECTION_STATE_WARNING_MAJOR:
+ return warningMajorImage_.get();
+ case PageInfoModel::SECTION_STATE_ERROR:
+ return errorImage_.get();
+ default:
+ return nil;
+ }
+}
+
+// Sets proprties on the given |field| to act as the title or description labels
+// in the bubble.
+- (void)configureTextFieldAsLabel:(NSTextField*)textField {
+ [textField setEditable:NO];
+ [textField setDrawsBackground:NO];
+ [textField setBezeled:NO];
+}
+
+// Adds the title text field at the given x,y position, and returns the y
+// position for the next element.
+- (CGFloat)addTitleViewForInfo:(const PageInfoModel::SectionInfo&)info
+ toSubviews:(NSMutableArray*)subviews
+ atPoint:(NSPoint)point {
+ NSRect frame = NSMakeRect(point.x, point.y, kTextWidth, kImageSpacing);
+ scoped_nsobject<NSTextField> titleField(
+ [[NSTextField alloc] initWithFrame:frame]);
+ [self configureTextFieldAsLabel:titleField.get()];
+ [titleField setStringValue:base::SysUTF16ToNSString(info.title)];
+ [titleField setFont:[NSFont boldSystemFontOfSize:11]];
+ frame.size.height +=
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
+ titleField];
+ [titleField setFrame:frame];
+ [subviews addObject:titleField.get()];
+ return NSHeight(frame);
+}
+
+// Adds the description text field at the given x,y position, and returns the y
+// position for the next element.
+- (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info
+ toSubviews:(NSMutableArray*)subviews
+ atPoint:(NSPoint)point {
+ NSRect frame = NSMakeRect(point.x, point.y, kTextWidth, kImageSize);
+ scoped_nsobject<NSTextField> textField(
+ [[NSTextField alloc] initWithFrame:frame]);
+ [self configureTextFieldAsLabel:textField.get()];
+ [textField setStringValue:base::SysUTF16ToNSString(info.description)];
+ [textField setFont:[NSFont labelFontOfSize:11]];
+
+ // If the text is oversized, resize the text field.
+ frame.size.height +=
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:
+ textField];
+ [subviews addObject:textField.get()];
+ return NSHeight(frame);
+}
+
+// Adds the certificate button at a pre-determined x position and the given y.
+// Returns the y position for the next element.
+- (CGFloat)addCertificateButtonToSubviews:(NSMutableArray*)subviews
+ atOffset:(CGFloat)offset {
+ // Create the certificate button. The frame will be fixed up by GTM, so
+ // use arbitrary values.
+ NSRect frame = NSMakeRect(kTextXPosition, offset, 100, 20);
+ NSButton* certButton = [self certificateButtonWithFrame:frame];
+ [subviews addObject:certButton];
+ [GTMUILocalizerAndLayoutTweaker sizeToFitView:certButton];
+
+ // By default, assume that we don't have certificate information to show.
+ [certButton setEnabled:NO];
+ if (certID_) {
+ scoped_refptr<net::X509Certificate> cert;
+ CertStore::GetSharedInstance()->RetrieveCert(certID_, &cert);
+
+ // Don't bother showing certificates if there isn't one. Gears runs
+ // with no OS root certificate.
+ if (cert.get() && cert->os_cert_handle()) {
+ [certButton setEnabled:YES];
+ }
+ }
+
+ return NSHeight(frame) + kVerticalSpacing;
+}
+
+// Adds the state image at a pre-determined x position and the given y. This
+// does not affect the next Y position because the image is placed next to
+// a text field that is larger and accounts for the image's size.
+- (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info
+ toSubviews:(NSMutableArray*)subviews
+ atOffset:(CGFloat)offset {
+ NSRect frame = NSMakeRect(kFramePadding, offset - kImageSize, kImageSize,
+ kImageSize);
+ scoped_nsobject<NSImageView> imageView(
+ [[NSImageView alloc] initWithFrame:frame]);
+ [imageView setImageFrameStyle:NSImageFrameNone];
+ [imageView setImage:[self statusIconForState:info.state]];
+ [subviews addObject:imageView.get()];
+}
+
+@end
diff --git a/chrome/browser/cocoa/page_info_bubble_controller_unittest.mm b/chrome/browser/cocoa/page_info_bubble_controller_unittest.mm
new file mode 100644
index 0000000..b7a51ad
--- /dev/null
+++ b/chrome/browser/cocoa/page_info_bubble_controller_unittest.mm
@@ -0,0 +1,191 @@
+// Copyright (c) 2010 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 "app/l10n_util.h"
+#include "base/scoped_nsobject.h"
+#include "base/string_util.h"
+#include "base/string_number_conversions.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#import "chrome/browser/cocoa/page_info_bubble_controller.h"
+#include "chrome/browser/cocoa/browser_test_helper.h"
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "chrome/browser/page_info_model.h"
+#include "grit/generated_resources.h"
+
+namespace {
+
+class FakeModel : public PageInfoModel {
+ public:
+ void AddSection(SectionInfoState state,
+ const string16& title,
+ const string16& description,
+ SectionInfoType type) {
+ sections_.push_back(SectionInfo(
+ state, title, string16(), description, type));
+ }
+};
+
+class FakeBridge : public PageInfoModel::PageInfoModelObserver {
+ public:
+ void ModelChanged() {}
+};
+
+class PageInfoBubbleControllerTest : public CocoaTest {
+ public:
+ PageInfoBubbleControllerTest() {
+ controller_ = nil;
+ model_ = new FakeModel();
+ }
+
+ virtual void TearDown() {
+ [controller_ close];
+ CocoaTest::TearDown();
+ }
+
+ void CreateBubble() {
+ // The controller cleans up after itself when the window closes.
+ controller_ =
+ [[PageInfoBubbleController alloc] initWithPageInfoModel:model_
+ modelObserver:NULL
+ parentWindow:test_window()];
+ window_ = [controller_ window];
+ [controller_ showWindow:nil];
+ }
+
+ // Checks the controller's window for the requisite subviews in the given
+ // numbers.
+ void CheckWindow(int text_count,
+ int image_count,
+ int spacer_count,
+ int button_count) {
+ for (NSView* view in [[window_ contentView] subviews]) {
+ if ([view isKindOfClass:[NSTextField class]]) {
+ --text_count;
+ } else if ([view isKindOfClass:[NSImageView class]]) {
+ --image_count;
+ } else if ([view isKindOfClass:[NSBox class]]) {
+ --spacer_count;
+ } else if ([view isKindOfClass:[NSButton class]]) {
+ --button_count;
+ CheckButton(static_cast<NSButton*>(view));
+ } else {
+ ADD_FAILURE() << "Unknown subview: " << [[view description] UTF8String];
+ }
+ }
+ EXPECT_EQ(0, text_count);
+ EXPECT_EQ(0, image_count);
+ EXPECT_EQ(0, spacer_count);
+ EXPECT_EQ(0, button_count);
+ EXPECT_EQ([window_ delegate], controller_);
+ }
+
+ // Checks that a button is hooked up correctly.
+ void CheckButton(NSButton* button) {
+ EXPECT_EQ(@selector(showCertWindow:), [button action]);
+ EXPECT_EQ(controller_, [button target]);
+ EXPECT_TRUE([button stringValue]);
+ }
+
+ BrowserTestHelper helper_;
+
+ PageInfoBubbleController* controller_; // Weak, owns self.
+ FakeModel* model_; // Weak, owned by controller.
+ NSWindow* window_; // Weak, owned by controller.
+};
+
+
+TEST_F(PageInfoBubbleControllerTest, NoHistoryNoSecurity) {
+ model_->AddSection(PageInfoModel::SECTION_STATE_ERROR,
+ l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE),
+ l10n_util::GetStringFUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY,
+ ASCIIToUTF16("google.com")),
+ PageInfoModel::SECTION_INFO_IDENTITY);
+ model_->AddSection(PageInfoModel::SECTION_STATE_ERROR,
+ l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE),
+ l10n_util::GetStringFUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT,
+ ASCIIToUTF16("google.com")),
+ PageInfoModel::SECTION_INFO_CONNECTION);
+
+ CreateBubble();
+ CheckWindow(/*text=*/4, /*image=*/2, /*spacer=*/1, /*button=*/1);
+}
+
+
+TEST_F(PageInfoBubbleControllerTest, HistoryNoSecurity) {
+ model_->AddSection(PageInfoModel::SECTION_STATE_ERROR,
+ l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE),
+ l10n_util::GetStringFUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_UNKNOWN_PARTY,
+ ASCIIToUTF16("google.com")),
+ PageInfoModel::SECTION_INFO_IDENTITY);
+ model_->AddSection(PageInfoModel::SECTION_STATE_ERROR,
+ l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE),
+ l10n_util::GetStringFUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_NOT_ENCRYPTED_CONNECTION_TEXT,
+ ASCIIToUTF16("google.com")),
+ PageInfoModel::SECTION_INFO_CONNECTION);
+
+ // In practice, the history information comes later because it's queried
+ // asynchronously, so replicate the double-build here.
+ CreateBubble();
+
+ model_->AddSection(PageInfoModel::SECTION_STATE_ERROR,
+ l10n_util::GetStringUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_PERSONAL_HISTORY_TITLE),
+ l10n_util::GetStringUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_FIRST_VISITED_TODAY),
+ PageInfoModel::SECTION_INFO_FIRST_VISIT);
+
+ [controller_ performLayout];
+
+ CheckWindow(/*text=*/6, /*image=*/2, /*spacer=*/2, /*button=*/1);
+}
+
+
+TEST_F(PageInfoBubbleControllerTest, NoHistoryMixedSecurity) {
+ model_->AddSection(PageInfoModel::SECTION_STATE_OK,
+ l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_IDENTITY_TITLE),
+ l10n_util::GetStringFUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_SECURE_IDENTITY,
+ ASCIIToUTF16("Goat Security Systems")),
+ PageInfoModel::SECTION_INFO_IDENTITY);
+
+ // This string is super long and the text should overflow the default clip
+ // region (kImageSize).
+ string16 description = l10n_util::GetStringFUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_SENTENCE_LINK,
+ l10n_util::GetStringFUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_CONNECTION_TEXT,
+ ASCIIToUTF16("chrome.google.com"),
+ base::IntToString16(1024)),
+ l10n_util::GetStringUTF16(
+ IDS_PAGE_INFO_SECURITY_TAB_ENCRYPTED_INSECURE_CONTENT_WARNING));
+
+ model_->AddSection(PageInfoModel::SECTION_STATE_OK,
+ l10n_util::GetStringUTF16(IDS_PAGE_INFO_SECURITY_TAB_CONNECTION_TITLE),
+ description,
+ PageInfoModel::SECTION_INFO_CONNECTION);
+
+ CreateBubble();
+
+ NSArray* subviews = [[window_ contentView] subviews];
+ CheckWindow(/*text=*/4, /*image=*/2, /*spacer=*/1, /*button=*/1);
+
+ // Look for the over-sized box.
+ NSString* targetDesc = base::SysUTF16ToNSString(description);
+ for (NSView* subview in subviews) {
+ if ([subview isKindOfClass:[NSTextField class]]) {
+ NSTextField* desc = static_cast<NSTextField*>(subview);
+ if ([[desc stringValue] isEqualToString:targetDesc]) {
+ // Typical box frame is ~55px, make sure this is extra large.
+ EXPECT_LT(75, NSHeight([desc frame]));
+ }
+ }
+ }
+}
+
+} // namespace
diff --git a/chrome/browser/cocoa/page_info_window_mac.mm b/chrome/browser/cocoa/page_info_window_mac.mm
index c98e357..8ca4c1b 100644
--- a/chrome/browser/cocoa/page_info_window_mac.mm
+++ b/chrome/browser/cocoa/page_info_window_mac.mm
@@ -57,14 +57,6 @@ void ShowPageInfo(gfx::NativeWindow parent,
PageInfoWindowMac::ShowPageInfo(parent, profile, url, ssl, show_history);
}
-void ShowPageInfoBubble(gfx::NativeWindow parent,
- Profile* profile,
- const GURL& url,
- const NavigationEntry::SSLStatus& ssl,
- bool show_history) {
- NOTIMPLEMENTED();
-}
-
} // namespace browser
PageInfoWindowMac::PageInfoWindowMac(PageInfoWindowController* controller,
diff --git a/chrome/browser/labs.cc b/chrome/browser/labs.cc
index 874ba2f..c299b82 100644
--- a/chrome/browser/labs.cc
+++ b/chrome/browser/labs.cc
@@ -101,7 +101,7 @@ const Experiment kExperiments[] = {
"page-info-bubble", // Do not change; see above.
IDS_LABS_PAGE_INFO_BUBBLE_NAME,
IDS_LABS_PAGE_INFO_BUBBLE_DESCRIPTION,
- kOsWin | kOsLinux,
+ kOsAll,
switches::kEnableNewPageInfoBubble
},
{
diff --git a/chrome/browser/page_info_model.h b/chrome/browser/page_info_model.h
index 1f9942f..77ba614 100644
--- a/chrome/browser/page_info_model.h
+++ b/chrome/browser/page_info_model.h
@@ -23,10 +23,9 @@ class PageInfoModel {
public:
class PageInfoModelObserver {
public:
- virtual void ModelChanged() = 0;
-
- protected:
virtual ~PageInfoModelObserver() {}
+
+ virtual void ModelChanged() = 0;
};
enum SectionInfoType {
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 57e64bf..5db948f 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1056,6 +1056,8 @@
'browser/cocoa/objc_method_swizzle.mm',
'browser/cocoa/objc_zombie.h',
'browser/cocoa/objc_zombie.mm',
+ 'browser/cocoa/page_info_bubble_controller.h',
+ 'browser/cocoa/page_info_bubble_controller.mm',
'browser/cocoa/page_info_window_controller.h',
'browser/cocoa/page_info_window_controller.mm',
'browser/cocoa/page_info_window_mac.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index abcf1fa..48fc7f4 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1010,6 +1010,7 @@
'browser/cocoa/nsimage_cache_unittest.mm',
'browser/cocoa/nsmenuitem_additions_unittest.mm',
'browser/cocoa/objc_method_swizzle_unittest.mm',
+ 'browser/cocoa/page_info_bubble_controller_unittest.mm',
'browser/cocoa/page_info_window_mac_unittest.mm',
'browser/cocoa/preferences_window_controller_unittest.mm',
'browser/cocoa/reload_button_unittest.mm',