blob: a03b0e6b0b0d073a4837121d20d9d4410c59af92 [file] [log] [blame]
// Copyright 2018 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 "ios/chrome/browser/ui/autofill/manual_fill/password_mediator.h"
#include <vector>
#include "base/strings/sys_string_conversions.h"
#include "components/password_manager/core/browser/password_store.h"
#import "ios/chrome/browser/autofill/manual_fill/passwords_fetcher.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/action_cell.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/credential.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/credential_password_form.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_delegate.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/password_consumer.h"
#import "ios/chrome/browser/ui/autofill/manual_fill/password_list_delegate.h"
#import "ios/chrome/browser/ui/list_model/list_model.h"
#import "ios/chrome/browser/ui/table_view/table_view_model.h"
#import "ios/chrome/browser/web_state_list/web_state_list.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/web_state/web_state.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace manual_fill {
NSString* const ManagePasswordsAccessibilityIdentifier =
@"kManualFillManagePasswordsAccessibilityIdentifier";
NSString* const OtherPasswordsAccessibilityIdentifier =
@"kManualFillOtherPasswordsAccessibilityIdentifier";
} // namespace manual_fill
@interface ManualFillPasswordMediator ()<ManualFillContentDelegate,
PasswordFetcherDelegate>
// The |WebStateList| containing the active web state. Used to filter the list
// of credentials based on the active web state.
@property(nonatomic, assign) WebStateList* webStateList;
// The password fetcher to query the user profile.
@property(nonatomic, strong) PasswordFetcher* passwordFetcher;
// A cache of the credentials fetched from the store, not synced. Useful to
// reuse the mediator.
@property(nonatomic, strong) NSArray<ManualFillCredential*>* credentials;
// If the filter is disabled, the "Show All Passwords" button is not included
// in the model.
@property(nonatomic, assign, readonly) BOOL isAllPasswordButtonEnabled;
@end
@implementation ManualFillPasswordMediator
@synthesize consumer = _consumer;
@synthesize contentDelegate = _contentDelegate;
@synthesize credentials = _credentials;
@synthesize disableFilter = _disableFilter;
@synthesize navigationDelegate = _navigationDelegate;
@synthesize passwordFetcher = _passwordFetcher;
@synthesize webStateList = _webStateList;
- (instancetype)initWithWebStateList:(WebStateList*)webStateList
passwordStore:
(scoped_refptr<password_manager::PasswordStore>)
passwordStore {
self = [super init];
if (self) {
_credentials = @[];
_webStateList = webStateList;
_passwordFetcher =
[[PasswordFetcher alloc] initWithPasswordStore:passwordStore
delegate:self];
}
return self;
}
- (void)setConsumer:(id<ManualFillPasswordConsumer>)consumer {
if (consumer == _consumer) {
return;
}
_consumer = consumer;
[self postCredentialsToConsumer];
[self postActionsToConsumer];
}
#pragma mark - PasswordFetcherDelegate
- (void)passwordFetcher:(PasswordFetcher*)passwordFetcher
didFetchPasswords:
(std::vector<std::unique_ptr<autofill::PasswordForm>>&)passwords {
NSMutableArray<ManualFillCredential*>* credentials =
[[NSMutableArray alloc] initWithCapacity:passwords.size()];
for (auto it = passwords.begin(); it != passwords.end(); ++it) {
autofill::PasswordForm& form = **it;
ManualFillCredential* credential =
[[ManualFillCredential alloc] initWithPasswordForm:form];
[credentials addObject:credential];
}
self.credentials = credentials;
[self postCredentialsToConsumer];
}
#pragma mark - UISearchResultsUpdating
- (void)updateSearchResultsForSearchController:
(UISearchController*)searchController {
NSString* searchText = searchController.searchBar.text;
if (!searchText.length) {
auto credentials = [self createItemsForCredentials:self.credentials];
[self.consumer presentCredentials:credentials];
return;
}
NSPredicate* predicate = [NSPredicate
predicateWithFormat:@"host CONTAINS[cd] %@ OR username CONTAINS[cd] %@",
searchText, searchText];
NSArray* filteredCredentials =
[self.credentials filteredArrayUsingPredicate:predicate];
auto credentials = [self createItemsForCredentials:filteredCredentials];
[self.consumer presentCredentials:credentials];
}
#pragma mark - Private
// Posts the credentials to the consumer. If filtered is |YES| it only post the
// ones associated with the active web state.
- (void)postCredentialsToConsumer {
if (!self.consumer) {
return;
}
if (self.disableFilter) {
auto credentials = [self createItemsForCredentials:self.credentials];
[self.consumer presentCredentials:credentials];
return;
}
web::WebState* currentWebState = self.webStateList->GetActiveWebState();
if (!currentWebState) {
return;
}
GURL visibleURL = currentWebState->GetVisibleURL();
std::string site_name =
net::registry_controlled_domains::GetDomainAndRegistry(
visibleURL.host(),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
// Sometimes the site_name can be empty. i.e. if the host is an IP address.
if (site_name.empty()) {
site_name = visibleURL.host();
}
NSString* siteName = base::SysUTF8ToNSString(site_name);
NSPredicate* predicate =
[NSPredicate predicateWithFormat:@"siteName = %@", siteName];
NSArray* filteredCredentials =
[self.credentials filteredArrayUsingPredicate:predicate];
auto credentials = [self createItemsForCredentials:filteredCredentials];
[self.consumer presentCredentials:credentials];
}
// Creates a table view model with the passed credentials.
- (NSArray<ManualFillCredentialItem*>*)createItemsForCredentials:
(NSArray<ManualFillCredential*>*)credentials {
NSMutableArray* items =
[[NSMutableArray alloc] initWithCapacity:credentials.count];
for (ManualFillCredential* credential in credentials) {
auto item = [[ManualFillCredentialItem alloc] initWithCredential:credential
delegate:self];
[items addObject:item];
}
return items;
}
- (void)postActionsToConsumer {
if (!self.consumer) {
return;
}
if (self.isAllPasswordButtonEnabled) {
NSString* otherPasswordsTitleString = l10n_util::GetNSString(
IDS_IOS_MANUAL_FALLBACK_USE_OTHER_PASSWORD_WITH_DOTS);
__weak __typeof(self) weakSelf = self;
auto otherPasswordsItem = [[ManualFillActionItem alloc]
initWithTitle:otherPasswordsTitleString
action:^{
[weakSelf.navigationDelegate openAllPasswordsList];
}];
otherPasswordsItem.accessibilityIdentifier =
manual_fill::OtherPasswordsAccessibilityIdentifier;
NSString* managePasswordsTitle =
l10n_util::GetNSString(IDS_IOS_MANUAL_FALLBACK_MANAGE_PASSWORDS);
auto managePasswordsItem = [[ManualFillActionItem alloc]
initWithTitle:managePasswordsTitle
action:^{
[weakSelf.navigationDelegate openPasswordSettings];
}];
managePasswordsItem.accessibilityIdentifier =
manual_fill::ManagePasswordsAccessibilityIdentifier;
[self.consumer presentActions:@[ otherPasswordsItem, managePasswordsItem ]];
} else {
[self.consumer presentActions:@[]];
}
}
#pragma mark - Getters
- (BOOL)isAllPasswordButtonEnabled {
return !self.disableFilter;
}
#pragma mark - ManualFillContentDelegate
- (void)userDidPickContent:(NSString*)content isSecure:(BOOL)isSecure {
[self.navigationDelegate dismissPresentedViewController];
[self.contentDelegate userDidPickContent:content isSecure:isSecure];
}
@end