Chromium Code Reviews| 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 |