Commit 9baf76b3 authored by Maria Kazinova's avatar Maria Kazinova Committed by Commit Bot

[iOS] Using numeric renderer IDs for Autofill form filling.

Bug: 1075444, 1131038
Change-Id: I276aa7628088d810e422235f85564f0ac0930681
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2431428Reviewed-by: default avatarVadym Doroshenko  <dvadym@chromium.org>
Reviewed-by: default avatarOlivier Robin <olivierrobin@chromium.org>
Commit-Queue: Maria Kazinova <kazinova@google.com>
Cr-Commit-Position: refs/heads/master@{#811789}
parent acda9626
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/values.h" #include "base/values.h"
...@@ -62,8 +63,10 @@ ...@@ -62,8 +63,10 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
using base::NumberToString;
using base::SysNSStringToUTF8; using base::SysNSStringToUTF8;
using base::SysNSStringToUTF16; using base::SysNSStringToUTF16;
using base::SysUTF16ToNSString;
using autofill::FormRendererId; using autofill::FormRendererId;
using autofill::FieldDataManager; using autofill::FieldDataManager;
using autofill::FieldRendererId; using autofill::FieldRendererId;
...@@ -145,10 +148,11 @@ void UpdateFieldManagerForClearedIDs( ...@@ -145,10 +148,11 @@ void UpdateFieldManagerForClearedIDs(
// Manager for Autofill JavaScripts. // Manager for Autofill JavaScripts.
JsAutofillManager* _jsAutofillManager; JsAutofillManager* _jsAutofillManager;
// The name of the most recent autocomplete field; tracks the currently- // The name and the unique renderer ID of the most recent autocomplete field;
// focused form element in order to force filling of the currently selected // tracks the currently-focused form element in order to force filling of
// form element, even if it's non-empty. // the currently selected form element, even if it's non-empty.
base::string16 _pendingAutocompleteField; base::string16 _pendingAutocompleteField;
FieldRendererId _pendingAutocompleteFieldID;
// Suggestions state: // Suggestions state:
// The most recent form suggestions. // The most recent form suggestions.
...@@ -428,6 +432,7 @@ autofillManagerFromWebState:(web::WebState*)webState ...@@ -428,6 +432,7 @@ autofillManagerFromWebState:(web::WebState*)webState
if (suggestion.identifier > 0) { if (suggestion.identifier > 0) {
_pendingAutocompleteField = SysNSStringToUTF16(fieldIdentifier); _pendingAutocompleteField = SysNSStringToUTF16(fieldIdentifier);
_pendingAutocompleteFieldID = uniqueFieldID;
if (_popupDelegate) { if (_popupDelegate) {
// TODO(966411): Replace 0 with the index of the selected suggestion. // TODO(966411): Replace 0 with the index of the selected suggestion.
_popupDelegate->DidAcceptSuggestion(SysNSStringToUTF16(suggestion.value), _popupDelegate->DidAcceptSuggestion(SysNSStringToUTF16(suggestion.value),
...@@ -482,7 +487,14 @@ autofillManagerFromWebState:(web::WebState*)webState ...@@ -482,7 +487,14 @@ autofillManagerFromWebState:(web::WebState*)webState
auto autofillData = auto autofillData =
std::make_unique<base::Value>(base::Value::Type::DICTIONARY); std::make_unique<base::Value>(base::Value::Type::DICTIONARY);
autofillData->SetKey("formName", base::Value(base::UTF16ToUTF8(form.name))); autofillData->SetKey("formName", base::Value(base::UTF16ToUTF8(form.name)));
uint32_t formRendererID = form.unique_renderer_id
? form.unique_renderer_id.value()
: autofill::kNotSetRendererID;
autofillData->SetKey("formRendererID",
base::Value(static_cast<int>(formRendererID)));
bool useRendererIDs = base::FeatureList::IsEnabled(
autofill::features::kAutofillUseUniqueRendererIDsOnIOS);
base::Value fieldsData(base::Value::Type::DICTIONARY); base::Value fieldsData(base::Value::Type::DICTIONARY);
for (const auto& field : form.fields) { for (const auto& field : form.fields) {
// Skip empty fields and those that are not autofilled. // Skip empty fields and those that are not autofilled.
...@@ -492,7 +504,15 @@ autofillManagerFromWebState:(web::WebState*)webState ...@@ -492,7 +504,15 @@ autofillManagerFromWebState:(web::WebState*)webState
base::Value fieldData(base::Value::Type::DICTIONARY); base::Value fieldData(base::Value::Type::DICTIONARY);
fieldData.SetKey("value", base::Value(field.value)); fieldData.SetKey("value", base::Value(field.value));
fieldData.SetKey("section", base::Value(field.section)); fieldData.SetKey("section", base::Value(field.section));
fieldsData.SetKey(base::UTF16ToUTF8(field.unique_id), std::move(fieldData)); uint32_t fieldRendererID = field.unique_renderer_id
? field.unique_renderer_id.value()
: autofill::kNotSetRendererID;
if (useRendererIDs) {
fieldsData.SetKey(NumberToString(fieldRendererID), std::move(fieldData));
} else {
fieldsData.SetKey(base::UTF16ToUTF8(field.unique_id),
std::move(fieldData));
}
} }
autofillData->SetKey("fields", std::move(fieldsData)); autofillData->SetKey("fields", std::move(fieldsData));
...@@ -570,16 +590,16 @@ autofillManagerFromWebState:(web::WebState*)webState ...@@ -570,16 +590,16 @@ autofillManagerFromWebState:(web::WebState*)webState
// Value will contain the text to be filled in the selected element while // Value will contain the text to be filled in the selected element while
// displayDescription will contain a summary of the data to be filled in // displayDescription will contain a summary of the data to be filled in
// the other elements. // the other elements.
value = base::SysUTF16ToNSString(popup_suggestion.value); value = SysUTF16ToNSString(popup_suggestion.value);
displayDescription = base::SysUTF16ToNSString(popup_suggestion.label); displayDescription = SysUTF16ToNSString(popup_suggestion.label);
} else if (popup_suggestion.frontend_id == } else if (popup_suggestion.frontend_id ==
autofill::POPUP_ITEM_ID_CLEAR_FORM) { autofill::POPUP_ITEM_ID_CLEAR_FORM) {
// Show the "clear form" button. // Show the "clear form" button.
value = base::SysUTF16ToNSString(popup_suggestion.value); value = SysUTF16ToNSString(popup_suggestion.value);
} else if (popup_suggestion.frontend_id == } else if (popup_suggestion.frontend_id ==
autofill::POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS) { autofill::POPUP_ITEM_ID_SHOW_ACCOUNT_CARDS) {
// Show opt-in for showing cards from account. // Show opt-in for showing cards from account.
value = base::SysUTF16ToNSString(popup_suggestion.value); value = SysUTF16ToNSString(popup_suggestion.value);
} }
if (!value) if (!value)
...@@ -917,8 +937,8 @@ autofillManagerFromWebState:(web::WebState*)webState ...@@ -917,8 +937,8 @@ autofillManagerFromWebState:(web::WebState*)webState
} copy]; } copy];
__weak AutofillAgent* weakSelf = self; __weak AutofillAgent* weakSelf = self;
[_jsAutofillManager fillForm:std::move(data) [_jsAutofillManager fillForm:std::move(data)
forceFillFieldIdentifier:base::SysUTF16ToNSString( forceFillFieldIdentifier:SysUTF16ToNSString(_pendingAutocompleteField)
_pendingAutocompleteField) forceFillFieldUniqueID:_pendingAutocompleteFieldID
inFrame:frame inFrame:frame
completionHandler:^(NSString* jsonString) { completionHandler:^(NSString* jsonString) {
AutofillAgent* strongSelf = weakSelf; AutofillAgent* strongSelf = weakSelf;
......
...@@ -125,7 +125,14 @@ class AutofillAgentTests : public PlatformTest { ...@@ -125,7 +125,14 @@ class AutofillAgentTests : public PlatformTest {
// Tests that form's name and fields' identifiers, values, and whether they are // Tests that form's name and fields' identifiers, values, and whether they are
// autofilled are sent to the JS. Fields with empty values and those that are // autofilled are sent to the JS. Fields with empty values and those that are
// not autofilled are skipped. // not autofilled are skipped.
// TODO(crbug/1131038): Remove once using only renderer IDs is launched.
TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) {
base::test::ScopedFeatureList scoped_feature_list;
std::vector<base::Feature> disabled_features;
disabled_features.push_back(
autofill::features::kAutofillUseUniqueRendererIDsOnIOS);
scoped_feature_list.InitWithFeatures({}, disabled_features);
std::string locale("en"); std::string locale("en");
autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate( autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
&test_web_state_, &client_, nil, locale, &test_web_state_, &client_, nil, locale,
...@@ -135,6 +142,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { ...@@ -135,6 +142,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) {
form.url = GURL("https://myform.com"); form.url = GURL("https://myform.com");
form.action = GURL("https://myform.com/submit"); form.action = GURL("https://myform.com/submit");
form.name = base::ASCIIToUTF16("CC form"); form.name = base::ASCIIToUTF16("CC form");
form.unique_renderer_id = FormRendererId(0);
autofill::FormFieldData field; autofill::FormFieldData field;
field.form_control_type = "text"; field.form_control_type = "text";
...@@ -145,6 +153,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { ...@@ -145,6 +153,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) {
field.unique_id = field.id_attribute; field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("number_value"); field.value = base::ASCIIToUTF16("number_value");
field.is_autofilled = true; field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(1);
form.fields.push_back(field); form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Name on Card"); field.label = base::ASCIIToUTF16("Name on Card");
field.name = base::ASCIIToUTF16("name"); field.name = base::ASCIIToUTF16("name");
...@@ -153,6 +162,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { ...@@ -153,6 +162,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) {
field.unique_id = field.id_attribute; field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("name_value"); field.value = base::ASCIIToUTF16("name_value");
field.is_autofilled = true; field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(2);
form.fields.push_back(field); form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Expiry Month"); field.label = base::ASCIIToUTF16("Expiry Month");
field.name = base::ASCIIToUTF16("expiry_month"); field.name = base::ASCIIToUTF16("expiry_month");
...@@ -161,6 +171,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { ...@@ -161,6 +171,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) {
field.unique_id = field.id_attribute; field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("01"); field.value = base::ASCIIToUTF16("01");
field.is_autofilled = false; field.is_autofilled = false;
field.unique_renderer_id = FieldRendererId(3);
form.fields.push_back(field); form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Unknown field"); field.label = base::ASCIIToUTF16("Unknown field");
field.name = base::ASCIIToUTF16("unknown"); field.name = base::ASCIIToUTF16("unknown");
...@@ -169,6 +180,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { ...@@ -169,6 +180,7 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) {
field.unique_id = field.id_attribute; field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16(""); field.value = base::ASCIIToUTF16("");
field.is_autofilled = true; field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(4);
form.fields.push_back(field); form.fields.push_back(field);
[autofill_agent_ [autofill_agent_
fillFormData:form fillFormData:form
...@@ -178,14 +190,92 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) { ...@@ -178,14 +190,92 @@ TEST_F(AutofillAgentTests, OnFormDataFilledTestWithFrameMessaging) {
"__gCrWeb.autofill.fillForm({\"fields\":{\"name\":{\"section\":\"\"," "__gCrWeb.autofill.fillForm({\"fields\":{\"name\":{\"section\":\"\","
"\"value\":\"name_value\"}," "\"value\":\"name_value\"},"
"\"number\":{\"section\":\"\",\"value\":\"number_value\"}}," "\"number\":{\"section\":\"\",\"value\":\"number_value\"}},"
"\"formName\":\"CC form\"}, \"\");", "\"formName\":\"CC form\",\"formRendererID\":0}, \"\", -1, false);",
fake_main_frame_->GetLastJavaScriptCall()); fake_main_frame_->GetLastJavaScriptCall());
} }
// Tests that form's name and fields' identifiers, values, and whether they are
// autofilled are sent to the JS. Fields with empty values and those that are
// not autofilled are skipped. Tests logic based on renderer ids usage.
TEST_F(AutofillAgentTests,
OnFormDataFilledTestWithFrameMessagingUsingRendererIDs) {
base::test::ScopedFeatureList scoped_feature_list;
std::vector<base::Feature> enabled_features;
enabled_features.push_back(
autofill::features::kAutofillUseUniqueRendererIDsOnIOS);
scoped_feature_list.InitWithFeatures(enabled_features, {});
std::string locale("en");
autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
&test_web_state_, &client_, nil, locale,
autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
autofill::FormData form;
form.url = GURL("https://myform.com");
form.action = GURL("https://myform.com/submit");
form.name = base::ASCIIToUTF16("CC form");
form.unique_renderer_id = FormRendererId(0);
autofill::FormFieldData field;
field.form_control_type = "text";
field.label = base::ASCIIToUTF16("Card number");
field.name = base::ASCIIToUTF16("number");
field.name_attribute = field.name;
field.id_attribute = base::ASCIIToUTF16("number");
field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("number_value");
field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(1);
form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Name on Card");
field.name = base::ASCIIToUTF16("name");
field.name_attribute = field.name;
field.id_attribute = base::ASCIIToUTF16("name");
field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("name_value");
field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(2);
form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Expiry Month");
field.name = base::ASCIIToUTF16("expiry_month");
field.name_attribute = field.name;
field.id_attribute = base::ASCIIToUTF16("expiry_month");
field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("01");
field.is_autofilled = false;
field.unique_renderer_id = FieldRendererId(3);
form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Unknown field");
field.name = base::ASCIIToUTF16("unknown");
field.name_attribute = field.name;
field.id_attribute = base::ASCIIToUTF16("unknown");
field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("");
field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(4);
form.fields.push_back(field);
[autofill_agent_
fillFormData:form
inFrame:test_web_state_.GetWebFramesManager()->GetMainWebFrame()];
test_web_state_.WasShown();
EXPECT_EQ("__gCrWeb.autofill.fillForm({\"fields\":{\"1\":{\"section\":\"\","
"\"value\":\"number_value\"},"
"\"2\":{\"section\":\"\",\"value\":\"name_value\"}},"
"\"formName\":\"CC form\",\"formRendererID\":0}, \"\", -1, true);",
fake_main_frame_->GetLastJavaScriptCall());
}
// Tests that in the case of conflict in fields' identifiers, the last seen // Tests that in the case of conflict in fields' identifiers, the last seen
// value of a given field is used. // value of a given field is used.
// TODO(crbug/1131038): Remove once using only renderer IDs is launched.
TEST_F(AutofillAgentTests, TEST_F(AutofillAgentTests,
OnFormDataFilledWithNameCollisionTestFrameMessaging) { OnFormDataFilledWithNameCollisionTestFrameMessaging) {
base::test::ScopedFeatureList scoped_feature_list;
std::vector<base::Feature> disabled_features;
disabled_features.push_back(
autofill::features::kAutofillUseUniqueRendererIDsOnIOS);
scoped_feature_list.InitWithFeatures({}, disabled_features);
std::string locale("en"); std::string locale("en");
autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate( autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
&test_web_state_, &client_, nil, locale, &test_web_state_, &client_, nil, locale,
...@@ -194,6 +284,7 @@ TEST_F(AutofillAgentTests, ...@@ -194,6 +284,7 @@ TEST_F(AutofillAgentTests,
autofill::FormData form; autofill::FormData form;
form.url = GURL("https://myform.com"); form.url = GURL("https://myform.com");
form.action = GURL("https://myform.com/submit"); form.action = GURL("https://myform.com/submit");
form.unique_renderer_id = FormRendererId(0);
autofill::FormFieldData field; autofill::FormFieldData field;
field.form_control_type = "text"; field.form_control_type = "text";
...@@ -204,6 +295,7 @@ TEST_F(AutofillAgentTests, ...@@ -204,6 +295,7 @@ TEST_F(AutofillAgentTests,
field.unique_id = field.id_attribute; field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("California"); field.value = base::ASCIIToUTF16("California");
field.is_autofilled = true; field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(1);
form.fields.push_back(field); form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Other field"); field.label = base::ASCIIToUTF16("Other field");
field.name = base::ASCIIToUTF16("field1"); field.name = base::ASCIIToUTF16("field1");
...@@ -212,6 +304,7 @@ TEST_F(AutofillAgentTests, ...@@ -212,6 +304,7 @@ TEST_F(AutofillAgentTests,
field.unique_id = field.id_attribute; field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("value 1"); field.value = base::ASCIIToUTF16("value 1");
field.is_autofilled = true; field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(2);
form.fields.push_back(field); form.fields.push_back(field);
field.label = base::ASCIIToUTF16("Other field"); field.label = base::ASCIIToUTF16("Other field");
field.name = base::ASCIIToUTF16("field1"); field.name = base::ASCIIToUTF16("field1");
...@@ -220,6 +313,7 @@ TEST_F(AutofillAgentTests, ...@@ -220,6 +313,7 @@ TEST_F(AutofillAgentTests,
field.unique_id = field.id_attribute; field.unique_id = field.id_attribute;
field.value = base::ASCIIToUTF16("value 2"); field.value = base::ASCIIToUTF16("value 2");
field.is_autofilled = true; field.is_autofilled = true;
field.unique_renderer_id = FieldRendererId(3);
form.fields.push_back(field); form.fields.push_back(field);
// Fields are in alphabetical order. // Fields are in alphabetical order.
[autofill_agent_ [autofill_agent_
...@@ -229,7 +323,7 @@ TEST_F(AutofillAgentTests, ...@@ -229,7 +323,7 @@ TEST_F(AutofillAgentTests,
EXPECT_EQ("__gCrWeb.autofill.fillForm({\"fields\":{\"field1\":{\"section\":" EXPECT_EQ("__gCrWeb.autofill.fillForm({\"fields\":{\"field1\":{\"section\":"
"\"\",\"value\":\"value " "\"\",\"value\":\"value "
"2\"},\"region\":{\"section\":\"\",\"value\":\"California\"}}," "2\"},\"region\":{\"section\":\"\",\"value\":\"California\"}},"
"\"formName\":\"\"}, \"\");", "\"formName\":\"\",\"formRendererID\":0}, \"\", -1, false);",
fake_main_frame_->GetLastJavaScriptCall()); fake_main_frame_->GetLastJavaScriptCall());
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/ios/block_types.h" #include "base/ios/block_types.h"
#include "base/values.h" #include "base/values.h"
#include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/renderer_id.h"
namespace web { namespace web {
class WebFrame; class WebFrame;
...@@ -43,6 +44,7 @@ class WebFrame; ...@@ -43,6 +44,7 @@ class WebFrame;
// corresponding filled values. |completionHandler| cannot be nil. // corresponding filled values. |completionHandler| cannot be nil.
- (void)fillForm:(std::unique_ptr<base::Value>)data - (void)fillForm:(std::unique_ptr<base::Value>)data
forceFillFieldIdentifier:(NSString*)forceFillFieldIdentifier forceFillFieldIdentifier:(NSString*)forceFillFieldIdentifier
forceFillFieldUniqueID:(autofill::FieldRendererId)forceFillFieldUniqueID
inFrame:(web::WebFrame*)frame inFrame:(web::WebFrame*)frame
completionHandler:(void (^)(NSString*))completionHandler; completionHandler:(void (^)(NSString*))completionHandler;
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
using autofill::FieldRendererId;
@implementation JsAutofillManager @implementation JsAutofillManager
- (void)addJSDelayInFrame:(web::WebFrame*)frame { - (void)addJSDelayInFrame:(web::WebFrame*)frame {
...@@ -93,17 +95,26 @@ ...@@ -93,17 +95,26 @@
- (void)fillForm:(std::unique_ptr<base::Value>)data - (void)fillForm:(std::unique_ptr<base::Value>)data
forceFillFieldIdentifier:(NSString*)forceFillFieldIdentifier forceFillFieldIdentifier:(NSString*)forceFillFieldIdentifier
forceFillFieldUniqueID:(FieldRendererId)forceFillFieldUniqueID
inFrame:(web::WebFrame*)frame inFrame:(web::WebFrame*)frame
completionHandler:(void (^)(NSString*))completionHandler { completionHandler:(void (^)(NSString*))completionHandler {
DCHECK(data); DCHECK(data);
DCHECK(completionHandler); DCHECK(completionHandler);
std::string fieldIdentifier =
bool useRendererIDs = base::FeatureList::IsEnabled(
autofill::features::kAutofillUseUniqueRendererIDsOnIOS);
std::string fieldStringID =
forceFillFieldIdentifier forceFillFieldIdentifier
? base::SysNSStringToUTF8(forceFillFieldIdentifier) ? base::SysNSStringToUTF8(forceFillFieldIdentifier)
: "null"; : "null";
int fieldNumericID = forceFillFieldUniqueID ? forceFillFieldUniqueID.value()
: autofill::kNotSetRendererID;
std::vector<base::Value> parameters; std::vector<base::Value> parameters;
parameters.push_back(std::move(*data)); parameters.push_back(std::move(*data));
parameters.push_back(base::Value(fieldIdentifier)); parameters.push_back(base::Value(fieldStringID));
parameters.push_back(base::Value(fieldNumericID));
parameters.push_back(base::Value(useRendererIDs));
autofill::ExecuteJavaScriptFunction( autofill::ExecuteJavaScriptFunction(
"autofill.fillForm", parameters, frame, "autofill.fillForm", parameters, frame,
autofill::CreateStringCallback(completionHandler)); autofill::CreateStringCallback(completionHandler));
......
...@@ -21,6 +21,7 @@ goog.provide('__crWeb.autofill'); ...@@ -21,6 +21,7 @@ goog.provide('__crWeb.autofill');
* The autofill data for a form. * The autofill data for a form.
* @typedef {{ * @typedef {{
* formName: string, * formName: string,
* formRendererID: number,
* fields: !Object<string, !Object<string, string>>, * fields: !Object<string, !Object<string, string>>,
* }} * }}
*/ */
...@@ -189,11 +190,16 @@ function controlElementInputListener_(evt) { ...@@ -189,11 +190,16 @@ function controlElementInputListener_(evt) {
* |forceFillFieldName| will always be filled even if non-empty. * |forceFillFieldName| will always be filled even if non-empty.
* *
* @param {!FormData} data Autofill data to fill in. * @param {!FormData} data Autofill data to fill in.
* @param {string} forceFillFieldIdentifier Identified field will always be * @param {string} forceFillFieldStringID Identified field will always be
* filled even if non-empty. May be null. * filled even if non-empty. May be null.
* @param {number} forceFillFieldNumericID Identified field will always be
* filled even if non-empty. May be kNotSetRendererId.
* @param {bool} useRendererIDs Whether the logic should use numeric renderer
* IDs for form filling.
* @return {string} JSON encoded list of renderer IDs of filled elements. * @return {string} JSON encoded list of renderer IDs of filled elements.
*/ */
__gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) { __gCrWeb.autofill['fillForm'] = function(
data, forceFillFieldStringID, forceFillFieldNumericID, useRendererIDs) {
// Inject CSS to style the autofilled elements with a yellow background. // Inject CSS to style the autofilled elements with a yellow background.
if (!__gCrWeb.autofill.styleInjected) { if (!__gCrWeb.autofill.styleInjected) {
const style = document.createElement('style'); const style = document.createElement('style');
...@@ -207,7 +213,10 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) { ...@@ -207,7 +213,10 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
} }
const filledElements = {}; const filledElements = {};
const form = __gCrWeb.form.getFormElementFromIdentifier(data.formName); const form = useRendererIDs ?
__gCrWeb.form.getFormElementFromUniqueFormId(data.formRendererID) :
__gCrWeb.form.getFormElementFromIdentifier(data.formName);
const controlElements = form ? const controlElements = form ?
__gCrWeb.form.getFormControlElements(form) : __gCrWeb.form.getFormControlElements(form) :
__gCrWeb.fill.getUnownedAutofillableFormFieldElements( __gCrWeb.fill.getUnownedAutofillableFormFieldElements(
...@@ -227,7 +236,9 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) { ...@@ -227,7 +236,9 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
// Skip fields for which autofill data is missing. // Skip fields for which autofill data is missing.
const fieldIdentifier = __gCrWeb.form.getFieldIdentifier(element); const fieldIdentifier = __gCrWeb.form.getFieldIdentifier(element);
const fieldData = data.fields[fieldIdentifier]; const fieldRendererID = __gCrWeb.fill.getUniqueID(element);
const fieldData = useRendererIDs ? data.fields[fieldRendererID] :
data.fields[fieldIdentifier];
if (!fieldData) { if (!fieldData) {
continue; continue;
} }
...@@ -238,10 +249,12 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) { ...@@ -238,10 +249,12 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
// always autofilled; see AutofillManager::FillOrPreviewDataModelForm(). // always autofilled; see AutofillManager::FillOrPreviewDataModelForm().
// c) The "value" or "placeholder" attributes match the value, if any; or // c) The "value" or "placeholder" attributes match the value, if any; or
// d) The value has not been set by the user. // d) The value has not been set by the user.
const shouldBeForceFilled = useRendererIDs ?
fieldRendererID === forceFillFieldNumericID :
fieldIdentifier === forceFillFieldStringID;
if (element.value && __gCrWeb.form.fieldWasEditedByUser(element) && if (element.value && __gCrWeb.form.fieldWasEditedByUser(element) &&
!__gCrWeb.autofill.sanitizedFieldIsEmpty(element.value) && !__gCrWeb.autofill.sanitizedFieldIsEmpty(element.value) &&
fieldIdentifier !== forceFillFieldIdentifier && !shouldBeForceFilled && !__gCrWeb.fill.isSelectElement(element) &&
!__gCrWeb.fill.isSelectElement(element) &&
!((element.hasAttribute('value') && !((element.hasAttribute('value') &&
element.getAttribute('value') === element.value) || element.getAttribute('value') === element.value) ||
(element.hasAttribute('placeholder') && (element.hasAttribute('placeholder') &&
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
using autofill::FieldRendererId;
using base::SysNSStringToUTF8; using base::SysNSStringToUTF8;
namespace { namespace {
...@@ -507,9 +508,16 @@ TEST_F(JsAutofillManagerTest, TestExtractedFieldsIDs) { ...@@ -507,9 +508,16 @@ TEST_F(JsAutofillManagerTest, TestExtractedFieldsIDs) {
} }
} }
// Tests form filling (fillForm:forceFillIdentifier:inFrame:completionHandler:) // Tests form filling (fillForm:forceFillFieldIdentifier:forceFillFieldUniqueID:
// method. // :inFrame:completionHandler:) method.
// TODO(crbug/1131038): Remove once using only renderer IDs is launched.
TEST_F(JsAutofillManagerTest, FillForm) { TEST_F(JsAutofillManagerTest, FillForm) {
base::test::ScopedFeatureList scoped_feature_list;
std::vector<base::Feature> disabled_features;
disabled_features.push_back(
autofill::features::kAutofillUseUniqueRendererIDsOnIOS);
scoped_feature_list.InitWithFeatures({}, disabled_features);
LoadHtml(@"<html><body><form name='testform' method='post'>" LoadHtml(@"<html><body><form name='testform' method='post'>"
"<input type='text' id='firstname' name='firstname'/>" "<input type='text' id='firstname' name='firstname'/>"
"<input type='email' id='email' name='email'/>" "<input type='email' id='email' name='email'/>"
...@@ -538,6 +546,58 @@ TEST_F(JsAutofillManagerTest, FillForm) { ...@@ -538,6 +546,58 @@ TEST_F(JsAutofillManagerTest, FillForm) {
__block BOOL block_was_called = NO; __block BOOL block_was_called = NO;
[manager_ fillForm:std::move(autofillData) [manager_ fillForm:std::move(autofillData)
forceFillFieldIdentifier:@"firstname" forceFillFieldIdentifier:@"firstname"
forceFillFieldUniqueID:FieldRendererId(1)
inFrame:main_web_frame()
completionHandler:^(NSString* result) {
filling_result = [result copy];
block_was_called = YES;
}];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForActionTimeout, ^bool() {
return block_was_called;
}));
EXPECT_NSEQ(@"{\"1\":\"Cool User\",\"2\":\"coolemail@com\"}", filling_result);
}
// Tests form filling (fillForm:forceFillFieldIdentifier:forceFillFieldUniqueID:
// :inFrame:completionHandler:) method.
TEST_F(JsAutofillManagerTest, FillFormUsingRendererIDs) {
base::test::ScopedFeatureList scoped_feature_list;
std::vector<base::Feature> enabled_features;
enabled_features.push_back(
autofill::features::kAutofillUseUniqueRendererIDsOnIOS);
scoped_feature_list.InitWithFeatures(enabled_features, {});
LoadHtml(@"<html><body><form name='testform' method='post'>"
"<input type='text' id='firstname' name='firstname'/>"
"<input type='email' id='email' name='email'/>"
"</form></body></html>");
RunFormsSearch();
auto autofillData = std::make_unique<base::DictionaryValue>();
autofillData->SetKey("formName", base::Value("testform"));
autofillData->SetKey("formRendererID", base::Value(0));
base::Value fieldsData(base::Value::Type::DICTIONARY);
base::Value firstFieldData(base::Value::Type::DICTIONARY);
firstFieldData.SetStringKey("name", "firstname");
firstFieldData.SetStringKey("identifier", "firstname");
firstFieldData.SetStringKey("value", "Cool User");
fieldsData.SetKey("1", std::move(firstFieldData));
base::Value secondFieldData(base::Value::Type::DICTIONARY);
secondFieldData.SetStringKey("name", "email");
secondFieldData.SetStringKey("identifier", "email");
secondFieldData.SetStringKey("value", "coolemail@com");
fieldsData.SetKey("2", std::move(secondFieldData));
autofillData->SetKey("fields", std::move(fieldsData));
__block NSString* filling_result = nil;
__block BOOL block_was_called = NO;
[manager_ fillForm:std::move(autofillData)
forceFillFieldIdentifier:@"firstname"
forceFillFieldUniqueID:FieldRendererId(1)
inFrame:main_web_frame() inFrame:main_web_frame()
completionHandler:^(NSString* result) { completionHandler:^(NSString* result) {
filling_result = [result copy]; filling_result = [result copy];
......
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