OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/payments/core/payments_profile_comparator.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <memory> |
| 9 |
| 10 #include "base/strings/utf_string_conversions.h" |
| 11 #include "components/autofill/core/browser/address_i18n.h" |
| 12 #include "components/autofill/core/browser/autofill_country.h" |
| 13 #include "components/autofill/core/browser/autofill_profile.h" |
| 14 #include "components/autofill/core/browser/field_types.h" |
| 15 #include "components/autofill/core/browser/validation.h" |
| 16 #include "components/payments/core/payment_options_provider.h" |
| 17 #include "components/payments/core/payment_request_data_util.h" |
| 18 #include "components/strings/grit/components_strings.h" |
| 19 #include "third_party/libaddressinput/chromium/addressinput_util.h" |
| 20 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_da
ta.h" |
| 21 #include "ui/base/l10n/l10n_util.h" |
| 22 |
| 23 namespace payments { |
| 24 |
| 25 PaymentsProfileComparator::PaymentsProfileComparator( |
| 26 const std::string& app_locale, |
| 27 const PaymentOptionsProvider& options) |
| 28 : autofill::AutofillProfileComparator(app_locale), options_(options) {} |
| 29 |
| 30 PaymentsProfileComparator::~PaymentsProfileComparator() {} |
| 31 |
| 32 PaymentsProfileComparator::ProfileFields |
| 33 PaymentsProfileComparator::GetMissingProfileFields( |
| 34 const autofill::AutofillProfile* profile) const { |
| 35 if (!profile) |
| 36 return kName | kPhone | kEmail | kAddress; |
| 37 |
| 38 if (!cache_.count(profile->guid())) { |
| 39 cache_[profile->guid()] = ComputeMissingFields(*profile); |
| 40 } else { |
| 41 // Cache hit. In debug mode, recompute and check that invalidation has |
| 42 // occurred where necessary. |
| 43 DCHECK_EQ(cache_[profile->guid()], ComputeMissingFields(*profile)) |
| 44 << "Profiles must be invalidated when their contents change."; |
| 45 } |
| 46 |
| 47 return cache_[profile->guid()]; |
| 48 } |
| 49 |
| 50 std::vector<autofill::AutofillProfile*> |
| 51 PaymentsProfileComparator::FilterProfilesForContact( |
| 52 const std::vector<autofill::AutofillProfile*>& profiles) const { |
| 53 // We will be removing profiles, so we operate on a copy. |
| 54 std::vector<autofill::AutofillProfile*> processed = profiles; |
| 55 |
| 56 // Stable sort, since profiles are expected to be passed in frecency order. |
| 57 std::stable_sort( |
| 58 processed.begin(), processed.end(), |
| 59 std::bind(&PaymentsProfileComparator::IsContactMoreComplete, this, |
| 60 std::placeholders::_1, std::placeholders::_2)); |
| 61 |
| 62 auto it = processed.begin(); |
| 63 while (it != processed.end()) { |
| 64 if (GetContactCompletenessScore(*it) == 0) { |
| 65 // Since profiles are sorted by completeness, this and any further |
| 66 // profiles can be discarded. |
| 67 processed.erase(it, processed.end()); |
| 68 break; |
| 69 } |
| 70 |
| 71 // Attempt to find a matching element in the vector before the current. |
| 72 // This is quadratic, but the number of elements is generally small |
| 73 // (< 10), so a more complicated algorithm would be overkill. |
| 74 if (std::find_if(processed.begin(), it, |
| 75 [&](autofill::AutofillProfile* prior) { |
| 76 return IsContactEqualOrSuperset(*prior, **it); |
| 77 }) != it) { |
| 78 // Remove the subset profile. |it| will point to the next element after |
| 79 // erasure. |
| 80 it = processed.erase(it); |
| 81 } else { |
| 82 it++; |
| 83 } |
| 84 } |
| 85 |
| 86 return processed; |
| 87 } |
| 88 |
| 89 bool PaymentsProfileComparator::IsContactEqualOrSuperset( |
| 90 const autofill::AutofillProfile& super, |
| 91 const autofill::AutofillProfile& sub) const { |
| 92 if (options_.request_payer_name()) { |
| 93 if (sub.HasInfo(autofill::NAME_FULL) && |
| 94 !super.HasInfo(autofill::NAME_FULL)) { |
| 95 return false; |
| 96 } |
| 97 if (!HaveMergeableNames(super, sub)) |
| 98 return false; |
| 99 } |
| 100 if (options_.request_payer_phone()) { |
| 101 if (sub.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER) && |
| 102 !super.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) { |
| 103 return false; |
| 104 } |
| 105 if (!HaveMergeablePhoneNumbers(super, sub)) |
| 106 return false; |
| 107 } |
| 108 if (options_.request_payer_email()) { |
| 109 if (sub.HasInfo(autofill::EMAIL_ADDRESS) && |
| 110 !super.HasInfo(autofill::EMAIL_ADDRESS)) { |
| 111 return false; |
| 112 } |
| 113 if (!HaveMergeableEmailAddresses(super, sub)) |
| 114 return false; |
| 115 } |
| 116 return true; |
| 117 } |
| 118 |
| 119 int PaymentsProfileComparator::GetContactCompletenessScore( |
| 120 const autofill::AutofillProfile* profile) const { |
| 121 if (!profile) |
| 122 return 0; |
| 123 |
| 124 // Create a bitmask of the fields that are both present and required. |
| 125 ProfileFields present = |
| 126 ~GetMissingProfileFields(profile) & GetRequiredProfileFieldsForContact(); |
| 127 |
| 128 // Count how many are set. |
| 129 return !!(present & kName) + !!(present & kPhone) + !!(present & kEmail); |
| 130 } |
| 131 |
| 132 bool PaymentsProfileComparator::IsContactInfoComplete( |
| 133 const autofill::AutofillProfile* profile) const { |
| 134 // Mask the fields that are missing with those that are requried. If any bits |
| 135 // are set (i.e., the result is nonzero), then contact info is incomplete. |
| 136 return !(GetMissingProfileFields(profile) & |
| 137 GetRequiredProfileFieldsForContact()); |
| 138 } |
| 139 |
| 140 bool PaymentsProfileComparator::IsContactMoreComplete( |
| 141 const autofill::AutofillProfile* p1, |
| 142 const autofill::AutofillProfile* p2) const { |
| 143 return GetContactCompletenessScore(p1) > GetContactCompletenessScore(p2); |
| 144 } |
| 145 |
| 146 base::string16 PaymentsProfileComparator::GetStringForMissingContactFields( |
| 147 const autofill::AutofillProfile& profile) const { |
| 148 return GetStringForMissingFields(GetMissingProfileFields(&profile) & |
| 149 GetRequiredProfileFieldsForContact()); |
| 150 } |
| 151 |
| 152 bool PaymentsProfileComparator::IsShippingComplete( |
| 153 const autofill::AutofillProfile* profile) const { |
| 154 // Mask the fields that are missing with those that are requried. If any bits |
| 155 // are set (i.e., the result is nonzero), then shipping is incomplete. |
| 156 return !(GetMissingProfileFields(profile) & |
| 157 GetRequiredProfileFieldsForShipping()); |
| 158 } |
| 159 |
| 160 base::string16 PaymentsProfileComparator::GetStringForMissingShippingFields( |
| 161 const autofill::AutofillProfile& profile) const { |
| 162 return GetStringForMissingFields(GetMissingProfileFields(&profile) & |
| 163 GetRequiredProfileFieldsForShipping()); |
| 164 } |
| 165 |
| 166 void PaymentsProfileComparator::Invalidate( |
| 167 const autofill::AutofillProfile& profile) { |
| 168 cache_.erase(profile.guid()); |
| 169 } |
| 170 |
| 171 PaymentsProfileComparator::ProfileFields |
| 172 PaymentsProfileComparator::ComputeMissingFields( |
| 173 const autofill::AutofillProfile& profile) const { |
| 174 ProfileFields missing = 0; |
| 175 |
| 176 if (!profile.HasInfo(autofill::NAME_FULL)) |
| 177 missing |= kName; |
| 178 |
| 179 // Determine the country code to use when validating the phone number. Use |
| 180 // the profile's country if it has one, or the code for the app locale |
| 181 // otherwise. Note that international format numbers will always work--this |
| 182 // is just the region that will be used to check if the number is |
| 183 // potentially in a local format. |
| 184 std::string country = |
| 185 data_util::GetCountryCodeWithFallback(&profile, app_locale()); |
| 186 |
| 187 base::string16 phone = profile.GetInfo( |
| 188 autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER), app_locale()); |
| 189 if (!autofill::IsValidPhoneNumber(phone, country)) |
| 190 missing |= kPhone; |
| 191 |
| 192 base::string16 email = profile.GetInfo( |
| 193 autofill::AutofillType(autofill::EMAIL_ADDRESS), app_locale()); |
| 194 if (!autofill::IsValidEmailAddress(email)) |
| 195 missing |= kEmail; |
| 196 |
| 197 if (!AreRequiredAddressFieldsPresent(profile)) |
| 198 missing |= kAddress; |
| 199 |
| 200 return missing; |
| 201 } |
| 202 |
| 203 PaymentsProfileComparator::ProfileFields |
| 204 PaymentsProfileComparator::GetRequiredProfileFieldsForContact() const { |
| 205 ProfileFields required = 0; |
| 206 if (options_.request_payer_name()) |
| 207 required |= kName; |
| 208 if (options_.request_payer_phone()) |
| 209 required |= kPhone; |
| 210 if (options_.request_payer_email()) |
| 211 required |= kEmail; |
| 212 return required; |
| 213 } |
| 214 |
| 215 PaymentsProfileComparator::ProfileFields |
| 216 PaymentsProfileComparator::GetRequiredProfileFieldsForShipping() const { |
| 217 return options_.request_shipping() ? (kAddress | kName | kPhone) : 0; |
| 218 } |
| 219 |
| 220 base::string16 PaymentsProfileComparator::GetStringForMissingFields( |
| 221 PaymentsProfileComparator::ProfileFields fields) const { |
| 222 switch (fields) { |
| 223 case 0: |
| 224 // No bits are set, so no fields are missing. |
| 225 return base::string16(); |
| 226 case kName: |
| 227 return l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_REQUIRED); |
| 228 case kPhone: |
| 229 return l10n_util::GetStringUTF16(IDS_PAYMENTS_PHONE_NUMBER_REQUIRED); |
| 230 case kEmail: |
| 231 return l10n_util::GetStringUTF16(IDS_PAYMENTS_EMAIL_REQUIRED); |
| 232 case kAddress: |
| 233 return l10n_util::GetStringUTF16(IDS_PAYMENTS_INVALID_ADDRESS); |
| 234 default: |
| 235 // Either multiple bits are set (likely) or one bit that doesn't |
| 236 // correspond to a named constant is set (shouldn't happen). Return a |
| 237 // generic "More information" message. |
| 238 return l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED); |
| 239 } |
| 240 } |
| 241 |
| 242 bool PaymentsProfileComparator::AreRequiredAddressFieldsPresent( |
| 243 const autofill::AutofillProfile& profile) const { |
| 244 std::unique_ptr<::i18n::addressinput::AddressData> data = |
| 245 autofill::i18n::CreateAddressDataFromAutofillProfile(profile, |
| 246 app_locale()); |
| 247 |
| 248 return autofill::addressinput::HasAllRequiredFields(*data); |
| 249 } |
| 250 |
| 251 } // namespace payments |
OLD | NEW |