Commit b6f109c6 authored by Marian Fechete's avatar Marian Fechete Committed by Commit Bot

[Autofill Assistant] Refactor UseAddress and UseCard actions to extract fallback logic.

This CL is a first step to refactoring the fallback logic in Autofill Assistant.
Specifically, this refactors the UseAddress and UseCard actions to remove duplicated
code for checking required fields and filling in fallback values.

Bug: b/141362833
Change-Id: I7bbfb767e2768b79a73a07242e68e0925063c663
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1875257
Commit-Queue: Marian Fechete <marianfe@google.com>
Reviewed-by: default avatarClemens Arbesser <arbesser@google.com>
Cr-Commit-Position: refs/heads/master@{#709425}
parent 5dc635d6
...@@ -52,6 +52,8 @@ jumbo_static_library("browser") { ...@@ -52,6 +52,8 @@ jumbo_static_library("browser") {
"actions/popup_message_action.h", "actions/popup_message_action.h",
"actions/prompt_action.cc", "actions/prompt_action.cc",
"actions/prompt_action.h", "actions/prompt_action.h",
"actions/required_fields_fallback_handler.cc",
"actions/required_fields_fallback_handler.h",
"actions/reset_action.cc", "actions/reset_action.cc",
"actions/reset_action.h", "actions/reset_action.h",
"actions/select_option_action.cc", "actions/select_option_action.cc",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/batch_element_checker.h"
#include "components/autofill_assistant/browser/client_status.h"
namespace autofill_assistant {
RequiredFieldsFallbackHandler::RequiredFieldsFallbackHandler(
const std::vector<RequiredField>& required_fields,
base::RepeatingCallback<std::string(const RequiredField&,
const FallbackData&)>
field_value_getter,
base::OnceCallback<void(const ClientStatus&)> status_update_callback,
ActionDelegate* action_delegate) {
required_fields_.assign(required_fields.begin(), required_fields.end());
field_value_getter_ = std::move(field_value_getter);
status_update_callback_ = std::move(status_update_callback);
action_delegate_ = action_delegate;
}
RequiredFieldsFallbackHandler::~RequiredFieldsFallbackHandler() {}
RequiredFieldsFallbackHandler::FallbackData::FallbackData() {}
void RequiredFieldsFallbackHandler::CheckAndFallbackRequiredFields(
std::unique_ptr<FallbackData> fallback_data) {
if (required_fields_.empty()) {
std::move(status_update_callback_).Run(ClientStatus(ACTION_APPLIED));
return;
}
DCHECK(!batch_element_checker_);
batch_element_checker_ = std::make_unique<BatchElementChecker>();
for (size_t i = 0; i < required_fields_.size(); i++) {
if (required_fields_[i].forced) {
continue;
}
batch_element_checker_->AddFieldValueCheck(
required_fields_[i].selector,
base::BindOnce(&RequiredFieldsFallbackHandler::OnGetRequiredFieldValue,
weak_ptr_factory_.GetWeakPtr(), i));
}
batch_element_checker_->AddAllDoneCallback(
base::BindOnce(&RequiredFieldsFallbackHandler::OnCheckRequiredFieldsDone,
weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
action_delegate_->RunElementChecks(batch_element_checker_.get());
}
void RequiredFieldsFallbackHandler::OnGetRequiredFieldValue(
size_t required_fields_index,
const ClientStatus& element_status,
const std::string& value) {
required_fields_[required_fields_index].status =
value.empty() ? EMPTY : NOT_EMPTY;
}
void RequiredFieldsFallbackHandler::OnCheckRequiredFieldsDone(
std::unique_ptr<FallbackData> fallback_data) {
batch_element_checker_.reset();
// We process all fields with an empty value in order to perform the fallback
// on all those fields, if any.
bool should_fallback = false;
for (const RequiredField& required_field : required_fields_) {
if (required_field.ShouldFallback(fallback_data != nullptr)) {
should_fallback = true;
break;
}
}
if (!should_fallback) {
std::move(status_update_callback_).Run(ClientStatus(ACTION_APPLIED));
return;
}
if (!fallback_data) {
// Validation failed and we don't want to try the fallback.
std::move(status_update_callback_).Run(ClientStatus(MANUAL_FALLBACK));
return;
}
// If there are any fallbacks for the empty fields, set them, otherwise fail
// immediately.
bool has_fallbacks = false;
for (const RequiredField& required_field : required_fields_) {
if (!required_field.ShouldFallback(/* has_fallback_data= */ true))
continue;
if (!field_value_getter_.Run(required_field, *fallback_data).empty()) {
has_fallbacks = true;
}
}
if (!has_fallbacks) {
std::move(status_update_callback_).Run(ClientStatus(MANUAL_FALLBACK));
return;
}
// Set the fallback values and check again.
SetFallbackFieldValuesSequentially(0, std::move(fallback_data));
}
void RequiredFieldsFallbackHandler::SetFallbackFieldValuesSequentially(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data) {
// Skip non-empty fields.
while (required_fields_index < required_fields_.size() &&
!required_fields_[required_fields_index].ShouldFallback(
fallback_data != nullptr)) {
required_fields_index++;
}
// If there are no more fields to set, check the required fields again,
// but this time we don't want to try the fallback in case of failure.
if (required_fields_index >= required_fields_.size()) {
DCHECK_EQ(required_fields_index, required_fields_.size());
return CheckAndFallbackRequiredFields(/* fallback_data= */ nullptr);
}
// Set the next field to its fallback value.
const RequiredField& required_field = required_fields_[required_fields_index];
std::string fallback_value =
field_value_getter_.Run(required_field, *fallback_data);
if (fallback_value.empty()) {
DVLOG(3) << "No fallback for " << required_field.selector;
// If there is no fallback value, we skip this failed field.
return SetFallbackFieldValuesSequentially(++required_fields_index,
std::move(fallback_data));
}
DVLOG(3) << "Setting fallback value for " << required_field.selector;
action_delegate_->SetFieldValue(
required_field.selector, fallback_value,
required_field.simulate_key_presses, required_field.delay_in_millisecond,
base::BindOnce(&RequiredFieldsFallbackHandler::OnSetFallbackFieldValue,
weak_ptr_factory_.GetWeakPtr(), required_fields_index,
std::move(fallback_data)));
}
void RequiredFieldsFallbackHandler::OnSetFallbackFieldValue(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data,
const ClientStatus& setFieldStatus) {
if (!setFieldStatus.ok()) {
// Fallback failed: we stop the script without checking the fields.
std::move(status_update_callback_).Run(ClientStatus(MANUAL_FALLBACK));
return;
}
SetFallbackFieldValuesSequentially(++required_fields_index,
std::move(fallback_data));
}
} // namespace autofill_assistant
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REQUIRED_FIELDS_FALLBACK_HANDLER_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REQUIRED_FIELDS_FALLBACK_HANDLER_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/batch_element_checker.h"
namespace autofill {
class AutofillProfile;
} // namespace autofill
namespace autofill_assistant {
class ClientStatus;
// A handler for required fields and fallback values, used for example in
// UseAddressAction.
class RequiredFieldsFallbackHandler {
public:
enum FieldValueStatus { UNKNOWN, EMPTY, NOT_EMPTY };
struct RequiredField {
Selector selector;
bool simulate_key_presses = false;
int delay_in_millisecond = 0;
bool forced = false;
FieldValueStatus status = UNKNOWN;
// When filling in credit card, card_field must be set. When filling in
// address, address_field must be set.
UseAddressProto::RequiredField::AddressField address_field =
UseAddressProto::RequiredField::UNDEFINED;
UseCreditCardProto::RequiredField::CardField card_field =
UseCreditCardProto::RequiredField::UNDEFINED;
// Returns true if fallback is required for this field.
bool ShouldFallback(bool has_fallback_data) const {
return status == EMPTY || (forced && has_fallback_data);
}
};
// Data necessary for filling in the fallback fields. This is kept in a
// separate struct to make sure we don't keep it for longer than strictly
// necessary.
// TODO(marianfe): Refactor this to use a map instead.
struct FallbackData {
FallbackData();
~FallbackData() = default;
// autofill profile.
const autofill::AutofillProfile* profile;
// Card information for UseCreditCard fallback.
std::string cvc;
std::string card_holder_name;
std::string card_number;
int expiration_year = 0;
int expiration_month = 0;
private:
DISALLOW_COPY_AND_ASSIGN(FallbackData);
};
explicit RequiredFieldsFallbackHandler(
const std::vector<RequiredField>& required_fields,
base::RepeatingCallback<std::string(const RequiredField&,
const FallbackData&)>
field_value_getter,
base::OnceCallback<void(const ClientStatus&)> status_update_callback,
ActionDelegate* delegate);
~RequiredFieldsFallbackHandler();
// Check whether all required fields have a non-empty value. If it is the
// case, update the status to success. If it's not and |fallback_data|
// is null, update the status to failure. If |fallback_data| is non-null, use
// it to attempt to fill the failed fields without Autofill.
void CheckAndFallbackRequiredFields(
std::unique_ptr<FallbackData> fallback_data);
private:
// Triggers the check for a specific field.
void CheckRequiredFieldsSequentially(
bool allow_fallback,
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data);
// Updates the status of the required field.
void OnGetRequiredFieldValue(size_t required_fields_index,
const ClientStatus& element_status,
const std::string& value);
// Called when all required fields have been checked.
void OnCheckRequiredFieldsDone(std::unique_ptr<FallbackData> fallback_data);
// Sets fallback field values for empty fields.
void SetFallbackFieldValuesSequentially(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data);
// Called after trying to set form values without Autofill in case of fallback
// after failed validation.
void OnSetFallbackFieldValue(size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data,
const ClientStatus& status);
std::vector<RequiredField> required_fields_;
base::RepeatingCallback<std::string(const RequiredField&,
const FallbackData&)>
field_value_getter_;
base::OnceCallback<void(const ClientStatus&)> status_update_callback_;
ActionDelegate* action_delegate_;
std::unique_ptr<BatchElementChecker> batch_element_checker_;
base::WeakPtrFactory<RequiredFieldsFallbackHandler> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(RequiredFieldsFallbackHandler);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_REQUIRED_FIELDS_FALLBACK_HANDLER_H_
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h" #include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/batch_element_checker.h" #include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
#include "components/autofill_assistant/browser/client_memory.h" #include "components/autofill_assistant/browser/client_memory.h"
#include "components/autofill_assistant/browser/client_status.h" #include "components/autofill_assistant/browser/client_status.h"
namespace autofill_assistant { namespace autofill_assistant {
using RequiredField = RequiredFieldsFallbackHandler::RequiredField;
using FallbackData = RequiredFieldsFallbackHandler::FallbackData;
UseAddressAction::UseAddressAction(ActionDelegate* delegate, UseAddressAction::UseAddressAction(ActionDelegate* delegate,
const ActionProto& proto) const ActionProto& proto)
...@@ -27,10 +29,11 @@ UseAddressAction::UseAddressAction(ActionDelegate* delegate, ...@@ -27,10 +29,11 @@ UseAddressAction::UseAddressAction(ActionDelegate* delegate,
DCHECK(proto.has_use_address()); DCHECK(proto.has_use_address());
prompt_ = proto.use_address().prompt(); prompt_ = proto.use_address().prompt();
name_ = proto.use_address().name(); name_ = proto.use_address().name();
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()) {
required_fields_.emplace_back(); required_fields.emplace_back();
RequiredField& required_field = required_fields_.back(); RequiredField& required_field = required_fields.back();
required_field.address_field = required_field_proto.address_field(); required_field.address_field = required_field_proto.address_field();
required_field.selector = Selector(required_field_proto.element()); required_field.selector = Selector(required_field_proto.element());
required_field.simulate_key_presses = required_field.simulate_key_presses =
...@@ -40,6 +43,13 @@ UseAddressAction::UseAddressAction(ActionDelegate* delegate, ...@@ -40,6 +43,13 @@ UseAddressAction::UseAddressAction(ActionDelegate* delegate,
required_field.forced = required_field_proto.forced(); required_field.forced = required_field_proto.forced();
} }
required_fields_fallback_handler_ =
std::make_unique<RequiredFieldsFallbackHandler>(
required_fields,
base::BindRepeating(&UseAddressAction::GetFallbackValue,
base::Unretained(this)),
base::BindOnce(&UseAddressAction::EndAction, base::Unretained(this)),
delegate);
selector_ = Selector(proto.use_address().form_field_element()); selector_ = Selector(proto.use_address().form_field_element());
selector_.MustBeVisible(); selector_.MustBeVisible();
DCHECK(!selector_.empty()); DCHECK(!selector_.empty());
...@@ -62,17 +72,13 @@ void UseAddressAction::InternalProcessAction( ...@@ -62,17 +72,13 @@ void UseAddressAction::InternalProcessAction(
error_info->set_address_pointee_was_null( error_info->set_address_pointee_was_null(
!client_memory->has_selected_address(name_) || !client_memory->has_selected_address(name_) ||
!client_memory->selected_address(name_)); !client_memory->selected_address(name_));
EndAction(PRECONDITION_FAILED); EndAction(ClientStatus(PRECONDITION_FAILED));
return; return;
} }
FillFormWithData(); FillFormWithData();
} }
void UseAddressAction::EndAction(ProcessedActionStatusProto status) {
EndAction(ClientStatus(status));
}
void UseAddressAction::EndAction(const ClientStatus& status) { void UseAddressAction::EndAction(const ClientStatus& status) {
UpdateProcessedAction(status); UpdateProcessedAction(status);
std::move(process_action_callback_).Run(std::move(processed_action_proto_)); std::move(process_action_callback_).Run(std::move(processed_action_proto_));
...@@ -86,7 +92,7 @@ void UseAddressAction::FillFormWithData() { ...@@ -86,7 +92,7 @@ void UseAddressAction::FillFormWithData() {
void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) { void UseAddressAction::OnWaitForElement(const ClientStatus& element_status) {
if (!element_status.ok()) { if (!element_status.ok()) {
EndAction(element_status.proto_status()); EndAction(ClientStatus(element_status.proto_status()));
return; return;
} }
...@@ -110,154 +116,19 @@ void UseAddressAction::OnFormFilled(std::unique_ptr<FallbackData> fallback_data, ...@@ -110,154 +116,19 @@ void UseAddressAction::OnFormFilled(std::unique_ptr<FallbackData> fallback_data,
EndAction(status); EndAction(status);
return; return;
} }
CheckRequiredFields(std::move(fallback_data));
}
void UseAddressAction::CheckRequiredFields(
std::unique_ptr<FallbackData> fallback_data) {
// If there are no required fields, finish the action successfully.
if (required_fields_.empty()) {
EndAction(ACTION_APPLIED);
return;
}
DCHECK(!batch_element_checker_); required_fields_fallback_handler_->CheckAndFallbackRequiredFields(
batch_element_checker_ = std::make_unique<BatchElementChecker>(); std::move(fallback_data));
for (size_t i = 0; i < required_fields_.size(); i++) {
if (required_fields_[i].forced)
continue;
batch_element_checker_->AddFieldValueCheck(
required_fields_[i].selector,
base::BindOnce(&UseAddressAction::OnGetRequiredFieldValue,
weak_ptr_factory_.GetWeakPtr(), i));
}
batch_element_checker_->AddAllDoneCallback(
base::BindOnce(&UseAddressAction::OnCheckRequiredFieldsDone,
weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
delegate_->RunElementChecks(batch_element_checker_.get());
}
void UseAddressAction::OnGetRequiredFieldValue(
size_t required_fields_index,
const ClientStatus& element_status,
const std::string& value) {
required_fields_[required_fields_index].status =
value.empty() ? EMPTY : NOT_EMPTY;
}
void UseAddressAction::OnCheckRequiredFieldsDone(
std::unique_ptr<FallbackData> fallback_data) {
batch_element_checker_.reset();
// We process all fields with an empty value in order to perform the fallback
// on all those fields, if any.
bool should_fallback = false;
for (const RequiredField& required_field : required_fields_) {
if (required_field.ShouldFallback(fallback_data != nullptr)) {
should_fallback = true;
break;
}
}
if (!should_fallback) {
EndAction(ACTION_APPLIED);
return;
}
if (!fallback_data) {
// Validation failed and we don't want to try the fallback.
EndAction(MANUAL_FALLBACK);
return;
}
// If there are any fallbacks for the empty fields, set them, otherwise fail
// immediately.
bool has_fallbacks = false;
for (const RequiredField& required_field : required_fields_) {
if (!required_field.ShouldFallback(/* has_fallback_data= */ true))
continue;
if (!GetFallbackValue(required_field, *fallback_data).empty()) {
has_fallbacks = true;
}
}
if (!has_fallbacks) {
EndAction(MANUAL_FALLBACK);
return;
}
// Set the fallback values and check again.
SetFallbackFieldValuesSequentially(0, std::move(fallback_data));
}
void UseAddressAction::SetFallbackFieldValuesSequentially(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data) {
// Skip non-empty fields.
while (required_fields_index < required_fields_.size() &&
!required_fields_[required_fields_index].ShouldFallback(
fallback_data != nullptr)) {
required_fields_index++;
}
// If there are no more fields to set, check the required fields again,
// but this time we don't want to try the fallback in case of failure.
if (required_fields_index >= required_fields_.size()) {
DCHECK_EQ(required_fields_index, required_fields_.size());
CheckRequiredFields(/* fallback_data= */ nullptr);
return;
}
// Set the next field to its fallback value.
const RequiredField& required_field = required_fields_[required_fields_index];
std::string fallback_value = GetFallbackValue(required_field, *fallback_data);
if (fallback_value.empty()) {
DVLOG(3) << "No fallback for " << required_field.selector;
// If there is no fallback value, we skip this failed field.
SetFallbackFieldValuesSequentially(++required_fields_index,
std::move(fallback_data));
return;
}
DVLOG(3) << "Setting fallback value for " << required_field.selector;
delegate_->SetFieldValue(
required_field.selector, fallback_value,
required_field.simulate_key_presses, required_field.delay_in_millisecond,
base::BindOnce(&UseAddressAction::OnSetFallbackFieldValue,
weak_ptr_factory_.GetWeakPtr(), required_fields_index,
std::move(fallback_data)));
}
void UseAddressAction::OnSetFallbackFieldValue(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data,
const ClientStatus& status) {
if (!status.ok()) {
// Fallback failed: we stop the script without checking the fields.
EndAction(MANUAL_FALLBACK);
return;
}
SetFallbackFieldValuesSequentially(++required_fields_index,
std::move(fallback_data));
} }
std::string UseAddressAction::GetFallbackValue( std::string UseAddressAction::GetFallbackValue(
const RequiredField& required_field, const RequiredField& required_field,
const FallbackData& fallback_data) { const FallbackData& fallback_data) {
if (required_field.address_field != return base::UTF16ToUTF8(
UseAddressProto::RequiredField::UNDEFINED && GetFieldValue(fallback_data.profile, required_field.address_field));
fallback_data.profile) {
return base::UTF16ToUTF8(GetAddressFieldValue(
fallback_data.profile, required_field.address_field));
}
NOTREACHED() << "Unsupported field type for " << required_field.selector;
return "";
} }
base::string16 UseAddressAction::GetAddressFieldValue( base::string16 UseAddressAction::GetFieldValue(
const autofill::AutofillProfile* profile, const autofill::AutofillProfile* profile,
const UseAddressProto::RequiredField::AddressField& address_field) { const UseAddressProto::RequiredField::AddressField& address_field) {
// TODO(crbug.com/806868): Get the actual application locale. // TODO(crbug.com/806868): Get the actual application locale.
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h" #include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/batch_element_checker.h" #include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
namespace autofill { namespace autofill {
class AutofillProfile; class AutofillProfile;
...@@ -29,42 +29,10 @@ class UseAddressAction : public Action { ...@@ -29,42 +29,10 @@ class UseAddressAction : public Action {
~UseAddressAction() override; ~UseAddressAction() override;
private: private:
enum FieldValueStatus { UNKNOWN, EMPTY, NOT_EMPTY };
struct RequiredField {
Selector selector;
bool simulate_key_presses = false;
int delay_in_millisecond = 0;
bool forced = false;
FieldValueStatus status = UNKNOWN;
// When filling in address, address_field must be set.
UseAddressProto::RequiredField::AddressField address_field =
UseAddressProto::RequiredField::UNDEFINED;
// Returns true if fallback is required for this field.
bool ShouldFallback(bool has_fallback_data) const {
return status == EMPTY || (forced && has_fallback_data);
}
};
// Data necessary for filling in the fallback fields. This is kept in a
// separate struct to make sure we don't keep it for longer than strictly
// necessary.
struct FallbackData {
FallbackData() = default;
~FallbackData() = default;
// Profile for UseAddress fallback.
const autofill::AutofillProfile* profile = nullptr;
private:
DISALLOW_COPY_AND_ASSIGN(FallbackData);
};
// Overrides Action: // Overrides Action:
void InternalProcessAction(ProcessActionCallback callback) override; void InternalProcessAction(ProcessActionCallback callback) override;
void EndAction(ProcessedActionStatusProto status);
void EndAction(const ClientStatus& status); void EndAction(const ClientStatus& status);
// Fill the form using data in client memory. Return whether filling succeeded // Fill the form using data in client memory. Return whether filling succeeded
...@@ -73,59 +41,28 @@ class UseAddressAction : public Action { ...@@ -73,59 +41,28 @@ class UseAddressAction : public Action {
void OnWaitForElement(const ClientStatus& element_status); void OnWaitForElement(const ClientStatus& element_status);
// Called when the address has been filled. // Called when the address has been filled.
void OnFormFilled(std::unique_ptr<FallbackData> fallback_data, void OnFormFilled(std::unique_ptr<RequiredFieldsFallbackHandler::FallbackData>
fallback_data,
const ClientStatus& status); const ClientStatus& status);
// Check whether all required fields have a non-empty value. If it is the
// case, finish the action successfully. If it's not and |fallback_data|
// is null, fail the action. If |fallback_data| is non-null, use it to attempt
// to fill the failed fields without Autofill.
void CheckRequiredFields(std::unique_ptr<FallbackData> fallback_data);
// Triggers the check for a specific field.
void CheckRequiredFieldsSequentially(
bool allow_fallback,
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data);
// Updates |required_fields_value_status_|.
void OnGetRequiredFieldValue(size_t required_fields_index,
const ClientStatus& element_status,
const std::string& value);
// Called when all required fields have been checked.
void OnCheckRequiredFieldsDone(std::unique_ptr<FallbackData> fallback_data);
// Gets the fallback value. // Gets the fallback value.
std::string GetFallbackValue(const RequiredField& required_field, std::string GetFallbackValue(
const FallbackData& fallback_data); const RequiredFieldsFallbackHandler::RequiredField& required_field,
const RequiredFieldsFallbackHandler::FallbackData& fallback_data);
// Get the value of |address_field| associated to profile |profile|. Return // Get the value of |address_field| associated to profile |profile|. Return
// empty string if there is no data available. // empty string if there is no data available.
base::string16 GetAddressFieldValue( base::string16 GetFieldValue(
const autofill::AutofillProfile* profile, const autofill::AutofillProfile* profile,
const UseAddressProto::RequiredField::AddressField& address_field); const UseAddressProto::RequiredField::AddressField& address_field);
// Sets fallback field values for empty fields from
// |required_fields_value_status_|.
void SetFallbackFieldValuesSequentially(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data);
// Called after trying to set form values without Autofill in case of fallback
// after failed validation.
void OnSetFallbackFieldValue(size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data,
const ClientStatus& status);
// Usage of the autofilled address. // Usage of the autofilled address.
std::string name_; std::string name_;
std::string prompt_; std::string prompt_;
Selector selector_; Selector selector_;
std::vector<RequiredField> required_fields_; std::unique_ptr<RequiredFieldsFallbackHandler>
required_fields_fallback_handler_;
std::unique_ptr<BatchElementChecker> batch_element_checker_;
ProcessActionCallback process_action_callback_; ProcessActionCallback process_action_callback_;
base::WeakPtrFactory<UseAddressAction> weak_ptr_factory_{this}; base::WeakPtrFactory<UseAddressAction> weak_ptr_factory_{this};
......
...@@ -16,21 +16,23 @@ ...@@ -16,21 +16,23 @@
#include "components/autofill/core/browser/data_model/autofill_profile.h" #include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h" #include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h" #include "components/autofill_assistant/browser/actions/action_delegate.h"
#include "components/autofill_assistant/browser/batch_element_checker.h" #include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
#include "components/autofill_assistant/browser/client_memory.h" #include "components/autofill_assistant/browser/client_memory.h"
#include "components/autofill_assistant/browser/client_status.h" #include "components/autofill_assistant/browser/client_status.h"
namespace autofill_assistant { namespace autofill_assistant {
UseCreditCardAction::FallbackData::FallbackData() {} using RequiredField = RequiredFieldsFallbackHandler::RequiredField;
using FallbackData = RequiredFieldsFallbackHandler::FallbackData;
UseCreditCardAction::UseCreditCardAction(ActionDelegate* delegate, UseCreditCardAction::UseCreditCardAction(ActionDelegate* delegate,
const ActionProto& proto) const ActionProto& proto)
: Action(delegate, proto) { : Action(delegate, proto) {
DCHECK(proto.has_use_card()); DCHECK(proto.has_use_card());
prompt_ = proto.use_card().prompt(); prompt_ = proto.use_card().prompt();
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()) {
required_fields_.emplace_back(); required_fields.emplace_back();
RequiredField& required_field = required_fields_.back(); RequiredField& required_field = required_fields.back();
required_field.card_field = required_field_proto.card_field(); required_field.card_field = required_field_proto.card_field();
required_field.selector = Selector(required_field_proto.element()); required_field.selector = Selector(required_field_proto.element());
required_field.simulate_key_presses = required_field.simulate_key_presses =
...@@ -40,6 +42,14 @@ UseCreditCardAction::UseCreditCardAction(ActionDelegate* delegate, ...@@ -40,6 +42,14 @@ UseCreditCardAction::UseCreditCardAction(ActionDelegate* delegate,
required_field.forced = required_field_proto.forced(); required_field.forced = required_field_proto.forced();
} }
required_fields_fallback_handler_ =
std::make_unique<RequiredFieldsFallbackHandler>(
required_fields,
base::BindRepeating(&UseCreditCardAction::GetFallbackValue,
base::Unretained(this)),
base::BindOnce(&UseCreditCardAction::EndAction,
base::Unretained(this)),
delegate);
selector_ = Selector(proto.use_card().form_field_element()); selector_ = Selector(proto.use_card().form_field_element());
selector_.MustBeVisible(); selector_.MustBeVisible();
DCHECK(!selector_.empty()); DCHECK(!selector_.empty());
...@@ -54,17 +64,13 @@ void UseCreditCardAction::InternalProcessAction( ...@@ -54,17 +64,13 @@ void UseCreditCardAction::InternalProcessAction(
// Ensure data already selected in a previous action. // Ensure data already selected in a previous action.
auto* client_memory = delegate_->GetClientMemory(); auto* client_memory = delegate_->GetClientMemory();
if (!client_memory->has_selected_card()) { if (!client_memory->has_selected_card()) {
EndAction(PRECONDITION_FAILED); EndAction(ClientStatus(PRECONDITION_FAILED));
return; return;
} }
FillFormWithData(); FillFormWithData();
} }
void UseCreditCardAction::EndAction(ProcessedActionStatusProto status) {
EndAction(ClientStatus(status));
}
void UseCreditCardAction::EndAction(const ClientStatus& status) { void UseCreditCardAction::EndAction(const ClientStatus& status) {
UpdateProcessedAction(status); UpdateProcessedAction(status);
std::move(process_action_callback_).Run(std::move(processed_action_proto_)); std::move(process_action_callback_).Run(std::move(processed_action_proto_));
...@@ -78,7 +84,7 @@ void UseCreditCardAction::FillFormWithData() { ...@@ -78,7 +84,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(element_status.proto_status()); EndAction(ClientStatus(element_status.proto_status()));
return; return;
} }
...@@ -91,7 +97,7 @@ void UseCreditCardAction::OnGetFullCard( ...@@ -91,7 +97,7 @@ void UseCreditCardAction::OnGetFullCard(
std::unique_ptr<autofill::CreditCard> card, std::unique_ptr<autofill::CreditCard> card,
const base::string16& cvc) { const base::string16& cvc) {
if (!card) { if (!card) {
EndAction(GET_FULL_CARD_FAILED); EndAction(ClientStatus(GET_FULL_CARD_FAILED));
return; return;
} }
...@@ -118,154 +124,15 @@ void UseCreditCardAction::OnFormFilled( ...@@ -118,154 +124,15 @@ void UseCreditCardAction::OnFormFilled(
EndAction(status); EndAction(status);
return; return;
} }
CheckRequiredFields(std::move(fallback_data));
}
void UseCreditCardAction::CheckRequiredFields(
std::unique_ptr<FallbackData> fallback_data) {
// If there are no required fields, finish the action successfully.
if (required_fields_.empty()) {
EndAction(ACTION_APPLIED);
return;
}
DCHECK(!batch_element_checker_);
batch_element_checker_ = std::make_unique<BatchElementChecker>();
for (size_t i = 0; i < required_fields_.size(); i++) {
if (required_fields_[i].forced)
continue;
batch_element_checker_->AddFieldValueCheck(
required_fields_[i].selector,
base::BindOnce(&UseCreditCardAction::OnGetRequiredFieldValue,
weak_ptr_factory_.GetWeakPtr(), i));
}
batch_element_checker_->AddAllDoneCallback(
base::BindOnce(&UseCreditCardAction::OnCheckRequiredFieldsDone,
weak_ptr_factory_.GetWeakPtr(), std::move(fallback_data)));
delegate_->RunElementChecks(batch_element_checker_.get());
}
void UseCreditCardAction::OnGetRequiredFieldValue(
size_t required_fields_index,
const ClientStatus& element_status,
const std::string& value) {
required_fields_[required_fields_index].status =
value.empty() ? EMPTY : NOT_EMPTY;
}
void UseCreditCardAction::OnCheckRequiredFieldsDone(
std::unique_ptr<FallbackData> fallback_data) {
batch_element_checker_.reset();
// We process all fields with an empty value in order to perform the fallback
// on all those fields, if any.
bool should_fallback = false;
for (const RequiredField& required_field : required_fields_) {
if (required_field.ShouldFallback(fallback_data != nullptr)) {
should_fallback = true;
break;
}
}
if (!should_fallback) {
EndAction(ACTION_APPLIED);
return;
}
if (!fallback_data) {
// Validation failed and we don't want to try the fallback.
EndAction(MANUAL_FALLBACK);
return;
}
// If there are any fallbacks for the empty fields, set them, otherwise fail
// immediately.
bool has_fallbacks = false;
for (const RequiredField& required_field : required_fields_) {
if (!required_field.ShouldFallback(/* has_fallback_data= */ true))
continue;
if (!GetFallbackValue(required_field, *fallback_data).empty()) {
has_fallbacks = true;
}
}
if (!has_fallbacks) {
EndAction(MANUAL_FALLBACK);
return;
}
// Set the fallback values and check again.
SetFallbackFieldValuesSequentially(0, std::move(fallback_data));
}
void UseCreditCardAction::SetFallbackFieldValuesSequentially(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data) {
// Skip non-empty fields.
while (required_fields_index < required_fields_.size() &&
!required_fields_[required_fields_index].ShouldFallback(
fallback_data != nullptr)) {
required_fields_index++;
}
// If there are no more fields to set, check the required fields again,
// but this time we don't want to try the fallback in case of failure.
if (required_fields_index >= required_fields_.size()) {
DCHECK_EQ(required_fields_index, required_fields_.size());
CheckRequiredFields(/* fallback_data= */ nullptr);
return;
}
// Set the next field to its fallback value.
const RequiredField& required_field = required_fields_[required_fields_index];
std::string fallback_value = GetFallbackValue(required_field, *fallback_data);
if (fallback_value.empty()) {
DVLOG(3) << "No fallback for " << required_field.selector;
// If there is no fallback value, we skip this failed field.
SetFallbackFieldValuesSequentially(++required_fields_index,
std::move(fallback_data));
return;
}
DVLOG(3) << "Setting fallback value for " << required_field.selector;
delegate_->SetFieldValue( required_fields_fallback_handler_->CheckAndFallbackRequiredFields(
required_field.selector, fallback_value, std::move(fallback_data));
required_field.simulate_key_presses, required_field.delay_in_millisecond,
base::BindOnce(&UseCreditCardAction::OnSetFallbackFieldValue,
weak_ptr_factory_.GetWeakPtr(), required_fields_index,
std::move(fallback_data)));
}
void UseCreditCardAction::OnSetFallbackFieldValue(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data,
const ClientStatus& status) {
if (!status.ok()) {
// Fallback failed: we stop the script without checking the fields.
EndAction(MANUAL_FALLBACK);
return;
}
SetFallbackFieldValuesSequentially(++required_fields_index,
std::move(fallback_data));
} }
std::string UseCreditCardAction::GetFallbackValue( std::string UseCreditCardAction::GetFallbackValue(
const RequiredField& required_field, const RequiredField& required_field,
const FallbackData& fallback_data) { const FallbackData& fallback_data) {
if (required_field.card_field != auto field = required_field.card_field;
UseCreditCardProto::RequiredField::UNDEFINED) {
return GetCreditCardFieldValue(required_field.card_field, fallback_data);
}
NOTREACHED() << "Unsupported field type for " << required_field.selector;
return "";
}
std::string UseCreditCardAction::GetCreditCardFieldValue(
UseCreditCardProto::RequiredField::CardField field,
const FallbackData& fallback_data) {
switch (field) { switch (field) {
case UseCreditCardProto::RequiredField::CREDIT_CARD_VERIFICATION_CODE: case UseCreditCardProto::RequiredField::CREDIT_CARD_VERIFICATION_CODE:
return fallback_data.cvc; return fallback_data.cvc;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/actions/action.h" #include "components/autofill_assistant/browser/actions/action.h"
#include "components/autofill_assistant/browser/batch_element_checker.h" #include "components/autofill_assistant/browser/actions/required_fields_fallback_handler.h"
namespace autofill { namespace autofill {
class CreditCard; class CreditCard;
...@@ -30,46 +30,10 @@ class UseCreditCardAction : public Action { ...@@ -30,46 +30,10 @@ class UseCreditCardAction : public Action {
~UseCreditCardAction() override; ~UseCreditCardAction() override;
private: private:
enum FieldValueStatus { UNKNOWN, EMPTY, NOT_EMPTY };
struct RequiredField {
Selector selector;
bool simulate_key_presses = false;
int delay_in_millisecond = 0;
bool forced = false;
FieldValueStatus status = UNKNOWN;
// When filling in credit card, card_field must be set.
UseCreditCardProto::RequiredField::CardField card_field =
UseCreditCardProto::RequiredField::UNDEFINED;
// Returns true if fallback is required for this field.
bool ShouldFallback(bool has_fallback_data) const {
return status == EMPTY || (forced && has_fallback_data);
}
};
// Data necessary for filling in the fallback fields. This is kept in a
// separate struct to make sure we don't keep it for longer than strictly
// necessary.
struct FallbackData {
FallbackData();
~FallbackData() = default;
// Card information for UseCreditCard fallback.
std::string cvc;
std::string card_holder_name;
std::string card_number;
int expiration_year = 0;
int expiration_month = 0;
private:
DISALLOW_COPY_AND_ASSIGN(FallbackData);
};
// Overrides Action: // Overrides Action:
void InternalProcessAction(ProcessActionCallback callback) override; void InternalProcessAction(ProcessActionCallback callback) override;
void EndAction(ProcessedActionStatusProto status);
void EndAction(const ClientStatus& status); void EndAction(const ClientStatus& status);
// Fill the form using data in client memory. Return whether filling succeeded // Fill the form using data in client memory. Return whether filling succeeded
...@@ -82,57 +46,20 @@ class UseCreditCardAction : public Action { ...@@ -82,57 +46,20 @@ class UseCreditCardAction : public Action {
const base::string16& cvc); const base::string16& cvc);
// Called when the form credit card has been filled. // Called when the form credit card has been filled.
void OnFormFilled(std::unique_ptr<FallbackData> fallback_data, void OnFormFilled(std::unique_ptr<RequiredFieldsFallbackHandler::FallbackData>
fallback_data,
const ClientStatus& status); const ClientStatus& status);
// Check whether all required fields have a non-empty value. If it is the
// case, finish the action successfully. If it's not and |fallback_data|
// is null, fail the action. If |fallback_data| is non-null, use it to attempt
// to fill the failed fields without Autofill.
void CheckRequiredFields(std::unique_ptr<FallbackData> fallback_data);
// Triggers the check for a specific field.
void CheckRequiredFieldsSequentially(
bool allow_fallback,
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data);
// Updates |required_fields_value_status_|.
void OnGetRequiredFieldValue(size_t required_fields_index,
const ClientStatus& element_status,
const std::string& value);
// Called when all required fields have been checked.
void OnCheckRequiredFieldsDone(std::unique_ptr<FallbackData> fallback_data);
// Gets the fallback value. // Gets the fallback value.
std::string GetFallbackValue(const RequiredField& required_field, std::string GetFallbackValue(
const FallbackData& fallback_data); const RequiredFieldsFallbackHandler::RequiredField& required_field,
const RequiredFieldsFallbackHandler::FallbackData& fallback_data);
// Gets the value of |field| from |fallback_data|, if available. Returns an
// empty string otherwise.
std::string GetCreditCardFieldValue(
UseCreditCardProto::RequiredField::CardField field,
const FallbackData& fallback_data);
// Sets fallback field values for empty fields from
// |required_fields_value_status_|.
void SetFallbackFieldValuesSequentially(
size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data);
// Called after trying to set form values without Autofill in case of fallback
// after failed validation.
void OnSetFallbackFieldValue(size_t required_fields_index,
std::unique_ptr<FallbackData> fallback_data,
const ClientStatus& status);
std::string prompt_; std::string prompt_;
Selector selector_; Selector selector_;
std::vector<RequiredField> required_fields_; std::unique_ptr<RequiredFieldsFallbackHandler>
required_fields_fallback_handler_;
std::unique_ptr<BatchElementChecker> batch_element_checker_;
ProcessActionCallback process_action_callback_; ProcessActionCallback process_action_callback_;
base::WeakPtrFactory<UseCreditCardAction> weak_ptr_factory_{this}; base::WeakPtrFactory<UseCreditCardAction> weak_ptr_factory_{this};
......
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