Commit ac419494 authored by dconnelly's avatar dconnelly Committed by Commit bot

Add ManagePasswordsBubbleManageViewController and unit tests.

BUG=328847

Review URL: https://codereview.chromium.org/462563002

Cr-Commit-Position: refs/heads/master@{#291889}
parent ee69a3bd
...@@ -8,12 +8,15 @@ ...@@ -8,12 +8,15 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "base/mac/scoped_nsobject.h" #import "base/mac/scoped_nsobject.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/common/password_manager_ui.h" #include "components/password_manager/core/common/password_manager_ui.h"
#import "ui/base/cocoa/tracking_area.h"
namespace autofill { namespace autofill {
struct PasswordForm; struct PasswordForm;
} // namespace autofill } // namespace autofill
@class HoverImageButton;
class ManagePasswordsBubbleModel; class ManagePasswordsBubbleModel;
// The state of the password item. // The state of the password item.
...@@ -23,6 +26,44 @@ enum ManagePasswordItemState { ...@@ -23,6 +26,44 @@ enum ManagePasswordItemState {
MANAGE_PASSWORD_ITEM_STATE_DELETED MANAGE_PASSWORD_ITEM_STATE_DELETED
}; };
// Abstract superclass for items that are clickable. Highlights on hover.
@interface ManagePasswordItemClickableView : NSView {
@private
BOOL hovering_;
ui::ScopedCrTrackingArea trackingArea_;
}
@end
// Shows the option to undelete a password.
@interface ManagePasswordItemUndoView : ManagePasswordItemClickableView {
@private
base::scoped_nsobject<NSButton> undoButton_;
}
- (id)initWithTarget:(id)target action:(SEL)action;
@end
@interface ManagePasswordItemUndoView (Testing)
@property(readonly) NSButton* undoButton;
@end
// Shows a username, obscured password, and delete button in a single row.
@interface ManagePasswordItemManageView : ManagePasswordItemClickableView {
@private
base::scoped_nsobject<NSTextField> usernameField_;
base::scoped_nsobject<NSSecureTextField> passwordField_;
base::scoped_nsobject<HoverImageButton> deleteButton_;
}
- (id)initWithForm:(const autofill::PasswordForm&)form
target:(id)target
action:(SEL)action;
@end
@interface ManagePasswordItemManageView (Testing)
@property(readonly) NSTextField* usernameField;
@property(readonly) NSSecureTextField* passwordField;
@property(readonly) NSButton* deleteButton;
@end
// Shows a username and obscured password in a single row. // Shows a username and obscured password in a single row.
@interface ManagePasswordItemPendingView : NSView { @interface ManagePasswordItemPendingView : NSView {
@private @private
...@@ -42,19 +83,20 @@ enum ManagePasswordItemState { ...@@ -42,19 +83,20 @@ enum ManagePasswordItemState {
@interface ManagePasswordItemViewController : NSViewController { @interface ManagePasswordItemViewController : NSViewController {
@private @private
ManagePasswordsBubbleModel* model_; // weak ManagePasswordsBubbleModel* model_; // weak
autofill::PasswordForm passwordForm_;
ManagePasswordItemState state_; ManagePasswordItemState state_;
password_manager::ui::PasswordItemPosition position_; password_manager::ui::PasswordItemPosition position_;
base::scoped_nsobject<NSView> contentView_; base::scoped_nsobject<NSView> contentView_;
CGFloat minWidth_;
} }
- (id)initWithModel:(ManagePasswordsBubbleModel*)model - (id)initWithModel:(ManagePasswordsBubbleModel*)model
position:(password_manager::ui::PasswordItemPosition)position passwordForm:(const autofill::PasswordForm&)passwordForm
minWidth:(CGFloat)minWidth; position:(password_manager::ui::PasswordItemPosition)position;
@end @end
@interface ManagePasswordItemViewController (Testing) @interface ManagePasswordItemViewController (Testing)
@property(readonly) ManagePasswordItemState state; @property(readonly) ManagePasswordItemState state;
@property(readonly) NSView* contentView; @property(readonly) NSView* contentView;
@property(readonly) autofill::PasswordForm passwordForm;
@end @end
#endif // CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORD_ITEM_VIEW_CONTROLLER_H_ #endif // CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORD_ITEM_VIEW_CONTROLLER_H_
...@@ -8,53 +8,142 @@ ...@@ -8,53 +8,142 @@
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "chrome/browser/password_manager/mock_password_store_service.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/ui/cocoa/cocoa_test_helper.h" #include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#import "chrome/browser/ui/cocoa/passwords/manage_password_item_view_controller.h" #import "chrome/browser/ui/cocoa/passwords/manage_password_item_view_controller.h"
#include "chrome/browser/ui/cocoa/passwords/manage_passwords_controller_test.h" #include "chrome/browser/ui/cocoa/passwords/manage_passwords_controller_test.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h" #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h"
#include "components/password_manager/core/browser/mock_password_store.h"
#include "components/password_manager/core/browser/password_store.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h" #include "testing/gtest_mac.h"
using namespace testing;
namespace { namespace {
static const CGFloat kArbitraryWidth = 500; NSString* const kItemTestUsername = @"foo";
NSString* const kItemTestPassword = @"bar";
} // namespace } // namespace
typedef ManagePasswordsControllerTest ManagePasswordItemViewControllerTest; MATCHER_P(PasswordFormEq, form, "") {
return form.username_value == arg.username_value &&
form.password_value == arg.password_value;
}
class ManagePasswordItemViewControllerTest
: public ManagePasswordsControllerTest {
public:
ManagePasswordItemViewControllerTest() {}
virtual ~ManagePasswordItemViewControllerTest() {}
virtual void SetUp() OVERRIDE {
ManagePasswordsControllerTest::SetUp();
PasswordStoreFactory::GetInstance()->SetTestingFactory(
profile(), MockPasswordStoreService::Build);
ui_controller()->SetPendingCredentials(credentials());
}
ManagePasswordItemViewController* controller() {
if (!controller_) {
controller_.reset([[ManagePasswordItemViewController alloc]
initWithModel:model()
passwordForm:ui_controller()->PendingCredentials()
position:password_manager::ui::FIRST_ITEM]);
}
return controller_.get();
}
autofill::PasswordForm credentials() {
autofill::PasswordForm form;
form.username_value = base::SysNSStringToUTF16(kItemTestUsername);
form.password_value = base::SysNSStringToUTF16(kItemTestPassword);
return form;
}
password_manager::MockPasswordStore* mockStore() {
password_manager::PasswordStore* store =
PasswordStoreFactory::GetForProfile(profile(), Profile::EXPLICIT_ACCESS)
.get();
password_manager::MockPasswordStore* mockStore =
static_cast<password_manager::MockPasswordStore*>(store);
return mockStore;
}
private:
base::scoped_nsobject<ManagePasswordItemViewController> controller_;
DISALLOW_COPY_AND_ASSIGN(ManagePasswordItemViewControllerTest);
};
TEST_F(ManagePasswordItemViewControllerTest, ManageStateShouldHaveManageView) {
model()->set_state(password_manager::ui::MANAGE_STATE);
EXPECT_EQ(MANAGE_PASSWORD_ITEM_STATE_MANAGE, [controller() state]);
EXPECT_NSEQ([ManagePasswordItemManageView class],
[[controller() contentView] class]);
}
TEST_F(ManagePasswordItemViewControllerTest,
ClickingDeleteShouldShowUndoViewAndDeletePassword) {
EXPECT_CALL(*mockStore(), RemoveLogin(PasswordFormEq(credentials())));
model()->set_state(password_manager::ui::MANAGE_STATE);
ManagePasswordItemManageView* manageView =
base::mac::ObjCCast<ManagePasswordItemManageView>(
controller().contentView);
[manageView.deleteButton performClick:nil];
EXPECT_NSEQ([ManagePasswordItemUndoView class],
[controller().contentView class]);
}
TEST_F(ManagePasswordItemViewControllerTest,
ClickingUndoShouldShowManageViewAndAddPassword) {
EXPECT_CALL(*mockStore(), AddLogin(PasswordFormEq(credentials())));
model()->set_state(password_manager::ui::MANAGE_STATE);
ManagePasswordItemManageView* manageView =
base::mac::ObjCCast<ManagePasswordItemManageView>(
controller().contentView);
[manageView.deleteButton performClick:nil];
ManagePasswordItemUndoView* undoView =
base::mac::ObjCCast<ManagePasswordItemUndoView>(controller().contentView);
[undoView.undoButton performClick:nil];
EXPECT_NSEQ([ManagePasswordItemManageView class],
[controller().contentView class]);
}
TEST_F(ManagePasswordItemViewControllerTest,
ManageViewShouldHaveCorrectUsernameAndObscuredPassword) {
model()->set_state(password_manager::ui::MANAGE_STATE);
ManagePasswordItemManageView* manageView =
base::mac::ObjCCast<ManagePasswordItemManageView>(
[controller() contentView]);
// Ensure the fields are populated properly and the password is obscured.
EXPECT_NSEQ(kItemTestUsername, manageView.usernameField.stringValue);
EXPECT_NSEQ(kItemTestPassword, manageView.passwordField.stringValue);
EXPECT_TRUE([[manageView.passwordField cell] echosBullets]);
}
TEST_F(ManagePasswordItemViewControllerTest, TEST_F(ManagePasswordItemViewControllerTest,
PendingStateShouldHavePendingView) { PendingStateShouldHavePendingView) {
base::scoped_nsobject<ManagePasswordItemViewController> controller( EXPECT_EQ(MANAGE_PASSWORD_ITEM_STATE_PENDING, [controller() state]);
[[ManagePasswordItemViewController alloc]
initWithModel:model()
position:password_manager::ui::FIRST_ITEM
minWidth:kArbitraryWidth]);
EXPECT_EQ(MANAGE_PASSWORD_ITEM_STATE_PENDING, [controller state]);
EXPECT_NSEQ([ManagePasswordItemPendingView class], EXPECT_NSEQ([ManagePasswordItemPendingView class],
[[controller contentView] class]); [[controller() contentView] class]);
} }
TEST_F(ManagePasswordItemViewControllerTest, TEST_F(ManagePasswordItemViewControllerTest,
PendingViewShouldHaveCorrectUsernameAndObscuredPassword) { PendingViewShouldHaveCorrectUsernameAndObscuredPassword) {
// Set the pending credentials. model()->set_state(password_manager::ui::PENDING_PASSWORD_STATE);
autofill::PasswordForm form;
NSString* const kUsername = @"foo";
NSString* const kPassword = @"bar";
form.username_value = base::SysNSStringToUTF16(kUsername);
form.password_value = base::SysNSStringToUTF16(kPassword);
ui_controller()->SetPendingCredentials(form);
ui_controller()->SetState(password_manager::ui::PENDING_PASSWORD_STATE);
base::scoped_nsobject<ManagePasswordItemViewController> controller(
[[ManagePasswordItemViewController alloc]
initWithModel:model()
position:password_manager::ui::FIRST_ITEM
minWidth:kArbitraryWidth]);
ManagePasswordItemPendingView* pendingView = ManagePasswordItemPendingView* pendingView =
base::mac::ObjCCast<ManagePasswordItemPendingView>( base::mac::ObjCCast<ManagePasswordItemPendingView>(
[controller contentView]); [controller() contentView]);
// Ensure the fields are populated properly and the password is obscured. // Ensure the fields are populated properly and the password is obscured.
EXPECT_NSEQ(kUsername, pendingView.usernameField.stringValue); EXPECT_NSEQ(kItemTestUsername, pendingView.usernameField.stringValue);
EXPECT_NSEQ(kPassword, pendingView.passwordField.stringValue); EXPECT_NSEQ(kItemTestPassword, pendingView.passwordField.stringValue);
EXPECT_TRUE([[pendingView.passwordField cell] echosBullets]); EXPECT_TRUE([[pendingView.passwordField cell] echosBullets]);
} }
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#import "chrome/browser/ui/cocoa/browser_window_controller.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "chrome/browser/ui/cocoa/info_bubble_view.h" #import "chrome/browser/ui/cocoa/info_bubble_view.h"
#import "chrome/browser/ui/cocoa/info_bubble_window.h" #import "chrome/browser/ui/cocoa/info_bubble_window.h"
#import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.h"
#include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
#import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller.h" #import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller.h"
#include "ui/base/cocoa/window_size_constants.h" #include "ui/base/cocoa/window_size_constants.h"
...@@ -54,6 +55,10 @@ ...@@ -54,6 +55,10 @@
[[ManagePasswordsBubbleConfirmationViewController alloc] [[ManagePasswordsBubbleConfirmationViewController alloc]
initWithModel:model_ initWithModel:model_
delegate:self]); delegate:self]);
} else if (model_->state() == password_manager::ui::MANAGE_STATE) {
currentController_.reset([[ManagePasswordsBubbleManageViewController alloc]
initWithModel:model_
delegate:self]);
} }
[self performLayout]; [self performLayout];
} }
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/cocoa/cocoa_test_helper.h" #include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#import "chrome/browser/ui/cocoa/info_bubble_window.h" #import "chrome/browser/ui/cocoa/info_bubble_window.h"
#import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.h"
#import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.h"
#include "chrome/browser/ui/cocoa/passwords/manage_passwords_controller_test.h" #include "chrome/browser/ui/cocoa/passwords/manage_passwords_controller_test.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h" #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -27,7 +29,6 @@ class ManagePasswordsBubbleControllerTest ...@@ -27,7 +29,6 @@ class ManagePasswordsBubbleControllerTest
virtual void SetUp() OVERRIDE { virtual void SetUp() OVERRIDE {
ManagePasswordsControllerTest::SetUp(); ManagePasswordsControllerTest::SetUp();
model()->set_state(password_manager::ui::PENDING_PASSWORD_STATE);
} }
ManagePasswordsBubbleController* controller() { ManagePasswordsBubbleController* controller() {
...@@ -44,12 +45,13 @@ class ManagePasswordsBubbleControllerTest ...@@ -44,12 +45,13 @@ class ManagePasswordsBubbleControllerTest
}; };
TEST_F(ManagePasswordsBubbleControllerTest, PendingStateShouldHavePendingView) { TEST_F(ManagePasswordsBubbleControllerTest, PendingStateShouldHavePendingView) {
// We start in the pending state. model()->set_state(password_manager::ui::PENDING_PASSWORD_STATE);
EXPECT_EQ([ManagePasswordsBubblePendingViewController class], EXPECT_EQ([ManagePasswordsBubblePendingViewController class],
[[controller() currentController] class]); [[controller() currentController] class]);
} }
TEST_F(ManagePasswordsBubbleControllerTest, DismissingShouldCloseWindow) { TEST_F(ManagePasswordsBubbleControllerTest, DismissingShouldCloseWindow) {
model()->set_state(password_manager::ui::PENDING_PASSWORD_STATE);
[controller() showWindow:nil]; [controller() showWindow:nil];
// Turn off animations so that closing happens immediately. // Turn off animations so that closing happens immediately.
...@@ -62,4 +64,10 @@ TEST_F(ManagePasswordsBubbleControllerTest, DismissingShouldCloseWindow) { ...@@ -62,4 +64,10 @@ TEST_F(ManagePasswordsBubbleControllerTest, DismissingShouldCloseWindow) {
EXPECT_FALSE([window isVisible]); EXPECT_FALSE([window isVisible]);
} }
TEST_F(ManagePasswordsBubbleControllerTest, ManageStateShouldHaveManageView) {
model()->set_state(password_manager::ui::MANAGE_STATE);
EXPECT_EQ([ManagePasswordsBubbleManageViewController class],
[[controller() currentController] class]);
}
} // namespace } // namespace
// Copyright 2014 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.
#ifndef CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORDS_BUBBLE_MANAGE_VIEW_CONTROLLER_H_
#define CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORDS_BUBBLE_MANAGE_VIEW_CONTROLLER_H_
#import <Cocoa/Cocoa.h>
#include "base/mac/scoped_nsobject.h"
#import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.h"
class ManagePasswordsBubbleModel;
// Shows a list of passwords saved for the current site..
@interface PasswordItemListView : NSView {
base::scoped_nsobject<NSArray> itemViews_;
}
- (id)initWithModel:(ManagePasswordsBubbleModel*)model;
@end
@interface PasswordItemListView (Testing)
@property(readonly) NSArray* itemViews;
@end
// Informs the user that no passwords are stored for the current site.
@interface NoPasswordsView : NSTextField
- (id)initWithWidth:(CGFloat)width;
@end
// Manages the view that allows users to manage passwords for a site.
@interface ManagePasswordsBubbleManageViewController
: ManagePasswordsBubbleContentViewController {
@private
ManagePasswordsBubbleModel* model_; // weak
id<ManagePasswordsBubbleContentViewDelegate> delegate_; // weak
base::scoped_nsobject<NSButton> doneButton_;
base::scoped_nsobject<NSButton> manageButton_;
base::scoped_nsobject<NSView> contentView_;
}
- (id)initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate;
@end
@interface ManagePasswordsBubbleManageViewController (Testing)
@property(readonly) NSButton* doneButton;
@property(readonly) NSButton* manageButton;
@property(readonly) NSView* contentView;
@end
#endif // CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORDS_BUBBLE_MANAGE_VIEW_CONTROLLER_H_
// Copyright 2014 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/passwords/manage_passwords_bubble_manage_view_controller.h"
#include <cmath>
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/chrome_style.h"
#import "chrome/browser/ui/cocoa/passwords/manage_password_item_view_controller.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "grit/generated_resources.h"
#include "skia/ext/skia_utils_mac.h"
#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
#import "ui/base/cocoa/controls/hyperlink_button_cell.h"
#include "ui/base/l10n/l10n_util.h"
using namespace password_manager::mac::ui;
@implementation PasswordItemListView
- (id)initWithModel:(ManagePasswordsBubbleModel*)model {
if ((self = [super initWithFrame:NSZeroRect])) {
base::scoped_nsobject<NSMutableArray> items([[NSMutableArray alloc] init]);
// Create and lay out the items.
const CGFloat curX = 0;
CGFloat maxX = 0;
CGFloat curY = 0;
for (autofill::ConstPasswordFormMap::const_reverse_iterator i =
model->best_matches().rbegin();
i != model->best_matches().rend();
++i) {
autofill::PasswordForm form = *i->second;
password_manager::ui::PasswordItemPosition position =
(&(*i) == &(*model->best_matches().begin()))
? password_manager::ui::FIRST_ITEM
: password_manager::ui::SUBSEQUENT_ITEM;
base::scoped_nsobject<ManagePasswordItemViewController> item(
[[ManagePasswordItemViewController alloc] initWithModel:model
passwordForm:form
position:position]);
[items addObject:item.get()];
NSView* itemView = [item view];
[self addSubview:itemView];
// The items stack up on each other.
[itemView setFrameOrigin:NSMakePoint(curX, curY)];
maxX = NSMaxX([itemView frame]);
curY = NSMaxY([itemView frame]);
}
[self setFrameSize:NSMakeSize(maxX, curY)];
itemViews_.reset(items.release());
}
return self;
}
@end
@implementation PasswordItemListView (Testing)
- (NSArray*)itemViews {
return itemViews_.get();
}
@end
@implementation NoPasswordsView
- (id)initWithWidth:(CGFloat)width {
if ((self = [super initWithFrame:NSZeroRect])) {
[self setEditable:NO];
[self setSelectable:NO];
[self setDrawsBackground:NO];
[self setBezeled:NO];
[self setStringValue:l10n_util::GetNSString(
IDS_MANAGE_PASSWORDS_NO_PASSWORDS)];
[self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
[[self cell] setWraps:YES];
[self setFrameSize:NSMakeSize(width, MAXFLOAT)];
[GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:self];
}
return self;
}
@end
@interface ManagePasswordsBubbleManageViewController ()
- (void)onDoneClicked:(id)sender;
- (void)onManageClicked:(id)sender;
@end
@implementation ManagePasswordsBubbleManageViewController
- (id)initWithModel:(ManagePasswordsBubbleModel*)model
delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
if ((self = [super initWithNibName:nil bundle:nil])) {
model_ = model;
delegate_ = delegate;
}
return self;
}
- (void)loadView {
self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
// -----------------------------------
// | Title |
// | ----------------------------- | (1 px border)
// | username password x |
// | ----------------------------- | (1 px border)
// | username password x |
// | ----------------------------- | (1 px border)
// | username password x |
// | ----------------------------- | (1 px border)
// | Manage [Done] |
// -----------------------------------
// The bubble should be wide enough to fit the title row, the username and
// password rows, and the buttons row on one line each, but not smaller than
// kDesiredBubbleWidth.
// Create the elements and add them to the view.
NSTextField* titleLabel =
[self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
// Content. If we have a list of passwords to store for the current site, we
// display them to the user for management. Otherwise, we show a "No passwords
// for this site" message.
if (model_->best_matches().empty()) {
const CGFloat noPasswordsWidth = std::max(
kDesiredBubbleWidth - 2 * kFramePadding, NSWidth([titleLabel frame]));
contentView_.reset(
[[NoPasswordsView alloc] initWithWidth:noPasswordsWidth]);
} else {
contentView_.reset(
[[PasswordItemListView alloc] initWithModel:model_]);
}
[self.view addSubview:contentView_];
DCHECK_GE(NSWidth([contentView_ frame]), NSWidth([titleLabel frame]));
// Done button.
doneButton_.reset([[self addButton:l10n_util::GetNSString(IDS_DONE)
target:self
action:@selector(onDoneClicked:)] retain]);
// Manage button.
manageButton_.reset([[NSButton alloc] initWithFrame:NSZeroRect]);
base::scoped_nsobject<HyperlinkButtonCell> cell([[HyperlinkButtonCell alloc]
initTextCell:base::SysUTF16ToNSString(model_->manage_link())]);
[cell setControlSize:NSSmallControlSize];
[cell setShouldUnderline:NO];
[cell setUnderlineOnHover:NO];
[cell setTextColor:gfx::SkColorToCalibratedNSColor(
chrome_style::GetLinkColor())];
[manageButton_ setCell:cell.get()];
[manageButton_ sizeToFit];
[manageButton_ setTarget:self];
[manageButton_ setAction:@selector(onManageClicked:)];
[self.view addSubview:manageButton_];
// Layout the elements, starting at the bottom and moving up.
// The Done button goes in the bottom-right corner.
const CGFloat width = 2 * kFramePadding + NSWidth([contentView_ frame]);
CGFloat curX = width - kFramePadding - NSWidth([doneButton_ frame]);
CGFloat curY = kFramePadding;
[doneButton_ setFrameOrigin:NSMakePoint(curX, curY)];
// The Manage button goes in the bottom-left corner, centered vertically with
// the Done button.
curX = kFramePadding;
const CGFloat diffY = std::ceil(
(NSHeight([doneButton_ frame]) - NSHeight([manageButton_ frame])) / 2.0);
[manageButton_ setFrameOrigin:NSMakePoint(curX, curY + diffY)];
// The content goes above the button row.
curX = kFramePadding;
curY = NSMaxY([doneButton_ frame]) + kUnrelatedControlVerticalPadding;
[contentView_ setFrameOrigin:NSMakePoint(curX, curY)];
// The title goes above the content.
curY = NSMaxY([contentView_ frame]) + kUnrelatedControlVerticalPadding;
[titleLabel setFrameOrigin:NSMakePoint(curX, curY)];
curX = NSMaxX([contentView_ frame]) + kFramePadding;
curY = NSMaxY([titleLabel frame]) + kFramePadding;
DCHECK_EQ(width, curX);
[self.view setFrameSize:NSMakeSize(curX, curY)];
}
- (void)onDoneClicked:(id)sender {
model_->OnDoneClicked();
[delegate_ viewShouldDismiss];
}
- (void)onManageClicked:(id)sender {
model_->OnManageLinkClicked();
[delegate_ viewShouldDismiss];
}
@end
@implementation ManagePasswordsBubbleManageViewController (Testing)
- (NSButton*)doneButton {
return doneButton_.get();
}
- (NSButton*)manageButton {
return manageButton_.get();
}
- (NSView*)contentView {
return contentView_.get();
}
@end
// Copyright 2014 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/passwords/manage_passwords_bubble_manage_view_controller.h"
#include "base/mac/foundation_util.h"
#include "base/strings/utf_string_conversions.h"
#import "chrome/browser/ui/cocoa/passwords/manage_password_item_view_controller.h"
#include "chrome/browser/ui/cocoa/passwords/manage_passwords_controller_test.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
@interface ManagePasswordsBubbleManageViewTestDelegate
: NSObject<ManagePasswordsBubbleContentViewDelegate> {
BOOL dismissed_;
}
@property(readonly) BOOL dismissed;
@end
@implementation ManagePasswordsBubbleManageViewTestDelegate
@synthesize dismissed = dismissed_;
- (void)viewShouldDismiss {
dismissed_ = YES;
}
@end
namespace {
class ManagePasswordsBubbleManageViewControllerTest
: public ManagePasswordsControllerTest {
public:
ManagePasswordsBubbleManageViewControllerTest() : controller_(nil) {}
virtual void SetUp() OVERRIDE {
ManagePasswordsControllerTest::SetUp();
delegate_.reset(
[[ManagePasswordsBubbleManageViewTestDelegate alloc] init]);
ui_controller()->SetState(password_manager::ui::MANAGE_STATE);
}
ManagePasswordsBubbleManageViewTestDelegate* delegate() {
return delegate_.get();
}
ManagePasswordsBubbleManageViewController* controller() {
if (!controller_) {
controller_.reset([[ManagePasswordsBubbleManageViewController alloc]
initWithModel:model()
delegate:delegate()]);
[controller_ loadView];
}
return controller_.get();
}
private:
base::scoped_nsobject<ManagePasswordsBubbleManageViewController> controller_;
base::scoped_nsobject<ManagePasswordsBubbleManageViewTestDelegate> delegate_;
DISALLOW_COPY_AND_ASSIGN(ManagePasswordsBubbleManageViewControllerTest);
};
TEST_F(ManagePasswordsBubbleManageViewControllerTest,
ShouldDismissWhenDoneClicked) {
[controller().doneButton performClick:nil];
EXPECT_TRUE([delegate() dismissed]);
}
TEST_F(ManagePasswordsBubbleManageViewControllerTest,
ShouldOpenPasswordsWhenManageClicked) {
[controller().manageButton performClick:nil];
EXPECT_TRUE([delegate() dismissed]);
EXPECT_TRUE(ui_controller()->navigated_to_settings_page());
}
TEST_F(ManagePasswordsBubbleManageViewControllerTest,
ShouldShowNoPasswordsWhenNoPasswordsExistForSite) {
EXPECT_TRUE(model()->best_matches().empty());
EXPECT_EQ([NoPasswordsView class], [controller().contentView class]);
}
TEST_F(ManagePasswordsBubbleManageViewControllerTest,
ShouldShowAllPasswordItemsWhenPasswordsExistForSite) {
// Add a few password entries.
autofill::ConstPasswordFormMap map;
autofill::PasswordForm form1;
form1.username_value = base::ASCIIToUTF16("username1");
form1.password_value = base::ASCIIToUTF16("password1");
map[base::ASCIIToUTF16("username1")] = &form1;
autofill::PasswordForm form2;
form2.username_value = base::ASCIIToUTF16("username2");
form2.password_value = base::ASCIIToUTF16("password2");
map[base::ASCIIToUTF16("username2")] = &form2;
// Add the entries to the model.
ui_controller()->SetPasswordFormMap(map);
model()->set_state(password_manager::ui::MANAGE_STATE);
// Check the view state.
EXPECT_FALSE(model()->best_matches().empty());
EXPECT_EQ([PasswordItemListView class], [controller().contentView class]);
NSArray* items = base::mac::ObjCCastStrict<PasswordItemListView>(
controller().contentView).itemViews;
EXPECT_EQ(2U, [items count]);
// Check the entry items.
for (ManagePasswordItemViewController* item in items) {
ManagePasswordItemManageView* itemContent =
base::mac::ObjCCastStrict<ManagePasswordItemManageView>(
item.contentView);
NSString* username = [itemContent.usernameField stringValue];
if ([username isEqualToString:@"username1"]) {
EXPECT_NSEQ(@"password1", [itemContent.passwordField stringValue]);
} else if ([username isEqualToString:@"username2"]) {
EXPECT_NSEQ(@"password2", [itemContent.passwordField stringValue]);
} else {
NOTREACHED();
}
}
}
} // namespace
...@@ -78,11 +78,10 @@ using namespace password_manager::mac::ui; ...@@ -78,11 +78,10 @@ using namespace password_manager::mac::ui;
// Password item. // Password item.
// It should be at least as wide as the box without the padding. // It should be at least as wide as the box without the padding.
const CGFloat itemMinWidth = kDesiredBubbleWidth - 2 * kFramePadding;
passwordItem_.reset([[ManagePasswordItemViewController alloc] passwordItem_.reset([[ManagePasswordItemViewController alloc]
initWithModel:model_ initWithModel:model_
position:password_manager::ui::FIRST_ITEM passwordForm:model_->pending_credentials()
minWidth:itemMinWidth]); position:password_manager::ui::FIRST_ITEM]);
NSView* password = [passwordItem_ view]; NSView* password = [passwordItem_ view];
[self.view addSubview:password]; [self.view addSubview:password];
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "components/password_manager/core/common/password_manager_ui.h" #include "components/password_manager/core/common/password_manager_ui.h"
#include "grit/generated_resources.h" #include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
using autofill::PasswordFormMap; using autofill::PasswordFormMap;
using content::WebContents; using content::WebContents;
...@@ -21,6 +22,19 @@ namespace metrics_util = password_manager::metrics_util; ...@@ -21,6 +22,19 @@ namespace metrics_util = password_manager::metrics_util;
namespace { namespace {
enum FieldType { USERNAME_FIELD, PASSWORD_FIELD };
const int kUsernameFieldSize = 30;
const int kPasswordFieldSize = 22;
// Returns the width of |type| field.
int GetFieldWidth(FieldType type) {
return ui::ResourceBundle::GetSharedInstance()
.GetFontList(ui::ResourceBundle::SmallFont)
.GetExpectedTextWidth(type == USERNAME_FIELD ? kUsernameFieldSize
: kPasswordFieldSize);
}
void SetupLinkifiedText(const base::string16& string_with_separator, void SetupLinkifiedText(const base::string16& string_with_separator,
base::string16* text, base::string16* text,
gfx::Range* link_range) { gfx::Range* link_range) {
...@@ -179,3 +193,13 @@ void ManagePasswordsBubbleModel::OnPasswordAction( ...@@ -179,3 +193,13 @@ void ManagePasswordsBubbleModel::OnPasswordAction(
else else
password_store->AddLogin(password_form); password_store->AddLogin(password_form);
} }
// static
int ManagePasswordsBubbleModel::UsernameFieldWidth() {
return GetFieldWidth(USERNAME_FIELD);
}
// static
int ManagePasswordsBubbleModel::PasswordFieldWidth() {
return GetFieldWidth(PASSWORD_FIELD);
}
...@@ -102,6 +102,10 @@ class ManagePasswordsBubbleModel : public content::WebContentsObserver { ...@@ -102,6 +102,10 @@ class ManagePasswordsBubbleModel : public content::WebContentsObserver {
void set_state(password_manager::ui::State state) { state_ = state; } void set_state(password_manager::ui::State state) { state_ = state; }
#endif #endif
// Upper limits on the size of the username and password fields.
static int UsernameFieldWidth();
static int PasswordFieldWidth();
private: private:
password_manager::ui::State state_; password_manager::ui::State state_;
base::string16 title_; base::string16 title_;
......
...@@ -18,23 +18,9 @@ ...@@ -18,23 +18,9 @@
namespace { namespace {
enum FieldType { USERNAME_FIELD, PASSWORD_FIELD };
// Upper limit on the size of the username and password fields.
const int kUsernameFieldSize = 30;
const int kPasswordFieldSize = 22;
// Returns the width of |type| field.
int GetFieldWidth(FieldType type) {
return ui::ResourceBundle::GetSharedInstance()
.GetFontList(ui::ResourceBundle::SmallFont)
.GetExpectedTextWidth(type == USERNAME_FIELD ? kUsernameFieldSize
: kPasswordFieldSize);
}
int FirstFieldWidth() { int FirstFieldWidth() {
return std::max( return std::max(
GetFieldWidth(USERNAME_FIELD), ManagePasswordsBubbleModel::UsernameFieldWidth(),
views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED)) views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED))
.GetPreferredSize() .GetPreferredSize()
.width()); .width());
...@@ -42,7 +28,7 @@ int FirstFieldWidth() { ...@@ -42,7 +28,7 @@ int FirstFieldWidth() {
int SecondFieldWidth() { int SecondFieldWidth() {
return std::max( return std::max(
GetFieldWidth(PASSWORD_FIELD), ManagePasswordsBubbleModel::PasswordFieldWidth(),
views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO)) views::Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_UNDO))
.GetPreferredSize() .GetPreferredSize()
.width()); .width());
......
...@@ -637,6 +637,8 @@ ...@@ -637,6 +637,8 @@
'browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.mm', 'browser/ui/cocoa/passwords/manage_passwords_bubble_content_view_controller.mm',
'browser/ui/cocoa/passwords/manage_passwords_bubble_controller.h', 'browser/ui/cocoa/passwords/manage_passwords_bubble_controller.h',
'browser/ui/cocoa/passwords/manage_passwords_bubble_controller.mm', 'browser/ui/cocoa/passwords/manage_passwords_bubble_controller.mm',
'browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.h',
'browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm',
'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.h', 'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.h',
'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm', 'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.mm',
'browser/ui/cocoa/pdf_password_dialog.mm', 'browser/ui/cocoa/pdf_password_dialog.mm',
......
...@@ -1627,6 +1627,7 @@ ...@@ -1627,6 +1627,7 @@
'browser/ui/cocoa/passwords/manage_passwords_bubble_cocoa_unittest.mm', 'browser/ui/cocoa/passwords/manage_passwords_bubble_cocoa_unittest.mm',
'browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller_unittest.mm', 'browser/ui/cocoa/passwords/manage_passwords_bubble_confirmation_view_controller_unittest.mm',
'browser/ui/cocoa/passwords/manage_passwords_bubble_controller_unittest.mm', 'browser/ui/cocoa/passwords/manage_passwords_bubble_controller_unittest.mm',
'browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller_unittest.mm',
'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm', 'browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller_unittest.mm',
'browser/ui/cocoa/passwords/manage_passwords_controller_test.h', 'browser/ui/cocoa/passwords/manage_passwords_controller_test.h',
'browser/ui/cocoa/passwords/manage_passwords_controller_test.mm', 'browser/ui/cocoa/passwords/manage_passwords_controller_test.mm',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment