Commit 34da76fa authored by sandromaggi's avatar sandromaggi Committed by Commit Bot

[Autofill Assistant] Add skip option for UseAddressProto

This is a proposal to add a boolean to skip Autofill, entirely relying
on the required fields. The purpose of this change is to give our client
full, deterministic control over what happens in a |UseAddressAction|.
The same change is introduced for |UseCreditCardAction|.

Bug: none
Change-Id: Iae4ce4fd00fec6038f5394dff7bf7dd88e2a80a6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2207458Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Reviewed-by: default avatarClemens Arbesser <arbesser@google.com>
Commit-Queue: Sandro Maggi <sandromaggi@google.com>
Cr-Commit-Position: refs/heads/master@{#772623}
parent 9b3a8e9a
...@@ -36,11 +36,17 @@ void UseAddressAction::InternalProcessAction( ...@@ -36,11 +36,17 @@ void UseAddressAction::InternalProcessAction(
ProcessActionCallback action_callback) { ProcessActionCallback action_callback) {
process_action_callback_ = std::move(action_callback); process_action_callback_ = std::move(action_callback);
if (selector_.empty()) { if (selector_.empty() && !proto_.use_address().skip_autofill()) {
VLOG(1) << "UseAddress failed: |selector| empty"; VLOG(1) << "UseAddress failed: |selector| empty";
EndAction(ClientStatus(INVALID_ACTION)); EndAction(ClientStatus(INVALID_ACTION));
return; return;
} }
if (proto_.use_address().skip_autofill() &&
proto_.use_address().required_fields().empty()) {
VLOG(1) << "UseAddress failed: |skip_autofill| without required fields";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
// Ensure data already selected in a previous action. // Ensure data already selected in a previous action.
switch (proto_.use_address().address_source_case()) { switch (proto_.use_address().address_source_case()) {
...@@ -121,19 +127,7 @@ void UseAddressAction::EndAction( ...@@ -121,19 +127,7 @@ void UseAddressAction::EndAction(
} }
void UseAddressAction::FillFormWithData() { void UseAddressAction::FillFormWithData() {
delegate_->ShortWaitForElement(
selector_, base::BindOnce(&UseAddressAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr()));
}
void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) {
if (!element_status.ok()) {
EndAction(ClientStatus(element_status.proto_status()));
return;
}
DCHECK(!selector_.empty());
DCHECK(profile_ != nullptr); DCHECK(profile_ != nullptr);
std::vector<RequiredField> required_fields; std::vector<RequiredField> required_fields;
for (const auto& required_field_proto : for (const auto& required_field_proto :
proto_.use_address().required_fields()) { proto_.use_address().required_fields()) {
...@@ -153,6 +147,25 @@ void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) { ...@@ -153,6 +147,25 @@ void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) {
/* locale = */ "en-US"), /* locale = */ "en-US"),
delegate_); delegate_);
if (proto_.use_address().skip_autofill()) {
fallback_handler_->CheckAndFallbackRequiredFields(
OkClientStatus(), base::BindOnce(&UseAddressAction::EndAction,
weak_ptr_factory_.GetWeakPtr()));
return;
}
delegate_->ShortWaitForElement(
selector_, base::BindOnce(&UseAddressAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr()));
}
void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) {
if (!element_status.ok()) {
EndAction(element_status);
return;
}
DCHECK(!selector_.empty());
delegate_->FillAddressForm(profile_.get(), selector_, delegate_->FillAddressForm(profile_.get(), selector_,
base::BindOnce(&UseAddressAction::OnFormFilled, base::BindOnce(&UseAddressAction::OnFormFilled,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
......
...@@ -137,6 +137,14 @@ TEST_F(UseAddressActionTest, InvalidActionNameSetButEmpty) { ...@@ -137,6 +137,14 @@ TEST_F(UseAddressActionTest, InvalidActionNameSetButEmpty) {
EXPECT_EQ(ProcessedActionStatusProto::INVALID_ACTION, ProcessAction(action)); EXPECT_EQ(ProcessedActionStatusProto::INVALID_ACTION, ProcessAction(action));
} }
TEST_F(UseAddressActionTest, InvalidActionSkipAutofillWithoutRequiredFields) {
ActionProto action;
UseAddressProto* use_address = action.mutable_use_address();
use_address->set_name(kAddressName);
use_address->set_skip_autofill(true);
EXPECT_EQ(ProcessedActionStatusProto::INVALID_ACTION, ProcessAction(action));
}
TEST_F(UseAddressActionTest, PreconditionFailedNoProfileForName) { TEST_F(UseAddressActionTest, PreconditionFailedNoProfileForName) {
ActionProto action; ActionProto action;
UseAddressProto* use_address = action.mutable_use_address(); UseAddressProto* use_address = action.mutable_use_address();
...@@ -496,5 +504,44 @@ TEST_F(UseAddressActionTest, ForcedFallbackWithKeystrokes) { ...@@ -496,5 +504,44 @@ TEST_F(UseAddressActionTest, ForcedFallbackWithKeystrokes) {
ProcessAction(action_proto)); ProcessAction(action_proto));
} }
TEST_F(UseAddressActionTest, SkippingAutofill) {
ON_CALL(mock_action_delegate_, GetElementTag(_, _))
.WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
ActionProto action_proto;
action_proto.mutable_use_address()->set_name(kAddressName);
AddRequiredField(&action_proto,
base::NumberToString(
static_cast<int>(autofill::ServerFieldType::NAME_FIRST)),
"#first_name");
action_proto.mutable_use_address()->set_skip_autofill(true);
EXPECT_CALL(mock_action_delegate_, OnFillAddressForm(_, _, _)).Times(0);
// First validation fails.
EXPECT_CALL(mock_web_controller_,
OnGetFieldValue(Selector({"#first_name"}), _))
.WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
// Fill first name.
Expectation set_first_name =
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(Selector({"#first_name"}), kFirstName, _))
.WillOnce(RunOnceCallback<2>(OkClientStatus()));
// Second validation succeeds.
EXPECT_CALL(mock_web_controller_,
OnGetFieldValue(Selector({"#first_name"}), _))
.After(set_first_name)
.WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
ProcessedActionProto processed_action;
EXPECT_CALL(callback_, Run(_)).WillOnce(SaveArgPointee<0>(&processed_action));
UseAddressAction action(&mock_action_delegate_, action_proto);
action.ProcessAction(callback_.Get());
EXPECT_EQ(processed_action.status(),
ProcessedActionStatusProto::ACTION_APPLIED);
}
} // namespace } // namespace
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -40,11 +40,17 @@ void UseCreditCardAction::InternalProcessAction( ...@@ -40,11 +40,17 @@ void UseCreditCardAction::InternalProcessAction(
ProcessActionCallback action_callback) { ProcessActionCallback action_callback) {
process_action_callback_ = std::move(action_callback); process_action_callback_ = std::move(action_callback);
if (selector_.empty()) { if (selector_.empty() && !proto_.use_card().skip_autofill()) {
VLOG(1) << "UseCreditCard failed: |selector| empty"; VLOG(1) << "UseCreditCard failed: |selector| empty";
EndAction(ClientStatus(INVALID_ACTION)); EndAction(ClientStatus(INVALID_ACTION));
return; return;
} }
if (proto_.use_card().skip_autofill() &&
proto_.use_card().required_fields().empty()) {
VLOG(1) << "UseCreditCard failed: |skip_autofill| without required fields";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
// Ensure data already selected in a previous action. // Ensure data already selected in a previous action.
if (proto_.use_card().has_model_identifier()) { if (proto_.use_card().has_model_identifier()) {
...@@ -102,6 +108,14 @@ void UseCreditCardAction::EndAction( ...@@ -102,6 +108,14 @@ void UseCreditCardAction::EndAction(
} }
void UseCreditCardAction::FillFormWithData() { void UseCreditCardAction::FillFormWithData() {
if (proto_.use_card().skip_autofill()) {
DCHECK(credit_card_ != nullptr);
delegate_->GetFullCard(credit_card_.get(),
base::BindOnce(&UseCreditCardAction::OnGetFullCard,
weak_ptr_factory_.GetWeakPtr()));
return;
}
delegate_->ShortWaitForElement( delegate_->ShortWaitForElement(
selector_, base::BindOnce(&UseCreditCardAction::OnWaitForElement, selector_, base::BindOnce(&UseCreditCardAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
...@@ -109,7 +123,7 @@ void UseCreditCardAction::FillFormWithData() { ...@@ -109,7 +123,7 @@ void UseCreditCardAction::FillFormWithData() {
void UseCreditCardAction::OnWaitForElement(const ClientStatus& element_status) { void UseCreditCardAction::OnWaitForElement(const ClientStatus& element_status) {
if (!element_status.ok()) { if (!element_status.ok()) {
EndAction(ClientStatus(element_status.proto_status())); EndAction(element_status);
return; return;
} }
...@@ -117,7 +131,6 @@ void UseCreditCardAction::OnWaitForElement(const ClientStatus& element_status) { ...@@ -117,7 +131,6 @@ void UseCreditCardAction::OnWaitForElement(const ClientStatus& element_status) {
delegate_->GetFullCard(credit_card_.get(), delegate_->GetFullCard(credit_card_.get(),
base::BindOnce(&UseCreditCardAction::OnGetFullCard, base::BindOnce(&UseCreditCardAction::OnGetFullCard,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
return;
} }
void UseCreditCardAction::OnGetFullCard( void UseCreditCardAction::OnGetFullCard(
...@@ -128,16 +141,6 @@ void UseCreditCardAction::OnGetFullCard( ...@@ -128,16 +141,6 @@ void UseCreditCardAction::OnGetFullCard(
return; return;
} }
std::map<int, std::string> fallback_values =
autofill_field_formatter::CreateAutofillMappings(*card,
/* locale = */ "en-US");
fallback_values.emplace(
static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE),
base::UTF16ToUTF8(cvc));
fallback_values.emplace(
(int)AutofillFormatProto::CREDIT_CARD_RAW_NUMBER,
base::UTF16ToUTF8(card->GetRawInfo(autofill::CREDIT_CARD_NUMBER)));
std::vector<RequiredField> required_fields; std::vector<RequiredField> required_fields;
for (const auto& required_field_proto : proto_.use_card().required_fields()) { for (const auto& required_field_proto : proto_.use_card().required_fields()) {
if (required_field_proto.value_expression().empty()) { if (required_field_proto.value_expression().empty()) {
...@@ -149,10 +152,27 @@ void UseCreditCardAction::OnGetFullCard( ...@@ -149,10 +152,27 @@ void UseCreditCardAction::OnGetFullCard(
required_fields.emplace_back(required_field); required_fields.emplace_back(required_field);
} }
std::map<int, std::string> fallback_values =
autofill_field_formatter::CreateAutofillMappings(*card,
/* locale = */ "en-US");
fallback_values.emplace(
static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE),
base::UTF16ToUTF8(cvc));
fallback_values.emplace(
(int)AutofillFormatProto::CREDIT_CARD_RAW_NUMBER,
base::UTF16ToUTF8(card->GetRawInfo(autofill::CREDIT_CARD_NUMBER)));
DCHECK(fallback_handler_ == nullptr); DCHECK(fallback_handler_ == nullptr);
fallback_handler_ = std::make_unique<RequiredFieldsFallbackHandler>( fallback_handler_ = std::make_unique<RequiredFieldsFallbackHandler>(
required_fields, fallback_values, delegate_); required_fields, fallback_values, delegate_);
if (proto_.use_card().skip_autofill()) {
fallback_handler_->CheckAndFallbackRequiredFields(
OkClientStatus(), base::BindOnce(&UseCreditCardAction::EndAction,
weak_ptr_factory_.GetWeakPtr()));
return;
}
delegate_->FillCardForm(std::move(card), cvc, selector_, delegate_->FillCardForm(std::move(card), cvc, selector_,
base::BindOnce(&UseCreditCardAction::OnFormFilled, base::BindOnce(&UseCreditCardAction::OnFormFilled,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
......
...@@ -132,6 +132,14 @@ TEST_F(UseCreditCardActionTest, InvalidActionNoSelectorSet) { ...@@ -132,6 +132,14 @@ TEST_F(UseCreditCardActionTest, InvalidActionNoSelectorSet) {
EXPECT_EQ(ProcessedActionStatusProto::INVALID_ACTION, ProcessAction(action)); EXPECT_EQ(ProcessedActionStatusProto::INVALID_ACTION, ProcessAction(action));
} }
TEST_F(UseCreditCardActionTest,
InvalidActionnSkipAutofillWithoutRequiredFields) {
ActionProto action;
auto* use_card = action.mutable_use_card();
use_card->set_skip_autofill(true);
EXPECT_EQ(ProcessedActionStatusProto::INVALID_ACTION, ProcessAction(action));
}
TEST_F(UseCreditCardActionTest, PreconditionFailedNoCreditCardInUserData) { TEST_F(UseCreditCardActionTest, PreconditionFailedNoCreditCardInUserData) {
ActionProto action; ActionProto action;
auto* use_card = action.mutable_use_card(); auto* use_card = action.mutable_use_card();
...@@ -380,6 +388,35 @@ TEST_F(UseCreditCardActionTest, ForcedFallbackWithKeystrokes) { ...@@ -380,6 +388,35 @@ TEST_F(UseCreditCardActionTest, ForcedFallbackWithKeystrokes) {
EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED, ProcessAction(action)); EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED, ProcessAction(action));
} }
TEST_F(UseCreditCardActionTest, SkippingAutofill) {
ON_CALL(mock_action_delegate_, GetElementTag(_, _))
.WillByDefault(RunOnceCallback<1>(OkClientStatus(), "INPUT"));
ActionProto action = CreateUseCreditCardAction();
AddRequiredField(&action,
base::NumberToString(static_cast<int>(
AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE)),
"#cvc");
action.mutable_use_card()->set_skip_autofill(true);
EXPECT_CALL(mock_action_delegate_, OnFillCardForm(_, _, _, _)).Times(0);
// First validation fails.
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#cvc"}), _))
.WillOnce(RunOnceCallback<1>(OkClientStatus(), ""));
// Fill cvc.
Expectation set_cvc =
EXPECT_CALL(mock_action_delegate_,
OnSetFieldValue(Selector({"#cvc"}), kFakeCvc, _))
.WillOnce(RunOnceCallback<2>(OkClientStatus()));
// Second validation succeeds.
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Selector({"#cvc"}), _))
.After(set_cvc)
.WillOnce(RunOnceCallback<1>(OkClientStatus(), "not empty"));
EXPECT_EQ(ProcessedActionStatusProto::ACTION_APPLIED, ProcessAction(action));
}
TEST_F(UseCreditCardActionTest, AutofillFailureWithoutRequiredFieldsIsFatal) { TEST_F(UseCreditCardActionTest, AutofillFailureWithoutRequiredFieldsIsFatal) {
ActionProto action_proto = CreateUseCreditCardAction(); ActionProto action_proto = CreateUseCreditCardAction();
......
...@@ -884,7 +884,7 @@ message UseAddressProto { ...@@ -884,7 +884,7 @@ message UseAddressProto {
// The client memory key from which to retrieve the address. // The client memory key from which to retrieve the address.
string name = 1; string name = 1;
// The client model identifier from which to retrieve the address. // The client model identifier from which to retrieve the address.
string model_identifier = 8; string model_identifier = 9;
} }
// Reference to an element in the form that should be filled. // Reference to an element in the form that should be filled.
...@@ -893,7 +893,11 @@ message UseAddressProto { ...@@ -893,7 +893,11 @@ message UseAddressProto {
// An optional list of fields that should be filled by this action. // An optional list of fields that should be filled by this action.
repeated RequiredField required_fields = 6; repeated RequiredField required_fields = 6;
reserved 2; // If true, this skips the Autofill step jumping straight to the
// |required_fields|.
optional bool skip_autofill = 10;
reserved 2, 7, 8;
} }
// Fill a form with a credit card if there is one stored in client memory, // Fill a form with a credit card if there is one stored in client memory,
...@@ -949,7 +953,11 @@ message UseCreditCardProto { ...@@ -949,7 +953,11 @@ message UseCreditCardProto {
repeated RequiredField required_fields = 7; repeated RequiredField required_fields = 7;
reserved 1; // If true, this skips the Autofill step jumping straight to the
// |required_fields|.
optional bool skip_autofill = 8;
reserved 1, 5, 6;
} }
// Ask Chrome to wait for an element in the DOM. This can be used to only // Ask Chrome to wait for an element in the DOM. This can be used to only
......
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