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
3 // found in the LICENSE file.
5 #include "components/autofill/core/browser/phone_number_i18n.h"
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "components/autofill/core/browser/autofill_country.h"
14 #include "third_party/libphonenumber/src/phonenumber_api.h"
16 using i18n::phonenumbers::PhoneNumber
;
17 using i18n::phonenumbers::PhoneNumberUtil
;
23 std::string
SanitizeRegion(const std::string
& region
,
24 const std::string
& app_locale
) {
25 if (region
.length() == 2)
28 return AutofillCountry::CountryCodeForLocale(app_locale
);
31 // Returns true if |phone_number| is valid.
32 bool IsValidPhoneNumber(const PhoneNumber
& phone_number
) {
33 PhoneNumberUtil
* phone_util
= PhoneNumberUtil::GetInstance();
34 if (!phone_util
->IsPossibleNumber(phone_number
))
37 // Verify that the number has a valid area code (that in some cases could be
38 // empty) for the parsed country code. Also verify that this is a valid
39 // number (for example, in the US 1234567 is not valid, because numbers do not
41 if (!phone_util
->IsValidNumber(phone_number
))
47 // Formats the given |number| as a human-readable string, and writes the result
48 // into |formatted_number|. Also, normalizes the formatted number, and writes
49 // that result into |normalized_number|. This function should only be called
50 // with numbers already known to be valid, i.e. validation should be done prior
51 // to calling this function. Note that the |country_code|, which determines
52 // whether to format in the national or in the international format, is passed
53 // in explicitly, as |number| might have an implicit country code set, even
54 // though the original input lacked a country code.
55 void FormatValidatedNumber(const PhoneNumber
& number
,
56 const base::string16
& country_code
,
57 base::string16
* formatted_number
,
58 base::string16
* normalized_number
) {
59 PhoneNumberUtil::PhoneNumberFormat format
=
60 country_code
.empty() ?
61 PhoneNumberUtil::NATIONAL
:
62 PhoneNumberUtil::INTERNATIONAL
;
64 PhoneNumberUtil
* phone_util
= PhoneNumberUtil::GetInstance();
65 std::string processed_number
;
66 phone_util
->Format(number
, format
, &processed_number
);
68 std::string region_code
;
69 phone_util
->GetRegionCodeForNumber(number
, ®ion_code
);
71 // Drop the leading '+' for US numbers as some US sites can't handle the "+",
72 // and in the US dialing "+1..." is the same as dialing "1...".
74 if (processed_number
[0] == '+') {
75 processed_number
= processed_number
.substr(1);
76 if (region_code
!= "US")
81 *formatted_number
= base::UTF8ToUTF16(prefix
+ processed_number
);
83 if (normalized_number
) {
84 phone_util
->NormalizeDigitsOnly(&processed_number
);
85 *normalized_number
= base::UTF8ToUTF16(prefix
+ processed_number
);
93 // Parses the number stored in |value| as it should be interpreted in the given
94 // |default_region|, and stores the results into the remaining arguments.
95 // The |default_region| should be sanitized prior to calling this function.
96 bool ParsePhoneNumber(const base::string16
& value
,
97 const std::string
& default_region
,
98 base::string16
* country_code
,
99 base::string16
* city_code
,
100 base::string16
* number
,
101 std::string
* inferred_region
,
102 PhoneNumber
* i18n_number
) {
103 country_code
->clear();
106 *i18n_number
= PhoneNumber();
108 std::string
number_text(base::UTF16ToUTF8(value
));
110 // Parse phone number based on the region.
111 PhoneNumberUtil
* phone_util
= PhoneNumberUtil::GetInstance();
113 // The |default_region| should already be sanitized.
114 DCHECK_EQ(2U, default_region
.size());
115 if (phone_util
->ParseAndKeepRawInput(
116 number_text
, default_region
, i18n_number
) !=
117 PhoneNumberUtil::NO_PARSING_ERROR
) {
121 if (!IsValidPhoneNumber(*i18n_number
))
124 std::string national_significant_number
;
125 phone_util
->GetNationalSignificantNumber(*i18n_number
,
126 &national_significant_number
);
128 int area_length
= phone_util
->GetLengthOfGeographicalAreaCode(*i18n_number
);
129 int destination_length
=
130 phone_util
->GetLengthOfNationalDestinationCode(*i18n_number
);
131 // Some phones have a destination code in lieu of area code: mobile operators
132 // in Europe, toll and toll-free numbers in USA, etc. From our point of view
133 // these two types of codes are the same.
134 if (destination_length
> area_length
)
135 area_length
= destination_length
;
137 std::string area_code
;
138 std::string subscriber_number
;
139 if (area_length
> 0) {
140 area_code
= national_significant_number
.substr(0, area_length
);
141 subscriber_number
= national_significant_number
.substr(area_length
);
143 subscriber_number
= national_significant_number
;
145 *number
= base::UTF8ToUTF16(subscriber_number
);
146 *city_code
= base::UTF8ToUTF16(area_code
);
148 // Check if parsed number has a country code that was not inferred from the
150 if (i18n_number
->has_country_code() &&
151 i18n_number
->country_code_source() != PhoneNumber::FROM_DEFAULT_COUNTRY
) {
152 *country_code
= base::UTF8ToUTF16(
153 base::StringPrintf("%d", i18n_number
->country_code()));
156 // The region might be different from what we started with.
157 phone_util
->GetRegionCodeForNumber(*i18n_number
, inferred_region
);
162 base::string16
NormalizePhoneNumber(const base::string16
& value
,
163 const std::string
& region
) {
164 DCHECK_EQ(2u, region
.size());
165 base::string16 country_code
, unused_city_code
, unused_number
;
166 std::string unused_region
;
167 PhoneNumber phone_number
;
168 if (!ParsePhoneNumber(value
, region
, &country_code
, &unused_city_code
,
169 &unused_number
, &unused_region
, &phone_number
)) {
170 return base::string16(); // Parsing failed - do not store phone.
173 base::string16 normalized_number
;
174 FormatValidatedNumber(phone_number
, country_code
, NULL
, &normalized_number
);
175 return normalized_number
;
178 bool ConstructPhoneNumber(const base::string16
& country_code
,
179 const base::string16
& city_code
,
180 const base::string16
& number
,
181 const std::string
& region
,
182 base::string16
* whole_number
) {
183 DCHECK_EQ(2u, region
.size());
184 whole_number
->clear();
186 base::string16 unused_country_code
, unused_city_code
, unused_number
;
187 std::string unused_region
;
188 PhoneNumber phone_number
;
189 if (!ParsePhoneNumber(country_code
+ city_code
+ number
, region
,
190 &unused_country_code
, &unused_city_code
, &unused_number
,
191 &unused_region
, &phone_number
)) {
195 FormatValidatedNumber(phone_number
, country_code
, whole_number
, NULL
);
199 bool PhoneNumbersMatch(const base::string16
& number_a
,
200 const base::string16
& number_b
,
201 const std::string
& raw_region
,
202 const std::string
& app_locale
) {
203 // Sanitize the provided |raw_region| before trying to use it for parsing.
204 const std::string region
= SanitizeRegion(raw_region
, app_locale
);
206 PhoneNumberUtil
* phone_util
= PhoneNumberUtil::GetInstance();
208 // Parse phone numbers based on the region
209 PhoneNumber i18n_number1
;
210 if (phone_util
->Parse(
211 base::UTF16ToUTF8(number_a
), region
.c_str(), &i18n_number1
) !=
212 PhoneNumberUtil::NO_PARSING_ERROR
) {
216 PhoneNumber i18n_number2
;
217 if (phone_util
->Parse(
218 base::UTF16ToUTF8(number_b
), region
.c_str(), &i18n_number2
) !=
219 PhoneNumberUtil::NO_PARSING_ERROR
) {
223 switch (phone_util
->IsNumberMatch(i18n_number1
, i18n_number2
)) {
224 case PhoneNumberUtil::INVALID_NUMBER
:
225 case PhoneNumberUtil::NO_MATCH
:
227 case PhoneNumberUtil::SHORT_NSN_MATCH
:
229 case PhoneNumberUtil::NSN_MATCH
:
230 case PhoneNumberUtil::EXACT_MATCH
:
238 PhoneObject::PhoneObject(const base::string16
& number
,
239 const std::string
& region
) {
240 DCHECK_EQ(2u, region
.size());
241 // TODO(isherman): Autofill profiles should always have a |region| set, but in
242 // some cases it should be marked as implicit. Otherwise, phone numbers
243 // might behave differently when they are synced across computers:
244 // [ http://crbug.com/100845 ]. Once the bug is fixed, add a DCHECK here to
247 scoped_ptr
<PhoneNumber
> i18n_number(new PhoneNumber
);
248 if (ParsePhoneNumber(number
, region
, &country_code_
, &city_code_
, &number_
,
249 ®ion_
, i18n_number
.get())) {
250 // The phone number was successfully parsed, so store the parsed version.
251 // The formatted and normalized versions will be set on the first call to
252 // the coresponding methods.
253 i18n_number_
= i18n_number
.Pass();
255 // Parsing failed. Store passed phone "as is" into |whole_number_|.
256 whole_number_
= number
;
260 PhoneObject::PhoneObject(const PhoneObject
& other
) { *this = other
; }
262 PhoneObject::PhoneObject() {}
264 PhoneObject::~PhoneObject() {}
266 const base::string16
& PhoneObject::GetFormattedNumber() const {
267 if (i18n_number_
&& formatted_number_
.empty()) {
268 FormatValidatedNumber(*i18n_number_
, country_code_
, &formatted_number_
,
272 return formatted_number_
;
275 base::string16
PhoneObject::GetNationallyFormattedNumber() const {
276 base::string16 formatted
= whole_number_
;
278 FormatValidatedNumber(*i18n_number_
, base::string16(), &formatted
, NULL
);
283 const base::string16
& PhoneObject::GetWholeNumber() const {
284 if (i18n_number_
&& whole_number_
.empty()) {
285 FormatValidatedNumber(*i18n_number_
, country_code_
, &formatted_number_
,
289 return whole_number_
;
292 PhoneObject
& PhoneObject::operator=(const PhoneObject
& other
) {
296 region_
= other
.region_
;
298 if (other
.i18n_number_
.get())
299 i18n_number_
.reset(new PhoneNumber(*other
.i18n_number_
));
301 i18n_number_
.reset();
303 country_code_
= other
.country_code_
;
304 city_code_
= other
.city_code_
;
305 number_
= other
.number_
;
307 formatted_number_
= other
.formatted_number_
;
308 whole_number_
= other
.whole_number_
;
314 } // namespace autofill