diff options
author | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-05 04:01:21 +0000 |
---|---|---|
committer | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-05 04:01:21 +0000 |
commit | 2a958550ec25aeb2da7fa893015133f2aae32f41 (patch) | |
tree | dafc82496cf0b1f14d39183b5e9860a8ca255e70 | |
parent | bed98da0c1e62387a477903910781f1b54916039 (diff) | |
download | chromium_src-2a958550ec25aeb2da7fa893015133f2aae32f41.zip chromium_src-2a958550ec25aeb2da7fa893015133f2aae32f41.tar.gz chromium_src-2a958550ec25aeb2da7fa893015133f2aae32f41.tar.bz2 |
Refactor Autofill parsing code. Most notably, add a helper class for parsing with lookahead.
* Adds an AutofillScanner class to help with lookahead parsing.
* Remove the NULL-termination from FormStructure's fields vector
* Remove some redundant DCHECKs
* Refactor PersonalDataManager::ImportFormData() to take a single form, not a vector of forms.
* Move EmailField class to its own file
* Remove some obsolete billing/shipping address distinguishing code
* Refactor the code to remove the really wonky FormFieldSet() class
* Refactor some interfaces to take |size_t| rather than |int|
* Remove some unused fields from FormStructure
* Const-correctness
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6910018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@84211 0039d316-1c4b-4281-b951-d872f2087c98
27 files changed, 897 insertions, 1011 deletions
diff --git a/chrome/browser/autofill/address_field.cc b/chrome/browser/autofill/address_field.cc index 1cf05c7..4624fa5 100644 --- a/chrome/browser/autofill/address_field.cc +++ b/chrome/browser/autofill/address_field.cc @@ -12,6 +12,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "grit/autofill_resources.h" #include "ui/base/l10n/l10n_util.h" @@ -52,44 +53,23 @@ bool AddressField::GetFieldInfo(FieldTypeMap* field_type_map) const { return false; } - bool ok; - ok = Add(field_type_map, company_, AutofillType(address_company)); - DCHECK(ok); - ok = ok && Add(field_type_map, address1_, AutofillType(address_line1)); - DCHECK(ok); - ok = ok && Add(field_type_map, address2_, AutofillType(address_line2)); - DCHECK(ok); - ok = ok && Add(field_type_map, city_, AutofillType(address_city)); - DCHECK(ok); - ok = ok && Add(field_type_map, state_, AutofillType(address_state)); - DCHECK(ok); - ok = ok && Add(field_type_map, zip_, AutofillType(address_zip)); - DCHECK(ok); - ok = ok && Add(field_type_map, country_, AutofillType(address_country)); - DCHECK(ok); - + bool ok = Add(field_type_map, company_, address_company); + ok = ok && Add(field_type_map, address1_, address_line1); + ok = ok && Add(field_type_map, address2_, address_line2); + ok = ok && Add(field_type_map, city_, address_city); + ok = ok && Add(field_type_map, state_, address_state); + ok = ok && Add(field_type_map, zip_, address_zip); + ok = ok && Add(field_type_map, country_, address_country); return ok; } -FormFieldType AddressField::GetFormFieldType() const { - return kAddressType; -} - -AddressField* AddressField::Parse( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml) { - DCHECK(iter); - if (!iter) +AddressField* AddressField::Parse(AutofillScanner* scanner, bool is_ecml) { + if (scanner->IsEnd()) return NULL; scoped_ptr<AddressField> address_field(new AddressField); - std::vector<AutofillField*>::const_iterator q = *iter; - string16 pattern; - - // The ECML standard uses 2 letter country codes. So we will - // have to remember that this is an ECML form, for when we fill - // it out. - address_field->is_ecml_ = is_ecml; + const AutofillField* initial_field = scanner->Cursor(); + scanner->SaveCursor(); string16 attention_ignored = l10n_util::GetStringUTF16(IDS_AUTOFILL_ATTENTION_IGNORED_RE); @@ -97,21 +77,21 @@ AddressField* AddressField::Parse( l10n_util::GetStringUTF16(IDS_AUTOFILL_REGION_IGNORED_RE); // Allow address fields to appear in any order. - while (true) { - if (ParseCompany(&q, is_ecml, address_field.get()) || - ParseAddressLines(&q, is_ecml, address_field.get()) || - ParseCity(&q, is_ecml, address_field.get()) || - ParseState(&q, is_ecml, address_field.get()) || - ParseZipCode(&q, is_ecml, address_field.get()) || - ParseCountry(&q, is_ecml, address_field.get())) { + while (!scanner->IsEnd()) { + if (ParseCompany(scanner, is_ecml, address_field.get()) || + ParseAddressLines(scanner, is_ecml, address_field.get()) || + ParseCity(scanner, is_ecml, address_field.get()) || + ParseState(scanner, is_ecml, address_field.get()) || + ParseZipCode(scanner, is_ecml, address_field.get()) || + ParseCountry(scanner, is_ecml, address_field.get())) { continue; - } else if (ParseText(&q, attention_ignored) || - ParseText(&q, region_ignored)) { + } else if (ParseText(scanner, attention_ignored) || + ParseText(scanner, region_ignored)) { // We ignore the following: // * Attention. // * Province/Region/Other. continue; - } else if (*q != **iter && ParseEmpty(&q)) { + } else if (scanner->Cursor() != initial_field && ParseEmpty(scanner)) { // Ignore non-labeled fields within an address; the page // MapQuest Driving Directions North America.html contains such a field. // We only ignore such fields after we've parsed at least one other field; @@ -133,10 +113,11 @@ AddressField* AddressField::Parse( address_field->city_ != NULL || address_field->state_ != NULL || address_field->zip_ != NULL || address_field->zip4_ || address_field->country_ != NULL) { - *iter = q; + address_field->type_ = address_field->FindType(); return address_field.release(); } + scanner->Rewind(); return NULL; } @@ -167,34 +148,31 @@ AddressField::AddressField() zip_(NULL), zip4_(NULL), country_(NULL), - type_(kGenericAddress), - is_ecml_(false) { + type_(kGenericAddress) { } // static -bool AddressField::ParseCompany( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field) { +bool AddressField::ParseCompany(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field) { if (address_field->company_ && !address_field->company_->IsEmpty()) return false; string16 pattern; - if (is_ecml) + if (is_ecml) { pattern = GetEcmlPattern(kEcmlShipToCompanyName, kEcmlBillToCompanyName, '|'); - else + } else { pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_COMPANY_RE); + } - if (!ParseText(iter, pattern, &address_field->company_)) - return false; - - return true; + return ParseText(scanner, pattern, &address_field->company_); } // static -bool AddressField::ParseAddressLines( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field) { +bool AddressField::ParseAddressLines(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field) { // We only match the string "address" in page text, not in element names, // because sometimes every element in a group of address fields will have // a name containing the string "address"; for example, on the page @@ -209,16 +187,16 @@ bool AddressField::ParseAddressLines( string16 pattern; if (is_ecml) { pattern = GetEcmlPattern(kEcmlShipToAddress1, kEcmlBillToAddress1, '|'); - if (!ParseText(iter, pattern, &address_field->address1_)) + if (!ParseText(scanner, pattern, &address_field->address1_)) return false; } else { pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_LINE_1_RE); string16 label_pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_LINE_1_LABEL_RE); - if (!ParseText(iter, pattern, &address_field->address1_)) - if (!ParseLabelText(iter, label_pattern, &address_field->address1_)) - return false; + if (!ParseText(scanner, pattern, &address_field->address1_) && + !ParseLabelText(scanner, label_pattern, &address_field->address1_)) + return false; } // Optionally parse more address lines, which may have empty labels. @@ -226,26 +204,26 @@ bool AddressField::ParseAddressLines( // Some pages even have 4 address lines (e.g. uk/ShoesDirect2.html)! if (is_ecml) { pattern = GetEcmlPattern(kEcmlShipToAddress2, kEcmlBillToAddress2, '|'); - if (!ParseEmptyText(iter, &address_field->address2_)) - ParseText(iter, pattern, &address_field->address2_); + if (!ParseEmptyText(scanner, &address_field->address2_)) + ParseText(scanner, pattern, &address_field->address2_); } else { pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_LINE_2_RE); string16 label_pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_LINE_1_LABEL_RE); - if (!ParseEmptyText(iter, &address_field->address2_)) - if (!ParseText(iter, pattern, &address_field->address2_)) - ParseLabelText(iter, label_pattern, &address_field->address2_); + if (!ParseEmptyText(scanner, &address_field->address2_) && + !ParseText(scanner, pattern, &address_field->address2_)) + ParseLabelText(scanner, label_pattern, &address_field->address2_); } // Try for a third line, which we will promptly discard. if (address_field->address2_ != NULL) { if (is_ecml) { pattern = GetEcmlPattern(kEcmlShipToAddress3, kEcmlBillToAddress3, '|'); - ParseText(iter, pattern); + ParseText(scanner, pattern); } else { pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_LINE_3_RE); - if (!ParseEmptyText(iter, NULL)) - ParseText(iter, pattern, NULL); + if (!ParseEmptyText(scanner, NULL)) + ParseText(scanner, pattern, NULL); } } @@ -253,9 +231,9 @@ bool AddressField::ParseAddressLines( } // static -bool AddressField::ParseCountry( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field) { +bool AddressField::ParseCountry(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field) { // Parse a country. The occasional page (e.g. // Travelocity_New Member Information1.html) calls this a "location". // Note: ECML standard uses 2 letter country code (ISO 3166) @@ -268,16 +246,13 @@ bool AddressField::ParseCountry( else pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_COUNTRY_RE); - if (!ParseText(iter, pattern, &address_field->country_)) - return false; - - return true; + return ParseText(scanner, pattern, &address_field->country_); } // static -bool AddressField::ParseZipCode( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field) { +bool AddressField::ParseZipCode(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field) { // Parse a zip code. On some UK pages (e.g. The China Shop2.html) this // is called a "post code". // @@ -288,10 +263,6 @@ bool AddressField::ParseZipCode( if (address_field->zip_) return false; - // We may be out of fields. - if (!**iter) - return false; - string16 pattern; if (is_ecml) { pattern = GetEcmlPattern(kEcmlShipToPostalCode, kEcmlBillToPostalCode, '|'); @@ -300,7 +271,7 @@ bool AddressField::ParseZipCode( } AddressType tempType; - string16 name = (**iter)->name; + string16 name = scanner->Cursor()->name; // Note: comparisons using the ecml compliant name as a prefix must be used in // order to accommodate Google Checkout. See FormFieldSet::GetEcmlPattern for @@ -314,14 +285,14 @@ bool AddressField::ParseZipCode( tempType = kGenericAddress; } - if (!ParseText(iter, pattern, &address_field->zip_)) + if (!ParseText(scanner, pattern, &address_field->zip_)) return false; address_field->type_ = tempType; if (!is_ecml) { // Look for a zip+4, whose field name will also often contain // the substring "zip". - ParseText(iter, + ParseText(scanner, l10n_util::GetStringUTF16(IDS_AUTOFILL_ZIP_4_RE), &address_field->zip4_); } @@ -330,9 +301,9 @@ bool AddressField::ParseZipCode( } // static -bool AddressField::ParseCity( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field) { +bool AddressField::ParseCity(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field) { // Parse a city name. Some UK pages (e.g. The China Shop2.html) use // the term "town". if (address_field->city_) @@ -344,16 +315,13 @@ bool AddressField::ParseCity( else pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CITY_RE); - if (!ParseText(iter, pattern, &address_field->city_)) - return false; - - return true; + return ParseText(scanner, pattern, &address_field->city_); } // static -bool AddressField::ParseState( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field) { +bool AddressField::ParseState(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field) { if (address_field->state_) return false; @@ -363,10 +331,7 @@ bool AddressField::ParseState( else pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_STATE_RE); - if (!ParseText(iter, pattern, &address_field->state_)) - return false; - - return true; + return ParseText(scanner, pattern, &address_field->state_); } AddressType AddressField::AddressTypeFromText(const string16 &text) { diff --git a/chrome/browser/autofill/address_field.h b/chrome/browser/autofill/address_field.h index 2d546e9..f2cc08a 100644 --- a/chrome/browser/autofill/address_field.h +++ b/chrome/browser/autofill/address_field.h @@ -9,26 +9,24 @@ #include <vector> #include "base/basictypes.h" +#include "base/compiler_specific.h" #include "base/string16.h" #include "chrome/browser/autofill/autofill_type.h" #include "chrome/browser/autofill/field_types.h" #include "chrome/browser/autofill/form_field.h" class AutofillField; +class AutofillScanner; class AddressField : public FormField { public: - virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const; - virtual FormFieldType GetFormFieldType() const; + virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const OVERRIDE; - static AddressField* Parse(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml); + static AddressField* Parse(AutofillScanner* scanner, bool is_ecml); // Tries to determine the billing/shipping type of this address. AddressType FindType() const; - void SetType(AddressType address_type) { type_ = address_type; } - // Returns true if this is a full address as opposed to an address fragment // such as a stand-alone ZIP code. bool IsFullAddress(); @@ -36,35 +34,39 @@ class AddressField : public FormField { private: AddressField(); - static bool ParseCompany(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field); - static bool ParseAddressLines( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field); - static bool ParseCountry(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field); - static bool ParseZipCode(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field); - static bool ParseCity(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field); - static bool ParseState(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml, AddressField* address_field); + static bool ParseCompany(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field); + static bool ParseAddressLines(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field); + static bool ParseCountry(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field); + static bool ParseZipCode(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field); + static bool ParseCity(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field); + static bool ParseState(AutofillScanner* scanner, + bool is_ecml, + AddressField* address_field); // Looks for an address type in the given text, which the caller must // convert to lowercase. static AddressType AddressTypeFromText(const string16& text); - AutofillField* company_; // optional - AutofillField* address1_; - AutofillField* address2_; // optional - AutofillField* city_; - AutofillField* state_; // optional - AutofillField* zip_; - AutofillField* zip4_; // optional ZIP+4; we don't fill this yet - AutofillField* country_; // optional + const AutofillField* company_; // optional + const AutofillField* address1_; + const AutofillField* address2_; // optional + const AutofillField* city_; + const AutofillField* state_; // optional + const AutofillField* zip_; + const AutofillField* zip4_; // optional ZIP+4; we don't fill this yet + const AutofillField* country_; // optional AddressType type_; - bool is_ecml_; DISALLOW_COPY_AND_ASSIGN(AddressField); }; diff --git a/chrome/browser/autofill/address_field_unittest.cc b/chrome/browser/autofill/address_field_unittest.cc index 7f63a3c..fe7a910 100644 --- a/chrome/browser/autofill/address_field_unittest.cc +++ b/chrome/browser/autofill/address_field_unittest.cc @@ -7,6 +7,7 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/address_field.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/glue/form_field.h" @@ -27,17 +28,15 @@ class AddressFieldTest : public testing::Test { }; TEST_F(AddressFieldTest, Empty) { - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_EQ(static_cast<AddressField*>(NULL), field_.get()); } TEST_F(AddressFieldTest, NonParse) { list_.push_back(new AutofillField); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_EQ(static_cast<AddressField*>(NULL), field_.get()); } @@ -50,9 +49,8 @@ TEST_F(AddressFieldTest, ParseOneLineAddress) { 0, false), ASCIIToUTF16("addr1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); @@ -71,16 +69,15 @@ TEST_F(AddressFieldTest, ParseOneLineAddressBilling) { 0, false), ASCIIToUTF16("addr1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kBillingAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( field_type_map_.find(ASCIIToUTF16("addr1")) != field_type_map_.end()); - EXPECT_EQ(ADDRESS_HOME_LINE1, field_type_map_[ASCIIToUTF16("addr1")]); + EXPECT_EQ(ADDRESS_BILLING_LINE1, field_type_map_[ASCIIToUTF16("addr1")]); } TEST_F(AddressFieldTest, ParseOneLineAddressShipping) { @@ -92,9 +89,8 @@ TEST_F(AddressFieldTest, ParseOneLineAddressShipping) { 0, false), ASCIIToUTF16("addr1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kShippingAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); @@ -114,9 +110,8 @@ TEST_F(AddressFieldTest, ParseOneLineAddressEcml) { 0, false), ASCIIToUTF16("addr1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, true)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kShippingAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); @@ -143,9 +138,8 @@ TEST_F(AddressFieldTest, ParseTwoLineAddress) { 0, false), ASCIIToUTF16("addr2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); @@ -183,9 +177,8 @@ TEST_F(AddressFieldTest, ParseThreeLineAddress) { 0, false), ASCIIToUTF16("addr3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); @@ -219,9 +212,8 @@ TEST_F(AddressFieldTest, ParseTwoLineAddressEcml) { 0, false), ASCIIToUTF16("addr2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, true)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kShippingAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); @@ -243,9 +235,8 @@ TEST_F(AddressFieldTest, ParseCity) { 0, false), ASCIIToUTF16("city1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -264,9 +255,8 @@ TEST_F(AddressFieldTest, ParseCityEcml) { 0, false), ASCIIToUTF16("city1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, true)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -285,9 +275,8 @@ TEST_F(AddressFieldTest, ParseState) { 0, false), ASCIIToUTF16("state1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -307,9 +296,8 @@ TEST_F(AddressFieldTest, ParseStateEcml) { 0, false), ASCIIToUTF16("state1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, true)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -328,9 +316,8 @@ TEST_F(AddressFieldTest, ParseZip) { 0, false), ASCIIToUTF16("zip1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -350,9 +337,8 @@ TEST_F(AddressFieldTest, ParseZipEcml) { 0, false), ASCIIToUTF16("zip1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, true)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -383,9 +369,8 @@ TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) { 0, false), ASCIIToUTF16("zip"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -407,9 +392,8 @@ TEST_F(AddressFieldTest, ParseCountry) { 0, false), ASCIIToUTF16("country1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -428,9 +412,8 @@ TEST_F(AddressFieldTest, ParseCountryEcml) { 0, false), ASCIIToUTF16("country1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, true)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -457,9 +440,8 @@ TEST_F(AddressFieldTest, ParseTwoLineAddressMissingLabel) { 0, false), ASCIIToUTF16("addr2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_TRUE(field_->IsFullAddress()); @@ -481,9 +463,8 @@ TEST_F(AddressFieldTest, ParseCompany) { 0, false), ASCIIToUTF16("company1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, false)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); @@ -503,9 +484,8 @@ TEST_F(AddressFieldTest, ParseCompanyEcml) { 0, false), ASCIIToUTF16("company1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(AddressField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(AddressField::Parse(&scanner, true)); ASSERT_NE(static_cast<AddressField*>(NULL), field_.get()); EXPECT_EQ(kGenericAddress, field_->FindType()); EXPECT_FALSE(field_->IsFullAddress()); diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index ea32437..e11a6d4 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -625,11 +625,8 @@ void AutofillManager::DeterminePossibleFieldTypesForUpload( } void AutofillManager::ImportFormData(const FormStructure& submitted_form) { - std::vector<const FormStructure*> submitted_forms; - submitted_forms.push_back(&submitted_form); - const CreditCard* imported_credit_card; - if (!personal_data_->ImportFormData(submitted_forms, &imported_credit_card)) + if (!personal_data_->ImportFormData(submitted_form, &imported_credit_card)) return; // If credit card information was submitted, show an infobar to offer to save @@ -742,11 +739,6 @@ bool AutofillManager::FindCachedFormAndField(const FormData& form, for (std::vector<AutofillField*>::const_iterator iter = (*form_structure)->begin(); iter != (*form_structure)->end(); ++iter) { - // The field list is terminated with a NULL AutofillField, so don't try to - // dereference it. - if (!*iter) - break; - if ((**iter) == field) { *autofill_field = *iter; break; @@ -793,10 +785,6 @@ void AutofillManager::GetProfileSuggestions(FormStructure* form, form_fields.reserve(form->field_count()); for (std::vector<AutofillField*>::const_iterator iter = form->begin(); iter != form->end(); ++iter) { - // The field list is terminated with a NULL AutofillField, so don't try to - // dereference it. - if (!*iter) - break; form_fields.push_back((*iter)->type()); } diff --git a/chrome/browser/autofill/autofill_merge_unittest.cc b/chrome/browser/autofill/autofill_merge_unittest.cc index 64ee661..6541e24 100644 --- a/chrome/browser/autofill/autofill_merge_unittest.cc +++ b/chrome/browser/autofill/autofill_merge_unittest.cc @@ -199,11 +199,10 @@ void AutofillMergeTest::MergeProfiles(const std::string& profiles, AutofillType::StringToFieldType(UTF16ToUTF8(field->name)); field->set_heuristic_type(type); } - std::vector<const FormStructure*> form_structures(1, &form_structure); // Import the profile. const CreditCard* imported_credit_card; - personal_data_->ImportFormData(form_structures, &imported_credit_card); + personal_data_->ImportFormData(form_structure, &imported_credit_card); EXPECT_FALSE(imported_credit_card); // Clear the |form| to start a new profile. diff --git a/chrome/browser/autofill/autofill_scanner.cc b/chrome/browser/autofill/autofill_scanner.cc new file mode 100644 index 0000000..9358854 --- /dev/null +++ b/chrome/browser/autofill/autofill_scanner.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/autofill/autofill_scanner.h" + +#include "base/logging.h" +#include "chrome/browser/autofill/autofill_field.h" + +AutofillScanner::AutofillScanner(const std::vector<AutofillField*>& fields) + : cursor_(fields.begin()), + end_(fields.end()) { +} + +void AutofillScanner::Advance() { + DCHECK(!IsEnd()); + ++cursor_; +} + +const AutofillField* AutofillScanner::Cursor() const { + if (IsEnd()) { + NOTREACHED(); + return NULL; + } + + return *cursor_; +} + +bool AutofillScanner::IsEnd() const { + return cursor_ == end_; +} + +void AutofillScanner::Rewind() { + DCHECK(!saved_cursors_.empty()); + cursor_ = saved_cursors_.back(); + saved_cursors_.pop_back(); +} + +void AutofillScanner::SaveCursor() { + saved_cursors_.push_back(cursor_); +} diff --git a/chrome/browser/autofill/autofill_scanner.h b/chrome/browser/autofill/autofill_scanner.h new file mode 100644 index 0000000..13e8b24 --- /dev/null +++ b/chrome/browser/autofill/autofill_scanner.h @@ -0,0 +1,50 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_SCANNER_H_ +#define CHROME_BROWSER_AUTOFILL_AUTOFILL_SCANNER_H_ +#pragma once + +#include <vector> + +#include "base/basictypes.h" + +class AutofillField; + +// A helper class for parsing a stream of |AutofillField|'s with lookahead. +class AutofillScanner { + public: + explicit AutofillScanner(const std::vector<AutofillField*>& fields); + + // Advances the cursor by one step, if possible. + void Advance(); + + // Returns the current field in the stream, or |NULL| if there are no more + // fields in the stream. + const AutofillField* Cursor() const; + + // Returns |true| if the cursor has reached the end of the stream. + bool IsEnd() const; + + // Returns the most recently saved cursor -- see also |SaveCursor()|. + void Rewind(); + + // Saves the current cursor position. Multiple cursor positions can be saved, + // with stack ordering semantics. See also |Rewind()|. + void SaveCursor(); + + private: + // Indicates the current position in the stream, represented as a vector. + std::vector<AutofillField*>::const_iterator cursor_; + + // A stack of saved positions in the stream. + std::vector<std::vector<AutofillField*>::const_iterator> saved_cursors_; + + // The past-the-end pointer for the stream. + const std::vector<AutofillField*>::const_iterator end_; + + DISALLOW_COPY_AND_ASSIGN(AutofillScanner); +}; + +#endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_SCANNER_H_ diff --git a/chrome/browser/autofill/credit_card_field.cc b/chrome/browser/autofill/credit_card_field.cc index 9e31e32..b322574 100644 --- a/chrome/browser/autofill/credit_card_field.cc +++ b/chrome/browser/autofill/credit_card_field.cc @@ -12,52 +12,41 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "chrome/browser/autofill/field_types.h" #include "grit/autofill_resources.h" #include "ui/base/l10n/l10n_util.h" bool CreditCardField::GetFieldInfo(FieldTypeMap* field_type_map) const { - bool ok = Add(field_type_map, number_, AutofillType(CREDIT_CARD_NUMBER)); - DCHECK(ok); + bool ok = Add(field_type_map, number_, CREDIT_CARD_NUMBER); // If the heuristics detected first and last name in separate fields, // then ignore both fields. Putting them into separate fields is probably // wrong, because the credit card can also contain a middle name or middle // initial. - if (cardholder_last_ == NULL) { - // Add() will check if cardholder_ is != NULL. - ok = ok && Add(field_type_map, cardholder_, AutofillType(CREDIT_CARD_NAME)); - DCHECK(ok); - } + if (cardholder_last_ == NULL) + ok = ok && Add(field_type_map, cardholder_, CREDIT_CARD_NAME); - ok = ok && Add(field_type_map, type_, AutofillType(CREDIT_CARD_TYPE)); - DCHECK(ok); - ok = ok && Add(field_type_map, expiration_month_, - AutofillType(CREDIT_CARD_EXP_MONTH)); - DCHECK(ok); + ok = ok && Add(field_type_map, type_, CREDIT_CARD_TYPE); + ok = ok && Add(field_type_map, expiration_month_, CREDIT_CARD_EXP_MONTH); ok = ok && Add(field_type_map, expiration_year_, - AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)); - DCHECK(ok); - + CREDIT_CARD_EXP_4_DIGIT_YEAR); return ok; } -FormFieldType CreditCardField::GetFormFieldType() const { - return kCreditCardType; -} - // static -CreditCardField* CreditCardField::Parse( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml) { +CreditCardField* CreditCardField::Parse(AutofillScanner* scanner, + bool is_ecml) { + if (scanner->IsEnd()) + return NULL; + scoped_ptr<CreditCardField> credit_card_field(new CreditCardField); - std::vector<AutofillField*>::const_iterator q = *iter; - string16 pattern; + scanner->SaveCursor(); // Credit card fields can appear in many different orders. // We loop until no more credit card related fields are found, see |break| at // bottom of the loop. - for (int fields = 0; true; ++fields) { + for (int fields = 0; !scanner->IsEnd(); ++fields) { // Sometimes the cardholder field is just labeled "name". Unfortunately this // is a dangerously generic word to search for, since it will often match a // name (not cardholder name) field before or after credit card fields. So @@ -79,22 +68,22 @@ CreditCardField* CreditCardField::Parse( } } - if (ParseText(&q, name_pattern, &credit_card_field->cardholder_)) + if (ParseText(scanner, name_pattern, &credit_card_field->cardholder_)) continue; // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html // and ExpediaBilling.html in our test suite), recognize separate fields // for the cardholder's first and last name if they have the labels "cfnm" // and "clnm". - std::vector<AutofillField*>::const_iterator p = q; - AutofillField* first; - if (!is_ecml && ParseText(&p, ASCIIToUTF16("^cfnm"), &first) && - ParseText(&p, ASCIIToUTF16("^clnm"), + scanner->SaveCursor(); + const AutofillField* first; + if (!is_ecml && ParseText(scanner, ASCIIToUTF16("^cfnm"), &first) && + ParseText(scanner, ASCIIToUTF16("^clnm"), &credit_card_field->cardholder_last_)) { credit_card_field->cardholder_ = first; - q = p; continue; } + scanner->Rewind(); } // We look for a card security code before we look for a credit @@ -102,14 +91,14 @@ CreditCardField* CreditCardField::Parse( // has a plethora of names; we've seen "verification #", // "verification number", "card identification number" and others listed // in the |pattern| below. - if (is_ecml) { + string16 pattern; + if (is_ecml) pattern = GetEcmlPattern(kEcmlCardVerification); - } else { + else pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_CVC_RE); - } - if (credit_card_field->verification_ == NULL && - ParseText(&q, pattern, &credit_card_field->verification_)) + if (!credit_card_field->verification_ && + ParseText(scanner, pattern, &credit_card_field->verification_)) continue; // TODO(jhawkins): Parse the type select control. @@ -119,12 +108,13 @@ CreditCardField* CreditCardField::Parse( else pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_NUMBER_RE); - if (credit_card_field->number_ == NULL && ParseText(&q, pattern, - &credit_card_field->number_)) + if (!credit_card_field->number_ && + ParseText(scanner, pattern, &credit_card_field->number_)) continue; - if ((*q) && LowerCaseEqualsASCII((*q)->form_control_type, "month")) { - credit_card_field->expiration_month_ = *q++; + if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) { + credit_card_field->expiration_month_ = scanner->Cursor(); + scanner->Advance(); } else { // "Expiration date" is the most common label here, but some pages have // "Expires", "exp. date" or "exp. month" and "exp. year". We also look @@ -144,22 +134,23 @@ CreditCardField* CreditCardField::Parse( pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_MONTH_RE); if ((!credit_card_field->expiration_month_ || - credit_card_field->expiration_month_->IsEmpty()) && - ParseText(&q, pattern, &credit_card_field->expiration_month_)) { - + credit_card_field->expiration_month_->IsEmpty()) && + ParseText(scanner, pattern, &credit_card_field->expiration_month_)) { if (is_ecml) pattern = GetEcmlPattern(kEcmlCardExpireYear); else pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_DATE_RE); - if (!ParseText(&q, pattern, &credit_card_field->expiration_year_)) { + if (!ParseText(scanner, pattern, + &credit_card_field->expiration_year_)) { + scanner->Rewind(); return NULL; } continue; } } - if (ParseText(&q, GetEcmlPattern(kEcmlCardExpireDay))) + if (ParseText(scanner, GetEcmlPattern(kEcmlCardExpireDay))) continue; // Some pages (e.g. ExpediaBilling.html) have a "card description" @@ -167,7 +158,8 @@ CreditCardField* CreditCardField::Parse( // We also ignore any other fields within a credit card block that // start with "card", under the assumption that they are related to // the credit card section being processed but are uninteresting to us. - if (ParseText(&q, l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_IGNORED_RE))) + if (ParseText(scanner, + l10n_util::GetStringUTF16(IDS_AUTOFILL_CARD_IGNORED_RE))) continue; break; @@ -176,10 +168,8 @@ CreditCardField* CreditCardField::Parse( // Some pages have a billing address field after the cardholder name field. // For that case, allow only just the cardholder name field. The remaining // CC fields will be picked up in a following CreditCardField. - if (credit_card_field->cardholder_) { - *iter = q; + if (credit_card_field->cardholder_) return credit_card_field.release(); - } // On some pages, the user selects a card type using radio buttons // (e.g. test page Apple Store Billing.html). We can't handle that yet, @@ -194,10 +184,10 @@ CreditCardField* CreditCardField::Parse( (LowerCaseEqualsASCII( credit_card_field->expiration_month_->form_control_type, "month")))) { - *iter = q; - return credit_card_field.release(); + return credit_card_field.release(); } + scanner->Rewind(); return NULL; } diff --git a/chrome/browser/autofill/credit_card_field.h b/chrome/browser/autofill/credit_card_field.h index bef9aae..8a0a53a 100644 --- a/chrome/browser/autofill/credit_card_field.h +++ b/chrome/browser/autofill/credit_card_field.h @@ -9,25 +9,24 @@ #include <vector> #include "base/basictypes.h" +#include "base/compiler_specific.h" #include "chrome/browser/autofill/autofill_type.h" #include "chrome/browser/autofill/form_field.h" class AutofillField; +class AutofillScanner; class CreditCardField : public FormField { public: // FormField implementation: - virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const; - virtual FormFieldType GetFormFieldType() const; + virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const OVERRIDE; - static CreditCardField* Parse( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml); + static CreditCardField* Parse(AutofillScanner* scanner, bool is_ecml); private: CreditCardField(); - AutofillField* cardholder_; // Optional. + const AutofillField* cardholder_; // Optional. // Occasionally pages have separate fields for the cardholder's first and // last names; for such pages cardholder_ holds the first name field and @@ -36,17 +35,18 @@ class CreditCardField : public FormField { // because the text patterns for matching a cardholder name are different // than for ordinary names, and because cardholder names never have titles, // middle names or suffixes.) - AutofillField* cardholder_last_; + const AutofillField* cardholder_last_; - AutofillField* type_; // Optional. TODO(jhawkins): Parse the select control. - AutofillField* number_; // Required. + // TODO(jhawkins): Parse the select control. + const AutofillField* type_; // Optional. + const AutofillField* number_; // Required. // The 3-digit card verification number; we don't currently fill this. - AutofillField* verification_; + const AutofillField* verification_; // Both required. TODO(jhawkins): Parse the select control. - AutofillField* expiration_month_; - AutofillField* expiration_year_; + const AutofillField* expiration_month_; + const AutofillField* expiration_year_; DISALLOW_COPY_AND_ASSIGN(CreditCardField); }; diff --git a/chrome/browser/autofill/credit_card_field_unittest.cc b/chrome/browser/autofill/credit_card_field_unittest.cc index d9b6fa9..7fd248a 100644 --- a/chrome/browser/autofill/credit_card_field_unittest.cc +++ b/chrome/browser/autofill/credit_card_field_unittest.cc @@ -6,6 +6,7 @@ #include "base/memory/scoped_vector.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "chrome/browser/autofill/credit_card_field.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/glue/form_field.h" @@ -27,17 +28,15 @@ class CreditCardFieldTest : public testing::Test { }; TEST_F(CreditCardFieldTest, Empty) { - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get()); } TEST_F(CreditCardFieldTest, NonParse) { list_.push_back(new AutofillField); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get()); } @@ -58,9 +57,8 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNoNumber) { 0, false), ASCIIToUTF16("year1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get()); } @@ -73,9 +71,8 @@ TEST_F(CreditCardFieldTest, ParseCreditCardNoDate) { 0, false), ASCIIToUTF16("number1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get()); } @@ -104,9 +101,8 @@ TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) { 0, false), ASCIIToUTF16("year1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -148,9 +144,8 @@ TEST_F(CreditCardFieldTest, ParseMiniumCreditCardEcml) { 0, false), ASCIIToUTF16("year1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -206,9 +201,8 @@ TEST_F(CreditCardFieldTest, ParseFullCreditCard) { 0, false), ASCIIToUTF16("cvc1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -273,9 +267,8 @@ TEST_F(CreditCardFieldTest, ParseFullCreditCardEcml) { 0, false), ASCIIToUTF16("cvc1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -331,9 +324,8 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear) { 0, false), ASCIIToUTF16("year"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -386,9 +378,8 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear2) { 0, false), ASCIIToUTF16("year"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(CreditCardField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(CreditCardField::Parse(&scanner, false)); ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( diff --git a/chrome/browser/autofill/email_field.cc b/chrome/browser/autofill/email_field.cc new file mode 100644 index 0000000..0f7a666 --- /dev/null +++ b/chrome/browser/autofill/email_field.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/autofill/email_field.h" + +#include "chrome/browser/autofill/autofill_scanner.h" +#include "grit/autofill_resources.h" +#include "ui/base/l10n/l10n_util.h" + +// static +EmailField* EmailField::Parse(AutofillScanner* scanner, bool is_ecml) { + string16 pattern; + if (is_ecml) + pattern = GetEcmlPattern(kEcmlShipToEmail, kEcmlBillToEmail, '|'); + else + pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EMAIL_RE); + + const AutofillField* field; + if (ParseText(scanner, pattern, &field)) + return new EmailField(field); + + return NULL; +} + +bool EmailField::GetFieldInfo(FieldTypeMap* field_type_map) const { + return Add(field_type_map, field_, EMAIL_ADDRESS); +} + +EmailField::EmailField(const AutofillField* field) : field_(field) { +} diff --git a/chrome/browser/autofill/email_field.h b/chrome/browser/autofill/email_field.h new file mode 100644 index 0000000..0de5ef1 --- /dev/null +++ b/chrome/browser/autofill/email_field.h @@ -0,0 +1,28 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_AUTOFILL_EMAIL_FIELD_H_ +#define CHROME_BROWSER_AUTOFILL_EMAIL_FIELD_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/autofill/form_field.h" + +class EmailField : public FormField { + public: + static EmailField* Parse(AutofillScanner* scanner, bool is_ecml); + + // FormField: + virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const OVERRIDE; + + private: + explicit EmailField(const AutofillField* field); + + const AutofillField* field_; + + DISALLOW_COPY_AND_ASSIGN(EmailField); +}; + +#endif // CHROME_BROWSER_AUTOFILL_EMAIL_FIELD_H_ diff --git a/chrome/browser/autofill/form_field.cc b/chrome/browser/autofill/form_field.cc index 19d0dab..3195c4e 100644 --- a/chrome/browser/autofill/form_field.cc +++ b/chrome/browser/autofill/form_field.cc @@ -14,7 +14,9 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/address_field.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "chrome/browser/autofill/credit_card_field.h" +#include "chrome/browser/autofill/email_field.h" #include "chrome/browser/autofill/field_types.h" #include "chrome/browser/autofill/form_structure.h" #include "chrome/browser/autofill/name_field.h" @@ -72,62 +74,117 @@ const char kEcmlCardExpireDay[] = "ecom_payment_card_expdate_day"; const char kEcmlCardExpireMonth[] = "ecom_payment_card_expdate_month"; const char kEcmlCardExpireYear[] = "ecom_payment_card_expdate_year"; -class EmailField : public FormField { - public: - virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const { - bool ok = Add(field_type_map, field_, AutofillType(EMAIL_ADDRESS)); - DCHECK(ok); - return true; - } +namespace { + +// Checks if any of the |form|'s labels are named according to the ECML +// standard. Returns true if at least one ECML named element is found. +bool HasECMLField(const std::vector<AutofillField*>& fields) { + struct EcmlField { + const char* name_; + const int length_; + } ecml_fields[] = { +#define ECML_STRING_ENTRY(x) { x, arraysize(x) - 1 }, + ECML_STRING_ENTRY(kEcmlShipToTitle) + ECML_STRING_ENTRY(kEcmlShipToFirstName) + ECML_STRING_ENTRY(kEcmlShipToMiddleName) + ECML_STRING_ENTRY(kEcmlShipToLastName) + ECML_STRING_ENTRY(kEcmlShipToNameSuffix) + ECML_STRING_ENTRY(kEcmlShipToCompanyName) + ECML_STRING_ENTRY(kEcmlShipToAddress1) + ECML_STRING_ENTRY(kEcmlShipToAddress2) + ECML_STRING_ENTRY(kEcmlShipToAddress3) + ECML_STRING_ENTRY(kEcmlShipToCity) + ECML_STRING_ENTRY(kEcmlShipToStateProv) + ECML_STRING_ENTRY(kEcmlShipToPostalCode) + ECML_STRING_ENTRY(kEcmlShipToCountry) + ECML_STRING_ENTRY(kEcmlShipToPhone) + ECML_STRING_ENTRY(kEcmlShipToPhone) + ECML_STRING_ENTRY(kEcmlShipToEmail) + ECML_STRING_ENTRY(kEcmlBillToTitle) + ECML_STRING_ENTRY(kEcmlBillToFirstName) + ECML_STRING_ENTRY(kEcmlBillToMiddleName) + ECML_STRING_ENTRY(kEcmlBillToLastName) + ECML_STRING_ENTRY(kEcmlBillToNameSuffix) + ECML_STRING_ENTRY(kEcmlBillToCompanyName) + ECML_STRING_ENTRY(kEcmlBillToAddress1) + ECML_STRING_ENTRY(kEcmlBillToAddress2) + ECML_STRING_ENTRY(kEcmlBillToAddress3) + ECML_STRING_ENTRY(kEcmlBillToCity) + ECML_STRING_ENTRY(kEcmlBillToStateProv) + ECML_STRING_ENTRY(kEcmlBillToPostalCode) + ECML_STRING_ENTRY(kEcmlBillToCountry) + ECML_STRING_ENTRY(kEcmlBillToPhone) + ECML_STRING_ENTRY(kEcmlBillToPhone) + ECML_STRING_ENTRY(kEcmlBillToEmail) + ECML_STRING_ENTRY(kEcmlCardHolder) + ECML_STRING_ENTRY(kEcmlCardType) + ECML_STRING_ENTRY(kEcmlCardNumber) + ECML_STRING_ENTRY(kEcmlCardVerification) + ECML_STRING_ENTRY(kEcmlCardExpireMonth) + ECML_STRING_ENTRY(kEcmlCardExpireYear) +#undef ECML_STRING_ENTRY + }; - static EmailField* Parse(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml) { - string16 pattern; - if (is_ecml) { - pattern = GetEcmlPattern(kEcmlShipToEmail, kEcmlBillToEmail, '|'); - } else { - pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EMAIL_RE); + const string16 ecom(ASCIIToUTF16("ecom")); + for (std::vector<AutofillField*>::const_iterator field = fields.begin(); + field != fields.end(); + ++field) { + const string16& utf16_name = (*field)->name; + if (StartsWith(utf16_name, ecom, true)) { + std::string name(UTF16ToUTF8(utf16_name)); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(ecml_fields); ++i) { + if (base::strncasecmp(name.c_str(), ecml_fields[i].name_, + ecml_fields[i].length_) == 0) { + return true; + } + } } + } - AutofillField* field; - if (ParseText(iter, pattern, &field)) - return new EmailField(field); + return false; +} - return NULL; - } +} // namespace - private: - explicit EmailField(AutofillField *field) : field_(field) {} +// static +void FormField::ParseFormFields(const std::vector<AutofillField*>& fields, + FieldTypeMap* field_type_map) { + // First, check if there is at least one form field with an ECML name. + // If there is, then we will match an element only if it is in the standard. + bool is_ecml = HasECMLField(fields); - AutofillField* field_; -}; + // Parse fields. + AutofillScanner scanner(fields); + while (!scanner.IsEnd()) { + FormField* form_field = FormField::ParseFormField(&scanner, is_ecml); + if (!form_field) { + scanner.Advance(); + continue; + } -FormFieldType FormField::GetFormFieldType() const { - return kOtherFieldType; + bool ok = form_field->GetFieldInfo(field_type_map); + DCHECK(ok); + } } // static -bool FormField::Match(AutofillField* field, +bool FormField::Match(const AutofillField* field, const string16& pattern, bool match_label_only) { - if (match_label_only) { - if (MatchLabel(field, pattern)) { - return true; - } - } else { - // For now, we apply the same pattern to the field's label and the field's - // name. Matching the name is a bit of a long shot for many patterns, but - // it generally doesn't hurt to try. - if (MatchLabel(field, pattern) || MatchName(field, pattern)) { - return true; - } - } + if (MatchLabel(field, pattern)) + return true; + + // For now, we apply the same pattern to the field's label and the field's + // name. Matching the name is a bit of a long shot for many patterns, but + // it generally doesn't hurt to try. + if (!match_label_only && MatchName(field, pattern)) + return true; return false; } // static -bool FormField::MatchName(AutofillField* field, const string16& pattern) { +bool FormField::MatchName(const AutofillField* field, const string16& pattern) { // TODO(jhawkins): Remove StringToLowerASCII. WebRegularExpression needs to // be fixed to take WebTextCaseInsensitive into account. WebKit::WebRegularExpression re(WebKit::WebString(pattern), @@ -138,7 +195,8 @@ bool FormField::MatchName(AutofillField* field, const string16& pattern) { } // static -bool FormField::MatchLabel(AutofillField* field, const string16& pattern) { +bool FormField::MatchLabel(const AutofillField* field, + const string16& pattern) { // TODO(jhawkins): Remove StringToLowerASCII. WebRegularExpression needs to // be fixed to take WebTextCaseInsensitive into account. WebKit::WebRegularExpression re(WebKit::WebString(pattern), @@ -149,71 +207,70 @@ bool FormField::MatchLabel(AutofillField* field, const string16& pattern) { } // static -FormField* FormField::ParseFormField( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml) { +FormField* FormField::ParseFormField(AutofillScanner* scanner, bool is_ecml) { FormField *field; - field = EmailField::Parse(iter, is_ecml); - if (field != NULL) + + field = EmailField::Parse(scanner, is_ecml); + if (field) return field; + // Parses both phone and fax. - field = PhoneField::Parse(iter, is_ecml); - if (field != NULL) + field = PhoneField::Parse(scanner, is_ecml); + if (field) return field; - field = AddressField::Parse(iter, is_ecml); - if (field != NULL) + + field = AddressField::Parse(scanner, is_ecml); + if (field) return field; - field = CreditCardField::Parse(iter, is_ecml); - if (field != NULL) + + field = CreditCardField::Parse(scanner, is_ecml); + if (field) return field; - // We search for a NameField last since it matches the word "name", which is + // We search for a |NameField| last since it matches the word "name", which is // relatively general. - return NameField::Parse(iter, is_ecml); + return NameField::Parse(scanner, is_ecml); } // static -bool FormField::ParseText(std::vector<AutofillField*>::const_iterator* iter, - const string16& pattern) { - AutofillField* field; - return ParseText(iter, pattern, &field); +bool FormField::ParseText(AutofillScanner* scanner, const string16& pattern) { + const AutofillField* field; + return ParseText(scanner, pattern, &field); } // static -bool FormField::ParseText(std::vector<AutofillField*>::const_iterator* iter, +bool FormField::ParseText(AutofillScanner* scanner, const string16& pattern, - AutofillField** dest) { - return ParseText(iter, pattern, dest, false); + const AutofillField** dest) { + return ParseText(scanner, pattern, dest, false); } // static -bool FormField::ParseEmptyText( - std::vector<AutofillField*>::const_iterator* iter, - AutofillField** dest) { - return ParseLabelText(iter, ASCIIToUTF16("^$"), dest); +bool FormField::ParseEmptyText(AutofillScanner* scanner, + const AutofillField** dest) { + return ParseLabelText(scanner, ASCIIToUTF16("^$"), dest); } // static -bool FormField::ParseLabelText( - std::vector<AutofillField*>::const_iterator* iter, - const string16& pattern, - AutofillField** dest) { - return ParseText(iter, pattern, dest, true); +bool FormField::ParseLabelText(AutofillScanner* scanner, + const string16& pattern, + const AutofillField** dest) { + return ParseText(scanner, pattern, dest, true); } // static -bool FormField::ParseText(std::vector<AutofillField*>::const_iterator* iter, +bool FormField::ParseText(AutofillScanner* scanner, const string16& pattern, - AutofillField** dest, + const AutofillField** dest, bool match_label_only) { - AutofillField* field = **iter; - if (!field) + if (scanner->IsEnd()) return false; + const AutofillField* field = scanner->Cursor(); if (Match(field, pattern, match_label_only)) { if (dest) *dest = field; - (*iter)++; + scanner->Advance(); return true; } @@ -221,18 +278,14 @@ bool FormField::ParseText(std::vector<AutofillField*>::const_iterator* iter, } // static -bool FormField::ParseLabelAndName( - std::vector<AutofillField*>::const_iterator* iter, - const string16& pattern, - AutofillField** dest) { - AutofillField* field = **iter; - if (!field) - return false; - +bool FormField::ParseLabelAndName(AutofillScanner* scanner, + const string16& pattern, + const AutofillField** dest) { + const AutofillField* field = scanner->Cursor(); if (MatchLabel(field, pattern) && MatchName(field, pattern)) { if (dest) *dest = field; - (*iter)++; + scanner->Advance(); return true; } @@ -240,19 +293,21 @@ bool FormField::ParseLabelAndName( } // static -bool FormField::ParseEmpty(std::vector<AutofillField*>::const_iterator* iter) { +bool FormField::ParseEmpty(AutofillScanner* scanner) { // TODO(jhawkins): Handle select fields. - return ParseLabelAndName(iter, ASCIIToUTF16("^$"), NULL); + return ParseLabelAndName(scanner, ASCIIToUTF16("^$"), NULL); } // static -bool FormField::Add(FieldTypeMap* field_type_map, AutofillField* field, - const AutofillType& type) { +bool FormField::Add(FieldTypeMap* field_type_map, + const AutofillField* field, + AutofillFieldType type) { // Several fields are optional. - if (field) - field_type_map->insert(make_pair(field->unique_name(), type.field_type())); + if (!field) + return true; - return true; + // TODO(isherman): Is this the intent? + return field_type_map->insert(make_pair(field->unique_name(), type)).second; } string16 FormField::GetEcmlPattern(const char* ecml_name) { @@ -263,113 +318,5 @@ string16 FormField::GetEcmlPattern(const char* ecml_name1, const char* ecml_name2, char pattern_operator) { return ASCIIToUTF16(StringPrintf("^%s%c^%s", - ecml_name1, pattern_operator, ecml_name2)); -} - -FormFieldSet::FormFieldSet(FormStructure* fields) { - std::vector<AddressField*> addresses; - - // First, find if there is one form field with an ECML name. If there is, - // then we will match an element only if it is in the standard. - bool is_ecml = CheckECML(fields); - - // Parse fields. - std::vector<AutofillField*>::const_iterator field = fields->begin(); - while (field != fields->end() && *field != NULL) { - FormField* form_field = FormField::ParseFormField(&field, is_ecml); - if (!form_field) { - field++; - continue; - } - - push_back(form_field); - - if (form_field->GetFormFieldType() == kAddressType) { - AddressField* address = static_cast<AddressField*>(form_field); - if (address->IsFullAddress()) - addresses.push_back(address); - } - } - - // Now determine an address type for each address. Note, if this is an ECML - // form, then we already got this info from the field names. - if (!is_ecml && !addresses.empty()) { - if (addresses.size() == 1) { - addresses[0]->SetType(addresses[0]->FindType()); - } else { - AddressType type0 = addresses[0]->FindType(); - AddressType type1 = addresses[1]->FindType(); - - // When there are two addresses on a page, they almost always appear in - // the order (billing, shipping). - bool reversed = (type0 == kShippingAddress && type1 == kBillingAddress); - addresses[0]->SetType(reversed ? kShippingAddress : kBillingAddress); - addresses[1]->SetType(reversed ? kBillingAddress : kShippingAddress); - } - } -} - -bool FormFieldSet::CheckECML(FormStructure* fields) { - size_t num_fields = fields->field_count(); - struct EcmlField { - const char* name_; - const int length_; - } form_fields[] = { -#define ECML_STRING_ENTRY(x) { x, arraysize(x) - 1 }, - ECML_STRING_ENTRY(kEcmlShipToTitle) - ECML_STRING_ENTRY(kEcmlShipToFirstName) - ECML_STRING_ENTRY(kEcmlShipToMiddleName) - ECML_STRING_ENTRY(kEcmlShipToLastName) - ECML_STRING_ENTRY(kEcmlShipToNameSuffix) - ECML_STRING_ENTRY(kEcmlShipToCompanyName) - ECML_STRING_ENTRY(kEcmlShipToAddress1) - ECML_STRING_ENTRY(kEcmlShipToAddress2) - ECML_STRING_ENTRY(kEcmlShipToAddress3) - ECML_STRING_ENTRY(kEcmlShipToCity) - ECML_STRING_ENTRY(kEcmlShipToStateProv) - ECML_STRING_ENTRY(kEcmlShipToPostalCode) - ECML_STRING_ENTRY(kEcmlShipToCountry) - ECML_STRING_ENTRY(kEcmlShipToPhone) - ECML_STRING_ENTRY(kEcmlShipToPhone) - ECML_STRING_ENTRY(kEcmlShipToEmail) - ECML_STRING_ENTRY(kEcmlBillToTitle) - ECML_STRING_ENTRY(kEcmlBillToFirstName) - ECML_STRING_ENTRY(kEcmlBillToMiddleName) - ECML_STRING_ENTRY(kEcmlBillToLastName) - ECML_STRING_ENTRY(kEcmlBillToNameSuffix) - ECML_STRING_ENTRY(kEcmlBillToCompanyName) - ECML_STRING_ENTRY(kEcmlBillToAddress1) - ECML_STRING_ENTRY(kEcmlBillToAddress2) - ECML_STRING_ENTRY(kEcmlBillToAddress3) - ECML_STRING_ENTRY(kEcmlBillToCity) - ECML_STRING_ENTRY(kEcmlBillToStateProv) - ECML_STRING_ENTRY(kEcmlBillToPostalCode) - ECML_STRING_ENTRY(kEcmlBillToCountry) - ECML_STRING_ENTRY(kEcmlBillToPhone) - ECML_STRING_ENTRY(kEcmlBillToPhone) - ECML_STRING_ENTRY(kEcmlBillToEmail) - ECML_STRING_ENTRY(kEcmlCardHolder) - ECML_STRING_ENTRY(kEcmlCardType) - ECML_STRING_ENTRY(kEcmlCardNumber) - ECML_STRING_ENTRY(kEcmlCardVerification) - ECML_STRING_ENTRY(kEcmlCardExpireMonth) - ECML_STRING_ENTRY(kEcmlCardExpireYear) -#undef ECML_STRING_ENTRY - }; - - const string16 ecom(ASCIIToUTF16("ecom")); - for (size_t index = 0; index < num_fields; ++index) { - const string16& utf16_name = fields->field(index)->name; - if (StartsWith(utf16_name, ecom, true)) { - std::string name(UTF16ToASCII(utf16_name)); - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(form_fields); ++i) { - if (base::strncasecmp(name.c_str(), form_fields[i].name_, - form_fields[i].length_) == 0) { - return true; - } - } - } - } - - return false; + ecml_name1, pattern_operator, ecml_name2)); } diff --git a/chrome/browser/autofill/form_field.h b/chrome/browser/autofill/form_field.h index 70dea64..3bb5c0e 100644 --- a/chrome/browser/autofill/form_field.h +++ b/chrome/browser/autofill/form_field.h @@ -13,7 +13,7 @@ #include "chrome/browser/autofill/autofill_type.h" class AutofillField; -class FormStructure; +class AutofillScanner; extern const char kEcmlShipToTitle[]; extern const char kEcmlShipToFirstName[]; @@ -57,12 +57,6 @@ extern const char kEcmlCardExpireDay[]; extern const char kEcmlCardExpireMonth[]; extern const char kEcmlCardExpireYear[]; -enum FormFieldType { - kAddressType, - kCreditCardType, - kOtherFieldType -}; - // Represents a logical form field in a web form. Classes that implement this // interface can identify themselves as a particular type of form field, e.g. // name, phone number, or address field. @@ -70,53 +64,53 @@ class FormField { public: virtual ~FormField() {} + // Associates each field in |fields| with its heuristically detected type. + // The association is stored into |field_type_map|. + static void ParseFormFields(const std::vector<AutofillField*>& fields, + FieldTypeMap* field_type_map); + // Associates the available AutofillTypes of a FormField into // |field_type_map|. virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const = 0; - // Returns the type of form field of the class implementing this interface. - virtual FormFieldType GetFormFieldType() const; - // Returns true if |field| contains the regexp |pattern| in the name or label. // If |match_label_only| is true, then only the field's label is considered. - static bool Match(AutofillField* field, + static bool Match(const AutofillField* field, const string16& pattern, bool match_label_only); // Parses a field using the different field views we know about. |is_ecml| // should be true when the field conforms to the ECML specification. - static FormField* ParseFormField( - std::vector<AutofillField*>::const_iterator* field, - bool is_ecml); + static FormField* ParseFormField(AutofillScanner* scanner, bool is_ecml); // Attempts to parse a text field with the given pattern; returns true on // success, but doesn't return the actual text field itself. - static bool ParseText(std::vector<AutofillField*>::const_iterator* iter, - const string16& pattern); + static bool ParseText(AutofillScanner* scanner, const string16& pattern); // Attempts to parse a text field with the given pattern. Returns true on // success and fills |dest| with a pointer to the field. - static bool ParseText(std::vector<AutofillField*>::const_iterator* iter, + static bool ParseText(AutofillScanner* scanner, const string16& pattern, - AutofillField** dest); + const AutofillField** dest); // Attempts to parse a text field with an empty name or label. Returns true // on success and fills |dest| with a pointer to the field. - static bool ParseEmptyText(std::vector<AutofillField*>::const_iterator* iter, - AutofillField** dest); + static bool ParseEmptyText(AutofillScanner* scanner, + const AutofillField** dest); // Attempts to parse a text field label with the given pattern. Returns true // on success and fills |dest| with a pointer to the field. - static bool ParseLabelText(std::vector<AutofillField*>::const_iterator* iter, + static bool ParseLabelText(AutofillScanner* scanner, const string16& pattern, - AutofillField** dest); + const AutofillField** dest); // Attempts to parse a control with an empty label. - static bool ParseEmpty(std::vector<AutofillField*>::const_iterator* iter); + static bool ParseEmpty(AutofillScanner* scanner); // Adds an association between a field and a type to |field_type_map|. - static bool Add(FieldTypeMap* field_type_map, AutofillField* field, - const AutofillType& type); + static bool Add(FieldTypeMap* field_type_map, + const AutofillField* field, + AutofillFieldType type); protected: // Only derived classes may instantiate. @@ -135,37 +129,19 @@ class FormField { char pattern_operator); private: - static bool ParseText(std::vector<AutofillField*>::const_iterator* iter, + static bool ParseText(AutofillScanner* scanner, const string16& pattern, - AutofillField** dest, + const AutofillField** dest, bool match_label_only); // For empty strings we need to test that both label and name are empty. - static bool ParseLabelAndName( - std::vector<AutofillField*>::const_iterator* iter, - const string16& pattern, - AutofillField** dest); - static bool MatchName(AutofillField* field, const string16& pattern); - static bool MatchLabel(AutofillField* field, const string16& pattern); + static bool ParseLabelAndName(AutofillScanner* scanner, + const string16& pattern, + const AutofillField** dest); + static bool MatchName(const AutofillField* field, const string16& pattern); + static bool MatchLabel(const AutofillField* field, const string16& pattern); DISALLOW_COPY_AND_ASSIGN(FormField); }; -class FormFieldSet : public std::vector<FormField*> { - public: - explicit FormFieldSet(FormStructure* form); - - ~FormFieldSet() { - for (iterator i = begin(); i != end(); ++i) - delete *i; - } - - private: - // Checks if any of the labels are named according to the ECML standard. - // Returns true if at least one ECML named element is found. - bool CheckECML(FormStructure* fields); - - DISALLOW_COPY_AND_ASSIGN(FormFieldSet); -}; - #endif // CHROME_BROWSER_AUTOFILL_FORM_FIELD_H_ diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc index e7b0ad9..ce38d80 100644 --- a/chrome/browser/autofill/form_structure.cc +++ b/chrome/browser/autofill/form_structure.cc @@ -47,9 +47,6 @@ FormStructure::FormStructure(const FormData& form) : form_name_(form.name), source_url_(form.origin), target_url_(form.action), - has_credit_card_field_(false), - has_autofillable_field_(false), - has_password_fields_(false), autofill_count_(0) { // Copy the form fields. std::vector<webkit_glue::FormField>::const_iterator field; @@ -66,9 +63,6 @@ FormStructure::FormStructure(const FormData& form) fields_.push_back(new AutofillField(*field, unique_name)); } - // Terminate the vector with a NULL item. - fields_.push_back(NULL); - std::string method = UTF16ToUTF8(form.method); if (StringToLowerASCII(method) == kFormMethodPost) { method_ = POST; @@ -82,16 +76,13 @@ FormStructure::FormStructure(const FormData& form) FormStructure::~FormStructure() {} void FormStructure::DetermineHeuristicTypes() { - has_credit_card_field_ = false; - has_autofillable_field_ = false; autofill_count_ = 0; FieldTypeMap field_type_map; - GetHeuristicFieldInfo(&field_type_map); + FormField::ParseFormFields(fields_.get(), &field_type_map); for (size_t index = 0; index < field_count(); index++) { AutofillField* field = fields_[index]; - DCHECK(field); FieldTypeMap::iterator iter = field_type_map.find(field->unique_name()); AutofillFieldType heuristic_autofill_type; @@ -105,10 +96,6 @@ void FormStructure::DetermineHeuristicTypes() { field->set_heuristic_type(heuristic_autofill_type); AutofillType autofill_type(field->type()); - if (autofill_type.group() == AutofillType::CREDIT_CARD) - has_credit_card_field_ = true; - if (autofill_type.field_type() != UNKNOWN_TYPE) - has_autofillable_field_ = true; } } @@ -223,18 +210,8 @@ void FormStructure::ParseQueryResponse(const std::string& response_xml, FormStructure* form = *iter; form->server_experiment_id_ = experiment_id; - if (form->has_autofillable_field_) - heuristics_detected_fillable_field = true; - - form->has_credit_card_field_ = false; - form->has_autofillable_field_ = false; - for (std::vector<AutofillField*>::iterator field = form->fields_.begin(); field != form->fields_.end(); ++field, ++current_type) { - // The field list is terminated by a NULL AutofillField. - if (!*field) - break; - // In some cases *successful* response does not return all the fields. // Quit the update of the types then. if (current_type == field_types.end()) @@ -244,15 +221,12 @@ void FormStructure::ParseQueryResponse(const std::string& response_xml, DCHECK_NE(*current_type, UNKNOWN_TYPE); AutofillFieldType heuristic_type = (*field)->type(); + if (heuristic_type != UNKNOWN_TYPE) + heuristics_detected_fillable_field = true; + (*field)->set_server_type(*current_type); if (heuristic_type != (*field)->type()) query_response_overrode_heuristics = true; - - AutofillType autofill_type((*field)->type()); - if (autofill_type.group() == AutofillType::CREDIT_CARD) - form->has_credit_card_field_ = true; - if (autofill_type.field_type() != UNKNOWN_TYPE) - form->has_autofillable_field_ = true; } form->UpdateAutofillCount(); @@ -330,8 +304,6 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) { for (std::vector<AutofillField*>::const_iterator iter = begin(); iter != end(); ++iter) { AutofillField* field = *iter; - if (!field) - continue; std::map<std::string, const AutofillField*>::const_iterator cached_field = cached_fields.find(field->FieldSignature()); @@ -466,21 +438,27 @@ void FormStructure::LogQualityMetrics( } } -void FormStructure::set_possible_types(int index, const FieldTypeSet& types) { - int num_fields = static_cast<int>(field_count()); - DCHECK(index >= 0 && index < num_fields); - if (index >= 0 && index < num_fields) - fields_[index]->set_possible_types(types); +void FormStructure::set_possible_types(size_t index, + const FieldTypeSet& types) { + if (index >= fields_.size()) { + NOTREACHED(); + return; + } + + fields_[index]->set_possible_types(types); } -const AutofillField* FormStructure::field(int index) const { +const AutofillField* FormStructure::field(size_t index) const { + if (index >= fields_.size()) { + NOTREACHED(); + return NULL; + } + return fields_[index]; } size_t FormStructure::field_count() const { - // Don't count the NULL terminator. - size_t field_size = fields_.size(); - return (field_size == 0) ? 0 : field_size - 1; + return fields_.size(); } std::string FormStructure::server_experiment_id() const { @@ -521,16 +499,6 @@ std::string FormStructure::Hash64Bit(const std::string& str) { return base::Uint64ToString(hash64); } -void FormStructure::GetHeuristicFieldInfo(FieldTypeMap* field_type_map) { - FormFieldSet fields(this); - - FormFieldSet::const_iterator field; - for (field = fields.begin(); field != fields.end(); field++) { - bool ok = (*field)->GetFieldInfo(field_type_map); - DCHECK(ok); - } -} - bool FormStructure::EncodeFormRequest( FormStructure::EncodeRequestType request_type, buzz::XmlElement* encompassing_xml_element) const { diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h index 341bf19..ee9b7a7 100644 --- a/chrome/browser/autofill/form_structure.h +++ b/chrome/browser/autofill/form_structure.h @@ -16,10 +16,6 @@ #include "googleurl/src/gurl.h" #include "webkit/glue/form_data.h" -namespace buzz { - class XmlElement; -} // namespace buzz - enum RequestMethod { GET, POST @@ -33,6 +29,10 @@ enum UploadRequired { class AutofillMetrics; +namespace buzz { +class XmlElement; +} + // FormStructure stores a single HTML form together with the values entered // in the fields along with additional information needed by Autofill. class FormStructure { @@ -92,9 +92,9 @@ class FormStructure { void LogQualityMetrics(const AutofillMetrics& metric_logger) const; // Sets the possible types for the field at |index|. - void set_possible_types(int index, const FieldTypeSet& types); + void set_possible_types(size_t index, const FieldTypeSet& types); - const AutofillField* field(int index) const; + const AutofillField* field(size_t index) const; size_t field_count() const; // Returns the number of fields that are able to be autofilled. @@ -129,9 +129,6 @@ class FormStructure { UPLOAD, }; - // Associates the field with the heuristic type for each of the field views. - void GetHeuristicFieldInfo(FieldTypeMap* field_types_map); - // Adds form info to |encompassing_xml_element|. |request_type| indicates if // it is a query or upload. bool EncodeFormRequest(EncodeRequestType request_type, @@ -150,15 +147,10 @@ class FormStructure { // The target URL. GURL target_url_; - bool has_credit_card_field_; - bool has_autofillable_field_; - bool has_password_fields_; - // The number of fields able to be auto-filled. size_t autofill_count_; - // A vector of all the input fields in the form. The vector is terminated by - // a NULL entry. + // A vector of all the input fields in the form. ScopedVector<AutofillField> fields_; // The names of the form input elements, that are part of the form signature. diff --git a/chrome/browser/autofill/form_structure_browsertest.cc b/chrome/browser/autofill/form_structure_browsertest.cc index 9578a74..71f7c13 100644 --- a/chrome/browser/autofill/form_structure_browsertest.cc +++ b/chrome/browser/autofill/form_structure_browsertest.cc @@ -77,9 +77,6 @@ std::string FormStructureBrowserTest::FormStructuresToString( (*iter)->begin(); field_iter != (*iter)->end(); ++field_iter) { - // The field list is NULL-terminated. Exit loop when at the end. - if (!*field_iter) - break; forms_string += AutofillType::FieldTypeToString((*field_iter)->type()); forms_string += "\n"; } diff --git a/chrome/browser/autofill/name_field.cc b/chrome/browser/autofill/name_field.cc index 3c86b39..87f98ce 100644 --- a/chrome/browser/autofill/name_field.cc +++ b/chrome/browser/autofill/name_field.cc @@ -8,60 +8,68 @@ #include "base/memory/scoped_ptr.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "chrome/browser/autofill/autofill_type.h" #include "grit/autofill_resources.h" #include "ui/base/l10n/l10n_util.h" -NameField* NameField::Parse(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml) { +NameField* NameField::Parse(AutofillScanner* scanner, bool is_ecml) { + if (scanner->IsEnd()) + return NULL; + // Try FirstLastNameField first since it's more specific. - NameField* field = FirstLastNameField::Parse(iter, is_ecml); - if (field == NULL && !is_ecml) - field = FullNameField::Parse(iter); + NameField* field = FirstLastNameField::Parse(scanner, is_ecml); + if (!field && !is_ecml) + field = FullNameField::Parse(scanner); return field; } bool FullNameField::GetFieldInfo(FieldTypeMap* field_type_map) const { - bool ok = Add(field_type_map, field_, AutofillType(NAME_FULL)); - DCHECK(ok); - return true; + return Add(field_type_map, field_, NAME_FULL); } -FullNameField* FullNameField::Parse( - std::vector<AutofillField*>::const_iterator* iter) { +FullNameField* FullNameField::Parse(AutofillScanner* scanner) { // Exclude labels containing the string "username", which typically // denotes a login ID rather than the user's actual name. - AutofillField* field = **iter; + const AutofillField* field = scanner->Cursor(); if (Match(field, l10n_util::GetStringUTF16(IDS_AUTOFILL_USERNAME_RE), false)) return NULL; // Searching for any label containing the word "name" is too general; // for example, Travelocity_Edit travel profile.html contains a field // "Travel Profile Name". - const string16 name_match = l10n_util::GetStringUTF16(IDS_AUTOFILL_NAME_RE); - if (ParseText(iter, name_match, &field)) + if (ParseText(scanner, l10n_util::GetStringUTF16(IDS_AUTOFILL_NAME_RE), + &field)) return new FullNameField(field); return NULL; } -FullNameField::FullNameField(AutofillField* field) +FullNameField::FullNameField(const AutofillField* field) : field_(field) { } -FirstLastNameField* FirstLastNameField::Parse1( - std::vector<AutofillField*>::const_iterator* iter) { +bool FirstLastNameField::GetFieldInfo(FieldTypeMap* field_type_map) const { + bool ok = Add(field_type_map, first_name_, NAME_FIRST); + ok = ok && Add(field_type_map, last_name_, NAME_LAST); + AutofillFieldType type = middle_initial_ ? NAME_MIDDLE_INITIAL : NAME_MIDDLE; + ok = ok && Add(field_type_map, middle_name_, type); + return ok; +} + +FirstLastNameField* FirstLastNameField::ParseSpecificName( + AutofillScanner* scanner) { // Some pages (e.g. Overstock_comBilling.html, SmithsonianCheckout.html) // have the label "Name" followed by two or three text fields. scoped_ptr<FirstLastNameField> v(new FirstLastNameField); - std::vector<AutofillField*>::const_iterator q = *iter; + scanner->SaveCursor(); - AutofillField* next; - if (ParseText(&q, + const AutofillField* next; + if (ParseText(scanner, l10n_util::GetStringUTF16(IDS_AUTOFILL_NAME_SPECIFIC_RE), &v->first_name_) && - ParseEmptyText(&q, &next)) { - if (ParseEmptyText(&q, &v->last_name_)) { + ParseEmptyText(scanner, &next)) { + if (ParseEmptyText(scanner, &v->last_name_)) { // There are three name fields; assume that the middle one is a // middle initial (it is, at least, on SmithsonianCheckout.html). v->middle_name_ = next; @@ -70,17 +78,17 @@ FirstLastNameField* FirstLastNameField::Parse1( v->last_name_ = next; } - *iter = q; return v.release(); } + scanner->Rewind(); return NULL; } -FirstLastNameField* FirstLastNameField::Parse2( - std::vector<AutofillField*>::const_iterator* iter) { +FirstLastNameField* FirstLastNameField::ParseComponentNames( + AutofillScanner* scanner) { scoped_ptr<FirstLastNameField> v(new FirstLastNameField); - std::vector<AutofillField*>::const_iterator q = *iter; + scanner->SaveCursor(); // A fair number of pages use the names "fname" and "lname" for naming // first and last name fields (examples from the test suite: @@ -90,8 +98,8 @@ FirstLastNameField* FirstLastNameField::Parse2( // so we match "initials" here (and just fill in a first name there, // American-style). // The ".*first$" matches fields ending in "first" (example in sample8.html). - string16 match = l10n_util::GetStringUTF16(IDS_AUTOFILL_FIRST_NAME_RE); - if (!ParseText(&q, match, &v->first_name_)) + if (!ParseText(scanner, l10n_util::GetStringUTF16(IDS_AUTOFILL_FIRST_NAME_RE), + &v->first_name_)) return NULL; // We check for a middle initial before checking for a middle name @@ -99,70 +107,55 @@ FirstLastNameField* FirstLastNameField::Parse2( // as both (the label text is "MI" and the element name is // "txtmiddlename"); such a field probably actually represents a // middle initial. - match = l10n_util::GetStringUTF16(IDS_AUTOFILL_MIDDLE_INITIAL_RE); - if (ParseText(&q, match, &v->middle_name_)) { + if (ParseText(scanner, + l10n_util::GetStringUTF16(IDS_AUTOFILL_MIDDLE_INITIAL_RE), + &v->middle_name_)) { v->middle_initial_ = true; } else { - match = l10n_util::GetStringUTF16(IDS_AUTOFILL_MIDDLE_NAME_RE); - ParseText(&q, match, &v->middle_name_); + ParseText(scanner, l10n_util::GetStringUTF16(IDS_AUTOFILL_MIDDLE_NAME_RE), + &v->middle_name_); } // The ".*last$" matches fields ending in "last" (example in sample8.html). - match = l10n_util::GetStringUTF16(IDS_AUTOFILL_LAST_NAME_RE); - if (!ParseText(&q, match, &v->last_name_)) - return NULL; + if (ParseText(scanner, l10n_util::GetStringUTF16(IDS_AUTOFILL_LAST_NAME_RE), + &v->last_name_)) { + return v.release(); + } - *iter = q; - return v.release(); + scanner->Rewind(); + return NULL; } FirstLastNameField* FirstLastNameField::ParseEcmlName( - std::vector<AutofillField*>::const_iterator* iter) { + AutofillScanner* scanner) { scoped_ptr<FirstLastNameField> field(new FirstLastNameField); - std::vector<AutofillField*>::const_iterator q = *iter; + scanner->SaveCursor(); string16 pattern = GetEcmlPattern(kEcmlShipToFirstName, kEcmlBillToFirstName, '|'); - if (!ParseText(&q, pattern, &field->first_name_)) + if (!ParseText(scanner, pattern, &field->first_name_)) return NULL; pattern = GetEcmlPattern(kEcmlShipToMiddleName, kEcmlBillToMiddleName, '|'); - ParseText(&q, pattern, &field->middle_name_); + ParseText(scanner, pattern, &field->middle_name_); pattern = GetEcmlPattern(kEcmlShipToLastName, kEcmlBillToLastName, '|'); - if (ParseText(&q, pattern, &field->last_name_)) { - *iter = q; + if (ParseText(scanner, pattern, &field->last_name_)) return field.release(); - } + scanner->Rewind(); return NULL; } -FirstLastNameField* FirstLastNameField::Parse( - std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml) { - if (is_ecml) { - return ParseEcmlName(iter); - } else { - FirstLastNameField* v = Parse1(iter); - if (v != NULL) - return v; - - return Parse2(iter); - } -} +FirstLastNameField* FirstLastNameField::Parse(AutofillScanner* scanner, + bool is_ecml) { + if (is_ecml) + return ParseEcmlName(scanner); -bool FirstLastNameField::GetFieldInfo(FieldTypeMap* field_type_map) const { - bool ok = Add(field_type_map, first_name_, AutofillType(NAME_FIRST)); - DCHECK(ok); - ok = ok && Add(field_type_map, last_name_, AutofillType(NAME_LAST)); - DCHECK(ok); - AutofillType type = middle_initial_ ? - AutofillType(NAME_MIDDLE_INITIAL) : AutofillType(NAME_MIDDLE); - ok = ok && Add(field_type_map, middle_name_, type); - DCHECK(ok); - - return ok; + FirstLastNameField* field = ParseSpecificName(scanner); + if (!field) + field = ParseComponentNames(scanner); + return field; } FirstLastNameField::FirstLastNameField() @@ -171,4 +164,3 @@ FirstLastNameField::FirstLastNameField() last_name_(NULL), middle_initial_(false) { } - diff --git a/chrome/browser/autofill/name_field.h b/chrome/browser/autofill/name_field.h index 2625ae1..307b076 100644 --- a/chrome/browser/autofill/name_field.h +++ b/chrome/browser/autofill/name_field.h @@ -8,14 +8,16 @@ #include <vector> +#include "base/compiler_specific.h" #include "chrome/browser/autofill/autofill_field.h" #include "chrome/browser/autofill/form_field.h" +class AutofillScanner; + // A form field that can parse either a FullNameField or a FirstLastNameField. class NameField : public FormField { public: - static NameField* Parse(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml); + static NameField* Parse(AutofillScanner* scanner, bool is_ecml); protected: NameField() {} @@ -27,38 +29,33 @@ class NameField : public FormField { // A form field that can parse a full name field. class FullNameField : public NameField { public: - virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const; + virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const OVERRIDE; - static FullNameField* Parse( - std::vector<AutofillField*>::const_iterator* iter); + static FullNameField* Parse(AutofillScanner* scanner); private: - explicit FullNameField(AutofillField* field); + explicit FullNameField(const AutofillField* field); - AutofillField* field_; + const AutofillField* field_; DISALLOW_COPY_AND_ASSIGN(FullNameField); }; // A form field that can parse a first and last name field. class FirstLastNameField : public NameField { public: - static FirstLastNameField* Parse1( - std::vector<AutofillField*>::const_iterator* iter); - static FirstLastNameField* Parse2( - std::vector<AutofillField*>::const_iterator* iter); - static FirstLastNameField* ParseEcmlName( - std::vector<AutofillField*>::const_iterator* iter); - static FirstLastNameField* Parse( - std::vector<AutofillField*>::const_iterator* iter, bool is_ecml); + virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const OVERRIDE; - virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const; + static FirstLastNameField* ParseSpecificName(AutofillScanner* scanner); + static FirstLastNameField* ParseComponentNames(AutofillScanner* scanner); + static FirstLastNameField* ParseEcmlName(AutofillScanner* scanner); + static FirstLastNameField* Parse(AutofillScanner* scanner, bool is_ecml); private: FirstLastNameField(); - AutofillField* first_name_; - AutofillField* middle_name_; // Optional. - AutofillField* last_name_; + const AutofillField* first_name_; + const AutofillField* middle_name_; // Optional. + const AutofillField* last_name_; bool middle_initial_; // True if middle_name_ is a middle initial. DISALLOW_COPY_AND_ASSIGN(FirstLastNameField); diff --git a/chrome/browser/autofill/name_field_unittest.cc b/chrome/browser/autofill/name_field_unittest.cc index 2f93556..a583ea5 100644 --- a/chrome/browser/autofill/name_field_unittest.cc +++ b/chrome/browser/autofill/name_field_unittest.cc @@ -6,6 +6,7 @@ #include "base/memory/scoped_vector.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "chrome/browser/autofill/name_field.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/glue/form_field.h" @@ -51,9 +52,8 @@ TEST_F(NameFieldTest, FirstMiddleLast) { 0, false), ASCIIToUTF16("name3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -92,9 +92,8 @@ TEST_F(NameFieldTest, FirstMiddleLast2) { 0, false), ASCIIToUTF16("name3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -125,9 +124,8 @@ TEST_F(NameFieldTest, FirstLast) { 0, false), ASCIIToUTF16("name2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -155,9 +153,8 @@ TEST_F(NameFieldTest, FirstLast2) { 0, false), ASCIIToUTF16("name2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -193,9 +190,8 @@ TEST_F(NameFieldTest, FirstLastMiddleWithSpaces) { 0, false), ASCIIToUTF16("name3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -226,9 +222,8 @@ TEST_F(NameFieldTest, FirstLastEmpty) { 0, false), ASCIIToUTF16("name2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -264,9 +259,8 @@ TEST_F(NameFieldTest, FirstMiddleLastEmpty) { 0, false), ASCIIToUTF16("name3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -305,9 +299,8 @@ TEST_F(NameFieldTest, MiddleInitial) { 0, false), ASCIIToUTF16("name3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -338,9 +331,8 @@ TEST_F(NameFieldTest, MiddleInitialNoLastName) { 0, false), ASCIIToUTF16("name2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_EQ(static_cast<NameField*>(NULL), field_.get()); } @@ -371,9 +363,8 @@ TEST_F(NameFieldTest, MiddleInitialAtEnd) { 0, false), ASCIIToUTF16("name3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, false)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -396,9 +387,8 @@ TEST_F(NameFieldTest, ECMLNoName) { 0, false), ASCIIToUTF16("field1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, true)); ASSERT_EQ(static_cast<NameField*>(NULL), field_.get()); } @@ -419,9 +409,8 @@ TEST_F(NameFieldTest, ECMLMiddleInitialNoLastName) { 0, false), ASCIIToUTF16("name2"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, true)); ASSERT_EQ(static_cast<NameField*>(NULL), field_.get()); } @@ -450,9 +439,8 @@ TEST_F(NameFieldTest, ECMLFirstMiddleLast) { 0, false), ASCIIToUTF16("name3"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(NameField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(NameField::Parse(&scanner, true)); ASSERT_NE(static_cast<NameField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc index 0684c72..afe1f6b 100644 --- a/chrome/browser/autofill/personal_data_manager.cc +++ b/chrome/browser/autofill/personal_data_manager.cc @@ -189,7 +189,7 @@ void PersonalDataManager::OnStateChanged() { } bool PersonalDataManager::ImportFormData( - const std::vector<const FormStructure*>& form_structures, + const FormStructure& form, const CreditCard** imported_credit_card) { scoped_ptr<AutofillProfile> imported_profile(new AutofillProfile); scoped_ptr<CreditCard> local_imported_credit_card(new CreditCard); @@ -202,104 +202,101 @@ bool PersonalDataManager::ImportFormData( // Detect and discard forms with multiple fields of the same type. std::set<AutofillFieldType> types_seen; - for (iter = form_structures.begin(); iter != form_structures.end(); ++iter) { - const FormStructure* form = *iter; - for (size_t i = 0; i < form->field_count(); ++i) { - const AutofillField* field = form->field(i); - string16 value = CollapseWhitespace(field->value, false); + for (size_t i = 0; i < form.field_count(); ++i) { + const AutofillField* field = form.field(i); + string16 value = CollapseWhitespace(field->value, false); - // If we don't know the type of the field, or the user hasn't entered any - // information into the field, then skip it. - if (!field->IsFieldFillable() || value.empty()) - continue; - - AutofillFieldType field_type = field->type(); - FieldTypeGroup group(AutofillType(field_type).group()); + // If we don't know the type of the field, or the user hasn't entered any + // information into the field, then skip it. + if (!field->IsFieldFillable() || value.empty()) + continue; - // Abandon the import if two fields of the same type are encountered. - // This indicates ambiguous data or miscategorization of types. - // Make an exception for PHONE_HOME_NUMBER however as both prefix and - // suffix are stored against this type. - if (types_seen.count(field_type) && - field_type != PHONE_HOME_NUMBER && - field_type != PHONE_FAX_NUMBER) { - imported_profile.reset(); - local_imported_credit_card.reset(); - break; - } else { - types_seen.insert(field_type); - } + AutofillFieldType field_type = field->type(); + FieldTypeGroup group(AutofillType(field_type).group()); + + // Abandon the import if two fields of the same type are encountered. + // This indicates ambiguous data or miscategorization of types. + // Make an exception for PHONE_HOME_NUMBER however as both prefix and + // suffix are stored against this type. + if (types_seen.count(field_type) && + field_type != PHONE_HOME_NUMBER && + field_type != PHONE_FAX_NUMBER) { + imported_profile.reset(); + local_imported_credit_card.reset(); + break; + } else { + types_seen.insert(field_type); + } - if (group == AutofillType::CREDIT_CARD) { - // If the user has a password set, we have no way of setting credit - // card numbers. - if (!HasPassword()) { - if (LowerCaseEqualsASCII(field->form_control_type, "month")) { - DCHECK_EQ(CREDIT_CARD_EXP_MONTH, field_type); - local_imported_credit_card->SetInfoForMonthInputType(value); - } else { - if (field_type == CREDIT_CARD_NUMBER) { - // Clean up any imported credit card numbers. - value = CreditCard::StripSeparators(value); - } - local_imported_credit_card->SetInfo(field_type, value); + if (group == AutofillType::CREDIT_CARD) { + // If the user has a password set, we have no way of setting credit + // card numbers. + if (!HasPassword()) { + if (LowerCaseEqualsASCII(field->form_control_type, "month")) { + DCHECK_EQ(CREDIT_CARD_EXP_MONTH, field_type); + local_imported_credit_card->SetInfoForMonthInputType(value); + } else { + if (field_type == CREDIT_CARD_NUMBER) { + // Clean up any imported credit card numbers. + value = CreditCard::StripSeparators(value); } - ++importable_credit_card_fields; + local_imported_credit_card->SetInfo(field_type, value); } - } else { - // In the case of a phone number, if the whole phone number was entered - // into a single field, then parse it and set the sub components. - if (AutofillType(field_type).subgroup() == - AutofillType::PHONE_WHOLE_NUMBER) { - string16 number; - string16 city_code; - string16 country_code; - PhoneNumber::ParsePhoneNumber(value, - &number, - &city_code, - &country_code); - if (number.empty()) - continue; - - if (group == AutofillType::PHONE_HOME) { - imported_profile->SetInfo(PHONE_HOME_COUNTRY_CODE, country_code); - imported_profile->SetInfo(PHONE_HOME_CITY_CODE, city_code); - imported_profile->SetInfo(PHONE_HOME_NUMBER, number); - } else if (group == AutofillType::PHONE_FAX) { - imported_profile->SetInfo(PHONE_FAX_COUNTRY_CODE, country_code); - imported_profile->SetInfo(PHONE_FAX_CITY_CODE, city_code); - imported_profile->SetInfo(PHONE_FAX_NUMBER, number); - } - + ++importable_credit_card_fields; + } + } else { + // In the case of a phone number, if the whole phone number was entered + // into a single field, then parse it and set the sub components. + if (AutofillType(field_type).subgroup() == + AutofillType::PHONE_WHOLE_NUMBER) { + string16 number; + string16 city_code; + string16 country_code; + PhoneNumber::ParsePhoneNumber(value, + &number, + &city_code, + &country_code); + if (number.empty()) continue; - } - // Phone and fax numbers can be split across multiple fields, so we - // might have already stored the prefix, and now be at the suffix. - // If so, combine them to form the full number. - if (group == AutofillType::PHONE_HOME || - group == AutofillType::PHONE_FAX) { - AutofillFieldType number_type = PHONE_HOME_NUMBER; - if (group == AutofillType::PHONE_FAX) - number_type = PHONE_FAX_NUMBER; - - string16 stored_number = imported_profile->GetInfo(number_type); - if (stored_number.size() == - static_cast<size_t>(PhoneNumber::kPrefixLength) && - value.size() == static_cast<size_t>(PhoneNumber::kSuffixLength)) { - value = stored_number + value; - } + if (group == AutofillType::PHONE_HOME) { + imported_profile->SetInfo(PHONE_HOME_COUNTRY_CODE, country_code); + imported_profile->SetInfo(PHONE_HOME_CITY_CODE, city_code); + imported_profile->SetInfo(PHONE_HOME_NUMBER, number); + } else if (group == AutofillType::PHONE_FAX) { + imported_profile->SetInfo(PHONE_FAX_COUNTRY_CODE, country_code); + imported_profile->SetInfo(PHONE_FAX_CITY_CODE, city_code); + imported_profile->SetInfo(PHONE_FAX_NUMBER, number); } - imported_profile->SetInfo(field_type, value); + continue; + } - // Reject profiles with invalid country information. - if (field_type == ADDRESS_HOME_COUNTRY && - !value.empty() && imported_profile->CountryCode().empty()) { - imported_profile.reset(); - break; + // Phone and fax numbers can be split across multiple fields, so we + // might have already stored the prefix, and now be at the suffix. + // If so, combine them to form the full number. + if (group == AutofillType::PHONE_HOME || + group == AutofillType::PHONE_FAX) { + AutofillFieldType number_type = PHONE_HOME_NUMBER; + if (group == AutofillType::PHONE_FAX) + number_type = PHONE_FAX_NUMBER; + + string16 stored_number = imported_profile->GetInfo(number_type); + if (stored_number.size() == + static_cast<size_t>(PhoneNumber::kPrefixLength) && + value.size() == static_cast<size_t>(PhoneNumber::kSuffixLength)) { + value = stored_number + value; } } + + imported_profile->SetInfo(field_type, value); + + // Reject profiles with invalid country information. + if (field_type == ADDRESS_HOME_COUNTRY && + !value.empty() && imported_profile->CountryCode().empty()) { + imported_profile.reset(); + break; + } } } diff --git a/chrome/browser/autofill/personal_data_manager.h b/chrome/browser/autofill/personal_data_manager.h index 7b6299a..d98420c 100644 --- a/chrome/browser/autofill/personal_data_manager.h +++ b/chrome/browser/autofill/personal_data_manager.h @@ -9,6 +9,7 @@ #include <set> #include <vector> +#include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" @@ -63,12 +64,12 @@ class PersonalDataManager // ProfileSyncServiceObserver: virtual void OnStateChanged(); - // TODO(isherman): Update this comment - // If Autofill is able to determine the field types of a significant number of - // field types that contain information in the FormStructures a profile will - // be created with all of the information from recognized fields. Returns - // whether a profile was created. - bool ImportFormData(const std::vector<const FormStructure*>& form_structures, + // Scans the given |form| for importable Autofill data. If the form includes + // sufficient address data, it is immediately imported. If the form includes + // sufficient credit card data, it is stored into |credit_card|, so that we + // can prompt the user whether to save this data. + // Returns |true| if sufficient address or credit card data was found. + bool ImportFormData(const FormStructure& form, const CreditCard** credit_card); // Saves a credit card value detected in |ImportedFormData|. diff --git a/chrome/browser/autofill/personal_data_manager_unittest.cc b/chrome/browser/autofill/personal_data_manager_unittest.cc index 458f0e5c..4320e04 100644 --- a/chrome/browser/autofill/personal_data_manager_unittest.cc +++ b/chrome/browser/autofill/personal_data_manager_unittest.cc @@ -611,10 +611,9 @@ TEST_F(PersonalDataManagerTest, ImportFormData) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -658,10 +657,9 @@ TEST_F(PersonalDataManagerTest, ImportFormDataBadEmail) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure); const CreditCard* imported_credit_card; - EXPECT_FALSE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_FALSE(personal_data_->ImportFormData(form_structure, + &imported_credit_card)); ASSERT_EQ(static_cast<CreditCard*>(NULL), imported_credit_card); // Wait for the refresh. @@ -688,10 +686,9 @@ TEST_F(PersonalDataManagerTest, ImportFormDataNotEnoughFilledFields) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure); const CreditCard* imported_credit_card; - EXPECT_FALSE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_FALSE(personal_data_->ImportFormData(form_structure, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -741,10 +738,9 @@ TEST_F(PersonalDataManagerTest, ImportPhoneNumberSplitAcrossMultipleFields) { form.fields.push_back(field); FormStructure form_structure(form); form_structure.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -844,10 +840,9 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentProfiles) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -890,9 +885,8 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentProfiles) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -939,10 +933,9 @@ TEST_F(PersonalDataManagerTest, AggregateTwoProfilesWithMultiValue) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -985,9 +978,8 @@ TEST_F(PersonalDataManagerTest, AggregateTwoProfilesWithMultiValue) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1045,10 +1037,9 @@ TEST_F(PersonalDataManagerTest, AggregateSameProfileWithConflict) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1103,9 +1094,8 @@ TEST_F(PersonalDataManagerTest, AggregateSameProfileWithConflict) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1150,10 +1140,9 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInOld) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1196,9 +1185,8 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInOld) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1247,10 +1235,9 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInNew) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1294,9 +1281,8 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInNew) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1336,10 +1322,9 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithInsufficientAddress) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_FALSE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_FALSE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Wait for the refresh. @@ -1375,10 +1360,9 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentCreditCards) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1413,9 +1397,8 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentCreditCards) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1455,10 +1438,9 @@ TEST_F(PersonalDataManagerTest, AggregateInvalidCreditCard) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1493,9 +1475,8 @@ TEST_F(PersonalDataManagerTest, AggregateInvalidCreditCard) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_FALSE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_FALSE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Note: no refresh here. @@ -1525,10 +1506,9 @@ TEST_F(PersonalDataManagerTest, AggregateSameCreditCardWithConflict) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1564,9 +1544,8 @@ TEST_F(PersonalDataManagerTest, AggregateSameCreditCardWithConflict) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1607,10 +1586,9 @@ TEST_F(PersonalDataManagerTest, AggregateEmptyCreditCardWithConflict) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1642,9 +1620,8 @@ TEST_F(PersonalDataManagerTest, AggregateEmptyCreditCardWithConflict) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_FALSE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_FALSE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); EXPECT_FALSE(imported_credit_card); // Note: no refresh here. @@ -1678,10 +1655,9 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInNew) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1715,9 +1691,8 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInNew) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_FALSE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_FALSE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_FALSE(imported_credit_card); // Note: no refresh here. @@ -1749,10 +1724,9 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInOld) { FormStructure form_structure1(form1); form_structure1.DetermineHeuristicTypes(); - std::vector<const FormStructure*> forms; - forms.push_back(&form_structure1); const CreditCard* imported_credit_card; - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure1, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; @@ -1788,9 +1762,8 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInOld) { FormStructure form_structure2(form2); form_structure2.DetermineHeuristicTypes(); - forms.clear(); - forms.push_back(&form_structure2); - EXPECT_TRUE(personal_data_->ImportFormData(forms, &imported_credit_card)); + EXPECT_TRUE(personal_data_->ImportFormData(form_structure2, + &imported_credit_card)); ASSERT_TRUE(imported_credit_card); personal_data_->SaveImportedCreditCard(*imported_credit_card); delete imported_credit_card; diff --git a/chrome/browser/autofill/phone_field.cc b/chrome/browser/autofill/phone_field.cc index ef1695d..7306764 100644 --- a/chrome/browser/autofill/phone_field.cc +++ b/chrome/browser/autofill/phone_field.cc @@ -10,6 +10,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "chrome/browser/autofill/fax_number.h" #include "chrome/browser/autofill/home_phone_number.h" #include "grit/autofill_resources.h" @@ -97,14 +98,12 @@ PhoneField::Parser PhoneField::phone_field_grammars_[] = { PhoneField::~PhoneField() {} // static -PhoneField* PhoneField::Parse(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml) { - DCHECK(iter); - if (!iter) +PhoneField* PhoneField::Parse(AutofillScanner* scanner, bool is_ecml) { + if (scanner->IsEnd()) return NULL; if (is_ecml) - return ParseECML(iter); + return ParseECML(scanner); scoped_ptr<PhoneField> phone_field(new PhoneField); @@ -113,7 +112,7 @@ PhoneField* PhoneField::Parse(std::vector<AutofillField*>::const_iterator* iter, // but "area" and "someotherarea" parsed as HOME, for example). for (int i = PHONE_TYPE_MAX - 1; i >= PHONE_TYPE_FIRST; --i) { phone_field->SetPhoneType(static_cast<PhoneField::PhoneType>(i)); - if (ParseInternal(phone_field.get(), iter, i == HOME_PHONE)) + if (ParseInternal(phone_field.get(), scanner, i == HOME_PHONE)) return phone_field.release(); } @@ -121,12 +120,11 @@ PhoneField* PhoneField::Parse(std::vector<AutofillField*>::const_iterator* iter, } // static -PhoneField* PhoneField::ParseECML( - std::vector<AutofillField*>::const_iterator* iter) { +PhoneField* PhoneField::ParseECML(AutofillScanner* scanner) { string16 pattern(GetEcmlPattern(kEcmlShipToPhone, kEcmlBillToPhone, '|')); - AutofillField* field; - if (ParseText(iter, pattern, &field)) { + const AutofillField* field; + if (ParseText(scanner, pattern, &field)) { PhoneField* phone_field = new PhoneField(); phone_field->parsed_phone_fields_[FIELD_PHONE] = field; return phone_field; @@ -136,7 +134,7 @@ PhoneField* PhoneField::ParseECML( } bool PhoneField::GetFieldInfo(FieldTypeMap* field_type_map) const { - bool ok = false; + bool ok = true; DCHECK(parsed_phone_fields_[FIELD_PHONE]); // Phone was correctly parsed. @@ -144,36 +142,31 @@ bool PhoneField::GetFieldInfo(FieldTypeMap* field_type_map) const { (parsed_phone_fields_[FIELD_AREA_CODE] != NULL) || (parsed_phone_fields_[FIELD_SUFFIX] != NULL)) { if (parsed_phone_fields_[FIELD_COUNTRY_CODE] != NULL) { - ok = Add(field_type_map, - parsed_phone_fields_[FIELD_COUNTRY_CODE], - AutofillType(number_->GetCountryCodeType())); - DCHECK(ok); + ok = ok && Add(field_type_map, + parsed_phone_fields_[FIELD_COUNTRY_CODE], + number_->GetCountryCodeType()); } if (parsed_phone_fields_[FIELD_AREA_CODE] != NULL) { - ok = Add(field_type_map, - parsed_phone_fields_[FIELD_AREA_CODE], - AutofillType(number_->GetCityCodeType())); - DCHECK(ok); + ok = ok && Add(field_type_map, + parsed_phone_fields_[FIELD_AREA_CODE], + number_->GetCityCodeType()); } // We tag the prefix as PHONE_HOME_NUMBER, then when filling the form // we fill only the prefix depending on the size of the input field. - ok = Add(field_type_map, - parsed_phone_fields_[FIELD_PHONE], - AutofillType(number_->GetNumberType())); - DCHECK(ok); + ok = ok && Add(field_type_map, + parsed_phone_fields_[FIELD_PHONE], + number_->GetNumberType()); // We tag the suffix as PHONE_HOME_NUMBER, then when filling the form // we fill only the suffix depending on the size of the input field. if (parsed_phone_fields_[FIELD_SUFFIX] != NULL) { - ok = Add(field_type_map, - parsed_phone_fields_[FIELD_SUFFIX], - AutofillType(number_->GetNumberType())); - DCHECK(ok); + ok = ok && Add(field_type_map, + parsed_phone_fields_[FIELD_SUFFIX], + number_->GetNumberType()); } } else { ok = Add(field_type_map, parsed_phone_fields_[FIELD_PHONE], - AutofillType(number_->GetWholeNumberType())); - DCHECK(ok); + number_->GetWholeNumberType()); } return ok; @@ -256,28 +249,25 @@ string16 PhoneField::GetRegExp(RegexType regex_id) const { } // static -bool PhoneField::ParseInternal( - PhoneField *phone_field, - std::vector<AutofillField*>::const_iterator* iter, - bool regular_phone) { - DCHECK(iter); - +bool PhoneField::ParseInternal(PhoneField *phone_field, + AutofillScanner* scanner, + bool regular_phone) { DCHECK(phone_field); - if (!phone_field) - return false; - - std::vector<AutofillField*>::const_iterator q = *iter; + scanner->SaveCursor(); // The form owns the following variables, so they should not be deleted. - AutofillField* parsed_fields[FIELD_MAX]; + const AutofillField* parsed_fields[FIELD_MAX]; for (size_t i = 0; i < arraysize(phone_field_grammars_); ++i) { memset(parsed_fields, 0, sizeof(parsed_fields)); - q = *iter; - // Attempt to parse next possible match. + scanner->Rewind(); + scanner->SaveCursor(); + + // Attempt to parse according to the next grammar. for (; i < arraysize(phone_field_grammars_) && phone_field_grammars_[i].regex != REGEX_SEPARATOR; ++i) { - if (!ParseText(&q, phone_field->GetRegExp(phone_field_grammars_[i].regex), + if (!ParseText(scanner, + phone_field->GetRegExp(phone_field_grammars_[i].regex), &parsed_fields[phone_field_grammars_[i].phone_part])) break; if (phone_field_grammars_[i].max_size && @@ -287,19 +277,30 @@ bool PhoneField::ParseInternal( break; } } - if (i >= arraysize(phone_field_grammars_)) + + if (i >= arraysize(phone_field_grammars_)) { + scanner->Rewind(); return false; // Parsing failed. + } if (phone_field_grammars_[i].regex == REGEX_SEPARATOR) break; // Parsing succeeded. + + // Proceed to the next grammar. do { ++i; } while (i < arraysize(phone_field_grammars_) && phone_field_grammars_[i].regex != REGEX_SEPARATOR); - if (i + 1 == arraysize(phone_field_grammars_)) + + if (i + 1 == arraysize(phone_field_grammars_)) { + scanner->Rewind(); return false; // Tried through all the possibilities - did not match. + } } - if (!parsed_fields[FIELD_PHONE]) + + if (!parsed_fields[FIELD_PHONE]) { + scanner->Rewind(); return false; + } for (int i = 0; i < FIELD_MAX; ++i) phone_field->parsed_phone_fields_[i] = parsed_fields[i]; @@ -308,18 +309,17 @@ bool PhoneField::ParseInternal( // Look for a third text box. if (!phone_field->parsed_phone_fields_[FIELD_SUFFIX]) { - if (!ParseText(&q, phone_field->GetSuffixRegex(), + if (!ParseText(scanner, phone_field->GetSuffixRegex(), &phone_field->parsed_phone_fields_[FIELD_SUFFIX])) { - ParseText(&q, phone_field->GetSuffixSeparatorRegex(), + ParseText(scanner, phone_field->GetSuffixSeparatorRegex(), &phone_field->parsed_phone_fields_[FIELD_SUFFIX]); } } // Now look for an extension. - ParseText(&q, phone_field->GetExtensionRegex(), + ParseText(scanner, phone_field->GetExtensionRegex(), &phone_field->parsed_phone_fields_[FIELD_EXTENSION]); - *iter = q; return true; } diff --git a/chrome/browser/autofill/phone_field.h b/chrome/browser/autofill/phone_field.h index e79809f..de1181f 100644 --- a/chrome/browser/autofill/phone_field.h +++ b/chrome/browser/autofill/phone_field.h @@ -8,12 +8,14 @@ #include <vector> +#include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/autofill/autofill_type.h" #include "chrome/browser/autofill/form_field.h" #include "chrome/browser/autofill/phone_number.h" class AutofillField; +class AutofillScanner; // A phone number in one of the following formats: // - area code, prefix, suffix @@ -23,12 +25,10 @@ class PhoneField : public FormField { public: virtual ~PhoneField(); - static PhoneField* Parse(std::vector<AutofillField*>::const_iterator* iter, - bool is_ecml); - static PhoneField* ParseECML( - std::vector<AutofillField*>::const_iterator* iter); + virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const OVERRIDE; - virtual bool GetFieldInfo(FieldTypeMap* field_type_map) const; + static PhoneField* Parse(AutofillScanner* scanner, bool is_ecml); + static PhoneField* ParseECML(AutofillScanner* scanner); private: PhoneField(); @@ -83,7 +83,7 @@ class PhoneField : public FormField { // |regular_phone| - true if the parsed phone is a HOME phone, false // otherwise. static bool ParseInternal(PhoneField* field, - std::vector<AutofillField*>::const_iterator* iter, + AutofillScanner* scanner, bool regular_phone); void SetPhoneType(PhoneType phone_type); @@ -108,7 +108,7 @@ class PhoneField : public FormField { // FIELD_PHONE is always present; holds suffix if prefix is present. // The rest could be NULL. - AutofillField* parsed_phone_fields_[FIELD_MAX]; + const AutofillField* parsed_phone_fields_[FIELD_MAX]; static struct Parser { RegexType regex; // Field matching reg-ex. diff --git a/chrome/browser/autofill/phone_field_unittest.cc b/chrome/browser/autofill/phone_field_unittest.cc index daa598b..d8cfba7 100644 --- a/chrome/browser/autofill/phone_field_unittest.cc +++ b/chrome/browser/autofill/phone_field_unittest.cc @@ -6,6 +6,7 @@ #include "base/memory/scoped_vector.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_field.h" +#include "chrome/browser/autofill/autofill_scanner.h" #include "chrome/browser/autofill/phone_field.h" #include "testing/gtest/include/gtest/gtest.h" #include "webkit/glue/form_field.h" @@ -27,17 +28,15 @@ class PhoneFieldTest : public testing::Test { }; TEST_F(PhoneFieldTest, Empty) { - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_EQ(static_cast<PhoneField*>(NULL), field_.get()); } TEST_F(PhoneFieldTest, NonParse) { list_.push_back(new AutofillField); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_EQ(static_cast<PhoneField*>(NULL), field_.get()); } @@ -50,9 +49,8 @@ TEST_F(PhoneFieldTest, ParseOneLinePhone) { 0, false), ASCIIToUTF16("phone1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -69,9 +67,8 @@ TEST_F(PhoneFieldTest, ParseOneLinePhoneEcml) { 0, false), ASCIIToUTF16("phone1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, true)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, true)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -96,9 +93,8 @@ TEST_F(PhoneFieldTest, ParseTwoLinePhone) { 0, false), ASCIIToUTF16("phone1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -127,9 +123,8 @@ TEST_F(PhoneFieldTest, ParseTwoLinePhoneEcmlShipTo) { 0, false), ASCIIToUTF16("phone1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -158,9 +153,8 @@ TEST_F(PhoneFieldTest, ParseTwoLinePhoneEcmlBillTo) { 0, false), ASCIIToUTF16("phone1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -209,9 +203,8 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumber) { 0, false), ASCIIToUTF16("ext1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -253,9 +246,8 @@ TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) { 0, false), ASCIIToUTF16("suffix1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -278,9 +270,8 @@ TEST_F(PhoneFieldTest, ParseOneLineFax) { 0, false), ASCIIToUTF16("fax1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -305,9 +296,8 @@ TEST_F(PhoneFieldTest, ParseTwoLineFax) { 0, false), ASCIIToUTF16("fax1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( @@ -344,9 +334,8 @@ TEST_F(PhoneFieldTest, ThreePartFaxNumberPrefixSuffix) { 0, false), ASCIIToUTF16("suffix1"))); - list_.push_back(NULL); - iter_ = list_.begin(); - field_.reset(PhoneField::Parse(&iter_, false)); + AutofillScanner scanner(list_.get()); + field_.reset(PhoneField::Parse(&scanner, false)); ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get()); ASSERT_TRUE(field_->GetFieldInfo(&field_type_map_)); ASSERT_TRUE( diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 7eef26e..9ace239 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -159,6 +159,8 @@ 'browser/autofill/autofill_metrics.h', 'browser/autofill/autofill_profile.cc', 'browser/autofill/autofill_profile.h', + 'browser/autofill/autofill_scanner.cc', + 'browser/autofill/autofill_scanner.h', 'browser/autofill/autofill_type.cc', 'browser/autofill/autofill_type.h', 'browser/autofill/autofill_xml_parser.cc', @@ -169,6 +171,8 @@ 'browser/autofill/credit_card.h', 'browser/autofill/credit_card_field.cc', 'browser/autofill/credit_card_field.h', + 'browser/autofill/email_field.cc', + 'browser/autofill/email_field.h', 'browser/autofill/fax_number.cc', 'browser/autofill/fax_number.h', 'browser/autofill/field_types.h', |