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

Side by Side Diff: components/autofill/core/browser/autofill_profile.cc

Issue 2110563002: Use AutofillProfileComparator in place of ad-hoc merge logic. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@merge
Patch Set: Initial CL for review Created 4 years, 5 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 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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/autofill/core/browser/autofill_profile.h" 5 #include "components/autofill/core/browser/autofill_profile.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <functional> 8 #include <functional>
9 #include <map> 9 #include <map>
10 #include <memory> 10 #include <memory>
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 for (const ServerFieldType& it : *suggested_fields) { 182 for (const ServerFieldType& it : *suggested_fields) {
183 if (it != excluded_field && 183 if (it != excluded_field &&
184 GetStorableTypeCollapsingGroups(it) == effective_excluded_type) { 184 GetStorableTypeCollapsingGroups(it) == effective_excluded_type) {
185 distinguishing_fields->push_back(effective_excluded_type); 185 distinguishing_fields->push_back(effective_excluded_type);
186 break; 186 break;
187 } 187 }
188 } 188 }
189 } 189 }
190 } 190 }
191 191
192 // Collapse compound field types to their "full" type. I.e. First name
193 // collapses to full name, area code collapses to full phone, etc.
194 void CollapseCompoundFieldTypes(ServerFieldTypeSet* type_set) {
195 ServerFieldTypeSet collapsed_set;
196 for (const auto& it : *type_set) {
197 switch (it) {
198 case NAME_FIRST:
199 case NAME_MIDDLE:
200 case NAME_LAST:
201 case NAME_MIDDLE_INITIAL:
202 case NAME_FULL:
203 case NAME_SUFFIX:
204 collapsed_set.insert(NAME_FULL);
205 break;
206
207 case PHONE_HOME_NUMBER:
208 case PHONE_HOME_CITY_CODE:
209 case PHONE_HOME_COUNTRY_CODE:
210 case PHONE_HOME_CITY_AND_NUMBER:
211 case PHONE_HOME_WHOLE_NUMBER:
212 collapsed_set.insert(PHONE_HOME_WHOLE_NUMBER);
213 break;
214
215 default:
216 collapsed_set.insert(it);
217 }
218 }
219 std::swap(*type_set, collapsed_set);
220 }
221
222 } // namespace 192 } // namespace
223 193
224 AutofillProfile::AutofillProfile(const std::string& guid, 194 AutofillProfile::AutofillProfile(const std::string& guid,
225 const std::string& origin) 195 const std::string& origin)
226 : AutofillDataModel(guid, origin), 196 : AutofillDataModel(guid, origin),
227 record_type_(LOCAL_PROFILE), 197 record_type_(LOCAL_PROFILE),
228 phone_number_(this) { 198 phone_number_(this) {
229 } 199 }
230 200
231 AutofillProfile::AutofillProfile(RecordType type, const std::string& server_id) 201 AutofillProfile::AutofillProfile(RecordType type, const std::string& server_id)
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 } 378 }
409 379
410 bool AutofillProfile::operator==(const AutofillProfile& profile) const { 380 bool AutofillProfile::operator==(const AutofillProfile& profile) const {
411 return guid() == profile.guid() && EqualsSansGuid(profile); 381 return guid() == profile.guid() && EqualsSansGuid(profile);
412 } 382 }
413 383
414 bool AutofillProfile::operator!=(const AutofillProfile& profile) const { 384 bool AutofillProfile::operator!=(const AutofillProfile& profile) const {
415 return !operator==(profile); 385 return !operator==(profile);
416 } 386 }
417 387
418 const base::string16 AutofillProfile::PrimaryValue(
419 const std::string& app_locale) const {
420 std::vector<base::string16> primary_values{
421 GetInfo(AutofillType(NAME_FIRST), app_locale),
422 GetInfo(AutofillType(NAME_LAST), app_locale),
423 GetInfo(AutofillType(ADDRESS_HOME_LINE1), app_locale),
424 GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale)};
425 return CanonicalizeProfileString(
426 base::JoinString(primary_values, base::UTF8ToUTF16(" ")));
427 }
428
429 bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile, 388 bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile,
430 const std::string& app_locale) const { 389 const std::string& app_locale) const {
431 ServerFieldTypeSet types; 390 ServerFieldTypeSet types;
432 GetSupportedTypes(&types); 391 GetSupportedTypes(&types);
433 return IsSubsetOfForFieldSet(profile, app_locale, types); 392 return IsSubsetOfForFieldSet(profile, app_locale, types);
434 } 393 }
435 394
436 bool AutofillProfile::IsSubsetOfForFieldSet( 395 bool AutofillProfile::IsSubsetOfForFieldSet(
437 const AutofillProfile& profile, 396 const AutofillProfile& profile,
438 const std::string& app_locale, 397 const std::string& app_locale,
(...skipping 18 matching lines...) Expand all
457 continue; 416 continue;
458 } else if (!i18n::PhoneNumbersMatch( 417 } else if (!i18n::PhoneNumbersMatch(
459 value, profile.GetRawInfo(type), 418 value, profile.GetRawInfo(type),
460 base::UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY)), 419 base::UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY)),
461 app_locale)) { 420 app_locale)) {
462 return false; 421 return false;
463 } 422 }
464 } else { 423 } else {
465 if (!compare) 424 if (!compare)
466 compare.reset(new l10n::CaseInsensitiveCompare()); 425 compare.reset(new l10n::CaseInsensitiveCompare());
467 if (!compare->StringsEqual(value, profile.GetRawInfo(type))) 426 if (!compare->StringsEqual(value, profile.GetRawInfo(type)))
Mathieu 2016/06/29 15:50:57 I wonder if this code also needs changing. I'm war
Roger McFarlane (Chromium) 2016/06/29 18:21:37 Done.
468 return false; 427 return false;
469 } 428 }
470 } 429 }
471 430
472 return true; 431 return true;
473 } 432 }
474 433
475 bool AutofillProfile::OverwriteName(const NameInfo& imported_name, 434 bool AutofillProfile::OverwriteName(const NameInfo& imported_name,
476 const std::string& app_locale) { 435 const std::string& app_locale) {
477 // Check if the names parts are equal. 436 // Check if the names parts are equal.
(...skipping 29 matching lines...) Expand all
507 } 466 }
508 467
509 name_.OverwriteName(imported_name); 468 name_.OverwriteName(imported_name);
510 return true; 469 return true;
511 } 470 }
512 471
513 bool AutofillProfile::OverwriteWith(const AutofillProfile& profile, 472 bool AutofillProfile::OverwriteWith(const AutofillProfile& profile,
514 const std::string& app_locale) { 473 const std::string& app_locale) {
515 // Verified profiles should never be overwritten with unverified data. 474 // Verified profiles should never be overwritten with unverified data.
516 DCHECK(!IsVerified() || profile.IsVerified()); 475 DCHECK(!IsVerified() || profile.IsVerified());
476 AutofillProfileComparator comparator(app_locale);
477 DCHECK(comparator.AreMergeable(*this, profile));
478
479 NameInfo name;
480 EmailInfo email;
481 CompanyInfo company;
482 PhoneNumber phone_number(this);
483 Address address;
484
485 // The comparator's merge operations are biased to prefer the data in the
486 // first profile parameter when the data is the same modulo case. We pass the
487 // other profile in this position to prefer accepting updates instead of
Mathieu 2016/06/29 15:50:57 possibly change "other" -> "incoming"
Roger McFarlane (Chromium) 2016/06/29 18:21:37 Done.
488 // preserving the original data. I.e., passing the other profile first accepts
489 // case changes, the other ways does not.
490 if (!comparator.MergeNames(profile, *this, &name) ||
491 !comparator.MergeEmailAddresses(profile, *this, &email) ||
492 !comparator.MergeCompanyNames(profile, *this, &company) ||
493 !comparator.MergePhoneNumbers(profile, *this, &phone_number) ||
494 !comparator.MergeAddresses(profile, *this, &address)) {
495 NOTREACHED();
496 return false;
497 }
498
517 set_origin(profile.origin()); 499 set_origin(profile.origin());
518 set_language_code(profile.language_code()); 500 set_language_code(profile.language_code());
519 set_use_count(profile.use_count() + use_count()); 501 set_use_count(profile.use_count() + use_count());
520 if (profile.use_date() > use_date()) 502 if (profile.use_date() > use_date())
521 set_use_date(profile.use_date()); 503 set_use_date(profile.use_date());
522 504
523 // |types_to_overwrite| is initially populated with all types that have 505 bool modified = false;
Mathieu 2016/06/29 15:50:57 Please add an obvious comment above this block. So
Roger McFarlane (Chromium) 2016/06/29 18:21:36 Done.
524 // non-empty data in the incoming |profile|. After adjustment, all data from
525 // |profile| corresponding to types in |types_to_overwrite| is overwritten in
526 // |this| profile.
527 ServerFieldTypeSet types_to_overwrite;
528 profile.GetNonEmptyTypes(app_locale, &types_to_overwrite);
529 506
530 // Only transfer "full" types (e.g. full name) and not fragments (e.g. 507 if (name_ != name) {
531 // first name, last name). 508 name_ = name;
532 CollapseCompoundFieldTypes(&types_to_overwrite); 509 modified = true;
533
534 // Remove ADDRESS_HOME_STREET_ADDRESS to ensure a merge of the address line by
535 // line. See comment below.
536 types_to_overwrite.erase(ADDRESS_HOME_STREET_ADDRESS);
537
538 l10n::CaseInsensitiveCompare compare;
539
540 // Special case for addresses. With the whole address comparison, it is now
541 // necessary to make sure to keep the best address format: both lines used.
542 // This is because some sites might not have an address line 2 and the
543 // previous value should not be replaced with an empty string in that case.
544 if (compare.StringsEqual(
545 CanonicalizeProfileString(
546 profile.GetRawInfo(ADDRESS_HOME_STREET_ADDRESS)),
547 CanonicalizeProfileString(GetRawInfo(ADDRESS_HOME_STREET_ADDRESS))) &&
548 !GetRawInfo(ADDRESS_HOME_LINE2).empty() &&
549 profile.GetRawInfo(ADDRESS_HOME_LINE2).empty()) {
550 types_to_overwrite.erase(ADDRESS_HOME_LINE1);
551 types_to_overwrite.erase(ADDRESS_HOME_LINE2);
552 } 510 }
553 511
554 bool did_overwrite = false; 512 if (email_ != email) {
555 513 email_ = email;
556 for (const ServerFieldType field_type : types_to_overwrite) { 514 modified = true;
557 // Special case for names.
558 if (AutofillType(field_type).group() == NAME) {
559 did_overwrite |= OverwriteName(profile.name_, app_locale);
560 continue;
561 }
562
563 base::string16 new_value = profile.GetRawInfo(field_type);
564 // Overwrite the data in |this| profile for the field type and set
565 // |did_overwrite| if the previous data was different than the |new_value|.
566 if (GetRawInfo(field_type) != new_value) {
567 SetRawInfo(field_type, new_value);
568 did_overwrite = true;
569 }
570 } 515 }
571 516
572 return did_overwrite; 517 if (company_ != company) {
518 company_ = company;
519 modified = true;
520 }
521
522 if (phone_number_ != phone_number) {
523 phone_number_ = phone_number;
524 modified = true;
525 }
526
527 if (address_ != address) {
528 address_ = address;
529 modified = true;
530 }
531
532 return modified;
573 } 533 }
574 534
575 bool AutofillProfile::SaveAdditionalInfo(const AutofillProfile& profile, 535 bool AutofillProfile::SaveAdditionalInfo(const AutofillProfile& profile,
576 const std::string& app_locale) { 536 const std::string& app_locale) {
577 // If both profiles are verified, do not merge them. 537 // If both profiles are verified, do not merge them.
578 if (IsVerified() && profile.IsVerified()) 538 if (IsVerified() && profile.IsVerified())
579 return false; 539 return false;
580 540
581 ServerFieldTypeSet field_types, other_field_types; 541 ServerFieldTypeSet field_types, other_field_types;
582 GetNonEmptyTypes(app_locale, &field_types); 542 GetNonEmptyTypes(app_locale, &field_types);
sebsg 2016/06/29 15:35:24 We don't need those 3 lines either now :)
Roger McFarlane (Chromium) 2016/06/29 18:21:37 Removed. Also removed OverwriteName() which is al
583 profile.GetNonEmptyTypes(app_locale, &other_field_types); 543 profile.GetNonEmptyTypes(app_locale, &other_field_types);
584 544
585 // The address needs to be compared line by line to take into account the 545 AutofillProfileComparator comparator(app_locale);
586 // logic for empty fields implemented in the loop.
587 field_types.erase(ADDRESS_HOME_STREET_ADDRESS);
588 l10n::CaseInsensitiveCompare compare;
589 for (ServerFieldType field_type : field_types) {
590 if (other_field_types.count(field_type)) {
591 AutofillType type = AutofillType(field_type);
592 // Special cases for name and phone. If the whole/full value matches, skip
593 // the individual fields comparison.
594 if (type.group() == NAME &&
595 compare.StringsEqual(
596 profile.GetInfo(AutofillType(NAME_FULL), app_locale),
597 GetInfo(AutofillType(NAME_FULL), app_locale))) {
598 continue;
599 }
600 if (type.group() == PHONE_HOME &&
601 i18n::PhoneNumbersMatch(
602 GetRawInfo(PHONE_HOME_WHOLE_NUMBER),
603 profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER),
604 base::UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY)),
605 app_locale)) {
606 continue;
607 }
608 546
609 // Special case for postal codes, where postal codes with/without spaces 547 // SaveAdditionalInfo should not have been called if the profiles were not
610 // in them are considered equivalent. 548 // already deemed to be mergeable.
611 if (field_type == ADDRESS_HOME_ZIP) { 549 DCHECK(comparator.AreMergeable(*this, profile));
sebsg 2016/06/29 08:57:03 Would it be better to return false here instead? I
Roger McFarlane (Chromium) 2016/06/29 18:21:37 This dcheck firing indicates a programming error,
sebsg 2016/06/29 19:04:50 Yes, let's discuss it and fix it in a subsequent C
612 base::string16 profile_zip;
613 base::string16 current_zip;
614 base::RemoveChars(profile.GetRawInfo(field_type), ASCIIToUTF16(" "),
615 &profile_zip);
616 base::RemoveChars(GetRawInfo(field_type), ASCIIToUTF16(" "),
617 &current_zip);
618 if (!compare.StringsEqual(profile_zip, current_zip))
619 return false;
620 continue;
621 }
622 550
623 // Special case for the address because the comparison uses canonicalized 551 // We don't replace verified profile data with unverified profile data. But,
624 // values. Start by comparing the address line by line. If it fails, make 552 // we can merge two verified profiles or merge verified profile data into an
625 // sure that the address as a whole is different before returning false. 553 // unverified profile.
626 // It is possible that the user put the info from line 2 on line 1 because
627 // of a certain form for example.
628 if (field_type == ADDRESS_HOME_LINE1 ||
629 field_type == ADDRESS_HOME_LINE2) {
630 if (!compare.StringsEqual(
631 CanonicalizeProfileString(profile.GetRawInfo(field_type)),
632 CanonicalizeProfileString(GetRawInfo(field_type))) &&
633 !compare.StringsEqual(CanonicalizeProfileString(profile.GetRawInfo(
634 ADDRESS_HOME_STREET_ADDRESS)),
635 CanonicalizeProfileString(GetRawInfo(
636 ADDRESS_HOME_STREET_ADDRESS)))) {
637 return false;
638 }
639 continue;
640 }
641
642 // Special case for the state to support abbreviations. Currently only the
643 // US states are supported.
644 if (field_type == ADDRESS_HOME_STATE) {
645 base::string16 full;
646 base::string16 abbreviation;
647 state_names::GetNameAndAbbreviation(GetRawInfo(ADDRESS_HOME_STATE),
648 &full, &abbreviation);
649 if (compare.StringsEqual(profile.GetRawInfo(ADDRESS_HOME_STATE),
650 full) ||
651 compare.StringsEqual(profile.GetRawInfo(ADDRESS_HOME_STATE),
652 abbreviation))
653 continue;
654 }
655
656 // Special case for company names to support cannonicalized variations.
657 if (field_type == COMPANY_NAME) {
658 if (compare.StringsEqual(
659 CanonicalizeProfileString(profile.GetRawInfo(field_type)),
660 CanonicalizeProfileString(GetRawInfo(field_type)))) {
661 continue;
662 }
663 }
664
665 // Special case for middle name to support initials.
666 if (field_type == NAME_MIDDLE) {
667 base::string16 middle_name = GetRawInfo(NAME_MIDDLE);
668 base::string16 profile_middle_name = profile.GetRawInfo(NAME_MIDDLE);
669 DCHECK(!middle_name.empty());
670 DCHECK(!profile_middle_name.empty());
671 // If one of the two middle names is an initial that matches the first
672 // letter of the other middle name, they are considered equivalent.
673 if ((middle_name.size() == 1 || profile_middle_name.size() == 1) &&
674 middle_name[0] == profile_middle_name[0]) {
675 continue;
676 }
677 }
678
679 if (!compare.StringsEqual(profile.GetRawInfo(field_type),
680 GetRawInfo(field_type))) {
681 return false;
682 }
683 }
684 }
685
686 if (!IsVerified() || profile.IsVerified()) { 554 if (!IsVerified() || profile.IsVerified()) {
687 if (OverwriteWith(profile, app_locale)) { 555 if (OverwriteWith(profile, app_locale)) {
688 AutofillMetrics::LogProfileActionOnFormSubmitted( 556 AutofillMetrics::LogProfileActionOnFormSubmitted(
689 AutofillMetrics::EXISTING_PROFILE_UPDATED); 557 AutofillMetrics::EXISTING_PROFILE_UPDATED);
690 } else { 558 } else {
691 AutofillMetrics::LogProfileActionOnFormSubmitted( 559 AutofillMetrics::LogProfileActionOnFormSubmitted(
692 AutofillMetrics::EXISTING_PROFILE_USED); 560 AutofillMetrics::EXISTING_PROFILE_USED);
693 } 561 }
694 } 562 }
695 return true; 563 return true;
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
775 contents_utf8.append(language_code()); 643 contents_utf8.append(language_code());
776 server_id_ = base::SHA1HashString(contents_utf8); 644 server_id_ = base::SHA1HashString(contents_utf8);
777 } 645 }
778 646
779 void AutofillProfile::RecordAndLogUse() { 647 void AutofillProfile::RecordAndLogUse() {
780 UMA_HISTOGRAM_COUNTS_1000("Autofill.DaysSinceLastUse.Profile", 648 UMA_HISTOGRAM_COUNTS_1000("Autofill.DaysSinceLastUse.Profile",
781 (base::Time::Now() - use_date()).InDays()); 649 (base::Time::Now() - use_date()).InDays());
782 RecordUse(); 650 RecordUse();
783 } 651 }
784 652
785 // static
786 base::string16 AutofillProfile::CanonicalizeProfileString(
787 const base::string16& str) {
788 // The locale doesn't matter for general string canonicalization.
789 AutofillProfileComparator comparator("en-US");
790 return comparator.NormalizeForComparison(str);
791 }
792
793 void AutofillProfile::GetSupportedTypes( 653 void AutofillProfile::GetSupportedTypes(
794 ServerFieldTypeSet* supported_types) const { 654 ServerFieldTypeSet* supported_types) const {
795 FormGroupList info = FormGroups(); 655 FormGroupList info = FormGroups();
796 for (const auto& it : info) { 656 for (const auto& it : info) {
797 it->GetSupportedTypes(supported_types); 657 it->GetSupportedTypes(supported_types);
798 } 658 }
799 } 659 }
800 660
801 base::string16 AutofillProfile::ConstructInferredLabel( 661 base::string16 AutofillProfile::ConstructInferredLabel(
802 const std::vector<ServerFieldType>& included_fields, 662 const std::vector<ServerFieldType>& included_fields,
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
1019 << " " << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_CITY)) << " " 879 << " " << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_CITY)) << " "
1020 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE)) << " " 880 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE)) << " "
1021 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_ZIP)) << " " 881 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_ZIP)) << " "
1022 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)) << " " 882 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_SORTING_CODE)) << " "
1023 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)) << " " 883 << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY)) << " "
1024 << profile.language_code() << " " 884 << profile.language_code() << " "
1025 << UTF16ToUTF8(profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)); 885 << UTF16ToUTF8(profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
1026 } 886 }
1027 887
1028 } // namespace autofill 888 } // namespace autofill
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698