OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/payments/core/profile_util.h" | 5 #include "components/payments/core/profile_util.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <memory> |
8 | 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" |
9 #include "components/autofill/core/browser/autofill_profile.h" | 13 #include "components/autofill/core/browser/autofill_profile.h" |
10 #include "components/autofill/core/browser/field_types.h" | 14 #include "components/autofill/core/browser/field_types.h" |
| 15 #include "components/autofill/core/browser/validation.h" |
11 #include "components/payments/core/payment_options_provider.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" |
12 | 22 |
13 namespace payments { | 23 namespace payments { |
14 namespace profile_util { | |
15 | 24 |
16 std::vector<autofill::AutofillProfile*> FilterProfilesForContact( | 25 PaymentsProfileComparator::PaymentsProfileComparator( |
17 const std::vector<autofill::AutofillProfile*>& profiles, | |
18 const std::string& app_locale, | 26 const std::string& app_locale, |
19 const PaymentOptionsProvider& options) { | 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 { |
20 // We will be removing profiles, so we operate on a copy. | 53 // We will be removing profiles, so we operate on a copy. |
21 std::vector<autofill::AutofillProfile*> processed = profiles; | 54 std::vector<autofill::AutofillProfile*> processed = profiles; |
22 | 55 |
23 PaymentsProfileComparator comparator(app_locale, options); | |
24 | |
25 // Stable sort, since profiles are expected to be passed in frecency order. | 56 // Stable sort, since profiles are expected to be passed in frecency order. |
26 std::stable_sort( | 57 std::stable_sort( |
27 processed.begin(), processed.end(), | 58 processed.begin(), processed.end(), |
28 std::bind(&PaymentsProfileComparator::IsContactMoreComplete, &comparator, | 59 std::bind(&PaymentsProfileComparator::IsContactMoreComplete, this, |
29 std::placeholders::_1, std::placeholders::_2)); | 60 std::placeholders::_1, std::placeholders::_2)); |
30 | 61 |
31 auto it = processed.begin(); | 62 auto it = processed.begin(); |
32 while (it != processed.end()) { | 63 while (it != processed.end()) { |
33 if (comparator.GetContactCompletenessScore(*it) == 0) { | 64 if (GetContactCompletenessScore(*it) == 0) { |
34 // Since profiles are sorted by completeness, this and any further | 65 // Since profiles are sorted by completeness, this and any further |
35 // profiles can be discarded. | 66 // profiles can be discarded. |
36 processed.erase(it, processed.end()); | 67 processed.erase(it, processed.end()); |
37 break; | 68 break; |
38 } | 69 } |
39 | 70 |
40 // Attempt to find a matching element in the vector before the current. | 71 // Attempt to find a matching element in the vector before the current. |
41 // This is quadratic, but the number of elements is generally small | 72 // This is quadratic, but the number of elements is generally small |
42 // (< 10), so a more complicated algorithm would be overkill. | 73 // (< 10), so a more complicated algorithm would be overkill. |
43 if (std::find_if(processed.begin(), it, | 74 if (std::find_if(processed.begin(), it, |
44 [&](autofill::AutofillProfile* prior) { | 75 [&](autofill::AutofillProfile* prior) { |
45 return comparator.IsContactEqualOrSuperset(*prior, **it); | 76 return IsContactEqualOrSuperset(*prior, **it); |
46 }) != it) { | 77 }) != it) { |
47 // Remove the subset profile. |it| will point to the next element after | 78 // Remove the subset profile. |it| will point to the next element after |
48 // erasure. | 79 // erasure. |
49 it = processed.erase(it); | 80 it = processed.erase(it); |
50 } else { | 81 } else { |
51 it++; | 82 it++; |
52 } | 83 } |
53 } | 84 } |
54 | 85 |
55 return processed; | 86 return processed; |
56 } | 87 } |
57 | 88 |
58 PaymentsProfileComparator::PaymentsProfileComparator( | |
59 const std::string& app_locale, | |
60 const PaymentOptionsProvider& options) | |
61 : autofill::AutofillProfileComparator(app_locale), options_(options) {} | |
62 | |
63 PaymentsProfileComparator::~PaymentsProfileComparator() {} | |
64 | |
65 bool PaymentsProfileComparator::IsContactEqualOrSuperset( | 89 bool PaymentsProfileComparator::IsContactEqualOrSuperset( |
66 const autofill::AutofillProfile& super, | 90 const autofill::AutofillProfile& super, |
67 const autofill::AutofillProfile& sub) { | 91 const autofill::AutofillProfile& sub) const { |
68 if (options_.request_payer_name()) { | 92 if (options_.request_payer_name()) { |
69 if (sub.HasInfo(autofill::NAME_FULL) && | 93 if (sub.HasInfo(autofill::NAME_FULL) && |
70 !super.HasInfo(autofill::NAME_FULL)) { | 94 !super.HasInfo(autofill::NAME_FULL)) { |
71 return false; | 95 return false; |
72 } | 96 } |
73 if (!HaveMergeableNames(super, sub)) | 97 if (!HaveMergeableNames(super, sub)) |
74 return false; | 98 return false; |
75 } | 99 } |
76 if (options_.request_payer_phone()) { | 100 if (options_.request_payer_phone()) { |
77 if (sub.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER) && | 101 if (sub.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER) && |
78 !super.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) { | 102 !super.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) { |
79 return false; | 103 return false; |
80 } | 104 } |
81 if (!HaveMergeablePhoneNumbers(super, sub)) | 105 if (!HaveMergeablePhoneNumbers(super, sub)) |
82 return false; | 106 return false; |
83 } | 107 } |
84 if (options_.request_payer_email()) { | 108 if (options_.request_payer_email()) { |
85 if (sub.HasInfo(autofill::EMAIL_ADDRESS) && | 109 if (sub.HasInfo(autofill::EMAIL_ADDRESS) && |
86 !super.HasInfo(autofill::EMAIL_ADDRESS)) { | 110 !super.HasInfo(autofill::EMAIL_ADDRESS)) { |
87 return false; | 111 return false; |
88 } | 112 } |
89 if (!HaveMergeableEmailAddresses(super, sub)) | 113 if (!HaveMergeableEmailAddresses(super, sub)) |
90 return false; | 114 return false; |
91 } | 115 } |
92 return true; | 116 return true; |
93 } | 117 } |
94 | 118 |
95 int PaymentsProfileComparator::GetContactCompletenessScore( | 119 int PaymentsProfileComparator::GetContactCompletenessScore( |
96 const autofill::AutofillProfile* profile) { | 120 const autofill::AutofillProfile* profile) const { |
97 if (!profile) | 121 if (!profile) |
98 return 0; | 122 return 0; |
99 | 123 |
100 return (options_.request_payer_name() && | 124 // Create a bitmask of the fields that are both present and required. |
101 profile->HasInfo(autofill::NAME_FULL)) + | 125 ProfileFields present = |
102 (options_.request_payer_phone() && | 126 ~GetMissingProfileFields(profile) & GetRequiredProfileFieldsForContact(); |
103 profile->HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) + | 127 |
104 (options_.request_payer_email() && | 128 // Count how many are set. |
105 profile->HasInfo(autofill::EMAIL_ADDRESS)); | 129 return !!(present & kName) + !!(present & kPhone) + !!(present & kEmail); |
106 } | 130 } |
107 | 131 |
108 bool PaymentsProfileComparator::IsContactInfoComplete( | 132 bool PaymentsProfileComparator::IsContactInfoComplete( |
109 const autofill::AutofillProfile* profile) { | 133 const autofill::AutofillProfile* profile) const { |
110 int desired_score = options_.request_payer_name() + | 134 // Mask the fields that are missing with those that are requried. If any bits |
111 options_.request_payer_phone() + | 135 // are set (i.e., the result is nonzero), then contact info is incomplete. |
112 options_.request_payer_email(); | 136 return !(GetMissingProfileFields(profile) & |
113 return GetContactCompletenessScore(profile) == desired_score; | 137 GetRequiredProfileFieldsForContact()); |
114 } | 138 } |
115 | 139 |
116 bool PaymentsProfileComparator::IsContactMoreComplete( | 140 bool PaymentsProfileComparator::IsContactMoreComplete( |
117 const autofill::AutofillProfile* p1, | 141 const autofill::AutofillProfile* p1, |
118 const autofill::AutofillProfile* p2) { | 142 const autofill::AutofillProfile* p2) const { |
119 return GetContactCompletenessScore(p1) > GetContactCompletenessScore(p2); | 143 return GetContactCompletenessScore(p1) > GetContactCompletenessScore(p2); |
120 } | 144 } |
121 | 145 |
122 } // namespace profile_util | 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 |
123 } // namespace payments | 251 } // namespace payments |
OLD | NEW |