[go: nahoru, domu]

blob: f82d0353c94e41019b0f8bc0e7fc42bb0eeb1c38 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/infobars/modals/infobar_password_table_view_controller.h"
#import "base/apple/foundation_util.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "base/notreached.h"
#import "ios/chrome/browser/infobars/infobar_metrics_recorder.h"
#import "ios/chrome/browser/passwords/ios_chrome_password_infobar_metrics_recorder.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_button_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_edit_item.h"
#import "ios/chrome/browser/shared/ui/table_view/chrome_table_view_styler.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/ui/infobars/modals/infobar_modal_constants.h"
#import "ios/chrome/browser/ui/infobars/modals/infobar_password_modal_delegate.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"
namespace {
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierContent = kSectionIdentifierEnumZero,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeURL = kItemTypeEnumZero,
ItemTypeUsername,
ItemTypePassword,
ItemTypeSaveCredentials,
ItemTypeCancel,
};
const CGFloat kSymbolSize = 15;
} // namespace
@interface InfobarPasswordTableViewController () <UITextFieldDelegate>
// Properties backing InfobarPasswordModalConsumer interface.
@property(nonatomic, copy) NSString* username;
@property(nonatomic, copy) NSString* maskedPassword;
@property(nonatomic, copy) NSString* unmaskedPassword;
@property(nonatomic, copy) NSString* detailsTextMessage;
@property(nonatomic, copy) NSString* URL;
@property(nonatomic, copy) NSString* saveButtonText;
@property(nonatomic, copy) NSString* cancelButtonText;
@property(nonatomic, assign) BOOL currentCredentialsSaved;
// Item that holds the Username TextField information.
@property(nonatomic, strong) TableViewTextEditItem* usernameItem;
// Item that holds the Password TextField information.
@property(nonatomic, strong) TableViewTextEditItem* passwordItem;
// Item that holds the SaveCredentials Button information.
@property(nonatomic, strong) TableViewTextButtonItem* saveCredentialsItem;
// Item that holds the cancel Button for this Infobar. e.g. "Never Save for this
// site".
@property(nonatomic, strong) TableViewTextButtonItem* cancelInfobarItem;
// Username at the time the InfobarModal is presented.
@property(nonatomic, copy) NSString* originalUsername;
// InfobarPasswordModalDelegate for this ViewController.
@property(nonatomic, strong) id<InfobarPasswordModalDelegate>
infobarModalDelegate;
// Used to build and record metrics.
@property(nonatomic, strong) InfobarMetricsRecorder* metricsRecorder;
// Used to build and record metrics specific to passwords.
@property(nonatomic, strong)
IOSChromePasswordInfobarMetricsRecorder* passwordMetricsRecorder;
// Whether the current password being shown is masked or not.
@property(nonatomic, assign) BOOL passwordMasked;
@end
@implementation InfobarPasswordTableViewController
- (instancetype)initWithDelegate:(id<InfobarPasswordModalDelegate>)modalDelegate
type:(InfobarType)infobarType {
self = [super initWithStyle:UITableViewStylePlain];
if (self) {
_infobarModalDelegate = modalDelegate;
_metricsRecorder =
[[InfobarMetricsRecorder alloc] initWithType:infobarType];
switch (infobarType) {
case InfobarType::kInfobarTypePasswordUpdate:
_passwordMetricsRecorder =
[[IOSChromePasswordInfobarMetricsRecorder alloc]
initWithType:PasswordInfobarType::kPasswordInfobarTypeUpdate];
break;
case InfobarType::kInfobarTypePasswordSave:
_passwordMetricsRecorder =
[[IOSChromePasswordInfobarMetricsRecorder alloc]
initWithType:PasswordInfobarType::kPasswordInfobarTypeSave];
break;
default:
NOTREACHED();
break;
}
}
return self;
}
#pragma mark - ViewController Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor];
self.styler.cellBackgroundColor = [UIColor colorNamed:kBackgroundColor];
self.tableView.sectionHeaderHeight = 0;
[self.tableView
setSeparatorInset:UIEdgeInsetsMake(0, kTableViewHorizontalSpacing, 0, 0)];
// Configure the NavigationBar.
UIBarButtonItem* cancelButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self
action:@selector(dismissInfobarModal)];
cancelButton.accessibilityIdentifier = kInfobarModalCancelButton;
UIImage* gearImage = DefaultSymbolWithPointSize(kSettingsFilledSymbol,
kInfobarSymbolPointSize);
UIBarButtonItem* settingsButton = [[UIBarButtonItem alloc]
initWithImage:gearImage
style:UIBarButtonItemStylePlain
target:self
action:@selector(presentPasswordSettings)];
settingsButton.accessibilityLabel =
l10n_util::GetNSString(IDS_IOS_INFOBAR_MODAL_PASSWORD_SETTINGS_HINT);
self.navigationItem.leftBarButtonItem = cancelButton;
self.navigationItem.rightBarButtonItem = settingsButton;
self.navigationController.navigationBar.prefersLargeTitles = NO;
[self loadModel];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.metricsRecorder recordModalEvent:MobileMessagesModalEvent::Presented];
}
- (void)viewDidDisappear:(BOOL)animated {
[self.infobarModalDelegate modalInfobarWasDismissed:self];
[self.metricsRecorder recordModalEvent:MobileMessagesModalEvent::Dismissed];
[super viewDidDisappear:animated];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.tableView.scrollEnabled =
self.tableView.contentSize.height > self.view.frame.size.height;
}
#pragma mark - TableViewModel
- (void)loadModel {
[super loadModel];
TableViewModel* model = self.tableViewModel;
[model addSectionWithIdentifier:SectionIdentifierContent];
TableViewTextEditItem* URLItem =
[[TableViewTextEditItem alloc] initWithType:ItemTypeURL];
URLItem.fieldNameLabelText =
l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
URLItem.textFieldValue = self.URL;
URLItem.hideIcon = YES;
[model addItem:URLItem toSectionWithIdentifier:SectionIdentifierContent];
self.originalUsername = self.username;
self.usernameItem =
[[TableViewTextEditItem alloc] initWithType:ItemTypeUsername];
self.usernameItem.fieldNameLabelText =
l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
self.usernameItem.textFieldValue = self.username;
self.usernameItem.returnKeyType = UIReturnKeyDone;
self.usernameItem.textFieldEnabled = !self.currentCredentialsSaved;
self.usernameItem.autoCapitalizationType = UITextAutocapitalizationTypeNone;
[model addItem:self.usernameItem
toSectionWithIdentifier:SectionIdentifierContent];
self.passwordItem =
[[TableViewTextEditItem alloc] initWithType:ItemTypePassword];
self.passwordItem.fieldNameLabelText =
l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
self.passwordItem.textFieldValue = self.maskedPassword;
self.passwordItem.identifyingIcon =
DefaultSymbolWithPointSize(kShowActionSymbol, kSymbolSize);
self.passwordItem.identifyingIconEnabled = YES;
self.passwordItem.hideIcon = YES;
self.passwordItem.identifyingIconAccessibilityLabel = l10n_util::GetNSString(
IDS_IOS_INFOBAR_MODAL_PASSWORD_REVEAL_PASSWORD_HINT);
[model addItem:self.passwordItem
toSectionWithIdentifier:SectionIdentifierContent];
self.passwordMasked = YES;
self.saveCredentialsItem =
[[TableViewTextButtonItem alloc] initWithType:ItemTypeSaveCredentials];
self.saveCredentialsItem.textAlignment = NSTextAlignmentNatural;
self.saveCredentialsItem.text = self.detailsTextMessage;
self.saveCredentialsItem.buttonText = self.saveButtonText;
self.saveCredentialsItem.enabled = !self.currentCredentialsSaved;
self.saveCredentialsItem.disableButtonIntrinsicWidth = YES;
[model addItem:self.saveCredentialsItem
toSectionWithIdentifier:SectionIdentifierContent];
if ([self.cancelButtonText length]) {
self.cancelInfobarItem =
[[TableViewTextButtonItem alloc] initWithType:ItemTypeCancel];
self.cancelInfobarItem.buttonText = self.cancelButtonText;
self.cancelInfobarItem.buttonTextColor = [UIColor colorNamed:kBlueColor];
self.cancelInfobarItem.buttonBackgroundColor = [UIColor clearColor];
self.cancelInfobarItem.boldButtonText = NO;
[model addItem:self.cancelInfobarItem
toSectionWithIdentifier:SectionIdentifierContent];
}
}
#pragma mark - UITableViewDataSource
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
UITableViewCell* cell = [super tableView:tableView
cellForRowAtIndexPath:indexPath];
ItemType itemType = static_cast<ItemType>(
[self.tableViewModel itemTypeForIndexPath:indexPath]);
switch (itemType) {
case ItemTypeSaveCredentials: {
TableViewTextButtonCell* tableViewTextButtonCell =
base::apple::ObjCCastStrict<TableViewTextButtonCell>(cell);
[tableViewTextButtonCell.button
addTarget:self
action:@selector(saveCredentialsButtonWasPressed:)
forControlEvents:UIControlEventTouchUpInside];
tableViewTextButtonCell.separatorInset =
UIEdgeInsetsMake(0, 0, 0, self.tableView.bounds.size.width);
break;
}
case ItemTypeCancel: {
TableViewTextButtonCell* tableViewTextButtonCell =
base::apple::ObjCCastStrict<TableViewTextButtonCell>(cell);
[tableViewTextButtonCell.button
addTarget:self
action:@selector(neverSaveCredentialsForCurrentSite)
forControlEvents:UIControlEventTouchUpInside];
break;
}
case ItemTypeUsername: {
TableViewTextEditCell* editCell =
base::apple::ObjCCast<TableViewTextEditCell>(cell);
[editCell.textField addTarget:self
action:@selector(usernameEditDidBegin)
forControlEvents:UIControlEventEditingDidBegin];
[editCell.textField addTarget:self
action:@selector(updateSaveCredentialsButtonState)
forControlEvents:UIControlEventEditingChanged];
editCell.selectionStyle = UITableViewCellSelectionStyleNone;
editCell.textField.delegate = self;
break;
}
case ItemTypePassword: {
TableViewTextEditCell* editCell =
base::apple::ObjCCast<TableViewTextEditCell>(cell);
[editCell.textField addTarget:self
action:@selector(updateSaveCredentialsButtonState)
forControlEvents:UIControlEventEditingChanged];
[editCell.identifyingIconButton addTarget:self
action:@selector(togglePasswordMasking)
forControlEvents:UIControlEventTouchUpInside];
editCell.selectionStyle = UITableViewCellSelectionStyleNone;
break;
}
case ItemTypeURL:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
break;
}
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView*)tableView
heightForFooterInSection:(NSInteger)section {
return 0;
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
[textField resignFirstResponder];
return YES;
}
#pragma mark - Private Methods
- (void)updateSaveCredentialsButtonState {
BOOL currentButtonState = [self.saveCredentialsItem isEnabled];
BOOL newButtonState = [self.passwordItem.textFieldValue length] ? YES : NO;
if (currentButtonState != newButtonState) {
self.saveCredentialsItem.enabled = newButtonState;
[self reconfigureCellsForItems:@[ self.saveCredentialsItem ]];
}
// TODO(crbug.com/945478):Ideally the InfobarDelegate should update the button
// text. Once we have a consumer protocol we should be able to create a
// delegate that asks the InfobarDelegate for the correct text.
NSString* buttonText =
[self.usernameItem.textFieldValue isEqualToString:self.originalUsername]
? self.saveButtonText
: l10n_util::GetNSString(IDS_IOS_PASSWORD_MANAGER_SAVE_BUTTON);
if (![self.saveCredentialsItem.buttonText isEqualToString:buttonText]) {
self.saveCredentialsItem.buttonText = buttonText;
[self reconfigureCellsForItems:@[ self.saveCredentialsItem ]];
}
}
- (void)dismissInfobarModal {
base::RecordAction(
base::UserMetricsAction("MobileMessagesModalCancelledTapped"));
[self.metricsRecorder recordModalEvent:MobileMessagesModalEvent::Canceled];
[self.infobarModalDelegate dismissInfobarModal:self];
}
- (void)saveCredentialsButtonWasPressed:(UIButton*)sender {
base::RecordAction(
base::UserMetricsAction("MobileMessagesModalAcceptedTapped"));
[self.metricsRecorder recordModalEvent:MobileMessagesModalEvent::Accepted];
if ([self.saveCredentialsItem.buttonText
isEqualToString:l10n_util::GetNSString(
IDS_IOS_PASSWORD_MANAGER_SAVE_BUTTON)]) {
[self.passwordMetricsRecorder
recordModalDismiss:MobileMessagesPasswordsModalDismiss::
SavedCredentials];
} else {
[self.passwordMetricsRecorder
recordModalDismiss:MobileMessagesPasswordsModalDismiss::
UpdatedCredentials];
}
[self.infobarModalDelegate
updateCredentialsWithUsername:self.usernameItem.textFieldValue
password:self.unmaskedPassword];
}
- (void)presentPasswordSettings {
base::RecordAction(base::UserMetricsAction("MobileMessagesModalSettings"));
[self.metricsRecorder
recordModalEvent:MobileMessagesModalEvent::SettingsOpened];
[self.infobarModalDelegate presentPasswordSettings];
}
- (void)neverSaveCredentialsForCurrentSite {
base::RecordAction(base::UserMetricsAction("MobileMessagesModalNever"));
[self.passwordMetricsRecorder
recordModalDismiss:MobileMessagesPasswordsModalDismiss::
TappedNeverForThisSite];
[self.infobarModalDelegate neverSaveCredentialsForCurrentSite];
}
- (void)usernameEditDidBegin {
[self.passwordMetricsRecorder
recordModalEvent:MobileMessagesPasswordsModalEvent::EditedUserName];
}
- (void)togglePasswordMasking {
self.passwordMasked = !self.passwordMasked;
if (self.passwordMasked) {
self.passwordItem.identifyingIcon =
DefaultSymbolWithPointSize(kShowActionSymbol, kSymbolSize);
self.passwordItem.textFieldValue = self.maskedPassword;
self.passwordItem.identifyingIconAccessibilityLabel =
l10n_util::GetNSString(
IDS_IOS_INFOBAR_MODAL_PASSWORD_REVEAL_PASSWORD_HINT);
[self.passwordMetricsRecorder
recordModalEvent:MobileMessagesPasswordsModalEvent::MaskedPassword];
} else {
self.passwordItem.identifyingIcon =
DefaultSymbolWithPointSize(kHideActionSymbol, kSymbolSize);
self.passwordItem.textFieldValue = self.unmaskedPassword;
self.passwordItem.identifyingIconAccessibilityLabel =
l10n_util::GetNSString(
IDS_IOS_INFOBAR_MODAL_PASSWORD_HIDE_PASSWORD_HINT);
[self.passwordMetricsRecorder
recordModalEvent:MobileMessagesPasswordsModalEvent::UnmaskedPassword];
}
[self reconfigureCellsForItems:@[ self.passwordItem ]];
}
@end