Commit 14dbc848 authored by Maria Kazinova's avatar Maria Kazinova Committed by Chromium LUCI CQ

[iOS, Passwords] Stop rewriting user-typed data on credential filling.

On other platform, when the user manually typed the username into
the login form and then selects a suggestion from the password field,
the typed username is not rewritten. This CL ensures the same behaviour
on iOS.

Bug: 1157460
Change-Id: I7fca9e69256c7efb3cdea3133a9a830b9df1af98
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2584303
Commit-Queue: Maria Kazinova <kazinova@google.com>
Reviewed-by: default avatarVadym Doroshenko  <dvadym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835725}
parent c53d9494
......@@ -18,8 +18,11 @@ namespace password_manager {
struct FillData;
// Serializes |fillData| so it can be used by the JS side of PasswordController.
// Includes both username and password data if |fillUsername|, and only password
// data otherwise.
std::unique_ptr<base::Value> SerializeFillData(
const password_manager::FillData& fillData);
const password_manager::FillData& fillData,
BOOL fillUsername);
// Serializes |formData| so it can be used by the JS side of PasswordController.
std::unique_ptr<base::Value> SerializePasswordFormFillData(
......
......@@ -75,9 +75,11 @@ std::unique_ptr<base::Value> SerializePasswordFormFillData(
}
std::unique_ptr<base::Value> SerializeFillData(
const password_manager::FillData& fillData) {
const password_manager::FillData& fillData,
BOOL fillUsername) {
return SerializeFillData(
fillData.origin, fillData.form_id, fillData.username_element_id,
fillData.origin, fillData.form_id,
fillUsername ? fillData.username_element_id : FieldRendererId(),
fillData.username_value, fillData.password_element_id,
fillData.password_value);
}
......
......@@ -94,10 +94,12 @@ class WebState;
generatedPassword:(NSString*)generatedPassword
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.
// Autofills credentials into the page on credential suggestion selection.
// Credentials and input fields are specified by |fillData|. |uniqueFieldID|
// specifies the unput on which the suggestion was accepted. Invokes
// |completionHandler| when finished with YES if successful and NO otherwise.
- (void)fillPasswordFormWithFillData:(const password_manager::FillData&)fillData
triggeredOnField:(autofill::FieldRendererId)uniqueFieldID
completionHandler:(nullable void (^)(BOOL))completionHandler;
// Finds the password form with unique ID |formIdentifier| and calls
......
......@@ -370,6 +370,7 @@ constexpr char kCommandPrefix[] = "passwordForm";
}
- (void)fillPasswordFormWithFillData:(const password_manager::FillData&)fillData
triggeredOnField:(FieldRendererId)uniqueFieldID
completionHandler:
(nullable void (^)(BOOL))completionHandler {
// Necessary copy so the values can be used inside a block.
......@@ -378,9 +379,13 @@ constexpr char kCommandPrefix[] = "passwordForm";
base::string16 usernameValue = fillData.username_value;
base::string16 passwordValue = fillData.password_value;
// Do not fill the username if filling was triggered on a password field and
// the username field has user typed input.
BOOL fillUsername = uniqueFieldID == usernameID ||
!_fieldDataManager->DidUserType(usernameID);
__weak PasswordFormHelper* weakSelf = self;
[self.jsPasswordManager
fillPasswordForm:SerializeFillData(fillData)
fillPasswordForm:SerializeFillData(fillData, fillUsername)
inFrame:GetMainFrame(_webState)
withUsername:UTF16ToUTF8(usernameValue)
password:UTF16ToUTF8(passwordValue)
......
......@@ -225,12 +225,15 @@ TEST_F(PasswordFormHelperTest, FillPasswordFormWithFillData) {
// Run password forms search to set up unique IDs.
EXPECT_TRUE(ExecuteJavaScript(@"__gCrWeb.passwords.findPasswordForms();"));
const std::string base_url = BaseUrl();
FieldRendererId username_field_id(1);
FieldRendererId password_field_id(2);
FillData fill_data;
SetFillData(base_url, 0, 1, "john.doe@gmail.com", 2, "super!secret",
&fill_data);
SetFillData(base_url, 0, username_field_id.value(), "john.doe@gmail.com",
password_field_id.value(), "super!secret", &fill_data);
__block int call_counter = 0;
[helper_ fillPasswordFormWithFillData:fill_data
triggeredOnField:username_field_id
completionHandler:^(BOOL complete) {
++call_counter;
EXPECT_TRUE(complete);
......@@ -332,11 +335,14 @@ TEST_F(PasswordFormHelperTest, RefillFormFilledOnUserTrigger) {
// Fill the form on user trigger.
const std::string base_url = BaseUrl();
FieldRendererId username_field_id(1);
FieldRendererId password_field_id(2);
FillData fill_data;
SetFillData(base_url, 0, 1, "john.doe@gmail.com", 2, "super!secret",
&fill_data);
SetFillData(base_url, 0, username_field_id.value(), "john.doe@gmail.com",
password_field_id.value(), "super!secret", &fill_data);
__block int call_counter = 0;
[helper_ fillPasswordFormWithFillData:fill_data
triggeredOnField:username_field_id
completionHandler:^(BOOL complete) {
++call_counter;
EXPECT_TRUE(complete);
......@@ -347,7 +353,8 @@ TEST_F(PasswordFormHelperTest, RefillFormFilledOnUserTrigger) {
// Try to autofill the form.
PasswordFormFillData form_data;
SetPasswordFormFillData(BaseUrl(), "", 0, "", 1, "someacc@store.com", "", 2,
SetPasswordFormFillData(BaseUrl(), "", 0, "", username_field_id.value(),
"someacc@store.com", "", password_field_id.value(),
"store!pw", "", "", NO, &form_data);
__block bool called = NO;
......@@ -412,6 +419,44 @@ TEST_F(PasswordFormHelperTest, RefillFormWithUserTypedInput) {
}));
}
// Tests that a form with username typed by user is not refilled when
// the user selects filling suggestion on password field.
TEST_F(PasswordFormHelperTest, FillPasswordIntoFormWithUserTypedUsername) {
LoadHtml(@"<form><input id='u1' type='text' name='un1'>"
"<input id='p1' type='password' name='pw1'></form>");
ExecuteJavaScript(@"__gCrWeb.fill.setUpForUniqueIDs(0);");
// Run password forms search to set up unique IDs.
EXPECT_TRUE(ExecuteJavaScript(@"__gCrWeb.passwords.findPasswordForms();"));
FieldRendererId username_field_id(1);
FieldRendererId password_field_id(2);
ExecuteJavaScript(
@"document.getElementById('u1').value = 'typed@typed.com';");
[helper_ updateFieldDataOnUserInput:username_field_id
inputValue:@"typed@typed.com"];
// Try to autofill the form.
FillData fill_data;
SetFillData(BaseUrl(), 0, username_field_id.value(), "someacc@store.com",
password_field_id.value(), "store!pw", &fill_data);
__block bool called = NO;
__block bool success = NO;
[helper_ fillPasswordFormWithFillData:fill_data
triggeredOnField:password_field_id
completionHandler:^(BOOL res) {
called = YES;
success = res;
}];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
return called;
}));
EXPECT_EQ(success, YES);
id result = ExecuteJavaScript(kInputFieldValueVerificationScript);
EXPECT_NSEQ(@"u1=typed@typed.com;p1=store!pw;", result);
}
} // namespace
NS_ASSUME_NONNULL_END
......@@ -410,6 +410,7 @@ NSString* const kSuggestionSuffix = @" ••••••••";
}
[self.formHelper fillPasswordFormWithFillData:*fillData
triggeredOnField:uniqueFieldID
completionHandler:^(BOOL success) {
completion();
}];
......
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