Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(450)

Side by Side Diff: components/autofill/browser/phone_number_i18n.cc

Issue 17392006: In components/autofill, move browser/ to core/browser/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase to fix conflicts Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « components/autofill/browser/phone_number_i18n.h ('k') | components/autofill/browser/phone_number_i18n_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698