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

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: Sync Created 8 years 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..b6c5d5cff9bbbe383ad07ccb573abf79687fdb78
--- /dev/null
+++ b/chrome/browser/ui/cocoa/browser/password_generation_bubble_controller.mm
@@ -0,0 +1,388 @@
+// 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/mac/foundation_util.h"
+#include "base/sys_string_conversions.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 "chrome/common/autofill_messages.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.0;
+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 = 7.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.
+- (void)iconClicked;
+@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 {
+ // Let the cell handle the click if it's in the decoration.
+ if (![[self cell] mouseDown:theEvent inView:self]) {
+ if ([self currentEditor]) {
+ [[self currentEditor] mouseDown:theEvent];
+ } else {
+ // We somehow lost focus.
+ [super mouseDown:theEvent];
+ }
+ }
+}
+
+- (void)simulateIconClick {
+ [[self cell] iconClicked];
+}
+
+@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]) {
+ [self iconClicked];
+ return YES;
+ }
+ return NO;
+}
+
+- (void)iconClicked {
+ [controller_ regeneratePassword];
+}
+
+- (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;
+ 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 xOffset = [image size].width - NSWidth(iconFrame);
+ const CGFloat yOffset = ([image size].height - (NSHeight(iconFrame))) / 2.0;
+ NSRect croppedRect = NSMakeRect(xOffset,
+ yOffset,
+ 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
+
+@synthesize textField = textField_;
+
+- (id)initWithWindow:(NSWindow*)parentWindow
+ anchoredAt:(NSPoint)point
+ renderViewHost:(content::RenderViewHost*)renderViewHost
+ passwordManager:(PasswordManager*)passwordManager
+ usingGenerator:(autofill::PasswordGenerator*)passwordGenerator
+ forForm:(const content::PasswordForm&)form {
+ CGFloat width = (kBorderSize*2 +
+ kTextFieldWidth +
+ kHorizontalSpacing +
+ kButtonWidth);
+ CGFloat height = (kBorderSize*2 +
+ kTextFieldHeight +
+ kVerticalSpacing +
+ kTitleHeight -
+ kTopBorderOffset +
+ info_bubble::kBubbleArrowHeight);
+ 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_];
+
+ CGFloat buttonX = (NSMaxX([textField_ frame]) +
+ kHorizontalSpacing -
+ kButtonHorizontalPadding);
+ CGFloat buttonY = kBorderSize - kButtonVerticalPadding;
+ NSButton* button =
+ [[NSButton alloc] initWithFrame:NSMakeRect(
+ buttonX,
+ buttonY,
+ 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,
+ kTitleWidth,
+ kTitleHeight)];
Nico 2013/03/04 14:11:10 This is leaked. Fix at https://codereview.chromium
+ [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