blob: 1029275952431c9418f36301008d21d3d2326f43 [file] [log] [blame]
// 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/use_credit_card_action.h"
#include <map>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/data_model/autofill_profile.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/fallback_handler/required_field.h"
#include "components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.h"
#include "components/autofill_assistant/browser/client_status.h"
#include "components/autofill_assistant/browser/field_formatter.h"
#include "components/autofill_assistant/browser/user_model.h"
namespace autofill_assistant {
UseCreditCardAction::UseCreditCardAction(ActionDelegate* delegate,
const ActionProto& proto)
: Action(delegate, proto) {
DCHECK(proto.has_use_card());
selector_ = Selector(proto.use_card().form_field_element());
}
UseCreditCardAction::~UseCreditCardAction() = default;
void UseCreditCardAction::InternalProcessAction(
ProcessActionCallback action_callback) {
process_action_callback_ = std::move(action_callback);
if (selector_.empty() && !proto_.use_card().skip_autofill()) {
VLOG(1) << "UseCreditCard failed: |selector| empty";
EndAction(ClientStatus(INVALID_ACTION));
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.
if (proto_.use_card().has_model_identifier()) {
if (proto_.use_card().model_identifier().empty()) {
VLOG(1) << "UseCreditCard failed: |model_identifier| set but empty";
EndAction(ClientStatus(INVALID_ACTION));
return;
}
auto credit_card_value = delegate_->GetUserModel()->GetValue(
proto_.use_card().model_identifier());
if (!credit_card_value.has_value()) {
VLOG(1) << "UseCreditCard failed: "
<< proto_.use_card().model_identifier()
<< " not found in user model";
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
if (credit_card_value->credit_cards().values().size() != 1) {
VLOG(1) << "UseCreditCard failed: expected single card for "
<< proto_.use_card().model_identifier() << ", but got "
<< *credit_card_value;
}
auto* credit_card = delegate_->GetUserModel()->GetCreditCard(
credit_card_value->credit_cards().values(0).guid());
if (credit_card == nullptr) {
VLOG(1) << "UseCreditCard failed: card not found for guid "
<< *credit_card_value;
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
credit_card_ = std::make_unique<autofill::CreditCard>(*credit_card);
} else {
auto* credit_card = delegate_->GetUserData()->selected_card_.get();
if (credit_card == nullptr) {
VLOG(1) << "UseCreditCard failed: card not found in user_data";
EndAction(ClientStatus(PRECONDITION_FAILED));
return;
}
credit_card_ = std::make_unique<autofill::CreditCard>(*credit_card);
}
DCHECK(credit_card_ != nullptr);
FillFormWithData();
}
void UseCreditCardAction::EndAction(const ClientStatus& status) {
if (fallback_handler_)
action_stopwatch_.TransferToWaitTime(fallback_handler_->TotalWaitTime());
UpdateProcessedAction(status);
std::move(process_action_callback_).Run(std::move(processed_action_proto_));
}
void UseCreditCardAction::FillFormWithData() {
if (selector_.empty()) {
DCHECK(proto_.use_card().skip_autofill());
OnWaitForElement(OkClientStatus());
return;
}
delegate_->ShortWaitForElementWithSlowWarning(
selector_,
base::BindOnce(&UseCreditCardAction::OnWaitForElementTimed,
weak_ptr_factory_.GetWeakPtr(),
base::BindOnce(&UseCreditCardAction::OnWaitForElement,
weak_ptr_factory_.GetWeakPtr())));
}
void UseCreditCardAction::OnWaitForElement(const ClientStatus& element_status) {
if (!element_status.ok()) {
EndAction(element_status);
return;
}
DCHECK(credit_card_ != nullptr);
delegate_->GetFullCard(credit_card_.get(),
base::BindOnce(&UseCreditCardAction::OnGetFullCard,
weak_ptr_factory_.GetWeakPtr()));
action_stopwatch_.StartWaitTime();
}
void UseCreditCardAction::OnGetFullCard(
const ClientStatus& status,
std::unique_ptr<autofill::CreditCard> card,
const std::u16string& cvc) {
action_stopwatch_.StartActiveTime();
if (!status.ok()) {
EndAction(status);
return;
}
DCHECK(card);
std::vector<RequiredField> required_fields;
for (const auto& required_field_proto : proto_.use_card().required_fields()) {
if (!required_field_proto.has_value_expression()) {
continue;
}
RequiredField required_field;
required_field.FromProto(required_field_proto);
required_fields.emplace_back(required_field);
}
std::map<std::string, std::string> fallback_values =
field_formatter::CreateAutofillMappings(*card,
/* locale = */ "en-US");
fallback_values.emplace(
base::NumberToString(
static_cast<int>(AutofillFormatProto::CREDIT_CARD_VERIFICATION_CODE)),
base::UTF16ToUTF8(cvc));
fallback_values.emplace(
base::NumberToString(
static_cast<int>(AutofillFormatProto::CREDIT_CARD_RAW_NUMBER)),
base::UTF16ToUTF8(card->GetRawInfo(autofill::CREDIT_CARD_NUMBER)));
DCHECK(fallback_handler_ == nullptr);
fallback_handler_ = std::make_unique<RequiredFieldsFallbackHandler>(
required_fields, fallback_values, delegate_);
if (proto_.use_card().skip_autofill()) {
ExecuteFallback(OkClientStatus());
return;
}
DCHECK(!selector_.empty());
delegate_->FillCardForm(std::move(card), cvc, selector_,
base::BindOnce(&UseCreditCardAction::ExecuteFallback,
weak_ptr_factory_.GetWeakPtr()));
}
void UseCreditCardAction::ExecuteFallback(const ClientStatus& status) {
DCHECK(fallback_handler_ != nullptr);
action_stopwatch_.TransferToWaitTime(fallback_handler_->TotalWaitTime());
fallback_handler_->CheckAndFallbackRequiredFields(
status, base::BindOnce(&UseCreditCardAction::EndAction,
weak_ptr_factory_.GetWeakPtr()));
}
} // namespace autofill_assistant