diff options
author | dhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-03 20:52:28 +0000 |
---|---|---|
committer | dhollowa@chromium.org <dhollowa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-03 20:52:28 +0000 |
commit | a1107c5d54db86a52e2aa04ed090112e569442ae (patch) | |
tree | 1610a68124ff3a9375619a0c381975c20d8cebfc | |
parent | 93120fea1fb7a895ef0469bb95540aa39520c691 (diff) | |
download | chromium_src-a1107c5d54db86a52e2aa04ed090112e569442ae.zip chromium_src-a1107c5d54db86a52e2aa04ed090112e569442ae.tar.gz chromium_src-a1107c5d54db86a52e2aa04ed090112e569442ae.tar.bz2 |
Support autocompletion for HTMl5 tags:"email", "month" and "tel".
BUG=65654
TEST=1.make HTML5 forms with the following input types: - Email input:
<input type="email"> - Date picker: <input type="month"> for credit card
expiration. - Telephone input: <input type="tel"> - Placeholder text:
<input name="address" placeholder="1 Main Street"/> 2.Try to fill.
Implement tests for email, month and tel input type.
Also this patch needs WebKit patch, please refer to
https://bugs.webkit.org/show_bug.cgi?id=51809
Actually, original issue item says placeholder problem, but I cannot reproduce manually.
Review URL: http://codereview.chromium.org/6033010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@73660 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 16 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.h | 5 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager_unittest.cc | 212 | ||||
-rw-r--r-- | chrome/browser/autofill/credit_card.cc | 23 | ||||
-rw-r--r-- | chrome/browser/autofill/credit_card.h | 3 | ||||
-rw-r--r-- | chrome/browser/autofill/credit_card_field.cc | 60 | ||||
-rw-r--r-- | chrome/browser/autofill/personal_data_manager.cc | 9 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_manager.cc | 116 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_manager_browsertest.cc | 51 |
9 files changed, 377 insertions, 118 deletions
diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index 84af836..adad24b 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -799,10 +799,22 @@ void AutoFillManager::FillCreditCardFormField(const CreditCard* credit_card, DCHECK(type.group() == AutoFillType::CREDIT_CARD); DCHECK(field); - if (field->form_control_type() == ASCIIToUTF16("select-one")) + if (field->form_control_type() == ASCIIToUTF16("select-one")) { autofill::FillSelectControl(credit_card, type, field); - else + } else if (field->form_control_type() == ASCIIToUTF16("month")) { + // HTML5 input="month" consists of year-month. + string16 year = credit_card->GetFieldText( + AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)); + string16 month = credit_card->GetFieldText( + AutoFillType(CREDIT_CARD_EXP_MONTH)); + if (!year.empty() && !month.empty()) { + // Fill the value only if |credit_card| includes both year and month + // information. + field->set_value(year + ASCIIToUTF16("-") + month); + } + } else { field->set_value(credit_card->GetFieldText(type)); + } } void AutoFillManager::FillFormField(const AutoFillProfile* profile, diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index 816cb67..569ffbc 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -224,6 +224,11 @@ class AutoFillManager : public TabContentsObserver, friend class AutoFillManagerTest; friend class FormStructureBrowserTest; FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillCreditCardForm); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, + FillCreditCardFormNoYearNoMonth); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillCreditCardFormYearNoMonth); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillCreditCardFormNoYearMonth); + FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillCreditCardFormYearMonth); FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillAddressForm); FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillAddressAndCreditCardForm); FRIEND_TEST_ALL_PREFIXES(AutoFillManagerTest, FillAutoFilledForm); diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc index 2f0f750..f58eaf8 100644 --- a/chrome/browser/autofill/autofill_manager_unittest.cc +++ b/chrome/browser/autofill/autofill_manager_unittest.cc @@ -90,6 +90,16 @@ class TestPersonalDataManager : public PersonalDataManager { credit_cards_.reset(); } + void CreateTestCreditCardsYearAndMonth(const char* year, const char* month) { + ClearCreditCards(); + CreditCard* credit_card = new CreditCard; + autofill_test::SetCreditCardInfo(credit_card, "Miku", "Miku Hatsune", + "4234567890654321", // Visa + month, year); + credit_card->set_guid("00000000-0000-0000-0000-000000000007"); + credit_cards_->push_back(credit_card); + } + private: void CreateTestAutoFillProfiles(ScopedVector<AutoFillProfile>* profiles) { AutoFillProfile* profile = new AutoFillProfile; @@ -184,12 +194,20 @@ void CreateTestAddressFormData(FormData* form) { autofill_test::CreateTestFormField( "Email", "email", "", "text", &field); form->fields.push_back(field); + autofill_test::CreateTestFormField( + "Email", "email2", "", "email", &field); + form->fields.push_back(field); + autofill_test::CreateTestFormField( + "Phone Number", "phonenumber2", "", "tel", &field); + form->fields.push_back(field); } // Populates |form| with data corresponding to a simple credit card form. // Note that this actually appends fields to the form data, which can be useful // for building up more complex test forms. -void CreateTestCreditCardFormData(FormData* form, bool is_https) { +void CreateTestCreditCardFormData(FormData* form, + bool is_https, + bool use_month_type) { form->name = ASCIIToUTF16("MyForm"); form->method = ASCIIToUTF16("POST"); if (is_https) { @@ -208,12 +226,18 @@ void CreateTestCreditCardFormData(FormData* form, bool is_https) { autofill_test::CreateTestFormField( "Card Number", "cardnumber", "", "text", &field); form->fields.push_back(field); - autofill_test::CreateTestFormField( - "Expiration Date", "ccmonth", "", "text", &field); - form->fields.push_back(field); - autofill_test::CreateTestFormField( - "", "ccyear", "", "text", &field); - form->fields.push_back(field); + if (use_month_type) { + autofill_test::CreateTestFormField( + "Expiration Date", "ccmonth", "", "month", &field); + form->fields.push_back(field); + } else { + autofill_test::CreateTestFormField( + "Expiration Date", "ccmonth", "", "text", &field); + form->fields.push_back(field); + autofill_test::CreateTestFormField( + "", "ccyear", "", "text", &field); + form->fields.push_back(field); + } } void ExpectSuggestions(int page_id, @@ -244,7 +268,7 @@ void ExpectSuggestions(int page_id, // Verifies that the |filled_form| has been filled with the given data. // Verifies address fields if |has_address_fields| is true, and verifies // credit card fields if |has_credit_card_fields| is true. Verifies both if both -// are true. +// are true. |use_month_type| is used for credit card input month type. void ExpectFilledForm(int page_id, const FormData& filled_form, int expected_page_id, @@ -265,10 +289,11 @@ void ExpectFilledForm(int page_id, const char* expiration_month, const char* expiration_year, bool has_address_fields, - bool has_credit_card_fields) { + bool has_credit_card_fields, + bool use_month_type) { // The number of fields in the address and credit card forms created above. - const size_t kAddressFormSize = 12; - const size_t kCreditCardFormSize = 4; + const size_t kAddressFormSize = 14; + const size_t kCreditCardFormSize = use_month_type ? 3 : 4; EXPECT_EQ(expected_page_id, page_id); EXPECT_EQ(ASCIIToUTF16("MyForm"), filled_form.name); @@ -327,6 +352,12 @@ void ExpectFilledForm(int page_id, autofill_test::CreateTestFormField( "Email", "email", email, "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[11])); + autofill_test::CreateTestFormField( + "Email", "email2", email, "email", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[12])); + autofill_test::CreateTestFormField( + "Phone Number", "phonenumber2", phone, "tel", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[13])); } if (has_credit_card_fields) { @@ -337,12 +368,23 @@ void ExpectFilledForm(int page_id, autofill_test::CreateTestFormField( "Card Number", "cardnumber", card_number, "text", &field); EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[offset + 1])); - autofill_test::CreateTestFormField( - "Expiration Date", "ccmonth", expiration_month, "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[offset + 2])); - autofill_test::CreateTestFormField( - "", "ccyear", expiration_year, "text", &field); - EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[offset + 3])); + if (use_month_type) { + std::string exp_year = expiration_year; + std::string exp_month = expiration_month; + std::string date; + if (!exp_year.empty() && !exp_month.empty()) + date = exp_year + "-" + exp_month; + autofill_test::CreateTestFormField( + "Expiration Date", "ccmonth", date.c_str(), "month", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[offset + 2])); + } else { + autofill_test::CreateTestFormField( + "Expiration Date", "ccmonth", expiration_month, "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[offset + 2])); + autofill_test::CreateTestFormField( + "", "ccyear", expiration_year, "text", &field); + EXPECT_TRUE(field.StrictlyEqualsHack(filled_form.fields[offset + 3])); + } } } @@ -354,7 +396,7 @@ void ExpectFilledAddressFormElvis(int page_id, "Presley", "3734 Elvis Presley Blvd.", "Apt. 10", "Memphis", "Tennessee", "38116", "USA", "12345678901", "", "theking@gmail.com", "", "", "", "", true, - has_credit_card_fields); + has_credit_card_fields, false); } void ExpectFilledCreditCardFormElvis(int page_id, @@ -364,7 +406,19 @@ void ExpectFilledCreditCardFormElvis(int page_id, ExpectFilledForm(page_id, filled_form, expected_page_id, "", "", "", "", "", "", "", "", "", "", "", "", "Elvis Presley", "4234567890123456", "04", "2012", - has_address_fields, true); + has_address_fields, true, false); +} + +void ExpectFilledCreditCardYearMonthWithYearMonth(int page_id, + const FormData& filled_form, + int expected_page_id, + bool has_address_fields, + const char* year, + const char* month) { + ExpectFilledForm(page_id, filled_form, expected_page_id, + "", "", "", "", "", "", "", "", "", "", "", "", + "Miku Hatsune", "4234567890654321", month, year, + has_address_fields, true, true); } class TestAutoFillManager : public AutoFillManager { @@ -768,7 +822,7 @@ TEST_F(AutoFillManagerTest, GetProfileSuggestionsMethodGet) { TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsEmptyValue) { // Set up our form data. FormData form; - CreateTestCreditCardFormData(&form, true); + CreateTestCreditCardFormData(&form, true, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -811,7 +865,7 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsEmptyValue) { TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsMatchCharacter) { // Set up our form data. FormData form; - CreateTestCreditCardFormData(&form, true); + CreateTestCreditCardFormData(&form, true, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -847,7 +901,7 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsMatchCharacter) { TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonCCNumber) { // Set up our form data. FormData form; - CreateTestCreditCardFormData(&form, true); + CreateTestCreditCardFormData(&form, true, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -890,7 +944,7 @@ TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonCCNumber) { TEST_F(AutoFillManagerTest, GetCreditCardSuggestionsNonHTTPS) { // Set up our form data. FormData form; - CreateTestCreditCardFormData(&form, false); + CreateTestCreditCardFormData(&form, false, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -955,7 +1009,7 @@ TEST_F(AutoFillManagerTest, GetAddressAndCreditCardSuggestions) { // Set up our form data. FormData form; CreateTestAddressFormData(&form); - CreateTestCreditCardFormData(&form, true); + CreateTestCreditCardFormData(&form, true, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -1029,7 +1083,7 @@ TEST_F(AutoFillManagerTest, GetAddressAndCreditCardSuggestionsNonHttps) { // Set up our form data. FormData form; CreateTestAddressFormData(&form); - CreateTestCreditCardFormData(&form, false); + CreateTestCreditCardFormData(&form, false, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -1289,7 +1343,7 @@ TEST_F(AutoFillManagerTest, FillAddressForm) { TEST_F(AutoFillManagerTest, FillCreditCardForm) { // Set up our form data. FormData form; - CreateTestCreditCardFormData(&form, true); + CreateTestCreditCardFormData(&form, true, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -1304,12 +1358,110 @@ TEST_F(AutoFillManagerTest, FillCreditCardForm) { ExpectFilledCreditCardFormElvis(page_id, results, kDefaultPageID, false); } +// Test that we correctly fill a credit card form with month input type. +// 1. year empty, month empty +TEST_F(AutoFillManagerTest, FillCreditCardFormNoYearNoMonth) { + // Same as the SetUp(), but generate 4 credit cards with year month + // combination. + test_personal_data_->CreateTestCreditCardsYearAndMonth("", ""); + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, true); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + std::string guid = autofill_manager_->GetLabeledCreditCard("Miku")->guid(); + FillAutoFillFormData( + kDefaultPageID, form, *form.fields.begin(), + autofill_manager_->PackGUIDs(guid, std::string())); + + int page_id = 0; + FormData results; + EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); + ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results, + kDefaultPageID, false, "", ""); +} + + +// Test that we correctly fill a credit card form with month input type. +// 2. year empty, month non-empty +TEST_F(AutoFillManagerTest, FillCreditCardFormNoYearMonth) { + // Same as the SetUp(), but generate 4 credit cards with year month + // combination. + test_personal_data_->CreateTestCreditCardsYearAndMonth("", "04"); + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, true); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + std::string guid = autofill_manager_->GetLabeledCreditCard("Miku")->guid(); + FillAutoFillFormData( + kDefaultPageID, form, *form.fields.begin(), + autofill_manager_->PackGUIDs(guid, std::string())); + + int page_id = 0; + FormData results; + EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); + ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results, + kDefaultPageID, false, "", "04"); +} + +// Test that we correctly fill a credit card form with month input type. +// 3. year non-empty, month empty +TEST_F(AutoFillManagerTest, FillCreditCardFormYearNoMonth) { + // Same as the SetUp(), but generate 4 credit cards with year month + // combination. + test_personal_data_->CreateTestCreditCardsYearAndMonth("2012", ""); + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, true); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + std::string guid = autofill_manager_->GetLabeledCreditCard("Miku")->guid(); + FillAutoFillFormData( + kDefaultPageID, form, *form.fields.begin(), + autofill_manager_->PackGUIDs(guid, std::string())); + + int page_id = 0; + FormData results; + EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); + ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results, + kDefaultPageID, false, "2012", ""); +} + +// Test that we correctly fill a credit card form with month input type. +// 4. year non-empty, month empty +TEST_F(AutoFillManagerTest, FillCreditCardFormYearMonth) { + // Same as the SetUp(), but generate 4 credit cards with year month + // combination. + test_personal_data_->ClearCreditCards(); + test_personal_data_->CreateTestCreditCardsYearAndMonth("2012", "04"); + // Set up our form data. + FormData form; + CreateTestCreditCardFormData(&form, true, true); + std::vector<FormData> forms(1, form); + FormsSeen(forms); + + std::string guid = autofill_manager_->GetLabeledCreditCard("Miku")->guid(); + FillAutoFillFormData( + kDefaultPageID, form, *form.fields.begin(), + autofill_manager_->PackGUIDs(guid, std::string())); + + int page_id = 0; + FormData results; + EXPECT_TRUE(GetAutoFillFormDataFilledMessage(&page_id, &results)); + ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results, + kDefaultPageID, false, "2012", "04"); +} + // Test that we correctly fill a combined address and credit card form. TEST_F(AutoFillManagerTest, FillAddressAndCreditCardForm) { // Set up our form data. FormData form; CreateTestAddressFormData(&form); - CreateTestCreditCardFormData(&form, true); + CreateTestCreditCardFormData(&form, true, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -1349,7 +1501,7 @@ TEST_F(AutoFillManagerTest, FillAutoFilledForm) { CreateTestAddressFormData(&form); // Mark one of the address fields as autofilled. form.fields[4].set_autofilled(true); - CreateTestCreditCardFormData(&form, true); + CreateTestCreditCardFormData(&form, true, false); std::vector<FormData> forms(1, form); FormsSeen(forms); @@ -1366,7 +1518,7 @@ TEST_F(AutoFillManagerTest, FillAutoFilledForm) { SCOPED_TRACE("Address"); ExpectFilledForm(page_id, results, kDefaultPageID, "Elvis", "", "", "", "", "", "", "", "", "", "", "", - "", "", "", "", true, true); + "", "", "", "", true, true, false); } // Now fill the credit card data. @@ -1402,7 +1554,7 @@ TEST_F(AutoFillManagerTest, FillAutoFilledForm) { SCOPED_TRACE("Credit card 2"); ExpectFilledForm(page_id, results, kPageID3, "", "", "", "", "", "", "", "", "", "", "", "", - "", "", "", "2012", true, true); + "", "", "", "2012", true, true, false); } } diff --git a/chrome/browser/autofill/credit_card.cc b/chrome/browser/autofill/credit_card.cc index 6296eba..20fca4e 100644 --- a/chrome/browser/autofill/credit_card.cc +++ b/chrome/browser/autofill/credit_card.cc @@ -7,6 +7,7 @@ #include <string> #include "base/basictypes.h" +#include "base/string_split.h" #include "base/string_util.h" #include "base/string_number_conversions.h" #include "base/string16.h" @@ -15,6 +16,8 @@ #include "chrome/browser/autofill/field_types.h" #include "chrome/common/guid.h" #include "grit/generated_resources.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebRegularExpression.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" #include "ui/base/l10n/l10n_util.h" namespace { @@ -329,6 +332,26 @@ const string16 CreditCard::Label() const { return label_; } +void CreditCard::SetInfoForMonthInputType(const string16& value) { + // Check if |text| is "yyyy-mm" format first, and check normal month format. + WebKit::WebRegularExpression re(WebKit::WebString("^[0-9]{4}\\-[0-9]{1,2}$"), + WebKit::WebTextCaseInsensitive); + bool match = re.match(WebKit::WebString(StringToLowerASCII(value))) != -1; + if (match) { + std::vector<string16> year_month; + base::SplitString(value, L'-', &year_month); + DCHECK_EQ((int)year_month.size(), 2); + int num = 0; + bool converted = false; + converted = base::StringToInt(year_month[0], &num); + DCHECK(converted); + set_expiration_year(num); + converted = base::StringToInt(year_month[1], &num); + DCHECK(converted); + set_expiration_month(num); + } +} + string16 CreditCard::ObfuscatedNumber() const { if (number().empty()) return string16(); // No CC number, means empty preview. diff --git a/chrome/browser/autofill/credit_card.h b/chrome/browser/autofill/credit_card.h index 2b2d089..951b605 100644 --- a/chrome/browser/autofill/credit_card.h +++ b/chrome/browser/autofill/credit_card.h @@ -34,6 +34,9 @@ class CreditCard : public FormGroup { virtual void SetInfo(const AutoFillType& type, const string16& value); virtual const string16 Label() const; + // Special method to set value for HTML5 month input type. + void SetInfoForMonthInputType(const string16& value); + // The number altered for display, for example: ******1234 string16 ObfuscatedNumber() const; // Credit card preview summary, for example: ******1234, Exp: 01/2020 diff --git a/chrome/browser/autofill/credit_card_field.cc b/chrome/browser/autofill/credit_card_field.cc index 7837463..746bdf9 100644 --- a/chrome/browser/autofill/credit_card_field.cc +++ b/chrome/browser/autofill/credit_card_field.cc @@ -118,35 +118,40 @@ CreditCardField* CreditCardField::Parse( &credit_card_field->number_)) continue; - // "Expiration date" is the most common label here, but some pages have - // "Expires", "exp. date" or "exp. month" and "exp. year". We also look for - // the field names ccmonth and ccyear, which appear on at least 4 of our - // test pages. - // - // -> On at least one page (The China Shop2.html) we find only the labels - // "month" and "year". So for now we match these words directly; we'll - // see if this turns out to be too general. - // - // Toolbar Bug 51451: indeed, simply matching "month" is too general for - // https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init. - // Instead, we match only words beginning with "month". - if (is_ecml) - pattern = GetEcmlPattern(kEcmlCardExpireMonth); - else - 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_)) { + if ((*q) && LowerCaseEqualsASCII((*q)->form_control_type(), "month")) { + credit_card_field->expiration_month_ = *q++; + } 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 + // for the field names ccmonth and ccyear, which appear on at least 4 of + // our test pages. + // + // -> On at least one page (The China Shop2.html) we find only the labels + // "month" and "year". So for now we match these words directly; we'll + // see if this turns out to be too general. + // + // Toolbar Bug 51451: indeed, simply matching "month" is too general for + // https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init. + // Instead, we match only words beginning with "month". if (is_ecml) - pattern = GetEcmlPattern(kEcmlCardExpireYear); + pattern = GetEcmlPattern(kEcmlCardExpireMonth); else - pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_DATE_RE); + pattern = l10n_util::GetStringUTF16(IDS_AUTOFILL_EXPIRATION_MONTH_RE); - if (!ParseText(&q, pattern, &credit_card_field->expiration_year_)) - return NULL; + if ((!credit_card_field->expiration_month_ || + credit_card_field->expiration_month_->IsEmpty()) && + ParseText(&q, pattern, &credit_card_field->expiration_month_)) { - continue; + 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_)) { + return NULL; + } + continue; + } } if (ParseText(&q, GetEcmlPattern(kEcmlCardExpireDay))) @@ -180,7 +185,10 @@ CreditCardField* CreditCardField::Parse( // the cvc and date were found independently they are returned. if ((credit_card_field->number_ || credit_card_field->verification_) && credit_card_field->expiration_month_ && - credit_card_field->expiration_year_) { + (credit_card_field->expiration_year_ || + (LowerCaseEqualsASCII( + credit_card_field->expiration_month_->form_control_type(), + "month")))) { *iter = q; return credit_card_field.release(); } diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc index 8430b15..8dfa553 100644 --- a/chrome/browser/autofill/personal_data_manager.cc +++ b/chrome/browser/autofill/personal_data_manager.cc @@ -191,8 +191,13 @@ bool PersonalDataManager::ImportFormData( // If the user has a password set, we have no way of setting credit // card numbers. if (!HasPassword()) { - imported_credit_card_->SetInfo(AutoFillType(field_type.field_type()), - value); + if (LowerCaseEqualsASCII(field->form_control_type(), "month")) { + DCHECK_EQ(CREDIT_CARD_EXP_MONTH, field_type.field_type()); + imported_credit_card_->SetInfoForMonthInputType(value); + } else { + imported_credit_card_->SetInfo( + AutoFillType(field_type.field_type()), value); + } ++importable_credit_card_fields; } } else { diff --git a/chrome/renderer/autofill/form_manager.cc b/chrome/renderer/autofill/form_manager.cc index cd24300..3e2b482 100644 --- a/chrome/renderer/autofill/form_manager.cc +++ b/chrome/renderer/autofill/form_manager.cc @@ -52,10 +52,13 @@ const size_t kRequiredAutoFillFields = 3; // The maximum length allowed for form data. const size_t kMaxDataLength = 1024; -// TODO(isherman): Replace calls to this with IsTextInput() once -// http://codereview.chromium.org/6033010/ lands. -bool IsTextElement(const WebFormControlElement& element) { - return element.formControlType() == WebString::fromUTF8("text"); +// In HTML5, all text fields except password are text input fields to +// autocomplete. +bool IsTextInput(const WebInputElement* element) { + if (!element) + return false; + + return element->isTextField() && !element->isPasswordField(); } bool IsSelectElement(const WebFormControlElement& element) { @@ -335,7 +338,8 @@ void FormManager::WebFormControlElementToFormField( field->set_name(element.nameForAutofill()); field->set_form_control_type(element.formControlType()); - if (IsTextElement(element)) { + const WebInputElement* input_element = toWebInputElement(&element); + if (IsTextInput(input_element)) { const WebInputElement& input_element = element.toConst<WebInputElement>(); field->set_max_length(input_element.maxLength()); field->set_autofilled(input_element.isAutofilled()); @@ -350,10 +354,9 @@ void FormManager::WebFormControlElementToFormField( return; string16 value; - if (IsTextElement(element) || + if (IsTextInput(input_element) || element.formControlType() == WebString::fromUTF8("hidden")) { - const WebInputElement& input_element = element.toConst<WebInputElement>(); - value = input_element.value(); + value = input_element->value(); } else if (IsSelectElement(element)) { const WebSelectElement select_element = element.toConst<WebSelectElement>(); value = select_element.value(); @@ -444,12 +447,10 @@ bool FormManager::WebFormElementToFormData(const WebFormElement& element, for (size_t i = 0; i < control_elements.size(); ++i) { const WebFormControlElement& control_element = control_elements[i]; - if (requirements & REQUIRE_AUTOCOMPLETE && IsTextElement(control_element)) { - const WebInputElement& input_element = - control_element.toConst<WebInputElement>(); - if (!input_element.autoComplete()) - continue; - } + const WebInputElement* input_element = toWebInputElement(&control_element); + if (requirements & REQUIRE_AUTOCOMPLETE && IsTextInput(input_element) && + !input_element->autoComplete()) + continue; if (requirements & REQUIRE_ENABLED && !control_element.isEnabled()) continue; @@ -654,21 +655,21 @@ bool FormManager::ClearFormWithNode(const WebNode& node) { for (size_t i = 0; i < form_element->control_elements.size(); ++i) { WebFormControlElement element = form_element->control_elements[i]; - if (IsTextElement(element)) { - WebInputElement input_element = element.to<WebInputElement>(); + WebInputElement* input_element = toWebInputElement(&element); + if (IsTextInput(input_element)) { // We don't modify the value of disabled fields. - if (!input_element.isEnabled()) + if (!input_element->isEnabled()) continue; - input_element.setValue(string16()); - input_element.setAutofilled(false); + input_element->setValue(string16()); + input_element->setAutofilled(false); // Clearing the value in the focused node (above) can cause selection // to be lost. We force selection range to restore the text cursor. - if (node == input_element) { - int length = input_element.value().length(); - input_element.setSelectionRange(length, length); + if (node == *input_element) { + int length = input_element->value().length(); + input_element->setSelectionRange(length, length); } } else if (IsSelectElement(element)) { WebSelectElement select_element = element.to<WebSelectElement>(); @@ -686,44 +687,43 @@ bool FormManager::ClearPreviewedFormWithNode(const WebNode& node, return false; for (size_t i = 0; i < form_element->control_elements.size(); ++i) { - WebFormControlElement* element = &form_element->control_elements[i]; - + WebInputElement* input_element = + toWebInputElement(&form_element->control_elements[i]); // Only text input elements can be previewed. - if (!IsTextElement(*element)) + if (!IsTextInput(input_element)) continue; // If the input element has not been auto-filled, FormManager has not // previewed this field, so we have nothing to reset. - WebInputElement input_element = element->to<WebInputElement>(); - if (!input_element.isAutofilled()) + if (!input_element->isAutofilled()) continue; // There might be unrelated elements in this form which have already been // auto-filled. For example, the user might have already filled the address // part of a form and now be dealing with the credit card section. We only // want to reset the auto-filled status for fields that were previewed. - if (input_element.suggestedValue().isEmpty()) + if (input_element->suggestedValue().isEmpty()) continue; // Clear the suggested value. For the initiating node, also restore the // original value. - input_element.setSuggestedValue(WebString()); - bool is_initiating_node = (node == input_element); + input_element->setSuggestedValue(WebString()); + bool is_initiating_node = (node == *input_element); if (is_initiating_node) { // Call |setValue()| to force the renderer to update the field's displayed // value. - input_element.setValue(input_element.value()); - input_element.setAutofilled(was_autofilled); + input_element->setValue(input_element->value()); + input_element->setAutofilled(was_autofilled); } else { - input_element.setAutofilled(false); + input_element->setAutofilled(false); } // Clearing the suggested value in the focused node (above) can cause // selection to be lost. We force selection range to restore the text // cursor. if (is_initiating_node) { - int length = input_element.value().length(); - input_element.setSelectionRange(length, length); + int length = input_element->value().length(); + input_element->setSelectionRange(length, length); } } @@ -750,12 +750,12 @@ bool FormManager::FormWithNodeIsAutoFilled(const WebNode& node) { return false; for (size_t i = 0; i < form_element->control_elements.size(); ++i) { - WebFormControlElement element = form_element->control_elements[i]; - if (!IsTextElement(element)) + WebInputElement* input_element = + toWebInputElement(&form_element->control_elements[i]); + if (!IsTextInput(input_element)) continue; - const WebInputElement& input_element = element.to<WebInputElement>(); - if (input_element.isAutofilled()) + if (input_element->isAutofilled()) return true; } @@ -836,22 +836,21 @@ void FormManager::ForEachMatchingFormField(FormElement* form, // More than likely |requirements| will contain REQUIRE_AUTOCOMPLETE and/or // REQUIRE_EMPTY, which both require text form control elements, so special- // case this type of element. - if (IsTextElement(*element)) { - const WebInputElement& input_element = - element->toConst<WebInputElement>(); + const WebInputElement* input_element = toWebInputElement(element); + if (IsTextInput(input_element)) { // TODO(jhawkins): WebKit currently doesn't handle the autocomplete // attribute for select control elements, but it probably should. - if (requirements & REQUIRE_AUTOCOMPLETE && !input_element.autoComplete()) + if (requirements & REQUIRE_AUTOCOMPLETE && !input_element->autoComplete()) continue; - is_initiating_node = (input_element == node); + is_initiating_node = (*input_element == node); // Don't require the node that initiated the auto-fill process to be // empty. The user is typing in this field and we should complete the // value when the user selects a value to fill out. if (requirements & REQUIRE_EMPTY && !is_initiating_node && - !input_element.value().isEmpty()) + !input_element->value().isEmpty()) continue; } @@ -874,16 +873,17 @@ void FormManager::FillFormField(WebFormControlElement* field, if (data->value().empty()) return; - if (IsTextElement(*field)) { - WebInputElement input_element = field->to<WebInputElement>(); + WebInputElement* input_element = toWebInputElement(field); + if (IsTextInput(input_element)) { // If the maxlength attribute contains a negative value, maxLength() // returns the default maxlength value. - input_element.setValue(data->value().substr(0, input_element.maxLength())); - input_element.setAutofilled(true); + input_element->setValue( + data->value().substr(0, input_element->maxLength())); + input_element->setAutofilled(true); if (is_initiating_node) { - int length = input_element.value().length(); - input_element.setSelectionRange(length, length); + int length = input_element->value().length(); + input_element->setSelectionRange(length, length); } } else if (IsSelectElement(*field)) { WebSelectElement select_element = field->to<WebSelectElement>(); @@ -899,18 +899,18 @@ void FormManager::PreviewFormField(WebFormControlElement* field, return; // Only preview input fields. - if (!IsTextElement(*field)) + WebInputElement* input_element = toWebInputElement(field); + if (!IsTextInput(input_element)) return; - WebInputElement input_element = field->to<WebInputElement>(); - // If the maxlength attribute contains a negative value, maxLength() // returns the default maxlength value. - input_element.setSuggestedValue( - data->value().substr(0, input_element.maxLength())); - input_element.setAutofilled(true); + input_element->setSuggestedValue( + data->value().substr(0, input_element->maxLength())); + input_element->setAutofilled(true); if (is_initiating_node) - input_element.setSelectionRange(0, input_element.suggestedValue().length()); + input_element->setSelectionRange(0, + input_element->suggestedValue().length()); } } // namespace autofill diff --git a/chrome/renderer/autofill/form_manager_browsertest.cc b/chrome/renderer/autofill/form_manager_browsertest.cc index ca71894..d6243fa 100644 --- a/chrome/renderer/autofill/form_manager_browsertest.cc +++ b/chrome/renderer/autofill/form_manager_browsertest.cc @@ -2846,6 +2846,8 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNode) { " <INPUT type=\"text\" id=\"firstname\" value=\"Wyatt\"/>" " <INPUT type=\"text\" id=\"lastname\"/>" " <INPUT type=\"text\" id=\"email\"/>" + " <INPUT type=\"email\" id=\"email2\"/>" + " <INPUT type=\"tel\" id=\"phone\"/>" " <INPUT type=\"submit\" value=\"Send\"/>" "</FORM>"); @@ -2870,10 +2872,18 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNode) { WebInputElement email = web_frame->document().getElementById("email").to<WebInputElement>(); email.setAutofilled(true); + WebInputElement email2 = + web_frame->document().getElementById("email2").to<WebInputElement>(); + email2.setAutofilled(true); + WebInputElement phone = + web_frame->document().getElementById("phone").to<WebInputElement>(); + phone.setAutofilled(true); // Set the suggested values on two of the elements. lastname.setSuggestedValue(ASCIIToUTF16("Earp")); email.setSuggestedValue(ASCIIToUTF16("wyatt@earp.com")); + email2.setSuggestedValue(ASCIIToUTF16("wyatt@earp.com")); + phone.setSuggestedValue(ASCIIToUTF16("650-777-9999")); // Clear the previewed fields. EXPECT_TRUE(form_manager.ClearPreviewedFormWithNode(lastname, false)); @@ -2890,6 +2900,12 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNode) { EXPECT_TRUE(email.value().isEmpty()); EXPECT_TRUE(email.suggestedValue().isEmpty()); EXPECT_FALSE(email.isAutofilled()); + EXPECT_TRUE(email2.value().isEmpty()); + EXPECT_TRUE(email2.suggestedValue().isEmpty()); + EXPECT_FALSE(email2.isAutofilled()); + EXPECT_TRUE(phone.value().isEmpty()); + EXPECT_TRUE(phone.suggestedValue().isEmpty()); + EXPECT_FALSE(phone.isAutofilled()); // Verify that the cursor position has been updated. EXPECT_EQ(0, lastname.selectionStart()); @@ -2901,6 +2917,8 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNonEmptyInitiatingNode) { " <INPUT type=\"text\" id=\"firstname\" value=\"W\"/>" " <INPUT type=\"text\" id=\"lastname\"/>" " <INPUT type=\"text\" id=\"email\"/>" + " <INPUT type=\"email\" id=\"email2\"/>" + " <INPUT type=\"tel\" id=\"phone\"/>" " <INPUT type=\"submit\" value=\"Send\"/>" "</FORM>"); @@ -2925,11 +2943,20 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNonEmptyInitiatingNode) { WebInputElement email = web_frame->document().getElementById("email").to<WebInputElement>(); email.setAutofilled(true); + WebInputElement email2 = + web_frame->document().getElementById("email2").to<WebInputElement>(); + email2.setAutofilled(true); + WebInputElement phone = + web_frame->document().getElementById("phone").to<WebInputElement>(); + phone.setAutofilled(true); + // Set the suggested values on all of the elements. firstname.setSuggestedValue(ASCIIToUTF16("Wyatt")); lastname.setSuggestedValue(ASCIIToUTF16("Earp")); email.setSuggestedValue(ASCIIToUTF16("wyatt@earp.com")); + email2.setSuggestedValue(ASCIIToUTF16("wyatt@earp.com")); + phone.setSuggestedValue(ASCIIToUTF16("650-777-9999")); // Clear the previewed fields. EXPECT_TRUE(form_manager.ClearPreviewedFormWithNode(firstname, false)); @@ -2948,6 +2975,12 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithNonEmptyInitiatingNode) { EXPECT_TRUE(email.value().isEmpty()); EXPECT_TRUE(email.suggestedValue().isEmpty()); EXPECT_FALSE(email.isAutofilled()); + EXPECT_TRUE(email2.value().isEmpty()); + EXPECT_TRUE(email2.suggestedValue().isEmpty()); + EXPECT_FALSE(email2.isAutofilled()); + EXPECT_TRUE(phone.value().isEmpty()); + EXPECT_TRUE(phone.suggestedValue().isEmpty()); + EXPECT_FALSE(phone.isAutofilled()); } TEST_F(FormManagerTest, ClearPreviewedFormWithAutofilledInitiatingNode) { @@ -2955,6 +2988,8 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithAutofilledInitiatingNode) { " <INPUT type=\"text\" id=\"firstname\" value=\"W\"/>" " <INPUT type=\"text\" id=\"lastname\"/>" " <INPUT type=\"text\" id=\"email\"/>" + " <INPUT type=\"email\" id=\"email2\"/>" + " <INPUT type=\"tel\" id=\"phone\"/>" " <INPUT type=\"submit\" value=\"Send\"/>" "</FORM>"); @@ -2979,11 +3014,19 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithAutofilledInitiatingNode) { WebInputElement email = web_frame->document().getElementById("email").to<WebInputElement>(); email.setAutofilled(true); + WebInputElement email2 = + web_frame->document().getElementById("email2").to<WebInputElement>(); + email2.setAutofilled(true); + WebInputElement phone = + web_frame->document().getElementById("phone").to<WebInputElement>(); + phone.setAutofilled(true); // Set the suggested values on all of the elements. firstname.setSuggestedValue(ASCIIToUTF16("Wyatt")); lastname.setSuggestedValue(ASCIIToUTF16("Earp")); email.setSuggestedValue(ASCIIToUTF16("wyatt@earp.com")); + email2.setSuggestedValue(ASCIIToUTF16("wyatt@earp.com")); + phone.setSuggestedValue(ASCIIToUTF16("650-777-9999")); // Clear the previewed fields. EXPECT_TRUE(form_manager.ClearPreviewedFormWithNode(firstname, true)); @@ -3002,6 +3045,12 @@ TEST_F(FormManagerTest, ClearPreviewedFormWithAutofilledInitiatingNode) { EXPECT_TRUE(email.value().isEmpty()); EXPECT_TRUE(email.suggestedValue().isEmpty()); EXPECT_FALSE(email.isAutofilled()); + EXPECT_TRUE(email2.value().isEmpty()); + EXPECT_TRUE(email2.suggestedValue().isEmpty()); + EXPECT_FALSE(email2.isAutofilled()); + EXPECT_TRUE(phone.value().isEmpty()); + EXPECT_TRUE(phone.suggestedValue().isEmpty()); + EXPECT_FALSE(phone.isAutofilled()); } TEST_F(FormManagerTest, FormWithNodeIsAutoFilled) { @@ -3009,6 +3058,8 @@ TEST_F(FormManagerTest, FormWithNodeIsAutoFilled) { " <INPUT type=\"text\" id=\"firstname\" value=\"Wyatt\"/>" " <INPUT type=\"text\" id=\"lastname\"/>" " <INPUT type=\"text\" id=\"email\"/>" + " <INPUT type=\"email\" id=\"email2\"/>" + " <INPUT type=\"tel\" id=\"phone\"/>" " <INPUT type=\"submit\" value=\"Send\"/>" "</FORM>"); |