| Index: components/payments/core/profile_util.cc
|
| diff --git a/components/payments/core/profile_util.cc b/components/payments/core/profile_util.cc
|
| index c1149c85ed8bffb1d96a792512f3b4d496bc9010..76b9634db096d71d3687793bd702c51ec4ca25ee 100644
|
| --- a/components/payments/core/profile_util.cc
|
| +++ b/components/payments/core/profile_util.cc
|
| @@ -5,32 +5,63 @@
|
| #include "components/payments/core/profile_util.h"
|
|
|
| #include <algorithm>
|
| +#include <memory>
|
|
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "components/autofill/core/browser/address_i18n.h"
|
| +#include "components/autofill/core/browser/autofill_country.h"
|
| #include "components/autofill/core/browser/autofill_profile.h"
|
| #include "components/autofill/core/browser/field_types.h"
|
| +#include "components/autofill/core/browser/validation.h"
|
| #include "components/payments/core/payment_options_provider.h"
|
| +#include "components/payments/core/payment_request_data_util.h"
|
| +#include "components/strings/grit/components_strings.h"
|
| +#include "third_party/libaddressinput/chromium/addressinput_util.h"
|
| +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
|
|
| namespace payments {
|
| -namespace profile_util {
|
|
|
| -std::vector<autofill::AutofillProfile*> FilterProfilesForContact(
|
| - const std::vector<autofill::AutofillProfile*>& profiles,
|
| +PaymentsProfileComparator::PaymentsProfileComparator(
|
| const std::string& app_locale,
|
| - const PaymentOptionsProvider& options) {
|
| + const PaymentOptionsProvider& options)
|
| + : autofill::AutofillProfileComparator(app_locale), options_(options) {}
|
| +
|
| +PaymentsProfileComparator::~PaymentsProfileComparator() {}
|
| +
|
| +PaymentsProfileComparator::ProfileFields
|
| +PaymentsProfileComparator::GetMissingProfileFields(
|
| + const autofill::AutofillProfile* profile) const {
|
| + if (!profile)
|
| + return kName | kPhone | kEmail | kAddress;
|
| +
|
| + if (!cache_.count(profile->guid())) {
|
| + cache_[profile->guid()] = ComputeMissingFields(profile);
|
| + } else {
|
| + // Cache hit. In debug mode, recompute and check that invalidation has
|
| + // occurred where necessary.
|
| + DCHECK_EQ(cache_[profile->guid()], ComputeMissingFields(profile))
|
| + << "Profiles must be invalidated when their contents change.";
|
| + }
|
| +
|
| + return cache_[profile->guid()];
|
| +}
|
| +
|
| +std::vector<autofill::AutofillProfile*>
|
| +PaymentsProfileComparator::FilterProfilesForContact(
|
| + const std::vector<autofill::AutofillProfile*>& profiles) const {
|
| // We will be removing profiles, so we operate on a copy.
|
| std::vector<autofill::AutofillProfile*> processed = profiles;
|
|
|
| - PaymentsProfileComparator comparator(app_locale, options);
|
| -
|
| // Stable sort, since profiles are expected to be passed in frecency order.
|
| std::stable_sort(
|
| processed.begin(), processed.end(),
|
| - std::bind(&PaymentsProfileComparator::IsContactMoreComplete, &comparator,
|
| + std::bind(&PaymentsProfileComparator::IsContactMoreComplete, this,
|
| std::placeholders::_1, std::placeholders::_2));
|
|
|
| auto it = processed.begin();
|
| while (it != processed.end()) {
|
| - if (comparator.GetContactCompletenessScore(*it) == 0) {
|
| + if (GetContactCompletenessScore(*it) == 0) {
|
| // Since profiles are sorted by completeness, this and any further
|
| // profiles can be discarded.
|
| processed.erase(it, processed.end());
|
| @@ -42,7 +73,7 @@ std::vector<autofill::AutofillProfile*> FilterProfilesForContact(
|
| // (< 10), so a more complicated algorithm would be overkill.
|
| if (std::find_if(processed.begin(), it,
|
| [&](autofill::AutofillProfile* prior) {
|
| - return comparator.IsContactEqualOrSuperset(*prior, **it);
|
| + return IsContactEqualOrSuperset(*prior, **it);
|
| }) != it) {
|
| // Remove the subset profile. |it| will point to the next element after
|
| // erasure.
|
| @@ -55,16 +86,9 @@ std::vector<autofill::AutofillProfile*> FilterProfilesForContact(
|
| return processed;
|
| }
|
|
|
| -PaymentsProfileComparator::PaymentsProfileComparator(
|
| - const std::string& app_locale,
|
| - const PaymentOptionsProvider& options)
|
| - : autofill::AutofillProfileComparator(app_locale), options_(options) {}
|
| -
|
| -PaymentsProfileComparator::~PaymentsProfileComparator() {}
|
| -
|
| bool PaymentsProfileComparator::IsContactEqualOrSuperset(
|
| const autofill::AutofillProfile& super,
|
| - const autofill::AutofillProfile& sub) {
|
| + const autofill::AutofillProfile& sub) const {
|
| if (options_.request_payer_name()) {
|
| if (sub.HasInfo(autofill::NAME_FULL) &&
|
| !super.HasInfo(autofill::NAME_FULL)) {
|
| @@ -93,31 +117,135 @@ bool PaymentsProfileComparator::IsContactEqualOrSuperset(
|
| }
|
|
|
| int PaymentsProfileComparator::GetContactCompletenessScore(
|
| - const autofill::AutofillProfile* profile) {
|
| + const autofill::AutofillProfile* profile) const {
|
| if (!profile)
|
| return 0;
|
|
|
| - return (options_.request_payer_name() &&
|
| - profile->HasInfo(autofill::NAME_FULL)) +
|
| - (options_.request_payer_phone() &&
|
| - profile->HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) +
|
| - (options_.request_payer_email() &&
|
| - profile->HasInfo(autofill::EMAIL_ADDRESS));
|
| + // Create a bitmask of the fields that are both present and required.
|
| + ProfileFields present =
|
| + ~GetMissingProfileFields(profile) & GetRequiredProfileFieldsForContact();
|
| +
|
| + // Count how many are set.
|
| + return !!(present & kName) + !!(present & kPhone) + !!(present & kEmail);
|
| }
|
|
|
| bool PaymentsProfileComparator::IsContactInfoComplete(
|
| - const autofill::AutofillProfile* profile) {
|
| - int desired_score = options_.request_payer_name() +
|
| - options_.request_payer_phone() +
|
| - options_.request_payer_email();
|
| - return GetContactCompletenessScore(profile) == desired_score;
|
| + const autofill::AutofillProfile* profile) const {
|
| + // Mask the fields that are missing with those that are requried. If any bits
|
| + // are set (i.e., the result is nonzero), then contact info is incomplete.
|
| + return !(GetMissingProfileFields(profile) &
|
| + GetRequiredProfileFieldsForContact());
|
| }
|
|
|
| bool PaymentsProfileComparator::IsContactMoreComplete(
|
| const autofill::AutofillProfile* p1,
|
| - const autofill::AutofillProfile* p2) {
|
| + const autofill::AutofillProfile* p2) const {
|
| return GetContactCompletenessScore(p1) > GetContactCompletenessScore(p2);
|
| }
|
|
|
| -} // namespace profile_util
|
| +base::string16 PaymentsProfileComparator::GetStringForMissingContactFields(
|
| + const autofill::AutofillProfile& profile) const {
|
| + return GetStringForMissingFields(GetMissingProfileFields(&profile) &
|
| + GetRequiredProfileFieldsForContact());
|
| +}
|
| +
|
| +bool PaymentsProfileComparator::IsShippingComplete(
|
| + const autofill::AutofillProfile* profile) const {
|
| + // Mask the fields that are missing with those that are requried. If any bits
|
| + // are set (i.e., the result is nonzero), then shipping is incomplete.
|
| + return !(GetMissingProfileFields(profile) &
|
| + GetRequiredProfileFieldsForShipping());
|
| +}
|
| +
|
| +base::string16 PaymentsProfileComparator::GetStringForMissingShippingFields(
|
| + const autofill::AutofillProfile& profile) const {
|
| + return GetStringForMissingFields(GetMissingProfileFields(&profile) &
|
| + GetRequiredProfileFieldsForShipping());
|
| +}
|
| +
|
| +void PaymentsProfileComparator::Invalidate(
|
| + const autofill::AutofillProfile& profile) {
|
| + cache_.erase(profile.guid());
|
| +}
|
| +
|
| +PaymentsProfileComparator::ProfileFields
|
| +PaymentsProfileComparator::ComputeMissingFields(
|
| + const autofill::AutofillProfile* profile) const {
|
| + ProfileFields missing = 0;
|
| +
|
| + if (!profile->HasInfo(autofill::NAME_FULL))
|
| + missing |= kName;
|
| +
|
| + // Determine the country code to use when validating the phone number. Use
|
| + // the profile's country if it has one, or the code for the app locale
|
| + // otherwise. Note that international format numbers will always work--this
|
| + // is just the region that will be used to check if the number is
|
| + // potentially in a local format.
|
| + std::string country =
|
| + data_util::GetCountryCodeWithFallback(profile, app_locale());
|
| +
|
| + base::string16 phone = profile->GetInfo(
|
| + autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER), app_locale());
|
| + if (!autofill::IsValidPhoneNumber(phone, country))
|
| + missing |= kPhone;
|
| +
|
| + base::string16 email = profile->GetInfo(
|
| + autofill::AutofillType(autofill::EMAIL_ADDRESS), app_locale());
|
| + if (!autofill::IsValidEmailAddress(email))
|
| + missing |= kEmail;
|
| +
|
| + if (!AreRequiredAddressFieldsPresent(*profile))
|
| + missing |= kAddress;
|
| +
|
| + return missing;
|
| +}
|
| +
|
| +PaymentsProfileComparator::ProfileFields
|
| +PaymentsProfileComparator::GetRequiredProfileFieldsForContact() const {
|
| + ProfileFields required = 0;
|
| + if (options_.request_payer_name())
|
| + required |= kName;
|
| + if (options_.request_payer_phone())
|
| + required |= kPhone;
|
| + if (options_.request_payer_email())
|
| + required |= kEmail;
|
| + return required;
|
| +}
|
| +
|
| +PaymentsProfileComparator::ProfileFields
|
| +PaymentsProfileComparator::GetRequiredProfileFieldsForShipping() const {
|
| + return options_.request_shipping() ? (kAddress | kName | kPhone) : 0;
|
| +}
|
| +
|
| +base::string16 PaymentsProfileComparator::GetStringForMissingFields(
|
| + PaymentsProfileComparator::ProfileFields fields) const {
|
| + switch (fields) {
|
| + case 0:
|
| + // No bits are set, so no fields are missing.
|
| + return base::string16();
|
| + case kName:
|
| + return l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_REQUIRED);
|
| + case kPhone:
|
| + return l10n_util::GetStringUTF16(IDS_PAYMENTS_PHONE_NUMBER_REQUIRED);
|
| + case kEmail:
|
| + return l10n_util::GetStringUTF16(IDS_PAYMENTS_EMAIL_REQUIRED);
|
| + case kAddress:
|
| + return l10n_util::GetStringUTF16(IDS_PAYMENTS_INVALID_ADDRESS);
|
| + default:
|
| + // Either multiple bits are set (likely) or one bit that doesn't
|
| + // correspond to a named constant is set (shouldn't happen). Return a
|
| + // generic "More information" message.
|
| + return l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED);
|
| + }
|
| +}
|
| +
|
| +bool PaymentsProfileComparator::AreRequiredAddressFieldsPresent(
|
| + const autofill::AutofillProfile& profile) const {
|
| + std::unique_ptr<::i18n::addressinput::AddressData> data =
|
| + autofill::i18n::CreateAddressDataFromAutofillProfile(profile,
|
| + app_locale());
|
| +
|
| + return autofill::addressinput::HasAllRequiredFields(*data);
|
| +}
|
| +
|
| } // namespace payments
|
|
|