blob: 762657133ea54781f3025c7be7eb25103bbdc8f3 [file] [log] [blame]
// Copyright 2015 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/extensions/api/autofill_private/autofill_util.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/containers/fixed_flat_map.h"
#include "base/containers/to_vector.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/settings_private/prefs_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/autofill_private.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/branded_strings.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/country_type.h"
#include "components/autofill/core/browser/data_manager/addresses/address_data_manager.h"
#include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
#include "components/autofill/core/browser/data_model/addresses/autofill_profile.h"
#include "components/autofill/core/browser/data_model/addresses/autofill_structured_address_component.h"
#include "components/autofill/core/browser/data_model/autofill_ai/entity_type.h"
#include "components/autofill/core/browser/data_model/autofill_ai/entity_type_names.h"
#include "components/autofill/core/browser/data_model/payments/bnpl_issuer.h"
#include "components/autofill/core/browser/data_model/payments/credit_card.h"
#include "components/autofill/core/browser/data_model/payments/iban.h"
#include "components/autofill/core/browser/field_type_utils.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/geo/autofill_country.h"
#include "components/autofill/core/browser/payments/constants.h"
#include "components/autofill/core/browser/ui/country_combobox_model.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/autofill/core/common/credit_card_network_identifiers.h"
#include "components/prefs/pref_service.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/base/features.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/variations/service/variations_service.h"
#include "extensions/browser/extensions_browser_client.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/gfx/image/image.h"
namespace autofill_private = extensions::api::autofill_private;
namespace {
bool ShouldUseNewFopDisplay() {
#if BUILDFLAG(IS_ANDROID)
return false;
#else
return base::FeatureList::IsEnabled(
autofill::features::kAutofillEnableNewFopDisplayDesktop);
#endif
}
// Gets the string corresponding to |type| from |profile|.
std::string GetStringFromProfile(const autofill::AutofillProfile& profile,
const autofill::FieldType& type) {
return base::UTF16ToUTF8(profile.GetRawInfo(type));
}
// Converts AutofillProfile::RecordType enum to the WebUI idl one.
autofill_private::AddressRecordType ConvertProfileRecordType(
autofill::AutofillProfile::RecordType record_type) {
switch (record_type) {
case autofill::AutofillProfile::RecordType::kLocalOrSyncable:
return autofill_private::AddressRecordType::kLocalOrSyncable;
case autofill::AutofillProfile::RecordType::kAccount:
return autofill_private::AddressRecordType::kAccount;
case autofill::AutofillProfile::RecordType::kAccountHome:
return autofill_private::AddressRecordType::kAccountHome;
case autofill::AutofillProfile::RecordType::kAccountWork:
return autofill_private::AddressRecordType::kAccountWork;
case autofill::AutofillProfile::RecordType::kAccountNameEmail:
return autofill_private::AddressRecordType::kAccountNameEmail;
}
NOTREACHED();
}
autofill_private::AddressEntry ProfileToAddressEntry(
const autofill::AutofillProfile& profile,
const std::u16string& label) {
autofill_private::AddressEntry address;
// Add all address fields to the entry.
address.guid = profile.guid();
std::ranges::transform(
autofill::AutofillProfile::kDatabaseStoredTypes,
back_inserter(address.fields), [&profile](auto field_type) {
autofill_private::AddressField field;
field.type =
autofill_private::ParseFieldType(FieldTypeToStringView(field_type));
field.value = GetStringFromProfile(profile, field_type);
return field;
});
address.language_code = profile.language_code();
// Parse |label| so that it can be used to create address metadata.
std::u16string separator =
l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR);
std::vector<std::u16string> label_pieces = base::SplitStringUsingSubstr(
label, separator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
// Create address metadata and add it to |address|.
address.metadata.emplace();
address.metadata->summary_label = base::UTF16ToUTF8(label_pieces[0]);
address.metadata->summary_sublabel =
base::UTF16ToUTF8(label.substr(label_pieces[0].size()));
address.metadata->record_type =
ConvertProfileRecordType(profile.record_type());
return address;
}
std::string CardNetworkToIconResourceIdString(const std::string& network) {
if (ShouldUseNewFopDisplay()) {
static constexpr auto kNetworkToResourceIdStringMap =
base::MakeFixedFlatMap<std::string_view, std::string_view>(
{{autofill::kAmericanExpressCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_AMEX"},
{autofill::kDiscoverCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_DISCOVER"},
{autofill::kDinersCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_DINERS"},
{autofill::kEloCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_ELO"},
{autofill::kJCBCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_JCB"},
{autofill::kMasterCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_MASTERCARD"},
{autofill::kMirCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_MIR"},
{autofill::kTroyCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_TROY"},
{autofill::kUnionPay,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_UNIONPAY"},
{autofill::kVerveCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_VERVE"},
{autofill::kVisaCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_VISA"}});
auto it = kNetworkToResourceIdStringMap.find(network);
return it != kNetworkToResourceIdStringMap.end()
? std::string(it->second)
: "chrome://theme/IDR_AUTOFILL_METADATA_CC_GENERIC";
}
static constexpr auto kNetworkToResourceIdStringMap =
base::MakeFixedFlatMap<std::string_view, std::string_view>(
{{autofill::kDiscoverCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_DISCOVER_OLD"},
{autofill::kMasterCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_MASTERCARD_OLD"},
{autofill::kVisaCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_VISA_OLD"},
{autofill::kAmericanExpressCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_AMEX_OLD"},
{autofill::kDinersCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_DINERS_OLD"},
{autofill::kJCBCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_JCB_OLD"},
{autofill::kEloCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_ELO_OLD"},
{autofill::kMirCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_MIR_OLD"},
{autofill::kTroyCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_TROY_OLD"},
{autofill::kUnionPay,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_UNIONPAY_OLD"},
{autofill::kVerveCard,
"chrome://theme/IDR_AUTOFILL_METADATA_CC_VERVE_OLD"}});
auto it = kNetworkToResourceIdStringMap.find(network);
return it != kNetworkToResourceIdStringMap.end()
? std::string(it->second)
: "chrome://theme/IDR_AUTOFILL_METADATA_CC_GENERIC_OLD";
}
autofill_private::IbanEntry IbanToIbanEntry(const autofill::Iban& iban) {
autofill_private::IbanEntry iban_entry;
// Populated IBAN fields need to be converted to an `IbanEntry` to be rendered
// in the settings page.
bool is_local = iban.record_type() == autofill::Iban::RecordType::kLocalIban;
if (is_local) {
iban_entry.guid = iban.guid();
} else {
iban_entry.instrument_id = base::NumberToString(iban.instrument_id());
}
if (!iban.nickname().empty()) {
iban_entry.nickname = base::UTF16ToUTF8(iban.nickname());
}
iban_entry.value = base::UTF16ToUTF8(iban.value());
// Create IBAN metadata and add it to `iban_entry`.
iban_entry.metadata.emplace();
iban_entry.metadata->summary_label =
base::UTF16ToUTF8(iban.GetIdentifierStringForAutofillDisplay());
iban_entry.metadata->is_local = is_local;
return iban_entry;
}
std::pair<std::string, std::string> PayOverTimeIssuerToIconResourceIdString(
autofill::BnplIssuer::IssuerId issuer) {
switch (issuer) {
case autofill::BnplIssuer::IssuerId::kBnplAffirm:
return std::pair<std::string, std::string>(
"chrome://theme/IDR_AUTOFILL_AFFIRM_LINKED",
"chrome://theme/IDR_AUTOFILL_AFFIRM_LINKED_DARK");
case autofill::BnplIssuer::IssuerId::kBnplZip:
return std::pair<std::string, std::string>(
"chrome://theme/IDR_AUTOFILL_ZIP_LINKED",
"chrome://theme/IDR_AUTOFILL_ZIP_LINKED_DARK");
// TODO(crbug.com/408268581): Handle Afterpay issuer enum value when adding
// Afterpay to the BNPL flow.
case autofill::BnplIssuer::IssuerId::kBnplAfterpay:
return std::pair<std::string, std::string>(
"chrome://theme/IDR_AUTOFILL_METADATA_BNPL_GENERIC",
"chrome://theme/IDR_AUTOFILL_METADATA_BNPL_GENERIC");
case autofill::BnplIssuer::IssuerId::kBnplKlarna:
return std::pair<std::string, std::string>(
"chrome://theme/IDR_AUTOFILL_KLARNA_LINKED",
"chrome://theme/IDR_AUTOFILL_KLARNA_LINKED_DARK");
}
NOTREACHED();
}
autofill_private::PayOverTimeIssuerEntry BnplIssuerToPayOverTimeIssuerEntry(
const autofill::BnplIssuer& issuer) {
CHECK(issuer.payment_instrument());
autofill_private::PayOverTimeIssuerEntry issuer_entry;
issuer_entry.issuer_id =
autofill::ConvertToBnplIssuerIdString(issuer.issuer_id());
issuer_entry.instrument_id =
base::NumberToString(issuer.payment_instrument()->instrument_id());
issuer_entry.display_name = base::UTF16ToUTF8(issuer.GetDisplayName());
std::pair<std::string, std::string> issuer_icons =
PayOverTimeIssuerToIconResourceIdString(issuer.issuer_id());
issuer_entry.image_src = std::move(issuer_icons.first);
issuer_entry.image_src_dark = std::move(issuer_icons.second);
return issuer_entry;
}
} // namespace
namespace extensions::autofill_util {
AddressEntryList GenerateAddressList(const autofill::AddressDataManager& adm) {
const std::vector<const autofill::AutofillProfile*> profiles =
adm.GetProfilesForSettings();
std::vector<std::u16string> labels =
autofill::AutofillProfile::CreateDifferentiatingLabels(
profiles, ExtensionsBrowserClient::Get()->GetApplicationLocale());
DCHECK_EQ(labels.size(), profiles.size());
AddressEntryList list;
list.reserve(profiles.size());
for (size_t i = 0; i < profiles.size(); ++i) {
list.push_back(ProfileToAddressEntry(*profiles[i], labels[i]));
}
return list;
}
CountryEntryList GenerateCountryList() {
autofill::CountryComboboxModel model;
const variations::VariationsService* variations_service =
g_browser_process->variations_service();
model.SetCountries(
GeoIpCountryCode(variations_service
? variations_service->GetLatestCountry()
: std::string()),
extensions::ExtensionsBrowserClient::Get()->GetApplicationLocale());
const std::vector<std::unique_ptr<autofill::AutofillCountry>>& countries =
model.countries();
extensions::autofill_util::CountryEntryList list;
for (const auto& country : countries) {
// A null `country` means "insert a space here", so we add a country w/o a
// `name` or `country_code` to the list and let the UI handle it.
if (!country) {
list.emplace_back();
continue;
}
autofill_private::CountryEntry& entry = list.emplace_back();
entry.name = base::UTF16ToUTF8(country->name());
entry.country_code = country->country_code();
}
return list;
}
CreditCardEntryList GenerateCreditCardList(
const autofill::PaymentsDataManager& paydm) {
return base::ToVector(
paydm.GetCreditCards(), [&paydm](const autofill::CreditCard* card) {
return CreditCardToCreditCardEntry(*card, paydm,
/*mask_local_cards=*/true);
});
}
IbanEntryList GenerateIbanList(const autofill::PaymentsDataManager& paydm) {
return base::ToVector(paydm.GetIbans(), [](const autofill::Iban* iban) {
return IbanToIbanEntry(*iban);
});
}
PayOverTimeIssuerEntryList GeneratePayOverTimeIssuerList(
const autofill::PaymentsDataManager& paydm) {
std::vector<autofill::BnplIssuer> linked_issuers =
base::ToVector(paydm.GetLinkedBnplIssuers());
// Remove the issuer entry if a BNPL issuer is linked externally, due to
// missing terms of services acceptance.
linked_issuers.erase(
std::remove_if(
linked_issuers.begin(), linked_issuers.end(),
[](autofill::BnplIssuer& issuer) {
return issuer.payment_instrument()->action_required().contains(
autofill::PaymentInstrument::ActionRequired::kAcceptTos);
}),
linked_issuers.end());
return base::ToVector(linked_issuers, &BnplIssuerToPayOverTimeIssuerEntry);
}
std::optional<api::autofill_private::AccountInfo> GetAccountInfo(
const autofill::AddressDataManager& adm) {
std::optional<CoreAccountInfo> account = adm.GetPrimaryAccountInfo();
if (!account.has_value()) {
return std::nullopt;
}
api::autofill_private::AccountInfo api_account;
api_account.email = account->email;
// TODO(crbug.com/40066949): Remove `is_sync_enabled_for_autofill_profiles`
// from `AccountInfo` in favor of `is_autofill_sync_toggle_enabled` after
// Sync-the-feature users are migrated to ConsentLevel::kSignin.
api_account.is_sync_enabled_for_autofill_profiles =
base::FeatureList::IsEnabled(syncer::kReplaceSyncPromosWithSignInPromos)
? adm.IsAutofillUserSelectableTypeEnabled()
: adm.IsSyncFeatureEnabledForAutofill();
api_account.is_eligible_for_address_account_storage =
adm.IsEligibleForAddressAccountStorage();
api_account.is_autofill_sync_toggle_enabled =
adm.IsAutofillUserSelectableTypeEnabled();
api_account.is_autofill_sync_toggle_available =
adm.IsAutofillSyncToggleAvailable();
return std::move(api_account);
}
autofill_private::CreditCardEntry CreditCardToCreditCardEntry(
const autofill::CreditCard& credit_card,
const autofill::PaymentsDataManager& paydm,
bool mask_local_cards) {
autofill_private::CreditCardEntry card;
// Add all credit card fields to the entry.
card.guid =
credit_card.record_type() == autofill::CreditCard::RecordType::kLocalCard
? credit_card.guid()
: credit_card.server_id();
if (credit_card.record_type() ==
autofill::CreditCard::RecordType::kMaskedServerCard) {
card.instrument_id = base::NumberToString(credit_card.instrument_id());
}
card.name = base::UTF16ToUTF8(
credit_card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
std::string full_card_number =
base::UTF16ToUTF8(credit_card.GetRawInfo(autofill::CREDIT_CARD_NUMBER));
card.card_number =
(credit_card.record_type() ==
autofill::CreditCard::RecordType::kLocalCard &&
full_card_number.length() > 4 && mask_local_cards)
? full_card_number.substr(full_card_number.length() - 4)
: full_card_number;
card.expiration_month = base::UTF16ToUTF8(
credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH));
card.expiration_year = base::UTF16ToUTF8(
credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR));
card.network = base::UTF16ToUTF8(credit_card.NetworkForDisplay());
if (!credit_card.nickname().empty()) {
card.nickname = base::UTF16ToUTF8(credit_card.nickname());
}
const gfx::Image* card_art_image =
paydm.GetCachedCardArtImageForUrl(credit_card.card_art_url());
card.image_src =
card_art_image ? webui::GetBitmapDataUrl(card_art_image->AsBitmap())
: CardNetworkToIconResourceIdString(credit_card.network());
if (paydm.IsCardEligibleForBenefits(credit_card) &&
credit_card.product_terms_url().is_valid()) {
card.product_terms_url = credit_card.product_terms_url().spec();
}
// Create card metadata and add it to |card|.
card.metadata.emplace();
std::pair<std::u16string, std::u16string> label_pieces =
credit_card.LabelPieces();
card.metadata->summary_label = base::UTF16ToUTF8(label_pieces.first);
card.metadata->summary_sublabel = base::UTF16ToUTF8(label_pieces.second);
card.metadata->is_local =
credit_card.record_type() == autofill::CreditCard::RecordType::kLocalCard;
// IsValid() checks if both card number and expiration date are valid.
// IsServerCard() checks whether there is a duplicated server card in
// `paydm`.
card.metadata->is_migratable =
credit_card.IsValid() && !paydm.IsServerCard(&credit_card);
card.metadata->is_virtual_card_enrollment_eligible =
credit_card.virtual_card_enrollment_state() ==
autofill::CreditCard::VirtualCardEnrollmentState::kEnrolled ||
credit_card.virtual_card_enrollment_state() ==
autofill::CreditCard::VirtualCardEnrollmentState::
kUnenrolledAndEligible;
card.metadata->is_virtual_card_enrolled =
credit_card.virtual_card_enrollment_state() ==
autofill::CreditCard::VirtualCardEnrollmentState::kEnrolled;
if (!credit_card.cvc().empty()) {
// Replace all the chars in the CVC with "•" for security when
// the `credit_card` type is a `kMaskedServerCard` or `mask_local_cards` is
// true.
card.cvc = base::UTF16ToUTF8(credit_card.cvc());
if (credit_card.record_type() ==
autofill::CreditCard::RecordType::kMaskedServerCard ||
mask_local_cards) {
card.cvc = base::UTF16ToUTF8(std::u16string(card.cvc->size(), u'•'));
}
}
return card;
}
} // namespace extensions::autofill_util