OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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_comparator.h" | 5 #include "components/autofill/core/browser/autofill_profile_comparator.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/i18n/case_conversion.h" | 10 #include "base/i18n/case_conversion.h" |
11 #include "base/i18n/char_iterator.h" | 11 #include "base/i18n/char_iterator.h" |
12 #include "base/strings/string_piece.h" | 12 #include "base/strings/string_piece.h" |
13 #include "base/strings/string_split.h" | 13 #include "base/strings/string_split.h" |
14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
15 #include "base/strings/utf_string_conversion_utils.h" | 15 #include "base/strings/utf_string_conversion_utils.h" |
16 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" |
| 17 #include "components/autofill/core/browser/address_rewriter.h" |
17 #include "components/autofill/core/browser/autofill_country.h" | 18 #include "components/autofill/core/browser/autofill_country.h" |
18 #include "components/autofill/core/browser/autofill_data_util.h" | 19 #include "components/autofill/core/browser/autofill_data_util.h" |
19 #include "components/autofill/core/browser/state_names.h" | 20 #include "components/autofill/core/browser/state_names.h" |
20 #include "third_party/libphonenumber/phonenumber_api.h" | 21 #include "third_party/libphonenumber/phonenumber_api.h" |
21 | 22 |
22 using i18n::phonenumbers::PhoneNumberUtil; | 23 using i18n::phonenumbers::PhoneNumberUtil; |
23 using base::UTF16ToUTF8; | 24 using base::UTF16ToUTF8; |
24 using base::UTF8ToUTF16; | 25 using base::UTF8ToUTF16; |
25 | 26 |
26 namespace autofill { | 27 namespace autofill { |
27 namespace { | 28 namespace { |
28 | 29 |
29 const base::char16 kSpace[] = {L' ', L'\0'}; | 30 const base::char16 kSpace[] = {L' ', L'\0'}; |
30 const base::char16 kUS[] = {L'U', L'S', L'\0'}; | |
31 | 31 |
32 bool ContainsNewline(base::StringPiece16 text) { | 32 bool ContainsNewline(base::StringPiece16 text) { |
33 return text.find('\n') != base::StringPiece16::npos; | 33 return text.find('\n') != base::StringPiece16::npos; |
34 } | 34 } |
35 | 35 |
36 std::ostream& operator<<(std::ostream& os, | 36 std::ostream& operator<<(std::ostream& os, |
37 const ::i18n::phonenumbers::PhoneNumber& n) { | 37 const ::i18n::phonenumbers::PhoneNumber& n) { |
38 os << "country_code: " << n.country_code() << " " | 38 os << "country_code: " << n.country_code() << " " |
39 << "national_number: " << n.national_number(); | 39 << "national_number: " << n.national_number(); |
40 if (n.has_extension()) | 40 if (n.has_extension()) |
(...skipping 641 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
682 const base::string16& zip1 = NormalizeForComparison( | 682 const base::string16& zip1 = NormalizeForComparison( |
683 p1.GetInfo(kZipCode, app_locale_), DISCARD_WHITESPACE); | 683 p1.GetInfo(kZipCode, app_locale_), DISCARD_WHITESPACE); |
684 const base::string16& zip2 = NormalizeForComparison( | 684 const base::string16& zip2 = NormalizeForComparison( |
685 p2.GetInfo(kZipCode, app_locale_), DISCARD_WHITESPACE); | 685 p2.GetInfo(kZipCode, app_locale_), DISCARD_WHITESPACE); |
686 if (!zip1.empty() && !zip2.empty() && | 686 if (!zip1.empty() && !zip2.empty() && |
687 zip1.find(zip2) == base::string16::npos && | 687 zip1.find(zip2) == base::string16::npos && |
688 zip2.find(zip1) == base::string16::npos) { | 688 zip2.find(zip1) == base::string16::npos) { |
689 return false; | 689 return false; |
690 } | 690 } |
691 | 691 |
| 692 AddressRewriter rewriter = |
| 693 AddressRewriter::ForCountryCode(country1.empty() ? country2 : country1); |
| 694 |
692 // State | 695 // State |
693 // ------ | 696 // ------ |
694 // Heuristic: States are mergeable if one is a (possibly empty) bag of words | 697 // Heuristic: States are mergeable if one is a (possibly empty) bag of words |
695 // subset of the other. | 698 // subset of the other. |
696 // | 699 // |
697 // TODO(rogerm): If the match is between non-empty zip codes then we can infer | 700 // TODO(rogerm): If the match is between non-empty zip codes then we can infer |
698 // that the two state strings are intended to have the same meaning. This | 701 // that the two state strings are intended to have the same meaning. This |
699 // handles the cases where we have invalid or poorly formed data in one of the | 702 // handles the cases where we have invalid or poorly formed data in one of the |
700 // state values (like "Select one", or "CA - California"). | 703 // state values (like "Select one", or "CA - California"). |
701 const AutofillType kState(ADDRESS_HOME_STATE); | 704 const AutofillType kState(ADDRESS_HOME_STATE); |
702 const base::string16& state1 = | 705 const base::string16& state1 = |
703 NormalizeForComparison(p1.GetInfo(kState, app_locale_)); | 706 rewriter.Rewrite(NormalizeForComparison(p1.GetInfo(kState, app_locale_))); |
704 const base::string16& state2 = | 707 const base::string16& state2 = |
705 NormalizeForComparison(p2.GetInfo(kState, app_locale_)); | 708 rewriter.Rewrite(NormalizeForComparison(p2.GetInfo(kState, app_locale_))); |
706 if (!IsMatchingState(GetNonEmptyOf(p1, p2, kCountryCode), state1, state2) && | 709 if (CompareTokens(state1, state2) == DIFFERENT_TOKENS) { |
707 CompareTokens(state1, state2) == DIFFERENT_TOKENS) { | |
708 return false; | 710 return false; |
709 } | 711 } |
710 | 712 |
711 // City | 713 // City |
712 // ------ | 714 // ------ |
713 // Heuristic: Cities are mergeable if one is a (possibly empty) bag of words | 715 // Heuristic: Cities are mergeable if one is a (possibly empty) bag of words |
714 // subset of the other. | 716 // subset of the other. |
715 // | 717 // |
716 // TODO(rogerm): If the match is between non-empty zip codes then we can infer | 718 // TODO(rogerm): If the match is between non-empty zip codes then we can infer |
717 // that the two city strings are intended to have the same meaning. This | 719 // that the two city strings are intended to have the same meaning. This |
718 // handles the cases where we have a city vs one of its suburbs. | 720 // handles the cases where we have a city vs one of its suburbs. |
719 const base::string16& city1 = NormalizeForComparison( | 721 const base::string16& city1 = rewriter.Rewrite(NormalizeForComparison( |
720 p1.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_)); | 722 p1.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_))); |
721 const base::string16& city2 = NormalizeForComparison( | 723 const base::string16& city2 = rewriter.Rewrite(NormalizeForComparison( |
722 p2.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_)); | 724 p2.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_))); |
723 if (CompareTokens(city1, city2) == DIFFERENT_TOKENS) { | 725 if (CompareTokens(city1, city2) == DIFFERENT_TOKENS) { |
724 return false; | 726 return false; |
725 } | 727 } |
726 | 728 |
727 // Address | 729 // Address |
728 // -------- | 730 // -------- |
729 // Heuristic: Street addresses are mergeable if one is a (possibly empty) bag | 731 // Heuristic: Street addresses are mergeable if one is a (possibly empty) bag |
730 // of words subset of the other. | 732 // of words subset of the other. |
731 const base::string16& address1 = NormalizeForComparison( | 733 const base::string16& address1 = rewriter.Rewrite(NormalizeForComparison( |
732 p1.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_)); | 734 p1.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_))); |
733 const base::string16& address2 = NormalizeForComparison( | 735 const base::string16& address2 = rewriter.Rewrite(NormalizeForComparison( |
734 p2.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_)); | 736 p2.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_))); |
735 if (CompareTokens(address1, address2) == DIFFERENT_TOKENS) { | 737 if (CompareTokens(address1, address2) == DIFFERENT_TOKENS) { |
736 return false; | 738 return false; |
737 } | 739 } |
738 | 740 |
739 return true; | 741 return true; |
740 } | 742 } |
741 | 743 |
742 bool AutofillProfileComparator::IsMatchingState( | |
743 const base::string16& country_code, | |
744 const base::string16& state1, | |
745 const base::string16& state2) const { | |
746 if (state1 == state2) | |
747 return true; | |
748 | |
749 if (country_code != kUS) | |
750 return false; | |
751 | |
752 // TODO(rogerm): Generalize this to all locals using string equivalence rules. | |
753 base::string16 name, abbreviation; | |
754 autofill::state_names::GetNameAndAbbreviation(state1, &name, &abbreviation); | |
755 if (abbreviation.empty()) { | |
756 // state1 wasn't recognized. There's no need to compare it to state2 | |
757 return false; | |
758 } | |
759 | |
760 return state2 == name || state2 == abbreviation; | |
761 } | |
762 | |
763 } // namespace autofill | 744 } // namespace autofill |
OLD | NEW |