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