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" | |
18 #include "components/autofill/core/browser/autofill_country.h" | 17 #include "components/autofill/core/browser/autofill_country.h" |
19 #include "components/autofill/core/browser/autofill_data_util.h" | 18 #include "components/autofill/core/browser/autofill_data_util.h" |
20 #include "components/autofill/core/browser/state_names.h" | 19 #include "components/autofill/core/browser/state_names.h" |
21 #include "third_party/libphonenumber/phonenumber_api.h" | 20 #include "third_party/libphonenumber/phonenumber_api.h" |
22 | 21 |
23 using i18n::phonenumbers::PhoneNumberUtil; | 22 using i18n::phonenumbers::PhoneNumberUtil; |
24 using base::UTF16ToUTF8; | 23 using base::UTF16ToUTF8; |
25 using base::UTF8ToUTF16; | 24 using base::UTF8ToUTF16; |
26 | 25 |
27 namespace autofill { | 26 namespace autofill { |
28 namespace { | 27 namespace { |
29 | 28 |
30 const base::char16 kSpace[] = {L' ', L'\0'}; | 29 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 | |
695 // State | 692 // State |
696 // ------ | 693 // ------ |
697 // Heuristic: States are mergeable if one is a (possibly empty) bag of words | 694 // Heuristic: States are mergeable if one is a (possibly empty) bag of words |
698 // subset of the other. | 695 // subset of the other. |
699 // | 696 // |
700 // TODO(rogerm): If the match is between non-empty zip codes then we can infer | 697 // TODO(rogerm): If the match is between non-empty zip codes then we can infer |
701 // that the two state strings are intended to have the same meaning. This | 698 // that the two state strings are intended to have the same meaning. This |
702 // handles the cases where we have invalid or poorly formed data in one of the | 699 // handles the cases where we have invalid or poorly formed data in one of the |
703 // state values (like "Select one", or "CA - California"). | 700 // state values (like "Select one", or "CA - California"). |
704 const AutofillType kState(ADDRESS_HOME_STATE); | 701 const AutofillType kState(ADDRESS_HOME_STATE); |
705 const base::string16& state1 = | 702 const base::string16& state1 = |
706 rewriter.Rewrite(NormalizeForComparison(p1.GetInfo(kState, app_locale_))); | 703 NormalizeForComparison(p1.GetInfo(kState, app_locale_)); |
707 const base::string16& state2 = | 704 const base::string16& state2 = |
708 rewriter.Rewrite(NormalizeForComparison(p2.GetInfo(kState, app_locale_))); | 705 NormalizeForComparison(p2.GetInfo(kState, app_locale_)); |
709 if (CompareTokens(state1, state2) == DIFFERENT_TOKENS) { | 706 if (!IsMatchingState(GetNonEmptyOf(p1, p2, kCountryCode), state1, state2) && |
| 707 CompareTokens(state1, state2) == DIFFERENT_TOKENS) { |
710 return false; | 708 return false; |
711 } | 709 } |
712 | 710 |
713 // City | 711 // City |
714 // ------ | 712 // ------ |
715 // Heuristic: Cities are mergeable if one is a (possibly empty) bag of words | 713 // Heuristic: Cities are mergeable if one is a (possibly empty) bag of words |
716 // subset of the other. | 714 // subset of the other. |
717 // | 715 // |
718 // TODO(rogerm): If the match is between non-empty zip codes then we can infer | 716 // TODO(rogerm): If the match is between non-empty zip codes then we can infer |
719 // that the two city strings are intended to have the same meaning. This | 717 // that the two city strings are intended to have the same meaning. This |
720 // handles the cases where we have a city vs one of its suburbs. | 718 // handles the cases where we have a city vs one of its suburbs. |
721 const base::string16& city1 = rewriter.Rewrite(NormalizeForComparison( | 719 const base::string16& city1 = NormalizeForComparison( |
722 p1.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_))); | 720 p1.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_)); |
723 const base::string16& city2 = rewriter.Rewrite(NormalizeForComparison( | 721 const base::string16& city2 = NormalizeForComparison( |
724 p2.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_))); | 722 p2.GetInfo(AutofillType(ADDRESS_HOME_CITY), app_locale_)); |
725 if (CompareTokens(city1, city2) == DIFFERENT_TOKENS) { | 723 if (CompareTokens(city1, city2) == DIFFERENT_TOKENS) { |
726 return false; | 724 return false; |
727 } | 725 } |
728 | 726 |
729 // Address | 727 // Address |
730 // -------- | 728 // -------- |
731 // Heuristic: Street addresses are mergeable if one is a (possibly empty) bag | 729 // Heuristic: Street addresses are mergeable if one is a (possibly empty) bag |
732 // of words subset of the other. | 730 // of words subset of the other. |
733 const base::string16& address1 = rewriter.Rewrite(NormalizeForComparison( | 731 const base::string16& address1 = NormalizeForComparison( |
734 p1.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_))); | 732 p1.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_)); |
735 const base::string16& address2 = rewriter.Rewrite(NormalizeForComparison( | 733 const base::string16& address2 = NormalizeForComparison( |
736 p2.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_))); | 734 p2.GetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), app_locale_)); |
737 if (CompareTokens(address1, address2) == DIFFERENT_TOKENS) { | 735 if (CompareTokens(address1, address2) == DIFFERENT_TOKENS) { |
738 return false; | 736 return false; |
739 } | 737 } |
740 | 738 |
741 return true; | 739 return true; |
742 } | 740 } |
743 | 741 |
| 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 |
744 } // namespace autofill | 763 } // namespace autofill |
OLD | NEW |