chromium/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl.h

// 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.

#ifndef CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ANDROID_TOUCH_TO_FILL_DELEGATE_ANDROID_IMPL_H_
#define CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ANDROID_TOUCH_TO_FILL_DELEGATE_ANDROID_IMPL_H_

#include <vector>

#include "base/memory/weak_ptr.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/data_model/iban.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/ui/fast_checkout_client.h"
#include "components/autofill/core/browser/ui/touch_to_fill_delegate.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"

namespace autofill {

// Enum that describes different outcomes to an attempt of triggering the
// Touch To Fill bottom sheet for credit cards or IBANs.
// The enum values are not exhaustive to avoid excessive metric collection.
// The cases where TTF is not shown because of other form type (not payment
// method) or TTF being not supported are skipped.
// Do not remove or renumber entries in this enum. It needs to be kept in
// sync with the enum of the same name in `enums.xml`.
enum class TouchToFillPaymentMethodTriggerOutcome {
  // The sheet was shown.
  kShown = 0,
  // The sheet was not shown because the clicked field was not focusable or
  // already had a value.
  kFieldNotEmptyOrNotFocusable = 1,
  // The sheet was not shown because there were no valid credit cards or IBANs
  // to suggest.
  kNoValidPaymentMethods = 2,
  // The sheet was not shown because either the client or the form was not
  // secure.
  kFormOrClientNotSecure = 3,
  // The sheet was not shown because it has already been shown before.
  kShownBefore = 4,
  // The sheet was not shown because Autofill UI cannot be shown.
  kCannotShowAutofillUi = 5,
  // There was a try to display the bottom sheet, but it failed due to unknown
  // reason.
  // A particular potential reason is that the keyboard is not suppressed
  // (anymore). This can happen due to race conditions involving the form parse
  // that may happen between
  // OnBeforeAskForValuesToFill() and TryToShowTouchToFill(). In particular, the
  // focused field's type may change (changing DryRun()'s value from a
  // non-`kShown` value to `kShown`).
  kFailedToDisplayBottomSheet = 6,
  // The sheet was not shown because the payment form was incomplete.
  kIncompleteForm = 7,
  // The form or field is not known to the form cache.
  kUnknownForm = 8,
  // The form is known to the form cache, but it doesn't contain the field.
  kUnknownField = 9,
  // TouchToFill is not supported for this field type. This value is not logged
  // to UMA.
  kUnsupportedFieldType = 10,
  // Fast Checkout was shown before TouchToFill could be triggered.
  kFastCheckoutWasShown = 11,
  // Form is considered to be already filled if fields of payment method info
  // already have non-empty values.
  kFormAlreadyFilled = 12,
  kMaxValue = kFormAlreadyFilled
};

inline constexpr const char kUmaTouchToFillCreditCardTriggerOutcome[] =
    "Autofill.TouchToFill.CreditCard.TriggerOutcome";
inline constexpr const char kUmaTouchToFillIbanTriggerOutcome[] =
    "Autofill.TouchToFill.Iban.TriggerOutcome";

class BrowserAutofillManager;
class FormStructure;

// Delegate for in-browser Touch To Fill (TTF) surface display and selection.
// Currently TTF surface is eligible for credit card and IBAN forms on click
// on an empty focusable field.
//
// If the surface was shown once, it won't be triggered again on the same page.
// But calling |Reset()| on navigation restores such showing eligibility.
//
// Due to asynchronous parsing, showing the TTF surface proceeds in two stages:
// IntendsToShowTouchToFill() is called before parsing, and only if this returns
// true, TryToShowTouchToFill() is called after parsing. This is necessary for
// the keyboard suppression mechanism to work; see
// `TouchToFillKeyboardSuppressor` for details.
//
// It is supposed to be owned by the given |BrowserAutofillManager|, and
// interact with it and its |AutofillClient| and |AutofillDriver|.
//
// TODO(crbug.com/40839529): Consider using more descriptive name.
class TouchToFillDelegateAndroidImpl : public TouchToFillDelegate {
 public:
  explicit TouchToFillDelegateAndroidImpl(BrowserAutofillManager* manager);
  TouchToFillDelegateAndroidImpl(const TouchToFillDelegateAndroidImpl&) =
      delete;
  TouchToFillDelegateAndroidImpl& operator=(
      const TouchToFillDelegateAndroidImpl&) = delete;
  ~TouchToFillDelegateAndroidImpl() override;

  // Checks whether TTF is eligible for the given web form data.
  // Only if this is true, the controller will show the view.
  bool IntendsToShowTouchToFill(FormGlobalId form_id,
                                FieldGlobalId field_id,
                                const FormData& form) override;

  // Checks whether TTF is eligible for the given web form data and, if
  // successful, triggers the corresponding surface and returns |true|.
  bool TryToShowTouchToFill(const FormData& form,
                            const FormFieldData& field) override;

  // Returns whether the TTF surface is currently being shown.
  bool IsShowingTouchToFill() override;

  // Hides the TTF surface if one is shown.
  void HideTouchToFill() override;

  // Resets the delegate to its starting state (e.g. on navigation).
  void Reset() override;

  // TouchToFillDelegate:
  AutofillManager* GetManager() override;
  bool ShouldShowScanCreditCard() override;
  void ScanCreditCard() override;
  void OnCreditCardScanned(const CreditCard& card) override;
  void ShowPaymentMethodSettings() override;
  void CreditCardSuggestionSelected(std::string unique_id,
                                    bool is_virtual) override;
  void IbanSuggestionSelected(
      absl::variant<Iban::Guid, Iban::InstrumentId> backend_id) override;
  void OnDismissed(bool dismissed_by_user) override;

  void LogMetricsAfterSubmission(const FormStructure& submitted_form) override;

  base::WeakPtr<TouchToFillDelegateAndroidImpl> GetWeakPtr();

 private:
  enum class TouchToFillState {
    kShouldShow,
    kIsShowing,
    kWasShown,
  };

  using TriggerOutcome = TouchToFillPaymentMethodTriggerOutcome;

  struct DryRunResult {
    DryRunResult(TriggerOutcome outcome,
                 absl::variant<std::vector<CreditCard>, std::vector<Iban>>
                     items_to_suggest);
    DryRunResult(DryRunResult&&);
    DryRunResult& operator=(DryRunResult&&);
    ~DryRunResult();

    TriggerOutcome outcome;
    absl::variant<std::vector<CreditCard>, std::vector<Iban>> items_to_suggest;
  };

  // Checks all preconditions for showing the TTF, that is, for calling
  // PaymentsAutofillClient::ShowTouchToFillCreditCard().
  //
  // If the DryRunResult::outcome is TriggerOutcome::kShow, the
  // DryRun::cards_to_suggest contains the cards; otherwise it is empty.
  // TODO(crbug.com/40282650): Remove received FormData. received_form is the
  // form received from the renderer, so it contains the current values. This is
  // needed for the non-empty checks.
  DryRunResult DryRun(FormGlobalId form_id,
                      FieldGlobalId field_id,
                      const FormData& received_form);

  // Returns a DryRunResult with the user's fillable IBANs, or
  // `kNoValidPaymentMethods` if no IBANs are available.
  DryRunResult DryRunForIban();

  // Returns a DryRunResult with the user's fillable credit cards, or
  // an error reason if TTF should not be triggered.
  DryRunResult DryRunForCreditCard(const AutofillField& field,
                                   const FormStructure& form,
                                   const FormData& received_form);

  bool HasAnyAutofilledFields(const FormStructure& submitted_form) const;

  // The form is considered perfectly filled if all non-empty fields are
  // autofilled without further edits.
  bool IsFillingPerfect(const FormStructure& submitted_form) const;

  // The form is considered correctly filled if all autofilled fields were not
  // edited by user afterwards.
  bool IsFillingCorrect(const FormStructure& submitted_form) const;

  // Checks if the credit card form is already filled with values. The form is
  // considered to be filled if the credit card number field is non-empty. The
  // expiration date fields are not checked because they might have arbitrary
  // placeholders.
  // TODO(crbug.com/40227496): FormData is used here to ensure that we check the
  // most recent form values. FormStructure knows only about the initial values.
  bool IsFormPrefilled(const FormData& form);

  // Creates a list of booleans which denotes if credit cards are acceptable by
  // the merchant. The list will be the same size as `credit_cards`, and the
  // indices will match (the acceptability of credit_cards[i] ==
  // card_acceptability[i]).
  std::vector<bool> GetCardAcceptabilities(
      base::span<const CreditCard> credit_cards);

  TouchToFillState ttf_payment_method_state_ = TouchToFillState::kShouldShow;

  const raw_ptr<BrowserAutofillManager> manager_;
  FormData query_form_;
  FormFieldData query_field_;
  bool dismissed_by_user_ = false;

  base::WeakPtrFactory<TouchToFillDelegateAndroidImpl> weak_ptr_factory_{this};
};

}  // namespace autofill

#endif  // CHROME_BROWSER_TOUCH_TO_FILL_AUTOFILL_ANDROID_TOUCH_TO_FILL_DELEGATE_ANDROID_IMPL_H_