Move password filling logic into components.
Note: Unit test will be added in https://chromium-review.googlesource.com/c/chromium/src/+/1195292.
Bug: 865114
Cq-Include-Trybots: luci.chromium.try:ios-simulator-cronet;luci.chromium.try:ios-simulator-full-configs
Change-Id: I91e187ae90b7515cc3bc37cdd80bdfbf33ae5ca1
Reviewed-on: https://chromium-review.googlesource.com/1195246
Commit-Queue: Scott Wu <scottwu@chromium.org>
Reviewed-by: Vasilii Sukhanov <vasilii@chromium.org>
Reviewed-by: John Wu <jzw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#587986}
diff --git a/components/password_manager/ios/password_controller_helper.h b/components/password_manager/ios/password_controller_helper.h
index 676e812..781feb7f 100644
--- a/components/password_manager/ios/password_controller_helper.h
+++ b/components/password_manager/ios/password_controller_helper.h
@@ -18,9 +18,11 @@
namespace autofill {
struct PasswordForm;
+struct PasswordFormFillData;
} // namespace autofill
namespace password_manager {
+struct FillData;
// Returns true if the trust level for the current page URL of |web_state| is
// kAbsolute. If |page_url| is not null, fills it with the current page URL.
bool GetPageURLAndCheckTrustLevel(web::WebState* web_state,
@@ -48,11 +50,35 @@
// The JsPasswordManager processing password form via javascript.
@property(nonatomic, readonly) JsPasswordManager* jsPasswordManager;
+// Last committed URL of current web state.
+// Returns empty URL if current web state is not available.
+@property(nonatomic, readonly) const GURL& lastCommittedURL;
+
// Uses JavaScript to find password forms. Calls |completionHandler| with the
// extracted information used for matching and saving passwords. Calls
// |completionHandler| with an empty vector if no password forms are found.
- (void)findPasswordFormsWithCompletionHandler:
- (void (^)(const std::vector<autofill::PasswordForm>&))completionHandler;
+ (nullable void (^)(const std::vector<autofill::PasswordForm>&))
+ completionHandler;
+
+// Autofills credentials into the page. Credentials and input fields are
+// specified by |formData|. Invokes |completionHandler| when finished with YES
+// if successful and NO otherwise.
+- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
+ completionHandler:(nullable void (^)(BOOL))completionHandler;
+
+// Autofills credentials into the page. Credentials and input fields are
+// specified by |fillData|. Invokes |completionHandler| when finished with YES
+// if successful and NO otherwise.
+- (void)fillPasswordFormWithFillData:(const password_manager::FillData&)fillData
+ completionHandler:(nullable void (^)(BOOL))completionHandler;
+
+// Finds password forms in the page and fills them with the |username| and
+// |password|. If not nil, |completionHandler| is called once per form filled.
+- (void)findAndFillPasswordFormsWithUserName:(NSString*)username
+ password:(NSString*)password
+ completionHandler:
+ (nullable void (^)(BOOL))completionHandler;
// Creates a instance with the given WebState, observer and delegate.
- (instancetype)initWithWebState:(web::WebState*)webState
diff --git a/components/password_manager/ios/password_controller_helper.mm b/components/password_manager/ios/password_controller_helper.mm
index 266e676..ad0de29 100644
--- a/components/password_manager/ios/password_controller_helper.mm
+++ b/components/password_manager/ios/password_controller_helper.mm
@@ -10,8 +10,10 @@
#include "base/values.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/password_form.h"
+#include "components/autofill/core/common/password_form_fill_data.h"
#include "components/autofill/ios/browser/autofill_util.h"
#include "components/password_manager/core/browser/form_parsing/ios_form_parser.h"
+#include "components/password_manager/ios/account_select_fill_data.h"
#include "components/password_manager/ios/js_password_manager.h"
#import "ios/web/public/web_state/web_state.h"
@@ -22,6 +24,8 @@
using autofill::FormData;
using autofill::PasswordForm;
using password_manager::GetPageURLAndCheckTrustLevel;
+using password_manager::FillData;
+using password_manager::SerializePasswordFormFillData;
namespace password_manager {
bool GetPageURLAndCheckTrustLevel(web::WebState* web_state,
@@ -63,6 +67,13 @@
pageURL:(const GURL&)pageURL
forms:(std::vector<autofill::PasswordForm>*)forms;
+// Autofills |username| and |password| into the form specified by |formData|,
+// invoking |completionHandler| when finished with YES if successful and
+// NO otherwise. |completionHandler| may be nil.
+- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
+ withUsername:(const base::string16&)username
+ password:(const base::string16&)password
+ completionHandler:(nullable void (^)(BOOL))completionHandler;
@end
@implementation PasswordControllerHelper {
@@ -83,6 +94,10 @@
@synthesize delegate = _delegate;
@synthesize jsPasswordManager = _jsPasswordManager;
+- (const GURL&)lastCommittedURL {
+ return _webState ? _webState->GetLastCommittedURL() : GURL::EmptyGURL();
+}
+
#pragma mark - Initialization
- (instancetype)initWithWebState:(web::WebState*)webState
@@ -258,17 +273,42 @@
for (const auto& formData : formsData) {
std::unique_ptr<PasswordForm> form =
ParseFormData(formData, password_manager::FormParsingMode::FILLING);
- if (form)
+ if (form) {
forms->push_back(*form);
+ }
}
}
+- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
+ withUsername:(const base::string16&)username
+ password:(const base::string16&)password
+ completionHandler:(nullable void (^)(BOOL))completionHandler {
+ if (formData.origin.GetOrigin() != self.lastCommittedURL.GetOrigin()) {
+ if (completionHandler) {
+ completionHandler(NO);
+ }
+ return;
+ }
+
+ // Send JSON over to the web view.
+ [self.jsPasswordManager
+ fillPasswordForm:SerializePasswordFormFillData(formData)
+ withUsername:base::SysUTF16ToNSString(username)
+ password:base::SysUTF16ToNSString(password)
+ completionHandler:^(BOOL result) {
+ if (completionHandler) {
+ completionHandler(result);
+ }
+ }];
+}
+
#pragma mark - Public methods
- (void)findPasswordFormsWithCompletionHandler:
(void (^)(const std::vector<autofill::PasswordForm>&))completionHandler {
- if (!_webState)
+ if (!_webState) {
return;
+ }
GURL pageURL;
if (!GetPageURLAndCheckTrustLevel(_webState, &pageURL)) {
@@ -284,4 +324,59 @@
}];
}
+- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
+ completionHandler:(nullable void (^)(BOOL))completionHandler {
+ // Don't fill immediately if waiting for the user to type a username.
+ if (formData.wait_for_username) {
+ if (completionHandler) {
+ completionHandler(NO);
+ }
+ return;
+ }
+
+ [self fillPasswordForm:formData
+ withUsername:formData.username_field.value
+ password:formData.password_field.value
+ completionHandler:completionHandler];
+}
+
+- (void)fillPasswordFormWithFillData:(const password_manager::FillData&)fillData
+ completionHandler:
+ (nullable void (^)(BOOL))completionHandler {
+ [self.jsPasswordManager
+ fillPasswordForm:SerializeFillData(fillData)
+ withUsername:base::SysUTF16ToNSString(fillData.username_value)
+ password:base::SysUTF16ToNSString(fillData.password_value)
+ completionHandler:^(BOOL result) {
+ if (completionHandler) {
+ completionHandler(result);
+ }
+ }];
+}
+
+- (void)findAndFillPasswordFormsWithUserName:(NSString*)username
+ password:(NSString*)password
+ completionHandler:
+ (nullable void (^)(BOOL))completionHandler {
+ __weak PasswordControllerHelper* weakSelf = self;
+ [self findPasswordFormsWithCompletionHandler:^(
+ const std::vector<autofill::PasswordForm>& forms) {
+ PasswordControllerHelper* strongSelf = weakSelf;
+ for (const auto& form : forms) {
+ autofill::PasswordFormFillData formData;
+ std::map<base::string16, const autofill::PasswordForm*> matches;
+ // Initialize |matches| to satisfy the expectation from
+ // InitPasswordFormFillData() that the preferred match (3rd parameter)
+ // should be one of the |matches|.
+ matches.insert(std::make_pair(form.username_value, &form));
+ autofill::InitPasswordFormFillData(form, matches, &form, false,
+ &formData);
+ [strongSelf fillPasswordForm:formData
+ withUsername:base::SysNSStringToUTF16(username)
+ password:base::SysNSStringToUTF16(password)
+ completionHandler:completionHandler];
+ }
+ }];
+}
+
@end
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index e2d4e252..b34a633 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -126,20 +126,6 @@
- (void)didFinishPasswordFormExtraction:
(const std::vector<autofill::PasswordForm>&)forms;
-// Autofills |username| and |password| into the form specified by |formData|,
-// invoking |completionHandler| when finished with YES if successful and
-// NO otherwise. |completionHandler| may be nil.
-- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
- withUsername:(const base::string16&)username
- password:(const base::string16&)password
- completionHandler:(void (^)(BOOL))completionHandler;
-
-// Autofills credentials into the page. Credentials and input fields are
-// specified by |fillData|. Invoking |completionHandler| when finished with YES
-// if successful and NO otherwise. |completionHandler| may be nil.
-- (void)fillPasswordFormWithFillData:(const password_manager::FillData&)fillData
- completionHandler:(void (^)(BOOL))completionHandler;
-
// Finds all password forms in DOM and sends them to the password store for
// fetching stored credentials.
- (void)findPasswordFormsAndSendThemToPasswordStore;
@@ -150,6 +136,11 @@
- (void)showInfoBarForForm:(std::unique_ptr<PasswordFormManagerForUI>)form
infoBarType:(PasswordInfoBarType)type;
+// Hides auto sign-in notification. Removes the view from superview and destroys
+// the controller.
+// TODO(crbug.com/435048): Animate disappearance.
+- (void)hideAutosigninNotification;
+
@end
namespace {
@@ -284,18 +275,7 @@
}
}
-- (ios::ChromeBrowserState*)browserState {
- return _webState ? ios::ChromeBrowserState::FromBrowserState(
- _webState->GetBrowserState())
- : nullptr;
-}
-
-- (const GURL&)lastCommittedURL {
- return _webState ? _webState->GetLastCommittedURL() : GURL::EmptyGURL();
-}
-
-#pragma mark -
-#pragma mark Properties
+#pragma mark - Properties
- (id<PasswordFormFiller>)passwordFormFiller {
return self;
@@ -309,37 +289,17 @@
return _passwordManagerDriver.get();
}
-- (PasswordManager*)passwordManager {
- return _passwordManager.get();
-}
-
-#pragma mark -
-#pragma mark PasswordFormFiller
+#pragma mark - PasswordFormFiller
- (void)findAndFillPasswordForms:(NSString*)username
password:(NSString*)password
completionHandler:(void (^)(BOOL))completionHandler {
- [self.helper findPasswordFormsWithCompletionHandler:^(
- const std::vector<autofill::PasswordForm>& forms) {
- for (const auto& form : forms) {
- autofill::PasswordFormFillData formData;
- std::map<base::string16, const autofill::PasswordForm*> matches;
- // Initialize |matches| to satisfy the expectation from
- // InitPasswordFormFillData() that the preferred match (3rd parameter)
- // should be one of the |matches|.
- matches.insert(std::make_pair(form.username_value, &form));
- autofill::InitPasswordFormFillData(form, matches, &form, false,
- &formData);
- [self fillPasswordForm:formData
- withUsername:base::SysNSStringToUTF16(username)
- password:base::SysNSStringToUTF16(password)
- completionHandler:completionHandler];
- }
- }];
+ [self.helper findAndFillPasswordFormsWithUserName:username
+ password:password
+ completionHandler:completionHandler];
}
-#pragma mark -
-#pragma mark CRWWebStateObserver
+#pragma mark - CRWWebStateObserver
// If Tab was shown, and there is a pending PasswordForm, display autosign-in
// notification.
@@ -399,52 +359,7 @@
_credentialManager.reset();
}
-#pragma mark - Private methods
-
-- (void)findPasswordFormsAndSendThemToPasswordStore {
- // Read all password forms from the page and send them to the password
- // manager.
- __weak PasswordController* weakSelf = self;
- [self.helper findPasswordFormsWithCompletionHandler:^(
- const std::vector<autofill::PasswordForm>& forms) {
- [weakSelf didFinishPasswordFormExtraction:forms];
- }];
-}
-
-- (void)didFinishPasswordFormExtraction:
- (const std::vector<autofill::PasswordForm>&)forms {
- // Do nothing if |self| has been detached.
- if (!self.passwordManager)
- return;
-
- if (!forms.empty()) {
- // Notify web_state about password forms, so that this can be taken into
- // account for the security state.
- if (_webState && !web::IsOriginSecure(_webState->GetLastCommittedURL())) {
- InsecureInputTabHelper::GetOrCreateForWebState(_webState)
- ->DidShowPasswordFieldInInsecureContext();
- }
-
- _sentRequestToStore = YES;
- // Invoke the password manager callback to autofill password forms
- // on the loaded page.
- self.passwordManager->OnPasswordFormsParsed(self.passwordManagerDriver,
- forms);
- } else {
- [self onNoSavedCredentials];
- }
- // Invoke the password manager callback to check if password was
- // accepted or rejected. If accepted, infobar is presented. If
- // rejected, the provisionally saved password is deleted. On Chrome
- // w/ a renderer, it is the renderer who calls OnPasswordFormsParsed()
- // and OnPasswordFormsRendered(). Bling has to improvised a bit on the
- // ordering of these two calls.
- self.passwordManager->OnPasswordFormsRendered(self.passwordManagerDriver,
- forms, true);
-}
-
-#pragma mark -
-#pragma mark FormSuggestionProvider
+#pragma mark - FormSuggestionProvider
- (id<FormSuggestionProvider>)suggestionProvider {
return self;
@@ -529,14 +444,28 @@
if (!fillData)
completion();
- [self fillPasswordFormWithFillData:*fillData
- completionHandler:^(BOOL success) {
- completion();
- }];
+ [self.helper fillPasswordFormWithFillData:*fillData
+ completionHandler:^(BOOL success) {
+ completion();
+ }];
}
#pragma mark - PasswordManagerClientDelegate
+- (ios::ChromeBrowserState*)browserState {
+ return _webState ? ios::ChromeBrowserState::FromBrowserState(
+ _webState->GetBrowserState())
+ : nullptr;
+}
+
+- (PasswordManager*)passwordManager {
+ return _passwordManager.get();
+}
+
+- (const GURL&)lastCommittedURL {
+ return self.helper.lastCommittedURL;
+}
+
- (void)showSavePasswordInfoBar:
(std::unique_ptr<PasswordFormManagerForUI>)formToSave {
[self showInfoBarForForm:std::move(formToSave)
@@ -549,16 +478,6 @@
infoBarType:PasswordInfoBarType::UPDATE];
}
-// Hides auto sign-in notification. Removes the view from superview and destroys
-// the controller.
-// TODO(crbug.com/435048): Animate disappearance.
-- (void)hideAutosigninNotification {
- [self.notifyAutoSigninViewController willMoveToParentViewController:nil];
- [self.notifyAutoSigninViewController.view removeFromSuperview];
- [self.notifyAutoSigninViewController removeFromParentViewController];
- self.notifyAutoSigninViewController = nil;
-}
-
// Shows auto sign-in notification and schedules hiding it after 3 seconds.
// TODO(crbug.com/435048): Animate appearance.
- (void)showAutosigninNotification:
@@ -599,29 +518,7 @@
}));
}
-#pragma mark -
-#pragma mark WebPasswordFormData Adaptation
-
-- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
- withUsername:(const base::string16&)username
- password:(const base::string16&)password
- completionHandler:(void (^)(BOOL))completionHandler {
- if (formData.origin.GetOrigin() != self.lastCommittedURL.GetOrigin()) {
- if (completionHandler)
- completionHandler(NO);
- return;
- }
-
- // Send JSON over to the web view.
- [self.helper.jsPasswordManager
- fillPasswordForm:SerializePasswordFormFillData(formData)
- withUsername:base::SysUTF16ToNSString(username)
- password:base::SysUTF16ToNSString(password)
- completionHandler:^(BOOL result) {
- if (completionHandler)
- completionHandler(result);
- }];
-}
+#pragma mark - PasswordManagerDriverDelegate
- (void)fillPasswordForm:(const autofill::PasswordFormFillData&)formData
completionHandler:(void (^)(BOOL))completionHandler {
@@ -632,17 +529,7 @@
_suggestionsAvailableCompletion = nil;
}
- // Don't fill immediately if waiting for the user to type a username.
- if (formData.wait_for_username) {
- if (completionHandler)
- completionHandler(NO);
- return;
- }
-
- [self fillPasswordForm:formData
- withUsername:formData.username_field.value
- password:formData.password_field.value
- completionHandler:completionHandler];
+ [self.helper fillPasswordForm:formData completionHandler:completionHandler];
}
- (void)onNoSavedCredentials {
@@ -651,19 +538,6 @@
_suggestionsAvailableCompletion = nil;
}
-- (void)fillPasswordFormWithFillData:(const password_manager::FillData&)fillData
- completionHandler:(void (^)(BOOL))completionHandler {
- // Send JSON over to the web view.
- [self.helper.jsPasswordManager
- fillPasswordForm:SerializeFillData(fillData)
- withUsername:base::SysUTF16ToNSString(fillData.username_value)
- password:base::SysUTF16ToNSString(fillData.password_value)
- completionHandler:^(BOOL result) {
- if (completionHandler)
- completionHandler(result);
- }];
-}
-
#pragma mark - PasswordControllerHelperDelegate
- (void)helper:(PasswordControllerHelper*)helper
@@ -682,6 +556,48 @@
#pragma mark - Private methods
+- (void)didFinishPasswordFormExtraction:
+ (const std::vector<autofill::PasswordForm>&)forms {
+ // Do nothing if |self| has been detached.
+ if (!self.passwordManager)
+ return;
+
+ if (!forms.empty()) {
+ // Notify web_state about password forms, so that this can be taken into
+ // account for the security state.
+ if (_webState && !web::IsOriginSecure(_webState->GetLastCommittedURL())) {
+ InsecureInputTabHelper::GetOrCreateForWebState(_webState)
+ ->DidShowPasswordFieldInInsecureContext();
+ }
+
+ _sentRequestToStore = YES;
+ // Invoke the password manager callback to autofill password forms
+ // on the loaded page.
+ self.passwordManager->OnPasswordFormsParsed(self.passwordManagerDriver,
+ forms);
+ } else {
+ [self onNoSavedCredentials];
+ }
+ // Invoke the password manager callback to check if password was
+ // accepted or rejected. If accepted, infobar is presented. If
+ // rejected, the provisionally saved password is deleted. On Chrome
+ // w/ a renderer, it is the renderer who calls OnPasswordFormsParsed()
+ // and OnPasswordFormsRendered(). Bling has to improvised a bit on the
+ // ordering of these two calls.
+ self.passwordManager->OnPasswordFormsRendered(self.passwordManagerDriver,
+ forms, true);
+}
+
+- (void)findPasswordFormsAndSendThemToPasswordStore {
+ // Read all password forms from the page and send them to the password
+ // manager.
+ __weak PasswordController* weakSelf = self;
+ [self.helper findPasswordFormsWithCompletionHandler:^(
+ const std::vector<autofill::PasswordForm>& forms) {
+ [weakSelf didFinishPasswordFormExtraction:forms];
+ }];
+}
+
- (void)showInfoBarForForm:(std::unique_ptr<PasswordFormManagerForUI>)form
infoBarType:(PasswordInfoBarType)type {
if (!_webState)
@@ -712,4 +628,11 @@
}
}
+- (void)hideAutosigninNotification {
+ [self.notifyAutoSigninViewController willMoveToParentViewController:nil];
+ [self.notifyAutoSigninViewController.view removeFromSuperview];
+ [self.notifyAutoSigninViewController removeFromParentViewController];
+ self.notifyAutoSigninViewController = nil;
+}
+
@end