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