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

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

Issue 2847503002: [WebPayments] Show labels on incomplete profiles (Closed)
Patch Set: test fix 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
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698