Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Unified Diff: chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm

Issue 11416047: Add mac UI for password generation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments and tests Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm
diff --git a/chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm b/chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..cbefefa135585e1bf11c76fb25bb4bbc63a6ef66
--- /dev/null
+++ b/chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm
@@ -0,0 +1,378 @@
+// Copyright (c) 2012 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/browser/password_generation_bubble_controller.h"
+
+#include "base/sys_string_conversions.h"
+#include "base/mac/foundation_util.h"
+#include "chrome/common/autofill_messages.h"
+#include "chrome/browser/autofill/password_generator.h"
+#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#import "chrome/browser/ui/cocoa/info_bubble_view.h"
+#import "chrome/browser/ui/cocoa/info_bubble_window.h"
+#include "chrome/browser/ui/cocoa/key_equivalent_constants.h"
+#import "chrome/browser/ui/cocoa/styled_text_field_cell.h"
+#import "chrome/browser/ui/cocoa/tracking_area.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/common/password_form.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+// Size of the border in the bubble.
+const CGFloat kBorderSize = 9.0;
+
+// Visible size of the textfield.
+const CGFloat kTextFieldHeight = 20.0;
+const CGFloat kTextFieldWidth = 172.0;
+
+// Frame padding necessary to make the textfield the correct visible size.
+const CGFloat kTextFieldTopPadding = 3.0;
+
+// Visible size of the button
+const CGFloat kButtonWidth = 63.0;
+const CGFloat kButtonHeight = 20.0;
+
+// Padding that is added to the frame around the button to make it the
+// correct visible size. Determined via visual inspection.
+const CGFloat kButtonHorizontalPadding = 6.5;
+const CGFloat kButtonVerticalPadding = 3.0;
+
+// Visible size of the title.
+const CGFloat kTitleWidth = 170.0;
+const CGFloat kTitleHeight = 15.0;
+
+// Space between the title and the textfield.
+const CGFloat kVerticalSpacing = 13.0;
+
+// Space between the textfield and the button.
+const CGFloat kHorizontalSpacing = 4.0;
+
+// We don't actually want the border to be kBorderSize on top as there is
+// whitespace in the title text that makes it looks substantially bigger.
+const CGFloat kTopBorderOffset = 3.0;
+
+const CGFloat kIconSize = 26.0;
+
+} // namespace
+
+// Customized StyledTextFieldCell to display one button decoration that changes
+// on hover.
+@interface PasswordGenerationTextFieldCell : StyledTextFieldCell {
+ @private
+ PasswordGenerationBubbleController* controller_;
+ BOOL hovering_;
+ scoped_nsobject<NSImage> normalImage_;
+ scoped_nsobject<NSImage> hoverImage_;
+}
+
+- (void)setUpWithController:(PasswordGenerationBubbleController*)controller
+ normalImage:(NSImage*)normalImage
+ hoverImage:(NSImage*)hoverImage;
+- (void)mouseEntered:(NSEvent*)theEvent
+ inView:(PasswordGenerationTextField*)controlView;
+- (void)mouseExited:(NSEvent*)theEvent
+ inView:(PasswordGenerationTextField*)controlView;
+- (BOOL)mouseDown:(NSEvent*)theEvent
+ inView:(PasswordGenerationTextField*)controlView;
+- (void)setUpTrackingAreaInRect:(NSRect)frame
+ ofView:(PasswordGenerationTextField*)controlView;
+// Exposed for testing.
+- (NSRect)getIconFrame:(NSRect)cellFrame;
+@end
+
+@implementation PasswordGenerationTextField
+
++ (Class)cellClass {
+ return [PasswordGenerationTextFieldCell class];
+}
+
+- (PasswordGenerationTextFieldCell*)cell {
+ return base::mac::ObjCCastStrict<PasswordGenerationTextFieldCell>(
+ [super cell]);
+}
+
+- (id)initWithFrame:(NSRect)frame
+ withController:(PasswordGenerationBubbleController*)controller
+ normalImage:(NSImage*)normalImage
+ hoverImage:(NSImage*)hoverImage {
+ self = [super initWithFrame:frame];
+ if (self) {
+ PasswordGenerationTextFieldCell* cell = [self cell];
+ [cell setUpWithController:controller
+ normalImage:normalImage
+ hoverImage:hoverImage];
+ [cell setUpTrackingAreaInRect:[self bounds] ofView:self];
+ }
+ return self;
+}
+
+- (void)mouseEntered:(NSEvent*)theEvent {
+ [[self cell] mouseEntered:theEvent inView:self];
+}
+
+- (void)mouseExited:(NSEvent*)theEvent {
+ [[self cell] mouseExited:theEvent inView:self];
+}
+
+- (void)mouseDown:(NSEvent*)theEvent {
Scott Hess - ex-Googler 2012/11/28 20:01:06 I'm looking at this and wondering what happens if
Garrett Casto 2012/11/30 20:56:26 Changed.
+ // Let the cell handle the click if it's in the decoration.
+ if (![[self cell] mouseDown:theEvent inView:self]) {
+ [[self currentEditor] mouseDown:theEvent];
+ }
+}
+
+- (NSRect)getIconFrame {
+ return [[self cell] getIconFrame:[self bounds]];
+}
+
+@end
+
+@implementation PasswordGenerationTextFieldCell
+
+- (void)setUpWithController:(PasswordGenerationBubbleController*)controller
+ normalImage:(NSImage*)normalImage
+ hoverImage:(NSImage*)hoverImage {
+ controller_ = controller;
+ hovering_ = NO;
+ normalImage_.reset([normalImage retain]);
+ hoverImage_.reset([hoverImage retain]);
+ [self setLineBreakMode:NSLineBreakByTruncatingTail];
+ [self setTruncatesLastVisibleLine:YES];
+}
+
+- (void)splitFrame:(NSRect*)cellFrame toIconFrame:(NSRect*)iconFrame {
+ NSDivideRect(*cellFrame, iconFrame, cellFrame,
+ kIconSize, NSMaxXEdge);
+}
+
+- (NSRect)getIconFrame:(NSRect)cellFrame {
+ NSRect iconFrame;
+ [self splitFrame:&cellFrame toIconFrame:&iconFrame];
+ return iconFrame;
+}
+
+- (NSRect)getTextFrame:(NSRect)cellFrame {
+ NSRect iconFrame;
+ [self splitFrame:&cellFrame toIconFrame:&iconFrame];
+ return cellFrame;
+}
+
+- (BOOL)eventIsInDecoration:(NSEvent*)theEvent
+ inView:(PasswordGenerationTextField*)controlView {
+ NSPoint mouseLocation = [controlView convertPoint:[theEvent locationInWindow]
+ fromView:nil];
+ NSRect cellFrame = [controlView bounds];
+ return NSMouseInRect(mouseLocation,
+ [self getIconFrame:cellFrame],
+ [controlView isFlipped]);
+}
+
+- (void)mouseEntered:(NSEvent*)theEvent
+ inView:(PasswordGenerationTextField*)controlView {
+ hovering_ = YES;
+ [controlView setNeedsDisplay:YES];
+}
+
+- (void)mouseExited:(NSEvent*)theEvent
+ inView:(PasswordGenerationTextField*)controlView {
+ hovering_ = NO;
+ [controlView setNeedsDisplay:YES];
+}
+
+- (BOOL)mouseDown:(NSEvent*)theEvent
+ inView:(PasswordGenerationTextField*)controlView {
+ if ([self eventIsInDecoration:theEvent inView:controlView]) {
+ [controller_ regeneratePassword];
+ return YES;
+ }
+ return NO;
+}
+
+- (NSImage*)getImage {
+ if (hovering_)
+ return hoverImage_;
+ return normalImage_;
+}
+
+- (NSRect)adjustFrameForFrame:(NSRect)frame {
+ // By default, there appears to be a 2 pixel gap between what is considered
+ // part of the textFrame and what is considered part of the icon.
+ // TODO(gcasto): This really should be fixed in StyledTextFieldCell, as it
+ // looks like the location bar also suffers from this issue.
+ frame.size.width += 2;
Scott Hess - ex-Googler 2012/11/28 20:01:06 Per comment I made probably against an earlier pat
Garrett Casto 2012/11/30 20:56:26 Looks right to me.
+ return frame;
+}
+
+- (NSRect)textFrameForFrame:(NSRect)cellFrame {
+ // Baseclass insets the rect by baselineAdjust.
+ NSRect textFrame = [super textFrameForFrame:cellFrame];
+ textFrame = [self getTextFrame:textFrame];
+ return [self adjustFrameForFrame:textFrame];
+}
+
+- (NSRect)textCursorFrameForFrame:(NSRect)cellFrame {
+ NSRect textFrame = [self getTextFrame:cellFrame];
+ return [self adjustFrameForFrame:textFrame];
+}
+
+- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
+ NSImage* image = [self getImage];
+ NSRect iconFrame = [self getIconFrame:cellFrame];
+ // Center the image in the available space. At the moment the image is
+ // slightly larger than the frame so we crop it.
+ // Offset the full difference on the left hand side since the border on the
+ // right takes up some space. Offset half the vertical difference on the
+ // bottom so that the image stays vertically centered.
+ const CGFloat x_offset = [image size].width - NSWidth(iconFrame);
+ const CGFloat y_offset = ([image size].height - (NSHeight(iconFrame))) / 2.0;
Scott Hess - ex-Googler 2012/11/28 20:01:06 xOffset, yOffset.
Garrett Casto 2012/11/30 20:56:26 Done.
+ NSRect croppedRect = NSMakeRect(x_offset,
+ y_offset,
+ NSWidth(iconFrame),
+ NSHeight(iconFrame));
+
+ [image drawInRect:iconFrame
+ fromRect:croppedRect
+ operation:NSCompositeSourceOver
+ fraction:1.0
+ respectFlipped:YES
+ hints:nil];
+
+ [super drawInteriorWithFrame:cellFrame inView:controlView];
+}
+
+- (void)setUpTrackingAreaInRect:(NSRect)frame
+ ofView:(PasswordGenerationTextField*)view {
+ NSRect iconFrame = [self getIconFrame:frame];
+ scoped_nsobject<CrTrackingArea> area(
+ [[CrTrackingArea alloc] initWithRect:iconFrame
+ options:NSTrackingMouseEnteredAndExited |
+ NSTrackingActiveAlways
+ owner:view
+ userInfo:nil]);
+ [view addTrackingArea:area];
+}
+
+- (CGFloat)baselineAdjust {
+ return 1.0;
+}
+
+- (CGFloat)cornerRadius {
+ return 4.0;
+}
+
+- (BOOL)shouldDrawBezel {
+ return YES;
+}
+
+@end
+
+@implementation PasswordGenerationBubbleController
+
+- (id)initWithWindow:(NSWindow*)parentWindow
+ anchoredAt:(NSPoint)point
+ renderViewHost:(content::RenderViewHost*)renderViewHost
+ passwordManager:(PasswordManager*)passwordManager
+ usingGenerator:(autofill::PasswordGenerator*)passwordGenerator
+ forForm:(const content::PasswordForm&)form {
+ int width = (kBorderSize*2 +
+ kTextFieldWidth +
+ kHorizontalSpacing +
+ kButtonWidth);
+ int height = (kBorderSize*2 +
+ kTextFieldHeight +
+ kVerticalSpacing +
+ kTitleHeight -
+ kTopBorderOffset +
+ info_bubble::kBubbleArrowHeight);
Scott Hess - ex-Googler 2012/11/28 20:01:06 These should probably both be CGFloat.
Garrett Casto 2012/11/30 20:56:26 Done.
+ NSRect contentRect = NSMakeRect(0, 0, width, height);
+ scoped_nsobject<InfoBubbleWindow> window(
+ [[InfoBubbleWindow alloc] initWithContentRect:contentRect
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO]);
+ if (self = [super initWithWindow:window
+ parentWindow:parentWindow
+ anchoredAt:point]) {
+ passwordGenerator_ = passwordGenerator;
+ renderViewHost_ = renderViewHost;
+ passwordManager_ = passwordManager;
+ form_ = form;
+ [[self bubble] setArrowLocation:info_bubble::kTopLeft];
+ [self performLayout];
+ }
+
+ return self;
+}
+
+- (void)performLayout {
+ NSView* contentView = [[self window] contentView];
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+
+ textField_ =
+ [[PasswordGenerationTextField alloc]
+ initWithFrame:NSMakeRect(kBorderSize,
+ kBorderSize,
+ kTextFieldWidth,
+ kTextFieldHeight + kTextFieldTopPadding)
+ withController:self
+ normalImage:rb.GetNativeImageNamed(IDR_RELOAD_DIMMED).ToNSImage()
+ hoverImage:rb.GetNativeImageNamed(IDR_RELOAD).ToNSImage()];
+ gfx::Font smallBoldFont =
+ rb.GetFont(ResourceBundle::SmallFont).DeriveFont(0, gfx::Font::BOLD);
+ [textField_ setFont:smallBoldFont.GetNativeFont()];
+ [textField_
+ setStringValue:base::SysUTF8ToNSString(passwordGenerator_->Generate())];
+ [contentView addSubview:textField_];
+
+ int button_x = (kBorderSize +
+ kTextFieldWidth +
+ kHorizontalSpacing -
+ kButtonVerticalPadding);
Scott Hess - ex-Googler 2012/11/28 20:01:06 CGFloat here, too. kButtonVerticalPadding when ca
Garrett Casto 2012/11/30 20:56:26 Yep, good catch. I was trying to figure out why th
+ int button_y = kBorderSize - kButtonVerticalPadding;
+ NSButton* button =
+ [[NSButton alloc] initWithFrame:NSMakeRect(
+ button_x,
+ button_y,
+ kButtonWidth + 2 * kButtonHorizontalPadding,
+ kButtonHeight + 2 * kButtonVerticalPadding)];
+ [button setBezelStyle:NSRoundedBezelStyle];
+ [button setTitle:l10n_util::GetNSString(IDS_PASSWORD_GENERATION_BUTTON_TEXT)];
+ [button setTarget:self];
+ [button setAction:@selector(fillPassword:)];
+ [contentView addSubview:button];
+
+ NSTextField* title = [[NSTextField alloc]
+ initWithFrame:NSMakeRect(
+ kBorderSize,
+ kBorderSize + kTextFieldHeight + kVerticalSpacing,
Scott Hess - ex-Googler 2012/11/28 20:01:06 As with the earlier comment, this could be NSMaxY(
Garrett Casto 2012/11/30 20:56:26 This isn't equivalent. The way I've been trying to
+ kTitleWidth,
+ kTitleHeight)];
+ [title setEditable:NO];
+ [title setBordered:NO];
+ [title setStringValue:l10n_util::GetNSString(
+ IDS_PASSWORD_GENERATION_BUBBLE_TITLE)];
+ [contentView addSubview:title];
+}
+
+- (IBAction)fillPassword:(id)sender {
+ renderViewHost_->Send(
+ new AutofillMsg_GeneratedPasswordAccepted(
+ renderViewHost_->GetRoutingID(),
+ base::SysNSStringToUTF16([textField_ stringValue])));
+ passwordManager_->SetFormHasGeneratedPassword(form_);
+ [self close];
+}
+
+- (void)regeneratePassword {
+ [textField_
+ setStringValue:base::SysUTF8ToNSString(passwordGenerator_->Generate())];
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698