summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/renderer/autofill/form_autofill_browsertest.cc175
-rw-r--r--chrome/test/data/autofill/heuristics/input/bug_555010.html282
-rw-r--r--chrome/test/data/autofill/heuristics/output/bug_555010.out22
-rw-r--r--components/autofill/content/renderer/form_autofill_util.cc71
-rw-r--r--content/public/test/render_view_test.cc99
-rw-r--r--content/public/test/render_view_test.h26
6 files changed, 615 insertions, 60 deletions
diff --git a/chrome/renderer/autofill/form_autofill_browsertest.cc b/chrome/renderer/autofill/form_autofill_browsertest.cc
index 9985fc2..c88bdc2 100644
--- a/chrome/renderer/autofill/form_autofill_browsertest.cc
+++ b/chrome/renderer/autofill/form_autofill_browsertest.cc
@@ -104,8 +104,10 @@ const char kFormHtml[] =
" <INPUT type='submit' name='reply-send' value='Send'/>"
"</FORM>";
+// This constant uses a mixed-case title tag to be sure that the title match is
+// not case-sensitive. Other tests in this file use an all-lower title tag.
const char kUnownedFormHtml[] =
- "<HEAD><TITLE>enter shipping info</TITLE></HEAD>"
+ "<HEAD><TITLE>Enter Shipping Info</TITLE></HEAD>"
"<INPUT type='text' id='firstname'/>"
"<INPUT type='text' id='lastname'/>"
"<INPUT type='hidden' id='imhidden'/>"
@@ -135,6 +137,72 @@ const char kUnownedFormHtml[] =
"<TEXTAREA id='textarea-nonempty'>Go&#10;away!</TEXTAREA>"
"<INPUT type='submit' name='reply-send' value='Send'/>";
+// This constant has no title tag, and should be passed to
+// LoadHTMLWithURLOverride to test the detection of unowned forms by URL.
+const char kUnownedUntitledFormHtml[] =
+ "<INPUT type='text' id='firstname'/>"
+ "<INPUT type='text' id='lastname'/>"
+ "<INPUT type='hidden' id='imhidden'/>"
+ "<INPUT type='text' id='notempty' value='Hi'/>"
+ "<INPUT type='text' autocomplete='off' id='noautocomplete'/>"
+ "<INPUT type='text' disabled='disabled' id='notenabled'/>"
+ "<INPUT type='text' readonly id='readonly'/>"
+ "<INPUT type='text' style='visibility: hidden'"
+ " id='invisible'/>"
+ "<INPUT type='text' style='display: none' id='displaynone'/>"
+ "<INPUT type='month' id='month'/>"
+ "<INPUT type='month' id='month-nonempty' value='2011-12'/>"
+ "<SELECT id='select'>"
+ " <OPTION></OPTION>"
+ " <OPTION value='CA'>California</OPTION>"
+ " <OPTION value='TX'>Texas</OPTION>"
+ "</SELECT>"
+ "<SELECT id='select-nonempty'>"
+ " <OPTION value='CA' selected>California</OPTION>"
+ " <OPTION value='TX'>Texas</OPTION>"
+ "</SELECT>"
+ "<SELECT id='select-unchanged'>"
+ " <OPTION value='CA' selected>California</OPTION>"
+ " <OPTION value='TX'>Texas</OPTION>"
+ "</SELECT>"
+ "<TEXTAREA id='textarea'></TEXTAREA>"
+ "<TEXTAREA id='textarea-nonempty'>Go&#10;away!</TEXTAREA>"
+ "<INPUT type='submit' name='reply-send' value='Send'/>";
+
+// This constant does not have a title tag, but should match an unowned form
+// anyway because it is not English.
+const char kUnownedNonEnglishFormHtml[] =
+ "<HTML LANG='fr'>"
+ "<INPUT type='text' id='firstname'/>"
+ "<INPUT type='text' id='lastname'/>"
+ "<INPUT type='hidden' id='imhidden'/>"
+ "<INPUT type='text' id='notempty' value='Hi'/>"
+ "<INPUT type='text' autocomplete='off' id='noautocomplete'/>"
+ "<INPUT type='text' disabled='disabled' id='notenabled'/>"
+ "<INPUT type='text' readonly id='readonly'/>"
+ "<INPUT type='text' style='visibility: hidden'"
+ " id='invisible'/>"
+ "<INPUT type='text' style='display: none' id='displaynone'/>"
+ "<INPUT type='month' id='month'/>"
+ "<INPUT type='month' id='month-nonempty' value='2011-12'/>"
+ "<SELECT id='select'>"
+ " <OPTION></OPTION>"
+ " <OPTION value='CA'>California</OPTION>"
+ " <OPTION value='TX'>Texas</OPTION>"
+ "</SELECT>"
+ "<SELECT id='select-nonempty'>"
+ " <OPTION value='CA' selected>California</OPTION>"
+ " <OPTION value='TX'>Texas</OPTION>"
+ "</SELECT>"
+ "<SELECT id='select-unchanged'>"
+ " <OPTION value='CA' selected>California</OPTION>"
+ " <OPTION value='TX'>Texas</OPTION>"
+ "</SELECT>"
+ "<TEXTAREA id='textarea'></TEXTAREA>"
+ "<TEXTAREA id='textarea-nonempty'>Go&#10;away!</TEXTAREA>"
+ "<INPUT type='submit' name='reply-send' value='Send'/>"
+ "</HTML>";
+
std::string RetrievalMethodToString(
const WebElementDescriptor::RetrievalMethod& method) {
switch (method) {
@@ -263,11 +331,15 @@ class FormAutofillTest : public ChromeRenderViewTest {
// Test FormFillxxx functions.
void TestFormFillFunctions(const char* html,
bool unowned,
+ const char* url_override,
const AutofillFieldCase* field_cases,
size_t number_of_field_cases,
FillFormFunction fill_form_function,
GetValueFunction get_value_function) {
- LoadHTML(html);
+ if (url_override)
+ LoadHTMLWithUrlOverride(html, url_override);
+ else
+ LoadHTML(html);
WebFrame* web_frame = GetMainFrame();
ASSERT_NE(nullptr, web_frame);
@@ -362,7 +434,7 @@ class FormAutofillTest : public ChromeRenderViewTest {
id).to<WebInputElement>();
}
- void TestFillForm(const char* html, bool unowned) {
+ void TestFillForm(const char* html, bool unowned, const char* url_override) {
static const AutofillFieldCase field_cases[] = {
// fields: form_control_type, name, initial_value, autocomplete_attribute,
// should_be_autofilled, autofill_value, expected_value
@@ -423,7 +495,8 @@ class FormAutofillTest : public ChromeRenderViewTest {
"some multi-\nline value",
"Go\naway!"},
};
- TestFormFillFunctions(html, unowned, field_cases, arraysize(field_cases),
+ TestFormFillFunctions(html, unowned, url_override,
+ field_cases, arraysize(field_cases),
FillForm, &GetValueWrapper);
// Verify preview selection.
WebInputElement firstname = GetInputElementById("firstname");
@@ -431,7 +504,8 @@ class FormAutofillTest : public ChromeRenderViewTest {
EXPECT_EQ(16, firstname.selectionEnd());
}
- void TestPreviewForm(const char* html, bool unowned) {
+ void TestPreviewForm(const char* html, bool unowned,
+ const char* url_override) {
static const AutofillFieldCase field_cases[] = {
// Normal empty fields should be previewed.
{"text",
@@ -495,7 +569,8 @@ class FormAutofillTest : public ChromeRenderViewTest {
"suggested multi-\nline value",
""},
};
- TestFormFillFunctions(html, unowned, field_cases, arraysize(field_cases),
+ TestFormFillFunctions(html, unowned, url_override,
+ field_cases, arraysize(field_cases),
&PreviewForm, &GetSuggestedValueWrapper);
// Verify preview selection.
@@ -504,6 +579,20 @@ class FormAutofillTest : public ChromeRenderViewTest {
EXPECT_EQ(19, firstname.selectionEnd());
}
+ void TestUnmatchedUnownedForm(const char* html, const char* url_override) {
+ if (url_override)
+ LoadHTMLWithUrlOverride(html, url_override);
+ else
+ LoadHTML(html);
+
+ WebFrame* web_frame = GetMainFrame();
+ ASSERT_NE(nullptr, web_frame);
+
+ FormCache form_cache(*web_frame);
+ std::vector<FormData> forms = form_cache.ExtractNewForms();
+ ASSERT_EQ(0U, forms.size());
+ }
+
void TestFindFormForInputElement(const char* html, bool unowned) {
LoadHTML(html);
WebFrame* web_frame = GetMainFrame();
@@ -2508,11 +2597,29 @@ TEST_F(FormAutofillTest, FindFormForTextAreaElementForUnownedForm) {
// Test regular FillForm function.
TEST_F(FormAutofillTest, FillForm) {
- TestFillForm(kFormHtml, false);
+ TestFillForm(kFormHtml, false, nullptr);
}
TEST_F(FormAutofillTest, FillFormForUnownedForm) {
- TestFillForm(kUnownedFormHtml, true);
+ TestFillForm(kUnownedFormHtml, true, nullptr);
+}
+
+TEST_F(FormAutofillTest, FillFormForUnownedUntitledForm) {
+ TestFillForm(kUnownedUntitledFormHtml, true,
+ "http://example.test/checkout_flow");
+}
+
+TEST_F(FormAutofillTest, FillFormForUnownedNonEnglishForm) {
+ TestFillForm(kUnownedNonEnglishFormHtml, true, nullptr);
+}
+
+TEST_F(FormAutofillTest, FillFormForUnownedNonASCIIForm) {
+ std::string html("<HEAD><TITLE>accented latin: \xC3\xA0, thai: \xE0\xB8\x81, "
+ "control: \x04, nbsp: \xEF\xBB\xBF, non-BMP: \xF0\x9F\x8C\x80; This "
+ "should match a CHECKOUT flow despite the non-ASCII chars"
+ "</TITLE></HEAD>");
+ html.append(kUnownedUntitledFormHtml);
+ TestFillForm(html.c_str(), true, nullptr);
}
TEST_F(FormAutofillTest, FillFormIncludingNonFocusableElements) {
@@ -2594,19 +2701,65 @@ TEST_F(FormAutofillTest, FillFormIncludingNonFocusableElements) {
"some multi-\nline value",
"some multi-\nline value"},
};
- TestFormFillFunctions(kFormHtml, false, field_cases, arraysize(field_cases),
+ TestFormFillFunctions(kFormHtml, false, nullptr,
+ field_cases, arraysize(field_cases),
&FillFormIncludingNonFocusableElementsWrapper,
&GetValueWrapper);
}
TEST_F(FormAutofillTest, PreviewForm) {
- TestPreviewForm(kFormHtml, false);
+ TestPreviewForm(kFormHtml, false, nullptr);
}
TEST_F(FormAutofillTest, PreviewFormForUnownedForm) {
- TestPreviewForm(kUnownedFormHtml, true);
+ TestPreviewForm(kUnownedFormHtml, true, nullptr);
+}
+
+TEST_F(FormAutofillTest, PreviewFormForUnownedUntitledForm) {
+ // This test uses a mixed-case URL to be sure that the url match is not
+ // case-sensitive.
+ TestPreviewForm(kUnownedUntitledFormHtml, true,
+ "http://example.test/Enter_Shipping_Address/");
+}
+
+TEST_F(FormAutofillTest, PreviewFormForUnownedNonEnglishForm) {
+ TestPreviewForm(kUnownedNonEnglishFormHtml, true, nullptr);
+}
+
+// Data that looks like an unowned form should NOT be matched unless an
+// additional indicator is present, such as title tag or url, to prevent false
+// positives.
+
+TEST_F(FormAutofillTest, UnmatchedFormNoURL) {
+ TestUnmatchedUnownedForm(kUnownedUntitledFormHtml, nullptr);
+}
+
+TEST_F(FormAutofillTest, UnmatchedFormPathWithoutKeywords) {
+ TestUnmatchedUnownedForm(kUnownedUntitledFormHtml,
+ "http://example.test/path_without_keywords");
+}
+
+TEST_F(FormAutofillTest, UnmatchedFormKeywordInQueryOnly) {
+ TestUnmatchedUnownedForm(kUnownedUntitledFormHtml,
+ "http://example.test/search?q=checkout+in+query");
}
+TEST_F(FormAutofillTest, UnmatchedFormTitleWithoutKeywords) {
+ std::string wrong_title_html(
+ "<TITLE>This title has nothing to do with autofill</TITLE>");
+ wrong_title_html += kUnownedUntitledFormHtml;
+ TestUnmatchedUnownedForm(wrong_title_html.c_str(), nullptr);
+}
+
+TEST_F(FormAutofillTest, UnmatchedFormNonASCII) {
+ std::string html("<HEAD><TITLE>Non-ASCII soft hyphen in the middle of "
+ "keyword prevents a match here: check\xC2\xADout"
+ "</TITLE></HEAD>");
+ html.append(kUnownedUntitledFormHtml);
+ TestUnmatchedUnownedForm(html.c_str(), nullptr);
+}
+
+
TEST_F(FormAutofillTest, Labels) {
ExpectJohnSmithLabels(
"<FORM name='TestForm' action='http://cnn.com' method='post'>"
diff --git a/chrome/test/data/autofill/heuristics/input/bug_555010.html b/chrome/test/data/autofill/heuristics/input/bug_555010.html
new file mode 100644
index 0000000..bf026a0
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/input/bug_555010.html
@@ -0,0 +1,282 @@
+<HTML>
+ <HEAD>
+ <meta name="viewport" content="width=320, user-scalable=no, initial-scale=1.0, maximum-scale=1.0" />
+ <meta http-equiv="x-ua-compatible" content="IE=edge" >
+ <title>Office Supplies, Technology, Ink Much More | Staples - add checkout tag because url is inaccessible from test</title>
+ <meta name="title" content="Office Supplies, Technology, Ink Much More | Staples" />
+ <meta name="description" content="Your partner for office supplies, technology and cleaning & breakroom. Discover more about Staples product range today. FREE shipping on orders over $45">
+ <meta charset="UTF-8" />
+ </HEAD>
+ <BODY style="margin:0px; padding: 0px;">
+ <div id="id_skPageContainer" class="cls_skCOPg">
+ <div id="id_skStudio_Wrapper">
+ <div id="id_scfHeader" class="scfHeader" align="center"></div>
+ <div id="studiop_31" class="scfPage skcWebPage sk-ui-layout-legacy" align="center" data-pageid="31" data-viewName = "mobile" data-pageUrl="office/supplies/StaplesCheckoutFlow" >
+ <div id="id_scfContent_" class="scfContent" align="left">
+ <div id="old_checkout" class="lyt_cont_div page" style= "width: 100%; height: 480px; " flexiPage="true">
+ <div id="skPageLayoutCell_31_id-center" class="skc_pageCellLayout cls_skWidget widget68 " widgetId = "306" widgetName = "widget68" widgetType = "6" style="width:100.0%;height:480px;float: left;position: relative; overflow: hidden;">
+ <div class="cls_richTextWidget" id=skPageLayoutCell_31_id-center >
+ <div id="id_skPageContainer">
+ <div id="id_skcontent" class="cls_skContent cls_skCntPanelClosed">
+ <div id="id_skPageContent">
+ <div class="testcheckout">
+ <div class="cls_skCOHeaderCont">
+ <div class="cls_skCOBackNav"></div>
+ <span class="cls_skManageAddrHeading cls_skHide">Manage Addresses</span>
+ <span class="cls_skEditAddrHeading cls_skHide">Edit Address</span>
+ <span class="cls_skAddAddrHeading cls_skHide">Add Address</span>
+ <span class="cls_skCOHeading cls_skHide">Checkout</span>
+ <span class="cls_skECOHeading cls_skHide">Guest Checkout</span>
+ <span class="cls_skStorePickUpHeading cls_skHide"></span>
+ <span class="cls_skAlternatePickUpHeading cls_skHide">Alternate Pick Up Person</span>
+ <span class="cls_skRCHeading cls_skHide">Returning Customer</span>
+ <span class="cls_skManageCardHeading cls_skHide">Manage Payment Methods</span>
+ <span class="cls_skEditCardHeading cls_skHide">Edit Payment Method</span>
+ <span class="cls_skAddCardHeading cls_skHide">Add Payment Method</span>
+ </div>
+ <div class="cls_skCOShippingText"></div>
+ <div class="cls_skLoginCont cls_skHide">
+ <div class="cls_skAppleSkuCOCont cls_skHide">
+ <b>Guest Checkout Not Available</b>
+ <br>Because of one or more items in your Cart, Guest Checkout is not available. Please sign in to continue.
+ </div>
+ <div class="cls_skGuestCOCont">
+ <div class="cls_skSignInCont"> Returning customer?
+ <div class="cls_skSignInBtn">SIGN IN</div>
+ </div>
+ <div class="cls_skGuestCOTxt">Guest Checkout</div>
+ <div class="cls_skClear"></div>
+ </div>
+ </div>
+ <div class="cls_skSigninWrap cls_skHide">
+ <form class="cls_skCOLogonForm">
+ <div id="id_skSigninErr" class="cls_skSigninErr"></div>
+ <div id="id_skSignInputName" class="cls_skInput">
+ <input id="id_skCOUsername" name="userName" class="cls_skInputTxt cls_skUsername scTrack scInput" sctype="scInput" scvalue="username" scinput="input" fieldtype="username" placeholder="Username or Email address" event="username field" tabindex="5" maxlength="55" autocomplete="on" autocorrect="off" autocapitalize="off" type="email">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div id="id_skSignInputPwd" class="cls_skInput">
+ <input id="id_skCOPwd" name="password" class="cls_skInputTxt cls_skPwd scTrack scInput" sctype="scInput" scvalue="password" scinput="input" fieldtype="pwd" placeholder="Password" event="password field" tabindex="5" maxlength="256" autocapitalize="off" autocomplete="on" type="password">
+ <div class="cls_skClearBut"></div>
+ </div>
+ </form>
+ <div class="cls_skForgotPwd scTrack scButton" sctype="scButton" scvalue="forgetpassword">Forgot Password?</div>
+ <div class="cls_skSignIn scTrack scButton" sctype="scButton" scvalue="Signin">Sign In</div>
+ <div class="cls_skClear"></div>
+ </div>
+ <div class="cls_skUserCont">
+ <div id="id_skCOErrorCont" class="cls_skCOErrorCont"></div>
+ <div id="id_skCOShippingCont" class="cls_skCOShippingCont cls_skHide">
+ <div id="id_skCheckoutInputCont" class="cls_skCheckoutInputCont">
+ <div id="id_skShipInputName" class="cls_skInput">
+ <input id="id_skCOName" class="cls_skInputTxt cls_skName scTrack scInput" sctype="scInput" scvalue="editname" scinput="input" fieldtype="name" placeholder="Full Name (First Name Last Name)" event="shipping name" tabindex="1" maxlength="128" name="cardName" autocomplete="on" autocorrect="off" autocapitalize="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div id="id_skShipInputAddress" class="cls_skInput">
+ <div class="cls_skAddressWrapper">
+ <div class="cls_skCOShipAddress cls_skAddressCont">
+ <input id="id_skCOShipAddress" class="cls_skInputTxt cls_skAddress cls_skShipToAddress scTrack scInput" sctype="scInput" scvalue="editadd" scinput="input" fieldtype="address" placeholder="Shipping Address" event="shipping address" tabindex="2" maxlength="128" addresstype="ship" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div class="cls_skCOShipAptBldg">
+ <input id="id_skCOShipAptBldg" class="cls_skInputTxt cls_skInputOptionalTxt cls_skApartment scInput" scvalue="apartment" scinput="input" fieldtype="aptbldg" placeholder="Apt/Bldg (optional)" event="shipping apt/suite" tabindex="3" maxlength="35" autocomplete="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ </div>
+ </div>
+ <div class="cls_skShipToStore cls_skHide">
+ <div class="cls_skShipToStoreDetailTxt"></div>
+ </div>
+ <div class="cls_skStorePickUpBtn cls_skHide">
+ <div class="cls_skStorePickUpBtnTxt"></div>
+ </div>
+ <div id="id_skShipInputPhone" class="cls_skInput">
+ <input id="id_skCOPhoneno" class="cls_skInputTxt cls_skCOPhoneno cls_skPhoneNo scTrack scInput" sctype="scInput" scvalue="editphone" scinput="input" maxlength="16" fieldtype="phnumber" placeholder="Phone" event="shipping phone number" tabindex="4" autocomplete="off" type="tel">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div id="id_skShipInputEmail" class="cls_skInput">
+ <input id="id_skCOEmail" class="cls_skInputTxt cls_skEmail scTrack scInput" sctype="scInput" scvalue="email" scinput="input" fieldtype="email" placeholder="Email" event="shipping email" tabindex="5" maxlength="256" autocapitalize="off" type="email">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div id="id_skShipInputCredit" class="cls_skInput">
+ <div class="cls_skAddressWrapper">
+ <div class="cls_skCrditCarDetailsCont">Credit Card Details</div>
+ <div class="cls_skCrditCardImg"></div>
+ <div class="cls_skCOCreditCardCont">
+ <label class="cls_skAstrickSymbol">*</label>
+ <input id="id_skCOCreditCard" class="cls_skInputTxt cls_skCreditCardNo cls_skCreditCard scTrack scInput" sctype="scInput" scvalue="creditcard" scinput="input" maxlength="19" fieldtype="cardNumber" event="card number" placeholder="1234 5647 8901 2345" tabindex="6" name="cardNumber" autocomplete="off" type="tel">
+ </div>
+ <div class="cls_skCOCreditYrMnthCont">
+ <input class="cls_skInputTxt cls_skCreditYrMth cls_skCreditCard scInput" scvalue="month/year" scinput="input" fieldtype="mnthYr" placeholder="EXP" event="expire month/year" tabindex="7" name="expirationMonth" autocomplete="off" type="tel">
+ </div>
+ <div class="cls_skCOCreditCcv">
+ <input class="cls_skInputTxt cls_skCreditCcv cls_skCreditCard scInput" scvalue="CVV" scinput="input" maxlength="4" fieldtype="cvv" placeholder="CVV" event="card id" tabindex="8" name="cvv" autocomplete="off" type="tel">
+ </div>
+ <div class="cls_skClearBut"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="cls_skCOSaveCCCont cls_skHide"> Save credit card
+ <div class="cls_skCOSaveCCtoggleCont">
+ <div class="cls_skCOSaveCCtoggleYes scTrack scOption" sctype="scOption" and="" scvalue="No|Yes"></div>
+ <div class="cls_skCOSaveCCtoggleNo scTrack scOption" sctype="scOption" and="" scvalue="No|Yes"></div>
+ </div>
+ </div>
+ <div class="cls_skCOUseAsCont cls_skHide"> Use<span></span>&nbsp;as billing address
+ <div class="cls_skCOUseAstoggleCont">
+ <div class="cls_skCOUseAstoggleYes scTrack scOption" sctype="scOption" and="" scvalue="No|Yes"></div>
+ <div class="cls_skCOUseAstoggleNo scTrack scOption" sctype="scOption" and="" scvalue="No|Yes"></div>
+ </div>
+ </div>
+ <div id="id_skCOBillingCont" class="cls_skCOBillingCont cls_skHide">
+ <div id="id_skCheckoutInputCont" class="cls_skCheckoutInputCont">
+ <div id="id_skBillInputName" class="cls_skInput">
+ <input id="id_skCOName" class="cls_skInputTxt cls_skName scTrack scInput" sctype="scInput" scvalue="bname" scinput="input" fieldtype="name" placeholder="Card Holder Name" event="billing name" tabindex="9" maxlength="128" autocomplete="on" autocorrect="off" autocapitalize="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div id="id_skBillInputAddress" class="cls_skInput">
+ <div class="cls_skAddressWrapper">
+ <div class="cls_skCOShipAddress cls_skAddressCont">
+ <input id="id_skCOShipAddress" class="cls_skInputTxt cls_skAddress cls_skBillToAddress scTrack scInput" sctype="scInput" scvalue="baddress" scinput="input" fieldtype="address" placeholder="Billing Address" event="billing address" tabindex="10" maxlength="128" autocomplete="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div class="cls_skCOShipAptBldg">
+ <input id="id_skCOShipAptBldg" class="cls_skInputTxt cls_skInputOptionalTxt cls_skApartment scInput" scvalue="apartment" scinput="input" fieldtype="aptbldg" placeholder="Apt/Bldg (optional)" event="billing apt/suite" tabindex="11" maxlength="35" autocomplete="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ </div>
+ </div>
+ <div id="id_skBillInputPhone" class="cls_skInput">
+ <input id="id_skCOPhoneno" class="cls_skInputTxt cls_skCOPhoneno cls_skPhoneNo scTrack scInput" sctype="scInput" scvalue="bphone" scinput="input" maxlength="16" fieldtype="phnumber" placeholder="Phone" event="billing phone number" tabindex="12" type="tel">
+ <div class="cls_skClearBut"></div>
+ </div>
+ </div>
+ </div>
+ <div id="id_skCartCouponsCont" class="cls_skCartCouponsCont cls_skHide"></div>
+ <div id="id_skSavedAddrWrap" class="cls_skHide">
+ <div id="id_skSavedAddrCont" class="cls_skSavedAddrCont">
+ </div>
+ </div>
+ <div id="id_skAddrWrap" class="cls_skHide">
+ <div id="id_skAddrCont" class="cls_skAddrCont">
+ <div id="id_skCheckoutInputCont" class="cls_skCheckoutInputCont">
+ <div class="cls_skInput">
+ <input id="id_skCOName" class="cls_skInputTxt cls_skName scTrack scInput" scvalue="name" scinput="input" fieldtype="name" placeholder="Name" event="shipping name" tabindex="9" maxlength="128" autocomplete="on" autocorrect="off" autocapitalize="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div class="cls_skInput">
+ <div class="cls_skCOShipAddress cls_skAddressCont">
+ <input id="id_skCOShipAddress" class="cls_skInputTxt cls_skAddress scTrack scInput" scvalue="address" scinput="input" fieldtype="address" placeholder="Address" event="shipping address" tabindex="10" maxlength="128" autocomplete="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div class="cls_skCOShipAptBldg">
+ <input id="id_skCOShipAptBldg" class="cls_skInputTxt cls_skInputOptionalTxt cls_skApartment scInput" scvalue="apartment" scinput="input" fieldtype="aptbldg" placeholder="Apt/Bldg (optional)" event="shipping apt/suite" tabindex="11" maxlength="35" autocomplete="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ </div>
+ <div class="cls_skInput">
+ <input id="id_skCOPhoneno" class="cls_skInputTxt cls_skCOPhoneno cls_skPhoneNo scTrack scInput" scvalue="phone" scinput="input" maxlength="16" fieldtype="phnumber" placeholder="Phone" event="shipping phone number" tabindex="12" type="tel">
+ <div class="cls_skClearBut"></div>
+ </div>
+ </div>
+ <div class="cls_skShipToAddr cls_skHide scTrack scButton" sctype="scButton" scvalue="shiptoaddress">SHIP TO THIS ADDRESS</div>
+ <div class="cls_skNewAddrContinue cls_skHide">CONTINUE</div>
+ </div>
+ </div>
+ <div id="id_skSavedCardWrap" class="cls_skHide">
+ <div id="id_skSavedCardCont" class="cls_skSavedCardCont"></div>
+ </div>
+ <div id="id_skCardWrap" class="cls_skHide">
+ <div id="id_skCardCont" class="cls_skCardCont">
+ <div id="id_skManageCardInputCont" class="cls_skCheckoutInputCont">
+ <div id="id_skManageInputCredit" class="cls_skInput">
+ <div class="cls_skCrditCarDetailsCont">Credit Card Details</div>
+ <div class="cls_skCrditCardImg"></div>
+ <div class="cls_skCOCreditCardCont">
+ <label class="cls_skAstrickSymbol">*</label>
+ <input id="id_skManageCreditCard" class="cls_skInputTxt cls_skCreditCardNo cls_skCreditCard scTrack scInput" scvalue="editcardno" scinput="input" maxlength="19" fieldtype="cardNumber" placeholder="1234 5647 8901 2345" tabindex="6" name="cardNumber" autocomplete="off" type="tel">
+ </div>
+ <div class="cls_skCOCreditYrMnthCont">
+ <input class="cls_skInputTxt cls_skCreditYrMth cls_skCreditCard scInput" scvalue="month/year" scinput="input" fieldtype="mnthYr" placeholder="EXP" tabindex="7" name="expirationMonth" autocomplete="off" type="tel">
+ </div>
+ <div class="cls_skCOCreditCcv">
+ <input class="cls_skInputTxt cls_skCreditCcv cls_skCreditCard scInput" scvalue="CVV" scinput="input" maxlength="4" fieldtype="cvv" placeholder="CVV" tabindex="8" name="cvv" autocomplete="off" type="tel">
+ </div>
+ <div class="cls_skClearBut"></div>
+ </div>
+ </div>
+ <div class="cls_skPayBtn cls_skHide scTrack scButton" sctype="scButton" scvalue="selectthiscard">SELECT THIS CARD</div>
+ <div class="cls_skNewCardContinue cls_skHide scTrack scButton" sctype="scButton" scvalue="addcard">ADD CARD</div>
+ </div>
+ </div>
+ <div id="id_skStorePickUpWrap" class="cls_skStorePickUpWrap cls_skHide">
+ <div class="cls_skCOaccContainer"></div>
+ </div>
+ <div id="id_skAltPickUpWrap" class="cls_skAltPickUpWrap cls_skHide">
+ <div class="cls_skStorePickupAlternateCont">
+ <div id="id_skAltPUInputPhone" class="cls_skInput">
+ <input id="id_skAltPUName" class="cls_skInputTxt cls_skName scInput" scvalue="name" scinput="input" fieldtype="name" placeholder="Name" tabindex="1" maxlength="128" autocomplete="on" autocorrect="off" autocapitalize="off" type="text">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div id="id_skAltPUInputPhone" class="cls_skInput">
+ <input id="id_skAltPUPhoneno" class="cls_skInputTxt cls_skCOPhoneno cls_skPhoneNo scInput" scvalue="phone" scinput="input" maxlength="16" fieldtype="phnumber" placeholder="Phone" tabindex="2" autocomplete="off" type="tel">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div id="id_skAltPUInputEmail" class="cls_skInput">
+ <input id="id_skAltPUEmail" class="cls_skInputTxt cls_skEmail scInput" scvalue="email" scinput="input" fieldtype="email" placeholder="Email" tabindex="3" maxlength="256" autocapitalize="off" type="email">
+ <div class="cls_skClearBut"></div>
+ </div>
+ <div class="cls_skAlternatePickupContinue">CONTINUE</div>
+ </div>
+ </div>
+ <div class="cls_skCheckoutVme cls_skHide">
+ <div class="cls_skVmeErrorMsg hide"></div>
+ <div class="cls_skCardImg"></div>
+ </div>
+ </div>
+ </div>
+ <div class="cls_skCOOrderCont cls_skHide">
+ <div class="cls_skCOOrderItemsCont">
+ <div class="cls_skCOItems cls_skCODetails"> Product Total <b>$0.00</b></div>
+ <div class="cls_skCOShipping cls_skCODetails"> Shipping <b>Free</b></div>
+ <div class="cls_skCOOversize cls_skCODetails"> Oversize Shipping Fees <b>$0.00</b></div>
+ <div class="cls_skCOCoupons cls_skCODetails"> Coupons&nbsp;&amp;&nbsp;Rewards <b>$0.00</b></div>
+ <div class="cls_skCOTax cls_skCODetails"> Estimated Tax <b>$0.00</b></div>
+ <div class="cls_skCOTotalCost cls_skCOTotAmt cls_skCODetails"> Total Amount Due <span>$0.00</span></div>
+ </div>
+ <div class="cls_skCOPayCont">
+ <div class="cls_skCOPayNow scTrack scPay" sctype="scPay" tabindex="13">PLACE ORDER</div>
+ <div class="cls_skCOPayNowLoading">
+ <div class="cls_skActivitySpinner"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="id_skTaxCalculationPopup" class="cls_skTaxCalculationPopup">
+ <div class="cls_skTaxCalculationCont">
+ <div class="cls_skLoadingImg"></div>
+ <div class="cls_skTaxCalculationMsg">Calculating Tax</div>
+ </div>
+ </div>
+ <div id="id_skMFLoadingGauge" class="cls_skPageLoadingGauge">
+ <div class="cls_skActivitySpinner"></div>
+ </div>
+ <div id="id_skPanelCntWrap" class="cls_skPanelCntWrap"></div>
+ <div class="cls_skCORPValidationPopup"></div>
+ <div class="cls_skVPopupTop"></div>
+ <div id="id_skHeightEl" class="cls_skHeightEl" style="display:none"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="id_scfFooter" class="scfFooter"></div>
+ </div>
+ </div>
+ <div class="cls_skCouponCont"></div>
+ <div class="cls_skATBPopupMask"></div>
+ </BODY>
+</HTML>
diff --git a/chrome/test/data/autofill/heuristics/output/bug_555010.out b/chrome/test/data/autofill/heuristics/output/bug_555010.out
new file mode 100644
index 0000000..32a2019
--- /dev/null
+++ b/chrome/test/data/autofill/heuristics/output/bug_555010.out
@@ -0,0 +1,22 @@
+CREDIT_CARD_NAME | cardName | Full Name (First Name Last Name) | | cardName_1-cc
+ADDRESS_HOME_LINE1 | id_skCOShipAddress | Shipping Address | | cardName_1-default
+UNKNOWN_TYPE | id_skCOShipAptBldg | Apt/Bldg (optional) | | cardName_1-default
+PHONE_HOME_WHOLE_NUMBER | id_skCOPhoneno | Phone | | cardName_1-default
+EMAIL_ADDRESS | id_skCOEmail | Email | | cardName_1-default
+CREDIT_CARD_NUMBER | cardNumber | 1234 5647 8901 2345 | | cardName_1-cc
+CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR | expirationMonth | EXP | | cardName_1-cc
+CREDIT_CARD_VERIFICATION_CODE | cvv | CVV | | cardName_1-cc
+CREDIT_CARD_NAME | id_skCOName | Card Holder Name | | id_skCOName_1-cc
+ADDRESS_HOME_LINE1 | id_skCOShipAddress | Billing Address | | id_skCOName_1-default
+UNKNOWN_TYPE | id_skCOShipAptBldg | Apt/Bldg (optional) | | id_skCOName_1-default
+PHONE_HOME_WHOLE_NUMBER | id_skCOPhoneno | Phone | | id_skCOName_1-default
+NAME_FULL | id_skCOName | Name | | id_skCOName_1-default
+ADDRESS_HOME_LINE1 | id_skCOShipAddress | Address | | id_skCOShipAddress_3-default
+UNKNOWN_TYPE | id_skCOShipAptBldg | Apt/Bldg (optional) | | id_skCOShipAddress_3-default
+PHONE_HOME_WHOLE_NUMBER | id_skCOPhoneno | Phone | | id_skCOShipAddress_3-default
+CREDIT_CARD_NUMBER | cardNumber | 1234 5647 8901 2345 | | id_skCOShipAddress_3-cc
+CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR | expirationMonth | EXP | | id_skCOShipAddress_3-cc
+CREDIT_CARD_VERIFICATION_CODE | cvv | CVV | | id_skCOShipAddress_3-cc
+CREDIT_CARD_NAME | id_skAltPUName | Name | | id_skCOShipAddress_3-cc
+PHONE_HOME_WHOLE_NUMBER | id_skAltPUPhoneno | Phone | | id_skCOShipAddress_3-default
+EMAIL_ADDRESS | id_skAltPUEmail | Email | | id_skCOShipAddress_3-default
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index b7bb401..40fbb03 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1468,40 +1468,57 @@ bool UnownedCheckoutFormElementsAndFieldSetsToFormData(
FormFieldData* field) {
// Only attempt formless Autofill on checkout flows. This avoids the many
// false positives found on the non-checkout web. See
- // http://crbug.com/462375. For now this early abort only applies to
- // English-language pages, because the regex is not translated. Note that
- // an empty "lang" attribute counts as English. A potential problem is that
- // this only checks document.title(), but should actually check the main
- // frame's title. Thus it may make bad decisions for iframes.
+ // http://crbug.com/462375.
WebElement html_element = document.documentElement();
+
+ // For now this restriction only applies to English-language pages, because
+ // the keywords are not translated. Note that an empty "lang" attribute
+ // counts as English.
std::string lang;
if (!html_element.isNull())
lang = html_element.getAttribute("lang").utf8();
- if (lang.empty() ||
- base::StartsWith(lang, "en", base::CompareCase::INSENSITIVE_ASCII)) {
- std::string title(base::UTF16ToUTF8(base::string16(document.title())));
- const char* const kKeywords[] = {
- "payment",
- "checkout",
- "address",
- "delivery",
- "shipping",
- };
-
- bool found = false;
- for (const auto& keyword : kKeywords) {
- if (title.find(keyword) != base::string16::npos) {
- found = true;
- break;
- }
+ if (!lang.empty() &&
+ !base::StartsWith(lang, "en", base::CompareCase::INSENSITIVE_ASCII)) {
+ return UnownedFormElementsAndFieldSetsToFormData(
+ fieldsets, control_elements, element, document, extract_mask, form,
+ field);
+ }
+
+ // A potential problem is that this only checks document.title(), but should
+ // actually check the main frame's title. Thus it may make bad decisions for
+ // iframes.
+ base::string16 title(base::ToLowerASCII(base::string16(document.title())));
+
+ // Don't check the path for url's without a standard format path component,
+ // such as data:.
+ std::string path;
+ GURL url(document.url());
+ if (url.IsStandard())
+ path = base::ToLowerASCII(url.path());
+
+ const char* const kKeywords[] = {
+ "payment",
+ "checkout",
+ "address",
+ "delivery",
+ "shipping",
+ };
+
+ for (const auto& keyword : kKeywords) {
+ // Compare char16 elements of |title| with char elements of |keyword| using
+ // operator==.
+ auto title_pos = std::search(title.begin(), title.end(),
+ keyword, keyword + strlen(keyword));
+ if (title_pos != title.end() ||
+ path.find(keyword) != std::string::npos) {
+ // Found a keyword: treat this as an unowned form.
+ return UnownedFormElementsAndFieldSetsToFormData(
+ fieldsets, control_elements, element, document, extract_mask, form,
+ field);
}
- if (!found)
- return false;
}
- return UnownedFormElementsAndFieldSetsToFormData(
- fieldsets, control_elements, element, document, extract_mask, form,
- field);
+ return false;
}
bool UnownedPasswordFormElementsAndFieldSetsToFormData(
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 3eabdb9..c17e342 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -36,6 +36,7 @@
#include "content/test/test_content_client.h"
#include "content/test/test_render_frame.h"
#include "third_party/WebKit/public/platform/WebScreenInfo.h"
+#include "third_party/WebKit/public/platform/WebURLLoader.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebHistoryItem.h"
@@ -64,6 +65,7 @@ using blink::WebLocalFrame;
using blink::WebMouseEvent;
using blink::WebScriptSource;
using blink::WebString;
+using blink::WebURLLoader;
using blink::WebURLRequest;
namespace {
@@ -104,37 +106,102 @@ bool GetWindowsKeyCode(char ascii_character, int* key_code) {
}
}
+WebURLRequest createDataRequest(const std::string& html) {
+ std::string url_str = "data:text/html;charset=utf-8,";
+ url_str.append(html);
+ GURL url(url_str);
+ WebURLRequest request(url);
+ request.setCheckForBrowserSideNavigation(false);
+ return request;
+}
+
} // namespace
namespace content {
-class RendererBlinkPlatformImplNoSandboxImpl
+const char kWrappedHTMLDataHeader[] = "X-WrappedHTMLData";
+
+// This loader checks all requests for the presence of the X-WrappedHTMLData
+// header and, if it's found, substitutes a data: url with the header's value
+// instead of loading the original request. It is used to implement
+// LoadHTMLWithURLOverride.
+class WebURLLoaderWrapper : public WebURLLoader {
+public:
+ WebURLLoaderWrapper(WebURLLoader* wrapped_loader)
+ : wrapped_loader_(wrapped_loader) { }
+
+ void loadSynchronously(const WebURLRequest& request,
+ blink::WebURLResponse& response,
+ blink::WebURLError& error,
+ blink::WebData& data) override {
+ std::string html = request.httpHeaderField(kWrappedHTMLDataHeader).utf8();
+ wrapped_loader_->loadSynchronously(
+ html.empty() ? request : createDataRequest(html),
+ response,
+ error,
+ data);
+ }
+
+ void loadAsynchronously(const WebURLRequest& request,
+ blink::WebURLLoaderClient* client) override {
+ std::string html = request.httpHeaderField(kWrappedHTMLDataHeader).utf8();
+ wrapped_loader_->loadAsynchronously(
+ html.empty() ? request : createDataRequest(html),
+ client);
+ }
+
+ void cancel() override {
+ wrapped_loader_->cancel();
+ }
+
+ void setDefersLoading(bool defer) override {
+ wrapped_loader_->setDefersLoading(defer);
+ }
+
+ void setLoadingTaskRunner(blink::WebTaskRunner* runner) override {
+ wrapped_loader_->setLoadingTaskRunner(runner);
+ }
+
+private:
+ std::unique_ptr<WebURLLoader> wrapped_loader_;
+};
+
+class RendererBlinkPlatformImplTestOverrideImpl
: public RendererBlinkPlatformImpl {
public:
- RendererBlinkPlatformImplNoSandboxImpl(
+ RendererBlinkPlatformImplTestOverrideImpl(
scheduler::RendererScheduler* scheduler)
: RendererBlinkPlatformImpl(scheduler) {}
+ // Get rid of the dependency to the sandbox, which is not available in
+ // RenderViewTest.
blink::WebSandboxSupport* sandboxSupport() override { return NULL; }
+
+ // Inject a WebURLLoader which rewrites requests that have the
+ // X-WrappedHTMLData header.
+ WebURLLoader* createURLLoader() override {
+ return new WebURLLoaderWrapper(
+ RendererBlinkPlatformImpl::createURLLoader());
+ }
};
-RenderViewTest::RendererBlinkPlatformImplNoSandbox::
- RendererBlinkPlatformImplNoSandbox() {
+RenderViewTest::RendererBlinkPlatformImplTestOverride::
+ RendererBlinkPlatformImplTestOverride() {
renderer_scheduler_ = scheduler::RendererScheduler::Create();
blink_platform_impl_.reset(
- new RendererBlinkPlatformImplNoSandboxImpl(renderer_scheduler_.get()));
+ new RendererBlinkPlatformImplTestOverrideImpl(renderer_scheduler_.get()));
}
-RenderViewTest::RendererBlinkPlatformImplNoSandbox::
- ~RendererBlinkPlatformImplNoSandbox() {
+RenderViewTest::RendererBlinkPlatformImplTestOverride::
+ ~RendererBlinkPlatformImplTestOverride() {
}
blink::Platform*
- RenderViewTest::RendererBlinkPlatformImplNoSandbox::Get() const {
+ RenderViewTest::RendererBlinkPlatformImplTestOverride::Get() const {
return blink_platform_impl_.get();
}
-void RenderViewTest::RendererBlinkPlatformImplNoSandbox::Shutdown() {
+void RenderViewTest::RendererBlinkPlatformImplTestOverride::Shutdown() {
renderer_scheduler_->Shutdown();
blink_platform_impl_->Shutdown();
}
@@ -177,11 +244,19 @@ bool RenderViewTest::ExecuteJavaScriptAndReturnIntValue(
}
void RenderViewTest::LoadHTML(const char* html) {
- std::string url_str = "data:text/html;charset=utf-8,";
- url_str.append(html);
- GURL url(url_str);
+ GetMainFrame()->loadRequest(createDataRequest(html));
+ // The load actually happens asynchronously, so we pump messages to process
+ // the pending continuation.
+ FrameLoadWaiter(view_->GetMainRenderFrame()).Wait();
+}
+
+void RenderViewTest::LoadHTMLWithUrlOverride(const char* html,
+ const char* url_override) {
+ GURL url(url_override);
WebURLRequest request(url);
request.setCheckForBrowserSideNavigation(false);
+ request.addHTTPHeaderField(kWrappedHTMLDataHeader, WebString::fromUTF8(html));
+
GetMainFrame()->loadRequest(request);
// The load actually happens asynchronously, so we pump messages to process
// the pending continuation.
diff --git a/content/public/test/render_view_test.h b/content/public/test/render_view_test.h
index 27cea17..6e351de 100644
--- a/content/public/test/render_view_test.h
+++ b/content/public/test/render_view_test.h
@@ -48,23 +48,23 @@ class FakeCompositorDependencies;
class MockRenderProcess;
class PageState;
class RendererMainPlatformDelegate;
-class RendererBlinkPlatformImplNoSandboxImpl;
+class RendererBlinkPlatformImplTestOverrideImpl;
class RenderView;
class RenderViewTest : public testing::Test, blink::WebLeakDetectorClient {
public:
- // A special BlinkPlatformImpl class for getting rid off the dependency to the
- // sandbox, which is not available in RenderViewTest.
- class RendererBlinkPlatformImplNoSandbox {
+ // A special BlinkPlatformImpl class with overrides that are useful for
+ // RenderViewTest.
+ class RendererBlinkPlatformImplTestOverride {
public:
- RendererBlinkPlatformImplNoSandbox();
- ~RendererBlinkPlatformImplNoSandbox();
+ RendererBlinkPlatformImplTestOverride();
+ ~RendererBlinkPlatformImplTestOverride();
blink::Platform* Get() const;
void Shutdown();
private:
scoped_ptr<scheduler::RendererScheduler> renderer_scheduler_;
- scoped_ptr<RendererBlinkPlatformImplNoSandboxImpl> blink_platform_impl_;
+ scoped_ptr<RendererBlinkPlatformImplTestOverrideImpl> blink_platform_impl_;
};
RenderViewTest();
@@ -88,10 +88,16 @@ class RenderViewTest : public testing::Test, blink::WebLeakDetectorClient {
bool ExecuteJavaScriptAndReturnIntValue(const base::string16& script,
int* result);
- // Loads the given HTML into the main frame as a data: URL and blocks until
- // the navigation is committed.
+ // Loads |html| into the main frame as a data: URL and blocks until the
+ // navigation is committed.
void LoadHTML(const char* html);
+ // Pretends to load |url| into the main frame, but substitutes |html| for the
+ // response body (and does not include any response headers). This can be used
+ // instead of LoadHTML for tests that cannot use a data: url (for example if
+ // document.location needs to be set to something specific.)
+ void LoadHTMLWithUrlOverride(const char* html, const char* url);
+
// Returns the current PageState.
// In OOPIF enabled modes, this returns a PageState object for the main frame.
PageState GetCurrentPageState();
@@ -188,7 +194,7 @@ class RenderViewTest : public testing::Test, blink::WebLeakDetectorClient {
// We use a naked pointer because we don't want to expose RenderViewImpl in
// the embedder's namespace.
RenderView* view_;
- RendererBlinkPlatformImplNoSandbox blink_platform_impl_;
+ RendererBlinkPlatformImplTestOverride blink_platform_impl_;
scoped_ptr<ContentClient> content_client_;
scoped_ptr<ContentBrowserClient> content_browser_client_;
scoped_ptr<ContentRendererClient> content_renderer_client_;