// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/fast_checkout/fast_checkout_client_impl.h"
#include <cmath>
#include "base/containers/flat_set.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/metrics_hashes.h"
#include "base/strings/utf_string_conversions.h"
#include "base/uuid.h"
#include "chrome/browser/fast_checkout/fast_checkout_accessibility_service_impl.h"
#include "chrome/browser/fast_checkout/fast_checkout_capabilities_fetcher_factory.h"
#include "chrome/browser/fast_checkout/fast_checkout_delegate_impl.h"
#include "chrome/browser/fast_checkout/fast_checkout_personal_data_helper_impl.h"
#include "chrome/browser/fast_checkout/fast_checkout_trigger_validator_impl.h"
#include "chrome/grit/generated_resources.h"
#include "components/autofill/content/browser/content_autofill_client.h"
#include "components/autofill/content/browser/content_autofill_driver.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/metrics/autofill_metrics_utils.h"
#include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h"
#include "components/autofill/core/browser/payments/payments_autofill_client.h"
#include "components/autofill/core/browser/payments_data_manager.h"
#include "components/autofill/core/browser/ui/fast_checkout_enums.h"
#include "components/autofill/core/common/dense_set.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
#include "components/autofill/core/common/signatures.h"
#include "content/public/browser/web_contents_user_data.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
namespace {
using ::autofill::FastCheckoutRunOutcome;
using ::autofill::FastCheckoutTriggerOutcome;
using ::autofill::FastCheckoutUIState;
constexpr base::TimeDelta kSleepBetweenTriggerFormExtractionCalls =
base::Seconds(1);
constexpr base::TimeDelta kTimeout = base::Minutes(30);
constexpr auto kSupportedFormTypes = base::MakeFixedFlatSet<autofill::FormType>(
{autofill::FormType::kAddressForm, autofill::FormType::kCreditCardForm});
constexpr auto kAddressFieldTypes =
base::MakeFixedFlatSet<autofill::FieldTypeGroup>(
{autofill::FieldTypeGroup::kName, autofill::FieldTypeGroup::kEmail,
autofill::FieldTypeGroup::kPhone, autofill::FieldTypeGroup::kAddress});
bool IsVisibleTextField(const autofill::AutofillField& field) {
return field.IsFocusable() && field.IsTextInputElement();
}
autofill::AutofillField* GetFieldToFill(
const std::vector<std::unique_ptr<autofill::AutofillField>>& fields,
bool is_credit_card_form) {
for (const std::unique_ptr<autofill::AutofillField>& field : fields) {
if (IsVisibleTextField(*field) && field->IsEmpty() &&
((!is_credit_card_form &&
kAddressFieldTypes.contains(field->Type().group())) ||
(is_credit_card_form &&
field->Type().GetStorableType() == autofill::CREDIT_CARD_NUMBER))) {
return field.get();
}
}
return nullptr;
}
bool IsNameOrAddress(autofill::FieldTypeGroup type_group) {
return type_group == autofill::FieldTypeGroup::kName ||
type_group == autofill::FieldTypeGroup::kAddress;
}
// Returns `true` if `form` is considered an address form containing only an
// `email` field but no `name` or `address` fields.
bool IsEmailForm(const autofill::FormStructure& form) {
// `kAddressForm` includes email fields.
bool is_address_form =
form.GetFormTypes().contains(autofill::FormType::kAddressForm);
bool has_name_or_address_field = base::ranges::any_of(
form.fields().begin(), form.fields().end(),
[](const std::unique_ptr<autofill::AutofillField>& field) {
autofill::FieldTypeGroup type_group = field->Type().group();
return IsNameOrAddress(type_group) && IsVisibleTextField(*field);
});
bool has_focusable_email_field = base::ranges::any_of(
form.fields().begin(), form.fields().end(),
[](const std::unique_ptr<autofill::AutofillField>& field) {
return field->Type().group() == autofill::FieldTypeGroup::kEmail &&
IsVisibleTextField(*field);
});
return is_address_form && has_focusable_email_field &&
!has_name_or_address_field;
}
// Returns `true` if `form_signature`'s form is in `forms` and is an email form.
bool ContainsEmailFormWithSignature(
const std::map<autofill::FormGlobalId,
std::unique_ptr<autofill::FormStructure>>& forms,
autofill::FormSignature form_signature) {
for (auto& [_, form] : forms) {
// It is possible to have multiple forms with the same form signature on the
// same page where only some are visible to the user. An example could be
// shipping and billing address forms. For that reason the `IsEmailForm`
// check must not be returned directly to avoid a premature return as we
// don't have any control over the order of `forms`.
if (form->form_signature() == form_signature && IsEmailForm(*form)) {
return true;
}
}
return false;
}
FastCheckoutDelegateImpl* GetDelegate(autofill::AutofillManager& manager) {
auto& bam = static_cast<autofill::BrowserAutofillManager&>(manager);
return static_cast<FastCheckoutDelegateImpl*>(bam.fast_checkout_delegate());
}
} // namespace
// No virtual functions of `client` must be called in the constructor.
FastCheckoutClientImpl::FastCheckoutClientImpl(
autofill::ContentAutofillClient* client)
: autofill_client_(client),
fetcher_(FastCheckoutCapabilitiesFetcherFactory::GetForBrowserContext(
client->GetWebContents().GetBrowserContext())),
personal_data_helper_(
std::make_unique<FastCheckoutPersonalDataHelperImpl>(
&client->GetWebContents())),
trigger_validator_(std::make_unique<FastCheckoutTriggerValidatorImpl>(
autofill_client_,
fetcher_,
personal_data_helper_.get())),
accessibility_service_(
std::make_unique<FastCheckoutAccessibilityServiceImpl>()),
keyboard_suppressor_(
client,
base::BindRepeating([](autofill::AutofillManager& manager) {
return GetDelegate(manager) &&
GetDelegate(manager)->IsShowingFastCheckoutUI();
}),
base::BindRepeating([](autofill::AutofillManager& manager,
autofill::FormGlobalId form,
autofill::FieldGlobalId field,
const autofill::FormData& form_data) {
return GetDelegate(manager) &&
GetDelegate(manager)->IntendsToShowFastCheckout(
manager, form, field, form_data);
}),
base::Seconds(1)) {
driver_factory_observation_.Observe(
&autofill_client_->GetAutofillDriverFactory());
}
FastCheckoutClientImpl::~FastCheckoutClientImpl() = default;
void FastCheckoutClientImpl::OnContentAutofillDriverFactoryDestroyed(
autofill::ContentAutofillDriverFactory& factory) {
driver_factory_observation_.Reset();
}
void FastCheckoutClientImpl::OnContentAutofillDriverCreated(
autofill::ContentAutofillDriverFactory& factory,
autofill::ContentAutofillDriver& driver) {
auto& manager = static_cast<autofill::BrowserAutofillManager&>(
driver.GetAutofillManager());
manager.set_fast_checkout_delegate(std::make_unique<FastCheckoutDelegateImpl>(
&autofill_client_->GetWebContents(), this, &manager));
}
bool FastCheckoutClientImpl::TryToStart(
const GURL& url,
const autofill::FormData& form,
const autofill::FormFieldData& field,
base::WeakPtr<autofill::AutofillManager> autofill_manager) {
if (!keyboard_suppressor_.is_suppressing()) {
return false;
}
if (!autofill_manager) {
return false;
}
FastCheckoutTriggerOutcome trigger_outcome = trigger_validator_->ShouldRun(
form, field, fast_checkout_ui_state_, is_running_, *autofill_manager);
if (trigger_outcome != FastCheckoutTriggerOutcome::kSuccess) {
return false;
}
autofill_manager_ = autofill_manager;
origin_ = url::Origin::Create(url);
is_running_ = true;
personal_data_manager_observation_.Observe(
personal_data_helper_->GetPersonalDataManager());
autofill_manager_observation_.Observe(autofill_manager_.get());
run_id_ =
base::HashMetricName(base::Uuid::GenerateRandomV4().AsLowercaseString());
SetFormsToFill();
fast_checkout_controller_ = CreateFastCheckoutController();
ShowFastCheckoutUI();
fast_checkout_ui_state_ = FastCheckoutUIState::kIsShowing;
autofill_client_->HideAutofillSuggestions(
autofill::SuggestionHidingReason::kOverlappingWithFastCheckoutSurface);
return true;
}
void FastCheckoutClientImpl::ShowFastCheckoutUI() {
fast_checkout_controller_->Show(
personal_data_helper_->GetProfilesToSuggest(),
personal_data_helper_->GetCreditCardsToSuggest());
}
void FastCheckoutClientImpl::OnRunComplete(FastCheckoutRunOutcome run_outcome,
bool allow_further_runs) {
ukm::builders::FastCheckout_RunOutcome run_outcome_builder(
autofill_client_->GetWebContents()
.GetPrimaryMainFrame()
->GetPageUkmSourceId());
run_outcome_builder.SetRunOutcome(static_cast<int64_t>(run_outcome));
run_outcome_builder.SetRunId(run_id_);
run_outcome_builder.Record(ukm::UkmRecorder::Get());
if (autofill_manager_) {
for (auto [form_id, filling_state] : form_filling_states_) {
autofill::FormSignature form_signature = form_id.first;
autofill::DenseSet<autofill::FormTypeNameForLogging> form_types;
for (auto& [_, form] : autofill_manager_->form_structures()) {
if (form->form_signature() == form_signature) {
form_types =
autofill::autofill_metrics::GetFormTypesForLogging(*form);
break;
}
}
ukm::builders::FastCheckout_FormStatus form_status_builder(
autofill_client_->GetWebContents()
.GetPrimaryMainFrame()
->GetPageUkmSourceId());
form_status_builder.SetFilled(filling_state == FillingState::kFilled);
form_status_builder.SetFormSignature(
autofill::HashFormSignature(form_signature));
form_status_builder.SetRunId(run_id_);
form_status_builder.SetFormTypes(
autofill::AutofillMetrics::FormTypesToBitVector(form_types));
form_status_builder.Record(ukm::UkmRecorder::Get());
}
}
InternalStop(allow_further_runs);
}
void FastCheckoutClientImpl::InternalStop(bool allow_further_runs) {
// `OnHidden` is not called if the bottom sheet never managed to show,
// e.g. due to a failed onboarding. This ensures that keyboard suppression
// stops.
keyboard_suppressor_.Unsuppress();
// Reset run related state.
is_running_ = false;
form_filling_states_.clear();
form_signatures_to_fill_.clear();
selected_autofill_profile_guid_ = std::nullopt;
selected_credit_card_id_ = std::nullopt;
timeout_timer_.AbandonAndStop();
credit_card_form_global_id_ = std::nullopt;
run_id_ = 0;
// Reset UI related state.
fast_checkout_controller_.reset();
// Reset personal data manager observation.
personal_data_manager_observation_.Reset();
// Reset `autofill_manager_` and related objects.
form_extraction_timer_.AbandonAndStop();
autofill_manager_observation_.Reset();
autofill_manager_.reset();
if (!allow_further_runs) {
fast_checkout_ui_state_ = FastCheckoutUIState::kWasShown;
} else {
fast_checkout_ui_state_ = FastCheckoutUIState::kNotShownYet;
}
}
void FastCheckoutClientImpl::Stop(bool allow_further_runs) {
InternalStop(allow_further_runs || !IsShowing());
}
bool FastCheckoutClientImpl::IsShowing() const {
return fast_checkout_ui_state_ == FastCheckoutUIState::kIsShowing;
}
bool FastCheckoutClientImpl::IsRunning() const {
return is_running_;
}
std::unique_ptr<FastCheckoutController>
FastCheckoutClientImpl::CreateFastCheckoutController() {
return std::make_unique<FastCheckoutControllerImpl>(
&autofill_client_->GetWebContents(), this);
}
void FastCheckoutClientImpl::OnHidden() {
fast_checkout_ui_state_ = FastCheckoutUIState::kWasShown;
keyboard_suppressor_.Unsuppress();
}
void FastCheckoutClientImpl::OnOptionsSelected(
std::unique_ptr<autofill::AutofillProfile> selected_profile,
std::unique_ptr<autofill::CreditCard> selected_credit_card) {
OnHidden();
selected_autofill_profile_guid_ = selected_profile->guid();
if (autofill::CreditCard::IsLocalCard(selected_credit_card.get())) {
selected_credit_card_id_ = selected_credit_card->guid();
selected_credit_card_is_local_ = true;
} else {
selected_credit_card_id_ = selected_credit_card->server_id();
selected_credit_card_is_local_ = false;
}
timeout_timer_.Start(FROM_HERE, kTimeout,
base::BindOnce(&FastCheckoutClientImpl::OnRunComplete,
weak_ptr_factory_.GetWeakPtr(),
FastCheckoutRunOutcome::kTimeout,
/*allow_further_runs=*/true));
TryToFillForms();
autofill_manager_->TriggerFormExtractionInAllFrames(
base::BindOnce(&FastCheckoutClientImpl::OnTriggerFormExtractionFinished,
weak_ptr_factory_.GetWeakPtr()));
}
void FastCheckoutClientImpl::SetFormsToFill() {
if (!fetcher_) {
return;
}
DCHECK(form_filling_states_.empty());
DCHECK(form_signatures_to_fill_.empty());
form_signatures_to_fill_ = fetcher_->GetFormsToFill(origin_);
}
void FastCheckoutClientImpl::OnDismiss() {
OnRunComplete(FastCheckoutRunOutcome::kBottomsheetDismissed,
/*allow_further_runs=*/false);
}
void FastCheckoutClientImpl::OnPersonalDataChanged() {
if (!IsShowing()) {
return;
}
if (trigger_validator_->HasValidPersonalData() !=
FastCheckoutTriggerOutcome::kSuccess) {
OnRunComplete(FastCheckoutRunOutcome::kInvalidPersonalData,
/*allow_further_runs=*/false);
} else {
ShowFastCheckoutUI();
}
}
bool FastCheckoutClientImpl::AllFormsAreFilled() const {
return base::ranges::all_of(form_filling_states_.begin(),
form_filling_states_.end(),
[](const auto& pair) {
return pair.second == FillingState::kFilled;
}) &&
base::ranges::all_of(
form_signatures_to_fill_.begin(), form_signatures_to_fill_.end(),
[&](autofill::FormSignature form_signature) {
return form_filling_states_.contains(std::make_pair(
form_signature, autofill::FormType::kAddressForm)) ||
form_filling_states_.contains(std::make_pair(
form_signature, autofill::FormType::kCreditCardForm));
});
}
bool FastCheckoutClientImpl::IsFilling() const {
return IsRunning() && selected_autofill_profile_guid_ &&
selected_credit_card_id_;
}
void FastCheckoutClientImpl::OnAfterLoadedServerPredictions(
autofill::AutofillManager& manager) {
TryToFillForms();
}
void FastCheckoutClientImpl::OnTriggerFormExtractionFinished(bool success) {
if (!form_extraction_timer_.IsRunning()) {
// Trigger form (re-)extraction in all frames continuously until the run
// stops. That will eventually trigger this
// (`OnAfterLoadedServerPredictions()`) method.
form_extraction_timer_.Start(
FROM_HERE, kSleepBetweenTriggerFormExtractionCalls,
base::BindOnce(
&autofill::AutofillManager::TriggerFormExtractionInAllFrames,
autofill_manager_,
base::BindOnce(
&FastCheckoutClientImpl::OnTriggerFormExtractionFinished,
weak_ptr_factory_.GetWeakPtr())));
}
}
void FastCheckoutClientImpl::TryToFillForms() {
if (!IsFilling()) {
return;
}
SetFormFillingStates();
for (const auto& [form_global_id, form] :
autofill_manager_->form_structures()) {
if (ShouldFillForm(*form, autofill::FormType::kAddressForm)) {
autofill::AutofillField* field =
GetFieldToFill(form->fields(), /*is_credit_card_form=*/false);
const autofill::AutofillProfile* autofill_profile =
GetSelectedAutofillProfile();
if (field && autofill_profile) {
form_filling_states_[std::make_pair(form->form_signature(),
autofill::FormType::kAddressForm)] =
FillingState::kFilling;
auto* bam = static_cast<autofill::BrowserAutofillManager*>(
autofill_manager_.get());
bam->SetFastCheckoutRunId(autofill::FieldTypeGroup::kAddress, run_id_);
bam->FillOrPreviewProfileForm(
autofill::mojom::ActionPersistence::kFill, form->ToFormData(),
*field, *autofill_profile,
autofill::AutofillTriggerDetails(
autofill::AutofillTriggerSource::kFastCheckout));
}
}
if (ShouldFillForm(*form, autofill::FormType::kCreditCardForm)) {
autofill::AutofillField* field =
GetFieldToFill(form->fields(), /*is_credit_card_form=*/true);
autofill::CreditCard* credit_card = GetSelectedCreditCard();
if (field && !credit_card_form_global_id_ && credit_card) {
if (autofill::CreditCard::IsLocalCard(credit_card)) {
FillCreditCardForm(*form, *field, *credit_card, u"");
} else {
autofill::CreditCardCvcAuthenticator& cvc_authenticator =
autofill_client_->GetPaymentsAutofillClient()
->GetCvcAuthenticator();
credit_card_form_global_id_ = form_global_id;
cvc_authenticator.GetFullCardRequest()->GetFullCard(
*credit_card,
autofill::payments::PaymentsAutofillClient::UnmaskCardReason::
kAutofill,
weak_ptr_factory_.GetWeakPtr(),
cvc_authenticator.GetAsFullCardRequestUIDelegate(),
autofill_client_->GetLastCommittedPrimaryMainFrameOrigin());
}
}
}
}
}
void FastCheckoutClientImpl::FillCreditCardForm(
const autofill::FormStructure& form,
const autofill::FormFieldData& field,
const autofill::CreditCard& credit_card,
const std::u16string& cvc) {
form_filling_states_[std::make_pair(form.form_signature(),
autofill::FormType::kCreditCardForm)] =
FillingState::kFilling;
auto* bam =
static_cast<autofill::BrowserAutofillManager*>(autofill_manager_.get());
bam->SetFastCheckoutRunId(autofill::FieldTypeGroup::kCreditCard, run_id_);
bam->FillOrPreviewCreditCardForm(
autofill::mojom::ActionPersistence::kFill, form.ToFormData(), field,
credit_card, cvc,
{.trigger_source = autofill::AutofillTriggerSource::kFastCheckout});
}
const autofill::AutofillProfile*
FastCheckoutClientImpl::GetSelectedAutofillProfile() {
const autofill::AutofillProfile* autofill_profile =
personal_data_helper_->GetPersonalDataManager()
->address_data_manager()
.GetProfileByGUID(selected_autofill_profile_guid_.value());
if (!autofill_profile) {
OnRunComplete(FastCheckoutRunOutcome::kAutofillProfileDeleted);
}
return autofill_profile;
}
autofill::CreditCard* FastCheckoutClientImpl::GetSelectedCreditCard() {
autofill::CreditCard* credit_card = nullptr;
if (selected_credit_card_is_local_) {
credit_card = personal_data_helper_->GetPersonalDataManager()
->payments_data_manager()
.GetCreditCardByGUID(selected_credit_card_id_.value());
} else {
credit_card =
personal_data_helper_->GetPersonalDataManager()
->payments_data_manager()
.GetCreditCardByServerId(selected_credit_card_id_.value());
}
if (!credit_card) {
OnRunComplete(FastCheckoutRunOutcome::kCreditCardDeleted);
}
return credit_card;
}
void FastCheckoutClientImpl::SetFormFillingStates() {
for (const auto& [_, form] : autofill_manager_->form_structures()) {
// Only attempt to fill forms that were provided by the
// `FastCheckoutCapabilitiesFetcher`.
if (!form_signatures_to_fill_.contains(form->form_signature())) {
continue;
}
autofill::DenseSet<autofill::FormType> form_types = form->GetFormTypes();
for (autofill::FormType form_type : kSupportedFormTypes) {
// Only attempt to fill forms if they match `form_type`.
if (!form_types.contains(form_type)) {
continue;
}
auto form_id = std::make_pair(form->form_signature(), form_type);
if (!form_filling_states_.contains(form_id)) {
form_filling_states_[form_id] = FillingState::kNotFilled;
}
}
}
}
void FastCheckoutClientImpl::OnFullCardRequestSucceeded(
const autofill::payments::FullCardRequest& full_card_request,
const autofill::CreditCard& card,
const std::u16string& cvc) {
if (!IsFilling() || !credit_card_form_global_id_) {
return;
}
if (!autofill_manager_->form_structures().contains(
credit_card_form_global_id_.value())) {
credit_card_form_global_id_ = std::nullopt;
return;
}
const std::unique_ptr<autofill::FormStructure>& form =
autofill_manager_->form_structures().at(
credit_card_form_global_id_.value());
if (autofill::AutofillField* field =
GetFieldToFill(form->fields(), /*is_credit_card_form=*/true)) {
FillCreditCardForm(*form, *field, card, cvc);
}
credit_card_form_global_id_ = std::nullopt;
}
void FastCheckoutClientImpl::OnFullCardRequestFailed(
autofill::CreditCard::RecordType card_type,
autofill::payments::FullCardRequest::FailureType failure_type) {
if (!IsFilling() || !credit_card_form_global_id_) {
return;
}
if (failure_type ==
autofill::payments::FullCardRequest::FailureType::PROMPT_CLOSED) {
OnRunComplete(FastCheckoutRunOutcome::kCvcPopupClosed,
/*allow_further_runs=*/false);
} else {
OnRunComplete(FastCheckoutRunOutcome::kCvcPopupError,
/*allow_further_runs=*/false);
}
}
void FastCheckoutClientImpl::OnAfterDidFillAutofillFormData(
autofill::AutofillManager& manager,
autofill::FormGlobalId form_id) {
if (!IsFilling()) {
return;
}
UpdateFillingStates();
if (AllFormsAreFilled()) {
OnRunComplete(FastCheckoutRunOutcome::kSuccess,
/*allow_further_runs=*/false);
}
}
void FastCheckoutClientImpl::UpdateFillingStates() {
for (auto& [form_id, filling_state] : form_filling_states_) {
const auto& [form_signature, form_type] = form_id;
if (form_type == autofill::FormType::kAddressForm &&
filling_state == FillingState::kFilling) {
// Assume that if `OnAfterDidFillAutofillFormData()` is called while
// `this` is in filling mode and there's an address form in `kFilling`
// state that it got filled.
filling_state = FillingState::kFilled;
A11yAnnounce(form_signature, /*is_credit_card_form=*/false);
} else if (form_type == autofill::FormType::kCreditCardForm) {
auto address_form_id =
std::make_pair(form_signature, autofill::FormType::kAddressForm);
if (form_filling_states_.contains(address_form_id) &&
form_filling_states_[address_form_id] == FillingState::kFilling) {
// Assume that the address part was filled first if the corresponding
// form is both an address and a credit card form.
continue;
} else if (filling_state == FillingState::kFilling) {
// Assume that if `OnAfterDidFillAutofillFormData()` is called while
// `this` is in filling mode and there's a credit card form in
// `kFilling` state - while no address form of the same signature is in
// `kFilling` state - that it got filled.
filling_state = FillingState::kFilled;
A11yAnnounce(form_signature, /*is_credit_card_form=*/true);
}
}
}
}
void FastCheckoutClientImpl::A11yAnnounce(
autofill::FormSignature form_signature,
bool is_credit_card_form) {
if (is_credit_card_form) {
if (autofill::CreditCard* credit_card = GetSelectedCreditCard()) {
accessibility_service_->Announce(l10n_util::GetStringFUTF16(
IDS_FAST_CHECKOUT_A11Y_CREDIT_CARD_FORM_FILLED,
credit_card->HasNonEmptyValidNickname()
? credit_card->nickname()
: credit_card->NetworkAndLastFourDigits()));
}
return;
}
if (ContainsEmailFormWithSignature(autofill_manager_->form_structures(),
form_signature)) {
accessibility_service_->Announce(
l10n_util::GetStringUTF16(IDS_FAST_CHECKOUT_A11Y_EMAIL_FILLED));
} else if (const autofill::AutofillProfile* autofill_profile =
GetSelectedAutofillProfile()) {
accessibility_service_->Announce(l10n_util::GetStringFUTF16(
IDS_FAST_CHECKOUT_A11Y_ADDRESS_FORM_FILLED,
base::UTF8ToUTF16(autofill_profile->profile_label())));
}
}
void FastCheckoutClientImpl::OnAutofillManagerStateChanged(
autofill::AutofillManager& manager,
autofill::AutofillManager::LifecycleState old_state,
autofill::AutofillManager::LifecycleState new_state) {
using enum autofill::AutofillManager::LifecycleState;
switch (new_state) {
case kInactive:
case kActive:
break;
case kPendingReset:
if (IsShowing()) {
OnRunComplete(
FastCheckoutRunOutcome::kNavigationWhileBottomsheetWasShown);
} else {
OnRunComplete(FastCheckoutRunOutcome::kPageRefreshed);
}
break;
case kPendingDeletion:
if (IsRunning()) {
if (autofill_client_->GetWebContents().IsBeingDestroyed()) {
OnRunComplete(FastCheckoutRunOutcome::kTabClosed);
} else {
OnRunComplete(FastCheckoutRunOutcome::kAutofillManagerDestroyed);
}
return;
}
InternalStop(/*allow_further_runs=*/true);
break;
}
}
bool FastCheckoutClientImpl::ShouldFillForm(
const autofill::FormStructure& form,
autofill::FormType expected_form_type) const {
// Only attempt to fill forms that were provided by the
// `FastCheckoutCapabilitiesFetcher`.
if (!form_signatures_to_fill_.contains(form.form_signature())) {
return false;
}
// Only attempt to fill forms if they match `expected_form_type`.
if (!form.GetFormTypes().contains(expected_form_type)) {
return false;
}
// Attempt to fill forms once only.
return form_filling_states_.at(
std::make_pair(form.form_signature(), expected_form_type)) ==
FillingState::kNotFilled;
}
void FastCheckoutClientImpl::OnNavigation(const GURL& url,
bool is_cart_or_checkout_url) {
if (!IsRunning()) {
fast_checkout_ui_state_ = FastCheckoutUIState::kNotShownYet;
return;
}
if (url::Origin::Create(url) != origin_) {
OnRunComplete(FastCheckoutRunOutcome::kOriginChange);
} else if (!is_cart_or_checkout_url) {
OnRunComplete(FastCheckoutRunOutcome::kNonCheckoutPage);
}
}
FastCheckoutTriggerOutcome FastCheckoutClientImpl::CanRun(
const autofill::FormData& form,
const autofill::FormFieldData& field,
const autofill::AutofillManager& autofill_manager) const {
return trigger_validator_->ShouldRun(form, field, fast_checkout_ui_state_,
is_running_, autofill_manager);
}
bool FastCheckoutClientImpl::IsNotShownYet() const {
return fast_checkout_ui_state_ == FastCheckoutUIState::kNotShownYet;
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(FastCheckoutClientImpl);