blob: 67a5227db8cd793bc54c7d37b6e3d77b1c82973c [file] [log] [blame]
// 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.
#include "components/autofill_assistant/browser/user_data_util.h"
#include <numeric>
#include "base/callback.h"
#include "base/i18n/case_conversion.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/geo/address_i18n.h"
#include "components/autofill_assistant/browser/action_value.pb.h"
#include "components/autofill_assistant/browser/field_formatter.h"
#include "components/autofill_assistant/browser/url_utils.h"
#include "components/autofill_assistant/browser/website_login_manager.h"
#include "third_party/libaddressinput/chromium/addressinput_util.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
#include "third_party/re2/src/re2/re2.h"
namespace autofill_assistant {
namespace {
// TODO: Share this helper function with use_address_action.
std::u16string GetProfileFullName(const autofill::AutofillProfile& profile) {
return autofill::data_util::JoinNameParts(
profile.GetRawInfo(autofill::NAME_FIRST),
profile.GetRawInfo(autofill::NAME_MIDDLE),
profile.GetRawInfo(autofill::NAME_LAST));
}
int CountCompleteContactFields(const CollectUserDataOptions& options,
const autofill::AutofillProfile& profile) {
int completed_fields = 0;
if (options.request_payer_name && !GetProfileFullName(profile).empty()) {
++completed_fields;
}
if (options.request_shipping &&
!profile.GetRawInfo(autofill::ADDRESS_HOME_STREET_ADDRESS).empty()) {
++completed_fields;
}
if (options.request_payer_email &&
!profile.GetRawInfo(autofill::EMAIL_ADDRESS).empty()) {
++completed_fields;
}
if (options.request_payer_phone &&
!profile.GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER).empty()) {
++completed_fields;
}
return completed_fields;
}
// Helper function that compares instances of AutofillProfile by completeness
// in regards to the current options. Full profiles should be ordered before
// empty ones and fall back to compare the profile's name in case of equality.
bool CompletenessCompareContacts(const CollectUserDataOptions& options,
const autofill::AutofillProfile& a,
const autofill::AutofillProfile& b) {
int complete_fields_a = CountCompleteContactFields(options, a);
int complete_fields_b = CountCompleteContactFields(options, b);
if (complete_fields_a == complete_fields_b) {
return base::i18n::ToLower(GetProfileFullName(a))
.compare(base::i18n::ToLower(GetProfileFullName(b))) < 0;
}
return complete_fields_a > complete_fields_b;
}
int GetAddressCompletenessRating(const CollectUserDataOptions& options,
const autofill::AutofillProfile& profile) {
auto address_data =
autofill::i18n::CreateAddressDataFromAutofillProfile(profile, "en-US");
std::multimap<i18n::addressinput::AddressField,
i18n::addressinput::AddressProblem>
problems;
autofill::addressinput::ValidateRequiredFields(
*address_data, /* filter= */ nullptr, &problems);
return -problems.size();
}
// Helper function that compares instances of AutofillProfile by completeness
// in regards to the current options. Full profiles should be ordered before
// empty ones and fall back to compare the profile's name in case of equality.
bool CompletenessCompareAddresses(const CollectUserDataOptions& options,
const autofill::AutofillProfile& a,
const autofill::AutofillProfile& b) {
int complete_fields_a = GetAddressCompletenessRating(options, a);
int complete_fields_b = GetAddressCompletenessRating(options, b);
if (complete_fields_a == complete_fields_b) {
return base::i18n::ToLower(GetProfileFullName(a))
.compare(base::i18n::ToLower(GetProfileFullName(b))) < 0;
}
return complete_fields_a > complete_fields_b;
}
int CountCompletePaymentInstrumentFields(const CollectUserDataOptions& options,
const PaymentInstrument& instrument) {
int complete_fields = 0;
if (!instrument.card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL).empty()) {
++complete_fields;
}
if (!instrument.card->GetRawInfo(autofill::CREDIT_CARD_NUMBER).empty()) {
++complete_fields;
}
if (!instrument.card->GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH).empty()) {
++complete_fields;
}
if (!instrument.card->GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR)
.empty()) {
++complete_fields;
}
if (instrument.billing_address != nullptr) {
++complete_fields;
if (options.require_billing_postal_code &&
!instrument.billing_address->GetRawInfo(autofill::ADDRESS_HOME_ZIP)
.empty()) {
++complete_fields;
}
}
return complete_fields;
}
// Helper function that compares instances of PaymentInstrument by completeness
// in regards to the current options. Full payment instruments should be
// ordered before empty ones and fall back to compare the full name on the
// credit card in case of equality.
bool CompletenessComparePaymentInstruments(
const CollectUserDataOptions& options,
const PaymentInstrument& a,
const PaymentInstrument& b) {
int complete_fields_a = CountCompletePaymentInstrumentFields(options, a);
int complete_fields_b = CountCompletePaymentInstrumentFields(options, b);
if (complete_fields_a == complete_fields_b) {
return base::i18n::ToLower(
a.card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL))
.compare(base::i18n::ToLower(
b.card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL))) < 0;
}
return complete_fields_a > complete_fields_b;
}
bool IsCompleteAddress(const autofill::AutofillProfile* profile,
bool require_postal_code) {
if (!profile) {
return false;
}
// We use a hard coded locale here since we are only interested in whether
// fields are empty or not.
auto address_data =
autofill::i18n::CreateAddressDataFromAutofillProfile(*profile, "en-US");
if (!autofill::addressinput::HasAllRequiredFields(*address_data)) {
return false;
}
if (require_postal_code && address_data->postal_code.empty()) {
return false;
}
return true;
}
template <typename T>
ClientStatus ExtractProfileAndFormatAutofillValue(
const T& profile,
const std::string& value_expression,
const UserData* user_data,
bool quote_meta,
std::string* out_value) {
if (profile.identifier().empty() || value_expression.empty()) {
VLOG(1) << "|autofill_value| with empty "
"|profile.identifier| or |value_expression|";
return ClientStatus(INVALID_ACTION);
}
const autofill::AutofillProfile* address =
user_data->selected_address(profile.identifier());
if (address == nullptr) {
VLOG(1) << "Requested unknown address '" << profile.identifier() << "'";
return ClientStatus(PRECONDITION_FAILED);
}
auto mappings =
field_formatter::CreateAutofillMappings(*address,
/* locale= */ "en-US");
if (quote_meta) {
for (const auto& it : mappings) {
mappings[it.first] = re2::RE2::QuoteMeta(it.second);
}
}
auto value = field_formatter::FormatString(value_expression, mappings,
/* strict= */ true);
if (!value.has_value()) {
return ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE);
}
out_value->assign(*value);
return OkClientStatus();
}
void OnGetStoredPassword(
base::OnceCallback<void(const ClientStatus&, const std::string&)> callback,
bool success,
std::string password) {
if (!success) {
std::move(callback).Run(ClientStatus(AUTOFILL_INFO_NOT_AVAILABLE),
std::string());
return;
}
std::move(callback).Run(OkClientStatus(), password);
}
} // namespace
std::unique_ptr<autofill::AutofillProfile> MakeUniqueFromProfile(
const autofill::AutofillProfile& profile) {
auto unique_profile = std::make_unique<autofill::AutofillProfile>(profile);
// Temporary workaround so that fields like first/last name a properly
// populated.
unique_profile->FinalizeAfterImport();
return unique_profile;
}
std::vector<int> SortContactsByCompleteness(
const CollectUserDataOptions& collect_user_data_options,
const std::vector<std::unique_ptr<autofill::AutofillProfile>>& profiles) {
std::vector<int> profile_indices(profiles.size());
std::iota(std::begin(profile_indices), std::end(profile_indices), 0);
std::sort(profile_indices.begin(), profile_indices.end(),
[&collect_user_data_options, &profiles](int i, int j) {
return CompletenessCompareContacts(collect_user_data_options,
*profiles[i], *profiles[j]);
});
return profile_indices;
}
int GetDefaultContactProfile(
const CollectUserDataOptions& collect_user_data_options,
const std::vector<std::unique_ptr<autofill::AutofillProfile>>& profiles) {
if (profiles.empty()) {
return -1;
}
auto sorted_indices =
SortContactsByCompleteness(collect_user_data_options, profiles);
if (!collect_user_data_options.default_email.empty()) {
for (int index : sorted_indices) {
if (base::UTF16ToUTF8(
profiles[index]->GetRawInfo(autofill::EMAIL_ADDRESS)) ==
collect_user_data_options.default_email) {
return index;
}
}
}
return sorted_indices[0];
}
std::vector<int> SortAddressesByCompleteness(
const CollectUserDataOptions& collect_user_data_options,
const std::vector<std::unique_ptr<autofill::AutofillProfile>>& profiles) {
std::vector<int> profile_indices(profiles.size());
std::iota(std::begin(profile_indices), std::end(profile_indices), 0);
std::sort(profile_indices.begin(), profile_indices.end(),
[&collect_user_data_options, &profiles](int i, int j) {
return CompletenessCompareAddresses(collect_user_data_options,
*profiles[i], *profiles[j]);
});
return profile_indices;
}
int GetDefaultAddressProfile(
const CollectUserDataOptions& collect_user_data_options,
const std::vector<std::unique_ptr<autofill::AutofillProfile>>& profiles) {
if (profiles.empty()) {
return -1;
}
auto sorted_indices =
SortContactsByCompleteness(collect_user_data_options, profiles);
return sorted_indices[0];
}
std::vector<int> SortPaymentInstrumentsByCompleteness(
const CollectUserDataOptions& collect_user_data_options,
const std::vector<std::unique_ptr<PaymentInstrument>>&
payment_instruments) {
std::vector<int> payment_instrument_indices(payment_instruments.size());
std::iota(std::begin(payment_instrument_indices),
std::end(payment_instrument_indices), 0);
std::sort(payment_instrument_indices.begin(),
payment_instrument_indices.end(),
[&collect_user_data_options, &payment_instruments](int a, int b) {
return CompletenessComparePaymentInstruments(
collect_user_data_options, *payment_instruments[a],
*payment_instruments[b]);
});
return payment_instrument_indices;
}
int GetDefaultPaymentInstrument(
const CollectUserDataOptions& collect_user_data_options,
const std::vector<std::unique_ptr<PaymentInstrument>>&
payment_instruments) {
if (payment_instruments.empty()) {
return -1;
}
auto sorted_indices = SortPaymentInstrumentsByCompleteness(
collect_user_data_options, payment_instruments);
return sorted_indices[0];
}
bool CompareContactDetails(
const CollectUserDataOptions& collect_user_data_options,
const autofill::AutofillProfile* a,
const autofill::AutofillProfile* b) {
std::vector<autofill::ServerFieldType> types;
if (collect_user_data_options.request_payer_name) {
types.emplace_back(autofill::NAME_FULL);
types.emplace_back(autofill::NAME_FIRST);
types.emplace_back(autofill::NAME_MIDDLE);
types.emplace_back(autofill::NAME_LAST);
}
if (collect_user_data_options.request_payer_phone) {
types.emplace_back(autofill::PHONE_HOME_WHOLE_NUMBER);
}
if (collect_user_data_options.request_payer_email) {
types.emplace_back(autofill::EMAIL_ADDRESS);
}
if (types.empty()) {
return a->guid() == b->guid();
}
for (auto type : types) {
int comparison = a->GetRawInfo(type).compare(b->GetRawInfo(type));
if (comparison != 0) {
return false;
}
}
return true;
}
bool IsCompleteContact(
const autofill::AutofillProfile* profile,
const CollectUserDataOptions& collect_user_data_options) {
if (!collect_user_data_options.request_payer_name &&
!collect_user_data_options.request_payer_email &&
!collect_user_data_options.request_payer_phone) {
return true;
}
if (!profile) {
return false;
}
if (collect_user_data_options.request_payer_name &&
!profile->HasInfo(autofill::NAME_FULL)) {
return false;
}
if (collect_user_data_options.request_payer_email &&
!profile->HasInfo(autofill::EMAIL_ADDRESS)) {
return false;
}
if (collect_user_data_options.request_payer_phone &&
!profile->HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) {
return false;
}
return true;
}
bool IsCompleteShippingAddress(
const autofill::AutofillProfile* profile,
const CollectUserDataOptions& collect_user_data_options) {
return !collect_user_data_options.request_shipping ||
IsCompleteAddress(profile, /* require_postal_code = */ false);
}
bool IsCompleteCreditCard(
const autofill::CreditCard* credit_card,
const autofill::AutofillProfile* billing_profile,
const CollectUserDataOptions& collect_user_data_options) {
if (!collect_user_data_options.request_payment_method) {
return true;
}
if (!credit_card || !billing_profile ||
credit_card->billing_address_id().empty()) {
return false;
}
if (!IsCompleteAddress(
billing_profile,
collect_user_data_options.require_billing_postal_code)) {
return false;
}
if (credit_card->record_type() != autofill::CreditCard::MASKED_SERVER_CARD &&
!credit_card->HasValidCardNumber()) {
// Can't check validity of masked server card numbers because they are
// incomplete until decrypted.
return false;
}
if (!credit_card->HasValidExpirationDate()) {
return false;
}
std::string basic_card_network =
autofill::data_util::GetPaymentRequestData(credit_card->network())
.basic_card_issuer_network;
if (!collect_user_data_options.supported_basic_card_networks.empty() &&
std::find(collect_user_data_options.supported_basic_card_networks.begin(),
collect_user_data_options.supported_basic_card_networks.end(),
basic_card_network) ==
collect_user_data_options.supported_basic_card_networks.end()) {
return false;
}
return true;
}
ClientStatus GetFormattedAutofillValue(const AutofillValue& autofill_value,
const UserData* user_data,
std::string* out_value) {
return ExtractProfileAndFormatAutofillValue<AutofillValue::Profile>(
autofill_value.profile(), autofill_value.value_expression(), user_data,
/* quote_meta= */ false, out_value);
}
ClientStatus GetFormattedAutofillValue(
const AutofillValueRegexp& autofill_value,
const UserData* user_data,
std::string* out_value) {
return ExtractProfileAndFormatAutofillValue<AutofillValueRegexp::Profile>(
autofill_value.profile(), autofill_value.value_expression().re2(),
user_data, /* quote_meta= */ true, out_value);
}
void GetPasswordManagerValue(
const PasswordManagerValue& password_manager_value,
const ElementFinder::Result& target_element,
const UserData* user_data,
WebsiteLoginManager* website_login_manager,
base::OnceCallback<void(const ClientStatus&, const std::string&)>
callback) {
if (!user_data->selected_login_) {
std::move(callback).Run(ClientStatus(PRECONDITION_FAILED), std::string());
return;
}
if (!target_element.container_frame_host ||
!url_utils::IsSamePublicSuffixDomain(
target_element.container_frame_host->GetLastCommittedURL(),
user_data->selected_login_->origin)) {
std::move(callback).Run(ClientStatus(PASSWORD_ORIGIN_MISMATCH),
std::string());
return;
}
switch (password_manager_value.credential_type()) {
case PasswordManagerValue::PASSWORD:
website_login_manager->GetPasswordForLogin(
*user_data->selected_login_,
base::BindOnce(&OnGetStoredPassword, std::move(callback)));
return;
case PasswordManagerValue::USERNAME:
std::move(callback).Run(OkClientStatus(),
user_data->selected_login_->username);
return;
case PasswordManagerValue::NOT_SET:
std::move(callback).Run(ClientStatus(INVALID_ACTION), std::string());
return;
}
}
ClientStatus GetClientMemoryStringValue(const std::string& client_memory_key,
const UserData* user_data,
std::string* out_value) {
if (client_memory_key.empty()) {
return ClientStatus(INVALID_ACTION);
}
if (!user_data->has_additional_value(client_memory_key) ||
user_data->additional_value(client_memory_key)
->strings()
.values()
.size() != 1) {
VLOG(1) << "Requested key '" << client_memory_key
<< "' not available in client memory";
return ClientStatus(PRECONDITION_FAILED);
}
out_value->assign(
user_data->additional_value(client_memory_key)->strings().values(0));
return OkClientStatus();
}
} // namespace autofill_assistant