[Autofill] Ensure that clients pass the correct region info when parsing phone numbers.
BUG=100845
Review URL: https://chromiumcodereview.appspot.com/11783045
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175975 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc
index aa08f071..94e95f3 100644
--- a/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc
+++ b/chrome/browser/autofill/autofill_ie_toolbar_import_win.cc
@@ -14,6 +14,7 @@
#include "base/logging.h"
#include "base/string16.h"
#include "base/win/registry.h"
+#include "chrome/browser/autofill/autofill_country.h"
#include "chrome/browser/autofill/autofill_profile.h"
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/autofill/crypto/rc4_decryptor.h"
@@ -65,15 +66,15 @@
return true;
}
-string16 ReadAndDecryptValue(RegKey* key, const wchar_t* value_name) {
+string16 ReadAndDecryptValue(const RegKey& key, const wchar_t* value_name) {
DWORD data_type = REG_BINARY;
DWORD data_size = 0;
- LONG result = key->ReadValue(value_name, NULL, &data_size, &data_type);
+ LONG result = key.ReadValue(value_name, NULL, &data_size, &data_type);
if ((result != ERROR_SUCCESS) || !data_size || data_type != REG_BINARY)
return string16();
std::vector<uint8> data;
data.resize(data_size);
- result = key->ReadValue(value_name, &(data[0]), &data_size, &data_type);
+ result = key.ReadValue(value_name, &(data[0]), &data_size, &data_type);
if (result == ERROR_SUCCESS) {
std::string out_data;
if (syncer::DecryptData(data, &out_data)) {
@@ -123,42 +124,61 @@
typedef std::map<std::wstring, AutofillFieldType> RegToFieldMap;
-bool ImportSingleProfile(FormGroup* profile,
- RegKey* key,
- const RegToFieldMap& reg_to_field ) {
- DCHECK(profile != NULL);
- if (!key->Valid())
+// Imports address or credit card data from the given registry |key| into the
+// given |form_group|, with the help of |reg_to_field|. When importing address
+// data, writes the phone data into |phone|; otherwise, |phone| should be null.
+// Returns true if any fields were set, false otherwise.
+bool ImportSingleFormGroup(const RegKey& key,
+ const RegToFieldMap& reg_to_field,
+ FormGroup* form_group,
+ PhoneNumber::PhoneCombineHelper* phone) {
+ if (!key.Valid())
return false;
bool has_non_empty_fields = false;
- // Phones need to be rebuilt.
- PhoneNumber::PhoneCombineHelper phone;
-
- for (uint32 i = 0; i < key->GetValueCount(); ++i) {
+ for (uint32 i = 0; i < key.GetValueCount(); ++i) {
std::wstring value_name;
- if (key->GetValueNameAt(i, &value_name) != ERROR_SUCCESS)
+ if (key.GetValueNameAt(i, &value_name) != ERROR_SUCCESS)
continue;
+
RegToFieldMap::const_iterator it = reg_to_field.find(value_name);
if (it == reg_to_field.end())
continue; // This field is not imported.
+
string16 field_value = ReadAndDecryptValue(key, value_name.c_str());
if (!field_value.empty()) {
- has_non_empty_fields = true;
if (it->second == CREDIT_CARD_NUMBER)
field_value = DecryptCCNumber(field_value);
- // We need to store phone data in |phone| before building the whole number
- // at the end. The rest of the fields are set "as is".
+ // Phone numbers are stored piece-by-piece, and then reconstructed from
+ // the pieces. The rest of the fields are set "as is".
// TODO(isherman): Call SetInfo(), rather than SetRawInfo().
- if (!phone.SetInfo(it->second, field_value))
- profile->SetRawInfo(it->second, field_value);
+ if (!phone || !phone->SetInfo(it->second, field_value)) {
+ has_non_empty_fields = true;
+ form_group->SetRawInfo(it->second, field_value);
+ }
}
}
+
+ return has_non_empty_fields;
+}
+
+// Imports address data from the given registry |key| into the given |profile|,
+// with the help of |reg_to_field|. Returns true if any fields were set, false
+// otherwise.
+bool ImportSingleProfile(const RegKey& key,
+ const RegToFieldMap& reg_to_field,
+ AutofillProfile* profile) {
+ PhoneNumber::PhoneCombineHelper phone;
+ bool has_non_empty_fields =
+ ImportSingleFormGroup(key, reg_to_field, profile, &phone);
+
// Now re-construct the phones if needed.
string16 constructed_number;
- if (!phone.IsEmpty() &&
- phone.ParseNumber(std::string("US"), &constructed_number)) {
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ if (phone.ParseNumber(*profile, app_locale, &constructed_number)) {
+ has_non_empty_fields = true;
profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number);
}
@@ -232,7 +252,7 @@
key_name.append(iterator_profiles.Name());
RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ);
AutofillProfile profile;
- if (ImportSingleProfile(&profile, &key, reg_to_field)) {
+ if (ImportSingleProfile(key, reg_to_field, &profile)) {
// Combine phones into whole phone #.
profiles->push_back(profile);
}
@@ -241,8 +261,8 @@
string16 salt;
RegKey cc_key(HKEY_CURRENT_USER, kCreditCardKey, KEY_READ);
if (cc_key.Valid()) {
- password_hash = ReadAndDecryptValue(&cc_key, kPasswordHashValue);
- salt = ReadAndDecryptValue(&cc_key, kSaltValue);
+ password_hash = ReadAndDecryptValue(cc_key, kPasswordHashValue);
+ salt = ReadAndDecryptValue(cc_key, kSaltValue);
}
// We import CC profiles only if they are not password protected.
@@ -255,7 +275,7 @@
key_name.append(iterator_cc.Name());
RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ);
CreditCard credit_card;
- if (ImportSingleProfile(&credit_card, &key, reg_to_field)) {
+ if (ImportSingleFormGroup(key, reg_to_field, &credit_card, NULL)) {
// TODO(isherman): Call into GetInfo() below, rather than GetRawInfo().
string16 cc_number = credit_card.GetRawInfo(CREDIT_CARD_NUMBER);
if (!cc_number.empty())
diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc
index 14b6b17..7d6f259 100644
--- a/chrome/browser/autofill/personal_data_manager.cc
+++ b/chrome/browser/autofill/personal_data_manager.cc
@@ -294,8 +294,7 @@
// Construct the phone number. Reject the profile if the number is invalid.
if (imported_profile.get() && !home.IsEmpty()) {
string16 constructed_number;
- if (!home.ParseNumber(imported_profile->CountryCode(),
- &constructed_number) ||
+ if (!home.ParseNumber(*imported_profile, app_locale, &constructed_number) ||
!imported_profile->SetInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number,
app_locale)) {
imported_profile.reset();
diff --git a/chrome/browser/autofill/phone_number.cc b/chrome/browser/autofill/phone_number.cc
index e5027fa..8d3f859 100644
--- a/chrome/browser/autofill/phone_number.cc
+++ b/chrome/browser/autofill/phone_number.cc
@@ -28,6 +28,19 @@
RemoveChars(*number, kPhoneNumberSeparators, number);
}
+// Returns the region code for this phone number, which is an ISO 3166 2-letter
+// country code. The returned value is based on the |profile|; if the |profile|
+// does not have a country code associated with it, falls back to the country
+// code corresponding to the |app_locale|.
+std::string GetRegion(const AutofillProfile& profile,
+ const std::string& app_locale) {
+ std::string country_code = profile.CountryCode();
+ if (!country_code.empty())
+ return country_code;
+
+ return AutofillCountry::CountryCodeForLocale(app_locale);
+}
+
} // namespace
PhoneNumber::PhoneNumber(AutofillProfile* profile)
@@ -97,7 +110,8 @@
// TODO(isherman): Can/should this use the cached_parsed_phone_?
string16 normalized_phone =
- autofill_i18n::NormalizePhoneNumber(phone, GetRegion(app_locale));
+ autofill_i18n::NormalizePhoneNumber(phone,
+ GetRegion(*profile_, app_locale));
return !normalized_phone.empty() ? normalized_phone : phone;
}
@@ -156,7 +170,7 @@
// For US numbers, also compare to the three-digit prefix and the four-digit
// suffix, since web sites often split numbers into these two fields.
string16 number = GetInfo(PHONE_HOME_NUMBER, app_locale);
- if (GetRegion(app_locale) == "US" &&
+ if (GetRegion(*profile_, app_locale) == "US" &&
number.size() == (kPrefixLength + kSuffixLength)) {
string16 prefix = number.substr(kPrefixOffset, kPrefixLength);
string16 suffix = number.substr(kSuffixOffset, kSuffixLength);
@@ -167,22 +181,15 @@
string16 whole_number = GetInfo(PHONE_HOME_WHOLE_NUMBER, app_locale);
if (!whole_number.empty()) {
string16 normalized_number =
- autofill_i18n::NormalizePhoneNumber(text, GetRegion(app_locale));
+ autofill_i18n::NormalizePhoneNumber(text,
+ GetRegion(*profile_, app_locale));
if (normalized_number == whole_number)
matching_types->insert(PHONE_HOME_WHOLE_NUMBER);
}
}
-std::string PhoneNumber::GetRegion(const std::string& app_locale) const {
- const std::string country_code = profile_->CountryCode();
- if (country_code.empty())
- return AutofillCountry::CountryCodeForLocale(app_locale);
-
- return country_code;
-}
-
void PhoneNumber::UpdateCacheIfNeeded(const std::string& app_locale) const {
- std::string region = GetRegion(app_locale);
+ std::string region = GetRegion(*profile_, app_locale);
if (!number_.empty() && cached_parsed_phone_.GetRegion() != region)
cached_parsed_phone_ = autofill_i18n::PhoneObject(number_, region);
}
@@ -223,15 +230,20 @@
return false;
}
-bool PhoneNumber::PhoneCombineHelper::ParseNumber(const std::string& region,
- string16* value) {
+bool PhoneNumber::PhoneCombineHelper::ParseNumber(
+ const AutofillProfile& profile,
+ const std::string& app_locale,
+ string16* value) {
+ if (IsEmpty())
+ return false;
+
if (!whole_number_.empty()) {
*value = whole_number_;
return true;
}
return autofill_i18n::ConstructPhoneNumber(
- country_, city_, phone_, region,
+ country_, city_, phone_, GetRegion(profile, app_locale),
(country_.empty() ?
autofill_i18n::NATIONAL : autofill_i18n::INTERNATIONAL),
value);
diff --git a/chrome/browser/autofill/phone_number.h b/chrome/browser/autofill/phone_number.h
index e2f203b2..ac02ed768 100644
--- a/chrome/browser/autofill/phone_number.h
+++ b/chrome/browser/autofill/phone_number.h
@@ -56,8 +56,13 @@
// returns true. For all other field types returs false.
bool SetInfo(AutofillFieldType type, const string16& value);
- // Returns true if parsing was successful, false otherwise.
- bool ParseNumber(const std::string& region, string16* value);
+ // Parses the number built up from pieces stored via SetInfo() according to
+ // the specified |profile|'s country code, falling back to the given
+ // |app_locale| if the |profile| has no associated country code. Returns
+ // true if parsing was successful, false otherwise.
+ bool ParseNumber(const AutofillProfile& profile,
+ const std::string& app_locale,
+ string16* value);
// Returns true if both |phone_| and |whole_number_| are empty.
bool IsEmpty() const;
@@ -73,13 +78,6 @@
// FormGroup:
virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
- // Returns the region code for this phone number, which is an ISO 3166
- // 2-letter country code. The name "region" is chosen since "country code"
- // already refers to part of a phone number. The returned value is based on
- // the |profile_|; if the |profile_| does not have a country code associated
- // with it, falls back to the country code corresponding to the |app_locale|.
- std::string GetRegion(const std::string& app_locale) const;
-
// Updates the cached parsed number if the profile's region has changed
// since the last time the cache was updated.
void UpdateCacheIfNeeded(const std::string& app_locale) const;
diff --git a/chrome/browser/autofill/phone_number_unittest.cc b/chrome/browser/autofill/phone_number_unittest.cc
index c2c77e1c..0aa83ed 100644
--- a/chrome/browser/autofill/phone_number_unittest.cc
+++ b/chrome/browser/autofill/phone_number_unittest.cc
@@ -81,6 +81,9 @@
}
TEST(PhoneNumberTest, PhoneCombineHelper) {
+ AutofillProfile profile;
+ profile.SetCountryCode("US");
+
PhoneNumber::PhoneCombineHelper number1;
EXPECT_FALSE(number1.SetInfo(ADDRESS_BILLING_CITY,
ASCIIToUTF16("1")));
@@ -91,7 +94,7 @@
EXPECT_TRUE(number1.SetInfo(PHONE_HOME_NUMBER,
ASCIIToUTF16("2345678")));
string16 parsed_phone;
- EXPECT_TRUE(number1.ParseNumber("US", &parsed_phone));
+ EXPECT_TRUE(number1.ParseNumber(profile, "en-US", &parsed_phone));
// International format as it has a country code.
EXPECT_EQ(ASCIIToUTF16("+1 650-234-5678"), parsed_phone);
@@ -100,7 +103,7 @@
ASCIIToUTF16("650")));
EXPECT_TRUE(number3.SetInfo(PHONE_HOME_NUMBER,
ASCIIToUTF16("2345680")));
- EXPECT_TRUE(number3.ParseNumber("US", &parsed_phone));
+ EXPECT_TRUE(number3.ParseNumber(profile, "en-US", &parsed_phone));
// National format as it does not have a country code.
EXPECT_EQ(ASCIIToUTF16("(650) 234-5680"), parsed_phone);
@@ -109,13 +112,13 @@
ASCIIToUTF16("123"))); // Incorrect city code.
EXPECT_TRUE(number4.SetInfo(PHONE_HOME_NUMBER,
ASCIIToUTF16("2345680")));
- EXPECT_FALSE(number4.ParseNumber("US", &parsed_phone));
+ EXPECT_FALSE(number4.ParseNumber(profile, "en-US", &parsed_phone));
EXPECT_EQ(string16(), parsed_phone);
PhoneNumber::PhoneCombineHelper number5;
EXPECT_TRUE(number5.SetInfo(PHONE_HOME_CITY_AND_NUMBER,
ASCIIToUTF16("6502345681")));
- EXPECT_TRUE(number5.ParseNumber("US", &parsed_phone));
+ EXPECT_TRUE(number5.ParseNumber(profile, "en-US", &parsed_phone));
EXPECT_EQ(ASCIIToUTF16("(650) 234-5681"), parsed_phone);
PhoneNumber::PhoneCombineHelper number6;
@@ -125,6 +128,18 @@
ASCIIToUTF16("234")));
EXPECT_TRUE(number6.SetInfo(PHONE_HOME_NUMBER,
ASCIIToUTF16("5682")));
- EXPECT_TRUE(number6.ParseNumber("US", &parsed_phone));
+ EXPECT_TRUE(number6.ParseNumber(profile, "en-US", &parsed_phone));
+ EXPECT_EQ(ASCIIToUTF16("(650) 234-5682"), parsed_phone);
+
+ // Ensure parsing is possible when falling back to detecting the country code
+ // based on the app locale.
+ PhoneNumber::PhoneCombineHelper number7;
+ EXPECT_TRUE(number7.SetInfo(PHONE_HOME_CITY_CODE,
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number7.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("234")));
+ EXPECT_TRUE(number7.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("5682")));
+ EXPECT_TRUE(number7.ParseNumber(AutofillProfile(), "en-US", &parsed_phone));
EXPECT_EQ(ASCIIToUTF16("(650) 234-5682"), parsed_phone);
}