Add ManagePasswordItemViewController and unit tests.

Currently only implements the Pending item view.

BUG=328847

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

Cr-Commit-Position: refs/heads/master@{#288332}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@288332 0039d316-1c4b-4281-b951-d872f2087c98
parent 9693157b
// 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_PASSWORD_ITEM_VIEW_CONTROLLER_H_
#define CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORD_ITEM_VIEW_CONTROLLER_H_
#import <Cocoa/Cocoa.h>
#import "base/mac/scoped_nsobject.h"
#include "components/password_manager/core/common/password_manager_ui.h"
namespace autofill {
struct PasswordForm;
} // namespace autofill
class ManagePasswordsBubbleModel;
// The state of the password item.
enum ManagePasswordItemState {
MANAGE_PASSWORD_ITEM_STATE_PENDING,
MANAGE_PASSWORD_ITEM_STATE_MANAGE,
MANAGE_PASSWORD_ITEM_STATE_DELETED
};
// Shows a username and obscured password in a single row.
@interface ManagePasswordItemPendingView : NSView {
@private
base::scoped_nsobject<NSTextField> usernameField_;
base::scoped_nsobject<NSSecureTextField> passwordField_;
}
- (id)initWithForm:(const autofill::PasswordForm&)form;
@end
@interface ManagePasswordItemPendingView (Testing)
@property(readonly) NSTextField* usernameField;
@property(readonly) NSSecureTextField* passwordField;
@end
// Shows a single item in a password management list. Transitions between
// PENDING, MANAGE, and DELETED states according to user interaction.
@interface ManagePasswordItemViewController : NSViewController {
@private
ManagePasswordsBubbleModel* model_; // weak
ManagePasswordItemState state_;
password_manager::ui::PasswordItemPosition position_;
base::scoped_nsobject<NSView> contentView_;
CGFloat minWidth_;
}
- (id)initWithModel:(ManagePasswordsBubbleModel*)model
position:(password_manager::ui::PasswordItemPosition)position
minWidth:(CGFloat)minWidth;
@end
@interface ManagePasswordItemViewController (Testing)
@property(readonly) ManagePasswordItemState state;
@property(readonly) NSView* contentView;
@end
#endif // CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORD_ITEM_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_password_item_view_controller.h"
#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/layout/layout_constants.h"
namespace {
// TODO(dconnelly): Figure out how to share all these constants.
static const CGFloat kBorderWidth = 1;
static const CGFloat kFramePadding = 16;
void InitLabel(NSTextField* textField, const base::string16& text) {
[textField setStringValue:base::SysUTF16ToNSString(text)];
[textField setEditable:NO];
[textField setSelectable:NO];
[textField setDrawsBackground:NO];
[textField setBezeled:NO];
NSFont* font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
[textField setFont:font];
[textField sizeToFit];
// TODO(dconnelly): Handle max width.
}
NSTextField* UsernameLabel(const base::string16& text) {
base::scoped_nsobject<NSTextField> textField(
[[NSTextField alloc] initWithFrame:NSZeroRect]);
InitLabel(textField, text);
return textField.autorelease();
}
NSSecureTextField* PasswordLabel(const base::string16& text) {
base::scoped_nsobject<NSSecureTextField> textField(
[[NSSecureTextField alloc] initWithFrame:NSZeroRect]);
InitLabel(textField, text);
return textField.autorelease();
}
} // namespace
@implementation ManagePasswordItemPendingView
- (id)initWithForm:(const autofill::PasswordForm&)form {
if ((self = [super initWithFrame:NSZeroRect])) {
CGFloat curX = 0;
CGFloat curY = 0;
// Add the username.
usernameField_.reset([UsernameLabel(form.username_value) retain]);
[usernameField_ setFrameOrigin:NSMakePoint(curX, curY)];
[self addSubview:usernameField_];
// Move to the right of the username and add the password.
curX += NSWidth([usernameField_ frame]) + views::kItemLabelSpacing;
passwordField_.reset([PasswordLabel(form.password_value) retain]);
[passwordField_ setFrameOrigin:NSMakePoint(curX, curY)];
[self addSubview:passwordField_];
// Move to the top-right of the password.
curX = NSMaxX([passwordField_ frame]);
curY = NSMaxY([passwordField_ frame]);
// Update the frame.
[self setFrameSize:NSMakeSize(curX, curY)];
}
return self;
}
@end
@implementation ManagePasswordItemPendingView (Testing)
- (NSTextField*)usernameField {
return usernameField_.get();
}
- (NSSecureTextField*)passwordField {
return passwordField_.get();
}
@end
@implementation ManagePasswordItemViewController
- (id)initWithModel:(ManagePasswordsBubbleModel*)model
position:(password_manager::ui::PasswordItemPosition)position
minWidth:(CGFloat)minWidth {
if ((self = [super initWithNibName:nil bundle:nil])) {
model_ = model;
position_ = position;
minWidth_ = minWidth;
state_ = password_manager::ui::IsPendingState(model_->state())
? MANAGE_PASSWORD_ITEM_STATE_PENDING
: MANAGE_PASSWORD_ITEM_STATE_MANAGE;
switch (state_) {
default:
NOTREACHED();
case MANAGE_PASSWORD_ITEM_STATE_PENDING:
contentView_.reset([[ManagePasswordItemPendingView alloc]
initWithForm:model_->pending_credentials()]);
break;
case MANAGE_PASSWORD_ITEM_STATE_MANAGE:
NOTIMPLEMENTED();
break;
case MANAGE_PASSWORD_ITEM_STATE_DELETED:
NOTIMPLEMENTED();
break;
};
}
return self;
}
- (void)loadView {
self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
[self.view addSubview:contentView_];
// Update the view size according to the content view size, expanding if
// necessary to fill the min width.
const NSSize contentSize = [contentView_ frame].size;
const CGFloat width =
std::max(contentSize.width + 2 * kFramePadding, minWidth_);
const CGFloat height =
contentSize.height + 2 * views::kRelatedControlVerticalSpacing;
[self.view setFrameSize:NSMakeSize(width, height)];
// Position the content view with some padding in the center of the view.
[contentView_
setFrameOrigin:NSMakePoint(kFramePadding,
views::kRelatedControlVerticalSpacing)];
// Add the borders, which go along the entire view.
CGColorRef borderColor =
gfx::CGColorCreateFromSkColor(ui::NativeTheme::instance()->GetSystemColor(
ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor));
// Mac views don't have backing layers by default.
base::scoped_nsobject<CALayer> rootLayer([[CALayer alloc] init]);
[rootLayer setFrame:NSRectToCGRect(self.view.frame)];
[self.view setLayer:rootLayer];
[self.view setWantsLayer:YES];
// The top border is only present for the first item.
if (position_ == password_manager::ui::FIRST_ITEM) {
base::scoped_nsobject<CALayer> topBorder([[CALayer alloc] init]);
[topBorder setBackgroundColor:borderColor];
[topBorder
setFrame:CGRectMake(0, height - kBorderWidth, width, kBorderWidth)];
[self.view.layer addSublayer:topBorder];
}
// The bottom border is always present.
base::scoped_nsobject<CALayer> bottomBorder([[CALayer alloc] init]);
[bottomBorder setBackgroundColor:borderColor];
[bottomBorder setFrame:CGRectMake(0, 0, width, kBorderWidth)];
[self.view.layer addSublayer:bottomBorder];
}
@end
@implementation ManagePasswordItemViewController (Testing)
- (ManagePasswordItemState)state {
return state_;
}
- (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_password_item_view_controller.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/string16.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/browser/ui/cocoa/cocoa_test_helper.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_ui_controller_mock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
namespace {
static const CGFloat kArbitraryWidth = 500;
} // namespace
typedef ManagePasswordsControllerTest ManagePasswordItemViewControllerTest;
TEST_F(ManagePasswordItemViewControllerTest,
PendingStateShouldHavePendingView) {
base::scoped_nsobject<ManagePasswordItemViewController> controller(
[[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],
[[controller contentView] class]);
}
TEST_F(ManagePasswordItemViewControllerTest,
PendingViewShouldHaveCorrectUsernameAndObscuredPassword) {
// Set the pending credentials.
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 =
base::mac::ObjCCast<ManagePasswordItemPendingView>(
[controller contentView]);
// Ensure the fields are populated properly and the password is obscured.
EXPECT_NSEQ(kUsername, pendingView.usernameField.stringValue);
EXPECT_NSEQ(kPassword, pendingView.passwordField.stringValue);
EXPECT_TRUE([[pendingView.passwordField cell] echosBullets]);
}
// 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_CONTROLLER_TEST_H_
#define CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORDS_CONTROLLER_TEST_H_
#include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
#include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
namespace content {
class WebContents;
} // namespace content
class ManagePasswordsUIControllerMock;
class ManagePasswordsBubbleModel;
class ManagePasswordsControllerTest : public CocoaProfileTest {
public:
ManagePasswordsControllerTest();
virtual ~ManagePasswordsControllerTest();
virtual void SetUp() OVERRIDE;
ManagePasswordsUIControllerMock* ui_controller() { return ui_controller_; }
ManagePasswordsBubbleModel* model();
private:
ManagePasswordsUIControllerMock* ui_controller_;
scoped_ptr<content::WebContents> test_web_contents_;
scoped_ptr<ManagePasswordsBubbleModel> model_;
};
#endif // CHROME_BROWSER_UI_COCOA_PASSWORDS_MANAGE_PASSWORDS_CONTROLLER_TEST_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.
#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 "content/public/test/web_contents_tester.h"
ManagePasswordsControllerTest::
ManagePasswordsControllerTest() {
}
ManagePasswordsControllerTest::
~ManagePasswordsControllerTest() {
}
void ManagePasswordsControllerTest::SetUp() {
CocoaProfileTest::SetUp();
// Create the test UIController here so that it's bound to and owned by
// |test_web_contents_| and therefore accessible to the model.
test_web_contents_.reset(
content::WebContentsTester::CreateTestWebContents(profile(), NULL));
ui_controller_ =
new ManagePasswordsUIControllerMock(test_web_contents_.get());
}
ManagePasswordsBubbleModel*
ManagePasswordsControllerTest::model() {
if (!model_) {
model_.reset(new ManagePasswordsBubbleModel(test_web_contents_.get()));
model_->set_state(password_manager::ui::PENDING_PASSWORD_STATE);
}
return model_.get();
}
......@@ -28,6 +28,11 @@ const autofill::PasswordForm&
return pending_credentials_;
}
void ManagePasswordsUIControllerMock::SetPendingCredentials(
autofill::PasswordForm pending_credentials) {
pending_credentials_ = pending_credentials;
}
bool ManagePasswordsUIControllerMock::IsInstalled() const {
return web_contents()->GetUserData(UserDataKey()) == this;
}
......
......@@ -41,6 +41,7 @@ class ManagePasswordsUIControllerMock
bool never_saved_password() const { return never_saved_password_; }
virtual const autofill::PasswordForm& PendingCredentials() const OVERRIDE;
void SetPendingCredentials(autofill::PasswordForm pending_credentials);
// Sneaky setters for testing.
void SetPasswordFormMap(const autofill::ConstPasswordFormMap& map) {
......
......@@ -194,7 +194,7 @@ ManagePasswordItemView::UndoView::~UndoView() {
ManagePasswordItemView::ManagePasswordItemView(
ManagePasswordsBubbleModel* manage_passwords_bubble_model,
autofill::PasswordForm password_form,
Position position)
password_manager::ui::PasswordItemPosition position)
: model_(manage_passwords_bubble_model),
password_form_(password_form),
delete_password_(false) {
......@@ -205,7 +205,7 @@ ManagePasswordItemView::ManagePasswordItemView(
// on both the top and bottom. When it's in the middle of a list, or at the
// end, it has a border only on the bottom.
SetBorder(views::Border::CreateSolidSidedBorder(
position == FIRST_ITEM ? 1 : 0,
position == password_manager::ui::FIRST_ITEM ? 1 : 0,
0,
1,
0,
......
......@@ -7,6 +7,7 @@
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/common/password_manager_ui.h"
class ManagePasswordsBubbleModel;
......@@ -67,12 +68,10 @@ class ManagePasswordItemView : public views::View {
ManagePasswordItemView* parent_;
};
enum Position { FIRST_ITEM, SUBSEQUENT_ITEM };
ManagePasswordItemView(
ManagePasswordsBubbleModel* manage_passwords_bubble_model,
autofill::PasswordForm password_form,
Position position);
password_manager::ui::PasswordItemPosition position);
private:
virtual ~ManagePasswordItemView();
......
......@@ -170,9 +170,9 @@ ManagePasswordsBubbleView::PendingView::PendingView(
// Create the pending credential item, save button and refusal combobox.
ManagePasswordItemView* item =
new ManagePasswordItemView(parent_->model(),
parent_->model()->pending_credentials(),
ManagePasswordItemView::FIRST_ITEM);
new ManagePasswordItemView(parent->model(),
parent->model()->pending_credentials(),
password_manager::ui::FIRST_ITEM);
save_button_ = new views::BlueButton(
this, l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
save_button_->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
......@@ -329,8 +329,8 @@ ManagePasswordsBubbleView::ManageView::ManageView(
parent_->model(),
*i->second,
i == parent_->model()->best_matches().begin()
? ManagePasswordItemView::FIRST_ITEM
: ManagePasswordItemView::SUBSEQUENT_ITEM);
? password_manager::ui::FIRST_ITEM
: password_manager::ui::SUBSEQUENT_ITEM);
layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
layout->AddView(item);
......
......@@ -625,6 +625,8 @@
'browser/ui/cocoa/panels/panel_utils_cocoa.mm',
'browser/ui/cocoa/panels/panel_window_controller_cocoa.h',
'browser/ui/cocoa/panels/panel_window_controller_cocoa.mm',
'browser/ui/cocoa/passwords/manage_password_item_view_controller.h',
'browser/ui/cocoa/passwords/manage_password_item_view_controller.mm',
'browser/ui/cocoa/pdf_password_dialog.mm',
'browser/ui/cocoa/presentation_mode_controller.h',
'browser/ui/cocoa/presentation_mode_controller.mm',
......
......@@ -1623,6 +1623,9 @@
'browser/ui/cocoa/omnibox/omnibox_view_mac_unittest.mm',
'browser/ui/cocoa/one_click_signin_bubble_controller_unittest.mm',
'browser/ui/cocoa/panels/panel_cocoa_unittest.mm',
'browser/ui/cocoa/passwords/manage_password_item_view_controller_unittest.mm',
'browser/ui/cocoa/passwords/manage_passwords_controller_test.h',
'browser/ui/cocoa/passwords/manage_passwords_controller_test.mm',
'browser/ui/cocoa/profiles/avatar_button_controller_unittest.mm',
'browser/ui/cocoa/profiles/avatar_icon_controller_unittest.mm',
'browser/ui/cocoa/profiles/avatar_label_button_unittest.mm',
......
......@@ -37,6 +37,15 @@ enum State {
BLACKLIST_STATE,
};
// The position of a password item in a list of credentials.
enum PasswordItemPosition {
// The password item is the first in the list.
FIRST_ITEM,
// The password item is not the first item in the list.
SUBSEQUENT_ITEM,
};
// Returns true if |state| represents a pending password.
bool IsPendingState(State state);
......
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