Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(312)

Side by Side Diff: components/payments/core/profile_util.cc

Issue 2847503002: [WebPayments] Show labels on incomplete profiles (Closed)
Patch Set: ios namespace Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698