Commit a546ed3d authored by vasilii's avatar vasilii Committed by Commit Bot

Implement "Show all passwords" in the keyboard accessory on iOS.

The new button appears when a password field is focused or when other password suggestions are displayed. The latter may happen when a username field is focused.
Click on the button opens the list of passwords. User can find the relevant one there and copy it.

Bug: 782287
Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: I199851bd28c76ac4b626f8a1ff81a86e8479a51e
Reviewed-on: https://chromium-review.googlesource.com/816479
Commit-Queue: Vasilii Sukhanov <vasilii@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#537679}
parent 33b9ef7d
...@@ -1635,6 +1635,22 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData( ...@@ -1635,6 +1635,22 @@ void MainControllerAuthenticationServiceDelegate::ClearBrowsingData(
completion:nil]; completion:nil];
} }
// TODO(crbug.com/779791) : Remove show settings commands from MainController.
- (void)showSavedPasswordsSettingsFromViewController:
(UIViewController*)baseViewController {
if (_settingsNavigationController) {
[_settingsNavigationController
showSavedPasswordsSettingsFromViewController:baseViewController];
return;
}
_settingsNavigationController =
[SettingsNavigationController newSavePasswordsController:_mainBrowserState
delegate:self];
[baseViewController presentViewController:_settingsNavigationController
animated:YES
completion:nil];
}
#pragma mark - chromeExecuteCommand #pragma mark - chromeExecuteCommand
- (IBAction)chromeExecuteCommand:(id)sender { - (IBAction)chromeExecuteCommand:(id)sender {
......
...@@ -33,6 +33,9 @@ class PasswordManagerDriver; ...@@ -33,6 +33,9 @@ class PasswordManagerDriver;
- (BOOL)displaySignInNotification:(UIViewController*)viewController - (BOOL)displaySignInNotification:(UIViewController*)viewController
fromTabId:(NSString*)tabId; fromTabId:(NSString*)tabId;
// Opens the list of saved passwords in the settings.
- (void)displaySavedPasswordList;
@end @end
// Per-tab password controller. Handles password autofill and saving. // Per-tab password controller. Handles password autofill and saving.
......
...@@ -45,10 +45,12 @@ ...@@ -45,10 +45,12 @@
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" #include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h"
#import "ios/chrome/browser/ui/commands/application_commands.h" #import "ios/chrome/browser/ui/commands/application_commands.h"
#include "ios/chrome/browser/web/tab_id_tab_helper.h" #include "ios/chrome/browser/web/tab_id_tab_helper.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/origin_util.h" #import "ios/web/public/origin_util.h"
#include "ios/web/public/url_scheme_util.h" #include "ios/web/public/url_scheme_util.h"
#import "ios/web/public/web_state/js/crw_js_injection_receiver.h" #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
#import "ios/web/public/web_state/web_state.h" #import "ios/web/public/web_state/web_state.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "url/gurl.h" #include "url/gurl.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
...@@ -162,7 +164,7 @@ namespace { ...@@ -162,7 +164,7 @@ namespace {
// Constructs an array of FormSuggestions, each corresponding to a username/ // Constructs an array of FormSuggestions, each corresponding to a username/
// password pair in |AccountSelectFillData|, such that |typedValue| is a prefix // password pair in |AccountSelectFillData|, such that |typedValue| is a prefix
// of the username of each suggestion. // of the username of each suggestion. "Show all" item is appended.
NSArray* BuildSuggestions(const AccountSelectFillData& fillData, NSArray* BuildSuggestions(const AccountSelectFillData& fillData,
NSString* formName, NSString* formName,
NSString* fieldName, NSString* fieldName,
...@@ -172,24 +174,34 @@ NSArray* BuildSuggestions(const AccountSelectFillData& fillData, ...@@ -172,24 +174,34 @@ NSArray* BuildSuggestions(const AccountSelectFillData& fillData,
base::string16 typed_value = base::SysNSStringToUTF16(typedValue); base::string16 typed_value = base::SysNSStringToUTF16(typedValue);
NSMutableArray* suggestions = [NSMutableArray array]; NSMutableArray* suggestions = [NSMutableArray array];
std::vector<password_manager::UsernameAndRealm> username_and_realms_ = if (fillData.IsSuggestionsAvailable(form_name, field_name)) {
fillData.RetrieveSuggestions(form_name, field_name, typed_value); std::vector<password_manager::UsernameAndRealm> username_and_realms_ =
if (username_and_realms_.empty()) fillData.RetrieveSuggestions(form_name, field_name, typed_value);
return suggestions;
// Add credentials.
for (const auto& username_and_realm : username_and_realms_) { for (const auto& username_and_realm : username_and_realms_) {
NSString* username = base::SysUTF16ToNSString(username_and_realm.username); NSString* username =
NSString* origin = username_and_realm.realm.empty() base::SysUTF16ToNSString(username_and_realm.username);
? nil NSString* origin =
: base::SysUTF8ToNSString(username_and_realm.realm); username_and_realm.realm.empty()
? nil
[suggestions addObject:[FormSuggestion suggestionWithValue:username : base::SysUTF8ToNSString(username_and_realm.realm);
displayDescription:origin
icon:nil [suggestions addObject:[FormSuggestion suggestionWithValue:username
identifier:0]]; displayDescription:origin
icon:nil
identifier:0]];
}
} }
return suggestions; // Add "Show all".
NSString* showAll = l10n_util::GetNSString(IDS_IOS_SHOW_ALL_PASSWORDS);
[suggestions addObject:[FormSuggestion suggestionWithValue:showAll
displayDescription:nil
icon:nil
identifier:1]];
return [suggestions copy];
} }
// Removes URL components not essential for matching the URL to // Removes URL components not essential for matching the URL to
...@@ -674,19 +686,21 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -674,19 +686,21 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
webState:(web::WebState*)webState webState:(web::WebState*)webState
completionHandler: completionHandler:
(SuggestionsAvailableCompletion)completion { (SuggestionsAvailableCompletion)completion {
if (!sentRequestToStore_ && [type isEqual:@"focus"]) { if (!GetPageURLAndCheckTrustLevel(webState, nullptr)) {
[self findPasswordFormsAndSendThemToPasswordStore];
completion(NO); completion(NO);
return; return;
} }
if (fillData_.Empty() || !GetPageURLAndCheckTrustLevel(webState, nullptr)) { if (!sentRequestToStore_ && [type isEqual:@"focus"])
completion(NO); [self findPasswordFormsAndSendThemToPasswordStore];
if ([fieldType isEqual:@"password"]) {
// Always dispay "Show all" on the password field.
completion(YES);
return; return;
} }
completion(!fillData_.Empty() && fillData_.IsSuggestionsAvailable(
// Suggestions are available for the username field of the password form. base::SysNSStringToUTF16(formName),
completion(fillData_.IsSuggestionsAvailable( base::SysNSStringToUTF16(fieldName)));
base::SysNSStringToUTF16(formName), base::SysNSStringToUTF16(fieldName)));
} }
- (void)retrieveSuggestionsForForm:(NSString*)formName - (void)retrieveSuggestionsForForm:(NSString*)formName
...@@ -697,10 +711,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -697,10 +711,7 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
webState:(web::WebState*)webState webState:(web::WebState*)webState
completionHandler:(SuggestionsReadyCompletion)completion { completionHandler:(SuggestionsReadyCompletion)completion {
DCHECK(GetPageURLAndCheckTrustLevel(webState, nullptr)); DCHECK(GetPageURLAndCheckTrustLevel(webState, nullptr));
if (fillData_.Empty()) {
completion(@[], nil);
return;
}
completion(BuildSuggestions(fillData_, formName, fieldName, typedValue), completion(BuildSuggestions(fillData_, formName, fieldName, typedValue),
self); self);
} }
...@@ -709,6 +720,12 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) { ...@@ -709,6 +720,12 @@ bool GetPageURLAndCheckTrustLevel(web::WebState* web_state, GURL* page_url) {
forField:(NSString*)fieldName forField:(NSString*)fieldName
form:(NSString*)formName form:(NSString*)formName
completionHandler:(SuggestionHandledCompletion)completion { completionHandler:(SuggestionHandledCompletion)completion {
if (suggestion.identifier == 1) {
// Navigate to the settings list.
[self.delegate displaySavedPasswordList];
completion();
return;
}
const base::string16 username = base::SysNSStringToUTF16(suggestion.value); const base::string16 username = base::SysNSStringToUTF16(suggestion.value);
std::unique_ptr<FillData> fillData = fillData_.GetFillData(username); std::unique_ptr<FillData> fillData = fillData_.GetFillData(username);
......
...@@ -187,14 +187,12 @@ class PasswordControllerTest : public web::WebTestWithWebState { ...@@ -187,14 +187,12 @@ class PasswordControllerTest : public web::WebTestWithWebState {
// YES on success, NO otherwise. // YES on success, NO otherwise.
BOOL BasicFormFill(NSString* html); BOOL BasicFormFill(NSString* html);
// Retrieve the current suggestions from suggestionController_ sorted in // Retrieve the current suggestions from suggestionController_.
// alphabetical order according to their value properties. NSArray* GetSuggestionValues() {
NSArray* GetSortedSuggestionValues() {
NSMutableArray* suggestion_values = [NSMutableArray array]; NSMutableArray* suggestion_values = [NSMutableArray array];
for (FormSuggestion* suggestion in [suggestionController_ suggestions]) for (FormSuggestion* suggestion in [suggestionController_ suggestions])
[suggestion_values addObject:suggestion.value]; [suggestion_values addObject:suggestion.value];
return [suggestion_values return [suggestion_values copy];
sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
} }
// Returns an identifier for the |form_number|th form in the page. // Returns an identifier for the |form_number|th form in the page.
...@@ -1115,6 +1113,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) { ...@@ -1115,6 +1113,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) {
EXPECT_NSEQ(@"[]=, onkeyup=false, onchange=false", EXPECT_NSEQ(@"[]=, onkeyup=false, onchange=false",
ExecuteJavaScript(kUsernamePasswordVerificationScript)); ExecuteJavaScript(kUsernamePasswordVerificationScript));
NSString* showAll = @"Show All\u2026";
// clang-format off // clang-format off
SuggestionTestData test_data[] = { SuggestionTestData test_data[] = {
{ {
...@@ -1123,16 +1122,16 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) { ...@@ -1123,16 +1122,16 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) {
"evt.initEvent('focus', true, true, window, 1);" "evt.initEvent('focus', true, true, window, 1);"
"username_.dispatchEvent(evt);"), "username_.dispatchEvent(evt);"),
@""], @""],
@[@"abc", @"user0"], @[@"user0", @"abc", showAll],
@"[]=, onkeyup=false, onchange=false" @"[]=, onkeyup=false, onchange=false"
}, },
{ {
"Should not show suggestions when focusing password field", "Should not show password suggestions when focusing password field",
@[(@"var evt = document.createEvent('Events');" @[(@"var evt = document.createEvent('Events');"
"evt.initEvent('focus', true, true, window, 1);" "evt.initEvent('focus', true, true, window, 1);"
"password_.dispatchEvent(evt);"), "password_.dispatchEvent(evt);"),
@""], @""],
@[], @[showAll],
@"[]=, onkeyup=false, onchange=false" @"[]=, onkeyup=false, onchange=false"
}, },
{ {
...@@ -1142,7 +1141,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) { ...@@ -1142,7 +1141,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) {
"evt.initEvent('focus', true, true, window, 1);" "evt.initEvent('focus', true, true, window, 1);"
"username_.dispatchEvent(evt);"), "username_.dispatchEvent(evt);"),
@""], @""],
@[@"abc"], @[@"abc", showAll],
@"ab[]=, onkeyup=false, onchange=false" @"ab[]=, onkeyup=false, onchange=false"
}, },
{ {
...@@ -1156,7 +1155,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) { ...@@ -1156,7 +1155,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) {
"evt.keyCode = 98;" "evt.keyCode = 98;"
"username_.dispatchEvent(evt);"), "username_.dispatchEvent(evt);"),
@""], @""],
@[@"abc"], @[@"abc", showAll],
@"ab[]=, onkeyup=true, onchange=false" @"ab[]=, onkeyup=true, onchange=false"
}, },
{ {
...@@ -1175,7 +1174,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) { ...@@ -1175,7 +1174,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) {
"evt.keyCode = 8;" "evt.keyCode = 8;"
"username_.dispatchEvent(evt);"), "username_.dispatchEvent(evt);"),
@""], @""],
@[@"abc", @"user0"], @[@"user0", @"abc", showAll],
@"[]=, onkeyup=true, onchange=false" @"[]=, onkeyup=true, onchange=false"
}, },
}; };
...@@ -1198,7 +1197,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) { ...@@ -1198,7 +1197,7 @@ TEST_F(PasswordControllerTest, SuggestionUpdateTests) {
WaitForBackgroundTasks(); WaitForBackgroundTasks();
} }
EXPECT_NSEQ(data.expected_suggestions, GetSortedSuggestionValues()); EXPECT_NSEQ(data.expected_suggestions, GetSuggestionValues());
EXPECT_NSEQ(data.expected_result, EXPECT_NSEQ(data.expected_result,
ExecuteJavaScript(kUsernamePasswordVerificationScript)); ExecuteJavaScript(kUsernamePasswordVerificationScript));
// Clear all suggestions. // Clear all suggestions.
...@@ -1274,7 +1273,7 @@ TEST_F(PasswordControllerTest, SelectingSuggestionShouldFillPasswordForm) { ...@@ -1274,7 +1273,7 @@ TEST_F(PasswordControllerTest, SelectingSuggestionShouldFillPasswordForm) {
webState:web_state() webState:web_state()
completionHandler:^(NSArray* suggestions, completionHandler:^(NSArray* suggestions,
id<FormSuggestionProvider> provider) { id<FormSuggestionProvider> provider) {
EXPECT_EQ(1u, [suggestions count]); EXPECT_EQ(2u, [suggestions count]);
block_was_called = YES; block_was_called = YES;
}]; }];
EXPECT_TRUE(block_was_called); EXPECT_TRUE(block_was_called);
......
...@@ -3274,6 +3274,10 @@ bubblePresenterForFeature:(const base::Feature&)feature ...@@ -3274,6 +3274,10 @@ bubblePresenterForFeature:(const base::Feature&)feature
} }
} }
- (void)displaySavedPasswordList {
[self.dispatcher showSavedPasswordsSettingsFromViewController:self];
}
#pragma mark - CRWWebStateDelegate methods. #pragma mark - CRWWebStateDelegate methods.
- (web::WebState*)webState:(web::WebState*)webState - (web::WebState*)webState:(web::WebState*)webState
......
...@@ -32,6 +32,10 @@ ...@@ -32,6 +32,10 @@
- (void)showSyncPassphraseSettingsFromViewController: - (void)showSyncPassphraseSettingsFromViewController:
(UIViewController*)baseViewController; (UIViewController*)baseViewController;
// Shows the list of saved passwords in the settings.
- (void)showSavedPasswordsSettingsFromViewController:
(UIViewController*)baseViewController;
@end @end
// Protocol for commands that will generally be handled by the application, // Protocol for commands that will generally be handled by the application,
......
...@@ -512,6 +512,16 @@ initWithRootViewController:(UIViewController*)rootViewController ...@@ -512,6 +512,16 @@ initWithRootViewController:(UIViewController*)rootViewController
[self pushViewController:controller animated:YES]; [self pushViewController:controller animated:YES];
} }
// TODO(crbug.com/779791) : Do not pass |baseViewController| through dispatcher.
- (void)showSavedPasswordsSettingsFromViewController:
(UIViewController*)baseViewController {
SavePasswordsCollectionViewController* controller =
[[SavePasswordsCollectionViewController alloc]
initWithBrowserState:mainBrowserState_];
controller.dispatcher = [delegate_ dispatcherForSettings];
[self pushViewController:controller animated:YES];
}
#pragma mark - Profile #pragma mark - Profile
- (ios::ChromeBrowserState*)mainBrowserState { - (ios::ChromeBrowserState*)mainBrowserState {
......
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