phone_number_i18n.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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. 4 5#include "components/autofill/core/browser/phone_number_i18n.h" 6 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" 15 16using i18n::phonenumbers::PhoneNumber; 17using i18n::phonenumbers::PhoneNumberUtil; 18 19namespace autofill { 20 21namespace { 22 23std::string SanitizeRegion(const std::string& region, 24 const std::string& app_locale) { 25 if (region.length() == 2) 26 return region; 27 28 return AutofillCountry::CountryCodeForLocale(app_locale); 29} 30 31// Returns true if |phone_number| is valid. 32bool IsValidPhoneNumber(const PhoneNumber& phone_number) { 33 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 34 if (!phone_util->IsPossibleNumber(phone_number)) 35 return false; 36 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 40 // start with 1). 41 if (!phone_util->IsValidNumber(phone_number)) 42 return false; 43 44 return true; 45} 46 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. 55void 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; 63 64 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 65 std::string processed_number; 66 phone_util->Format(number, format, &processed_number); 67 68 if (formatted_number) 69 *formatted_number = base::UTF8ToUTF16(processed_number); 70 71 if (normalized_number) { 72 phone_util->NormalizeDigitsOnly(&processed_number); 73 *normalized_number = base::UTF8ToUTF16(processed_number); 74 } 75} 76 77} // namespace 78 79namespace i18n { 80 81// Parses the number stored in |value| as it should be interpreted in the given 82// |default_region|, and stores the results into the remaining arguments. 83// The |default_region| should be sanitized prior to calling this function. 84bool ParsePhoneNumber(const base::string16& value, 85 const std::string& default_region, 86 base::string16* country_code, 87 base::string16* city_code, 88 base::string16* number, 89 std::string* inferred_region, 90 PhoneNumber* i18n_number) { 91 country_code->clear(); 92 city_code->clear(); 93 number->clear(); 94 *i18n_number = PhoneNumber(); 95 96 std::string number_text(base::UTF16ToUTF8(value)); 97 98 // Parse phone number based on the region. 99 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 100 101 // The |default_region| should already be sanitized. 102 DCHECK_EQ(2U, default_region.size()); 103 if (phone_util->ParseAndKeepRawInput( 104 number_text, default_region, i18n_number) != 105 PhoneNumberUtil::NO_PARSING_ERROR) { 106 return false; 107 } 108 109 if (!IsValidPhoneNumber(*i18n_number)) 110 return false; 111 112 std::string national_significant_number; 113 phone_util->GetNationalSignificantNumber(*i18n_number, 114 &national_significant_number); 115 116 int area_length = phone_util->GetLengthOfGeographicalAreaCode(*i18n_number); 117 int destination_length = 118 phone_util->GetLengthOfNationalDestinationCode(*i18n_number); 119 // Some phones have a destination code in lieu of area code: mobile operators 120 // in Europe, toll and toll-free numbers in USA, etc. From our point of view 121 // these two types of codes are the same. 122 if (destination_length > area_length) 123 area_length = destination_length; 124 125 std::string area_code; 126 std::string subscriber_number; 127 if (area_length > 0) { 128 area_code = national_significant_number.substr(0, area_length); 129 subscriber_number = national_significant_number.substr(area_length); 130 } else { 131 subscriber_number = national_significant_number; 132 } 133 *number = base::UTF8ToUTF16(subscriber_number); 134 *city_code = base::UTF8ToUTF16(area_code); 135 136 // Check if parsed number has a country code that was not inferred from the 137 // region. 138 if (i18n_number->has_country_code() && 139 i18n_number->country_code_source() != PhoneNumber::FROM_DEFAULT_COUNTRY) { 140 *country_code = base::UTF8ToUTF16( 141 base::StringPrintf("%d", i18n_number->country_code())); 142 } 143 144 // The region might be different from what we started with. 145 phone_util->GetRegionCodeForNumber(*i18n_number, inferred_region); 146 147 return true; 148} 149 150base::string16 NormalizePhoneNumber(const base::string16& value, 151 const std::string& region) { 152 DCHECK_EQ(2u, region.size()); 153 base::string16 country_code, unused_city_code, unused_number; 154 std::string unused_region; 155 PhoneNumber phone_number; 156 if (!ParsePhoneNumber(value, region, &country_code, &unused_city_code, 157 &unused_number, &unused_region, &phone_number)) { 158 return base::string16(); // Parsing failed - do not store phone. 159 } 160 161 base::string16 normalized_number; 162 FormatValidatedNumber(phone_number, country_code, NULL, &normalized_number); 163 return normalized_number; 164} 165 166bool ConstructPhoneNumber(const base::string16& country_code, 167 const base::string16& city_code, 168 const base::string16& number, 169 const std::string& region, 170 base::string16* whole_number) { 171 DCHECK_EQ(2u, region.size()); 172 whole_number->clear(); 173 174 base::string16 unused_country_code, unused_city_code, unused_number; 175 std::string unused_region; 176 PhoneNumber phone_number; 177 if (!ParsePhoneNumber(country_code + city_code + number, region, 178 &unused_country_code, &unused_city_code, &unused_number, 179 &unused_region, &phone_number)) { 180 return false; 181 } 182 183 FormatValidatedNumber(phone_number, country_code, whole_number, NULL); 184 return true; 185} 186 187bool PhoneNumbersMatch(const base::string16& number_a, 188 const base::string16& number_b, 189 const std::string& raw_region, 190 const std::string& app_locale) { 191 // Sanitize the provided |raw_region| before trying to use it for parsing. 192 const std::string region = SanitizeRegion(raw_region, app_locale); 193 194 PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance(); 195 196 // Parse phone numbers based on the region 197 PhoneNumber i18n_number1; 198 if (phone_util->Parse( 199 base::UTF16ToUTF8(number_a), region.c_str(), &i18n_number1) != 200 PhoneNumberUtil::NO_PARSING_ERROR) { 201 return false; 202 } 203 204 PhoneNumber i18n_number2; 205 if (phone_util->Parse( 206 base::UTF16ToUTF8(number_b), region.c_str(), &i18n_number2) != 207 PhoneNumberUtil::NO_PARSING_ERROR) { 208 return false; 209 } 210 211 switch (phone_util->IsNumberMatch(i18n_number1, i18n_number2)) { 212 case PhoneNumberUtil::INVALID_NUMBER: 213 case PhoneNumberUtil::NO_MATCH: 214 return false; 215 case PhoneNumberUtil::SHORT_NSN_MATCH: 216 return false; 217 case PhoneNumberUtil::NSN_MATCH: 218 case PhoneNumberUtil::EXACT_MATCH: 219 return true; 220 } 221 222 NOTREACHED(); 223 return false; 224} 225 226PhoneObject::PhoneObject(const base::string16& number, 227 const std::string& region) { 228 DCHECK_EQ(2u, region.size()); 229 // TODO(isherman): Autofill profiles should always have a |region| set, but in 230 // some cases it should be marked as implicit. Otherwise, phone numbers 231 // might behave differently when they are synced across computers: 232 // [ http://crbug.com/100845 ]. Once the bug is fixed, add a DCHECK here to 233 // verify. 234 235 scoped_ptr<PhoneNumber> i18n_number(new PhoneNumber); 236 if (ParsePhoneNumber(number, region, &country_code_, &city_code_, &number_, 237 ®ion_, i18n_number.get())) { 238 // The phone number was successfully parsed, so store the parsed version. 239 // The formatted and normalized versions will be set on the first call to 240 // the coresponding methods. 241 i18n_number_ = i18n_number.Pass(); 242 } else { 243 // Parsing failed. Store passed phone "as is" into |whole_number_|. 244 whole_number_ = number; 245 } 246} 247 248PhoneObject::PhoneObject(const PhoneObject& other) { *this = other; } 249 250PhoneObject::PhoneObject() {} 251 252PhoneObject::~PhoneObject() {} 253 254const base::string16& PhoneObject::GetFormattedNumber() const { 255 if (i18n_number_ && formatted_number_.empty()) { 256 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_, 257 &whole_number_); 258 } 259 260 return formatted_number_; 261} 262 263base::string16 PhoneObject::GetNationallyFormattedNumber() const { 264 base::string16 formatted = whole_number_; 265 if (i18n_number_) 266 FormatValidatedNumber(*i18n_number_, base::string16(), &formatted, NULL); 267 268 return formatted; 269} 270 271const base::string16& PhoneObject::GetWholeNumber() const { 272 if (i18n_number_ && whole_number_.empty()) { 273 FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_, 274 &whole_number_); 275 } 276 277 return whole_number_; 278} 279 280PhoneObject& PhoneObject::operator=(const PhoneObject& other) { 281 if (this == &other) 282 return *this; 283 284 region_ = other.region_; 285 286 if (other.i18n_number_.get()) 287 i18n_number_.reset(new PhoneNumber(*other.i18n_number_)); 288 else 289 i18n_number_.reset(); 290 291 country_code_ = other.country_code_; 292 city_code_ = other.city_code_; 293 number_ = other.number_; 294 295 formatted_number_ = other.formatted_number_; 296 whole_number_ = other.whole_number_; 297 298 return *this; 299} 300 301} // namespace i18n 302} // namespace autofill 303