Commit 570de658 authored by sebsg's avatar sebsg Committed by Commit bot

[Autofill] Respect the autocomplete=off attribute on desktop for non credit...

[Autofill] Respect the autocomplete=off attribute on desktop for non credit card related fields and forms.

BUG=468153

Review URL: https://codereview.chromium.org/1473733008

Cr-Commit-Position: refs/heads/master@{#363052}
parent fef2e255
......@@ -1433,7 +1433,7 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
}
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
AutofillSuggetionsForPasswordFormWithoutUsernameField) {
AutofillSuggestionsForPasswordFormWithoutUsernameField) {
std::string submit =
"document.getElementById('password').value = 'mypassword';"
"document.getElementById('submit-button').click();";
......@@ -1795,7 +1795,7 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
// Tests that if a site embeds the login and signup forms into one <form>, the
// login form still gets autofilled.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
AutofillSuggetionsForLoginSignupForm) {
AutofillSuggestionsForLoginSignupForm) {
std::string submit =
"document.getElementById('username').value = 'myusername';"
"document.getElementById('password').value = 'mypassword';"
......@@ -2261,7 +2261,7 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
// ambiguity in id attribute gets autofilled correctly.
IN_PROC_BROWSER_TEST_F(
PasswordManagerBrowserTestBase,
AutofillSuggetionsForPasswordFormWithAmbiguousIdAttribute) {
AutofillSuggestionsForPasswordFormWithAmbiguousIdAttribute) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2309,7 +2309,7 @@ IN_PROC_BROWSER_TEST_F(
// name and id attribute gets autofilled correctly.
IN_PROC_BROWSER_TEST_F(
PasswordManagerBrowserTestBase,
AutofillSuggetionsForPasswordFormWithoutNameOrIdAttribute) {
AutofillSuggestionsForPasswordFormWithoutNameOrIdAttribute) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2356,7 +2356,7 @@ IN_PROC_BROWSER_TEST_F(
// Test whether the change password form having username and password fields
// without name and id attribute gets autofilled correctly.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
AutofillSuggetionsForChangePwdWithEmptyNames) {
AutofillSuggestionsForChangePwdWithEmptyNames) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2416,7 +2416,7 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
// correctly.
IN_PROC_BROWSER_TEST_F(
PasswordManagerBrowserTestBase,
AutofillSuggetionsForChangePwdWithEmptyNamesAndAutocomplete) {
AutofillSuggestionsForChangePwdWithEmptyNamesAndAutocomplete) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2473,7 +2473,7 @@ IN_PROC_BROWSER_TEST_F(
// |autocomplete='new-password'| atrribute do not get autofilled.
IN_PROC_BROWSER_TEST_F(
PasswordManagerBrowserTestBase,
AutofillSuggetionsForChangePwdWithEmptyNamesButOnlyNewPwdField) {
AutofillSuggestionsForChangePwdWithEmptyNamesButOnlyNewPwdField) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2588,10 +2588,10 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
// Test whether the password form which is loaded as hidden is autofilled
// correctly. This happens very often in situations when in order to sign-in the
// user clicks a sign-in button and a hidden passsword form becomes visible.
// This test differs from AutofillSuggetionsForProblematicPasswordForm in that
// This test differs from AutofillSuggestionsForProblematicPasswordForm in that
// the form is hidden and in that test only some fields are hidden.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
AutofillSuggetionsHiddenPasswordForm) {
AutofillSuggestionsHiddenPasswordForm) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2638,7 +2638,7 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
// Test whether the password form with the problematic invisible password field
// gets autofilled correctly.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
AutofillSuggetionsForProblematicPasswordForm) {
AutofillSuggestionsForProblematicPasswordForm) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2685,7 +2685,7 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
// Test whether the password form with the problematic invisible password field
// in ambiguous password form gets autofilled correctly.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
AutofillSuggetionsForProblematicAmbiguousPasswordForm) {
AutofillSuggestionsForProblematicAmbiguousPasswordForm) {
// At first let us save credentials to the PasswordManager.
scoped_refptr<password_manager::PasswordStore> password_store =
PasswordStoreFactory::GetForProfile(browser()->profile(),
......@@ -2871,4 +2871,35 @@ IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
}
#endif
// Tests that the prompt to save the password is still shown if the fields have
// the "autocomplete" attribute set off.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
PromptForSubmitWithAutocompleteOff) {
NavigateToFile("/password/password_autocomplete_off_test.html");
NavigationObserver observer(WebContents());
scoped_ptr<PromptObserver> prompt_observer(
PromptObserver::Create(WebContents()));
std::string fill_and_submit =
"document.getElementById('username').value = 'temp';"
"document.getElementById('password').value = 'random';"
"document.getElementById('submit').click()";
ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
observer.Wait();
EXPECT_TRUE(prompt_observer->IsShowingPrompt());
}
// Tests that password suggestions still work if the fields have the
// "autocomplete" attribute set to off.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase,
AutofillSuggestionsForPasswordFormWithAutocompleteOff) {
std::string submit =
"document.getElementById('username').value = 'temp';"
"document.getElementById('password').value = 'mypassword';"
"document.getElementById('submit').click();";
VerifyPasswordIsSavedAndFilled(
"/password/password_autocomplete_off_test.html", submit, "password",
"mypassword");
}
} // namespace password_manager
......@@ -2068,6 +2068,67 @@ TEST_F(FormAutofillTest, WebFormElementToFormDataTooManyFields) {
&field));
}
// Tests that the |should_autocomplete| is set to false for all the fields when
// an autocomplete='off' attribute is set for the form in HTML.
TEST_F(FormAutofillTest, WebFormElementToFormData_AutocompleteOff_OnForm) {
LoadHTML(
"<FORM name='TestForm' id='form' action='http://cnn.com' method='post' "
"autocomplete='off'>"
" <LABEL for='firstname'>First name:</LABEL>"
" <INPUT type='text' id='firstname' value='John'/>"
" <LABEL for='lastname'>Last name:</LABEL>"
" <INPUT type='text' id='lastname' value='Smith'/>"
" <LABEL for='street-address'>Address:</LABEL>"
" <INPUT type='text' id='addressline1' value='123 Test st.'/>"
"</FORM>");
WebFrame* frame = GetMainFrame();
ASSERT_NE(nullptr, frame);
WebFormElement web_form =
frame->document().getElementById("form").to<WebFormElement>();
ASSERT_FALSE(web_form.isNull());
FormData form;
EXPECT_TRUE(WebFormElementToFormData(web_form, WebFormControlElement(),
EXTRACT_NONE, &form, nullptr));
for (const FormFieldData& field : form.fields) {
EXPECT_FALSE(field.should_autocomplete);
}
}
// Tests that the |should_autocomplete| is set to false only for the field
// which has an autocomplete='off' attribute set for it in HTML.
TEST_F(FormAutofillTest, WebFormElementToFormData_AutocompleteOff_OnField) {
LoadHTML(
"<FORM name='TestForm' id='form' action='http://cnn.com' method='post'>"
" <LABEL for='firstname'>First name:</LABEL>"
" <INPUT type='text' id='firstname' value='John' autocomplete='off'/>"
" <LABEL for='lastname'>Last name:</LABEL>"
" <INPUT type='text' id='lastname' value='Smith'/>"
" <LABEL for='street-address'>Address:</LABEL>"
" <INPUT type='text' id='addressline1' value='123 Test st.'/>"
"</FORM>");
WebFrame* frame = GetMainFrame();
ASSERT_NE(nullptr, frame);
WebFormElement web_form =
frame->document().getElementById("form").to<WebFormElement>();
ASSERT_FALSE(web_form.isNull());
FormData form;
EXPECT_TRUE(WebFormElementToFormData(web_form, WebFormControlElement(),
EXTRACT_NONE, &form, nullptr));
ASSERT_EQ(3U, form.fields.size());
EXPECT_FALSE(form.fields[0].should_autocomplete);
EXPECT_TRUE(form.fields[1].should_autocomplete);
EXPECT_TRUE(form.fields[2].should_autocomplete);
}
TEST_F(FormAutofillTest, ExtractForms) {
ExpectJohnSmithLabels(
"<FORM name='TestForm' action='http://cnn.com' method='post'>"
......
......@@ -5,15 +5,10 @@
<title>Password Test Form</title>
</head>
<body>
<form id="loginform" action="google.com" method="post">
<table cellspacing="3" cellpadding="5" border="0">
<tbody>
<tr><td>Username:</td><td align="left"><input type="text" name="Email" id="Email" size="18" value=""></td></tr>
<tr><td>Password:</td><td align="left"><input autocomplete="off" type="password" name="Passwd" id="Passwd" size="18"></td></tr>
<tr><td></td><td align="left"><input type="submit" class="button" name="signIn" id="signIn" value="Sign in"></td></tr>
</tbody>
</table>
<form method="POST" action="done.html" id="loginform">
<input type="text" id="username" autocomplete="off">
<input type="password" id="password" autocomplete="off">
<input type="submit" id="submit">
</form>
</body>
</html>
</html>
\ No newline at end of file
......@@ -20,6 +20,7 @@
#include "components/autofill/core/browser/state_names.h"
#include "components/autofill/core/common/autofill_l10n_util.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_util.h"
#include "grit/components_strings.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_formatter.h"
......@@ -500,6 +501,13 @@ bool AutofillField::FillFormField(const AutofillField& field,
FormFieldData* field_data) {
AutofillType type = field.Type();
// Don't fill if autocomplete=off is set on |field| on desktop for non credit
// card related fields.
if (!field.should_autocomplete && IsDesktopPlatform() &&
(type.group() != CREDIT_CARD)) {
return false;
}
if (type.GetStorableType() == PHONE_HOME_NUMBER) {
FillPhoneNumberField(field, value, field_data);
return true;
......
......@@ -9,6 +9,7 @@
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/autofill_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::ASCIIToUTF16;
......@@ -129,11 +130,45 @@ TEST(AutofillFieldTest, IsFieldFillable) {
field.set_server_type(NAME_LAST);
EXPECT_TRUE(field.IsFieldFillable());
// Field has autocomplete="off" set. Chrome ignores the attribute.
// Field has autocomplete="off" set. Since autofill was able to make a
// prediction, it is still considered a fillable field.
field.should_autocomplete = false;
EXPECT_TRUE(field.IsFieldFillable());
}
// Verify that non credit card related fields with the autocomplete attribute
// set to off don't get filled on desktop.
TEST(AutofillFieldTest, FillFormField_AutocompleteOff_AddressField) {
AutofillField field;
field.should_autocomplete = false;
// Non credit card related field.
AutofillField::FillFormField(field, ASCIIToUTF16("Test"), "en-US", "en-US",
&field);
// Verifiy that the field is filled on mobile but not on desktop.
if (IsDesktopPlatform()) {
EXPECT_EQ(base::string16(), field.value);
} else {
EXPECT_EQ(ASCIIToUTF16("Test"), field.value);
}
}
// Verify that credit card related fields with the autocomplete attribute
// set to off get filled.
TEST(AutofillFieldTest, FillFormField_AutocompleteOff_CreditCardField) {
AutofillField field;
field.should_autocomplete = false;
// Credit card related field.
field.set_heuristic_type(CREDIT_CARD_NUMBER);
AutofillField::FillFormField(field, ASCIIToUTF16("4111111111111111"), "en-US",
"en-US", &field);
// Verify that the field is filled.
EXPECT_EQ(ASCIIToUTF16("4111111111111111"), field.value);
}
TEST(AutofillFieldTest, FillPhoneNumber) {
AutofillField field;
field.SetHtmlType(HTML_TYPE_TEL_LOCAL_PREFIX, HtmlFieldMode());
......
......@@ -49,6 +49,7 @@
#include "components/autofill/core/common/autofill_data_validation.h"
#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/form_field_data.h"
......@@ -407,6 +408,12 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
got_autofillable_form) {
AutofillType type = autofill_field->Type();
bool is_filling_credit_card = (type.group() == CREDIT_CARD);
// On desktop, don't return non credit card related suggestions for forms or
// fields that have the "autocomplete" attribute set to off.
if (IsDesktopPlatform() && !is_filling_credit_card &&
!field.should_autocomplete) {
return;
}
if (is_filling_credit_card) {
suggestions = GetCreditCardSuggestions(field, type);
} else {
......
......@@ -35,6 +35,7 @@
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_pref_names.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "grit/components_strings.h"
......@@ -1895,6 +1896,89 @@ TEST_F(AutofillManagerTest, FillAddressAndCreditCardForm) {
}
}
// Test that non credit card related fields with the autocomplete attribute set
// to off are not filled on desktop.
TEST_F(AutofillManagerTest, FillAddressForm_AutocompleteOff) {
FormData address_form;
address_form.name = ASCIIToUTF16("MyForm");
address_form.origin = GURL("https://myform.com/form.html");
address_form.action = GURL("https://myform.com/submit.html");
FormFieldData field;
test::CreateTestFormField("First name", "firstname", "", "text", &field);
address_form.fields.push_back(field);
test::CreateTestFormField("Middle name", "middle", "", "text", &field);
field.should_autocomplete = false;
address_form.fields.push_back(field);
test::CreateTestFormField("Last name", "lastname", "", "text", &field);
field.should_autocomplete = true;
address_form.fields.push_back(field);
test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field);
field.should_autocomplete = false;
address_form.fields.push_back(field);
std::vector<FormData> address_forms(1, address_form);
FormsSeen(address_forms);
// Fill the address form.
const char guid[] = "00000000-0000-0000-0000-000000000001";
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(
kDefaultPageID, address_form, address_form.fields[0],
MakeFrontendID(std::string(), guid), &response_page_id, &response_data);
// The fist name should be filled.
ExpectFilledField("First name", "firstname", "Elvis", "text",
response_data.fields[0]);
// The middle name should not be filled on desktop.
if (IsDesktopPlatform()) {
ExpectFilledField("Middle name", "middle", "", "text",
response_data.fields[1]);
} else {
ExpectFilledField("Middle name", "middle", "Aaron", "text",
response_data.fields[1]);
}
// The last name should be filled.
ExpectFilledField("Last name", "lastname", "Presley", "text",
response_data.fields[2]);
// The address line 1 should not be filled on desktop.
if (IsDesktopPlatform()) {
ExpectFilledField("Address Line 1", "addr1", "", "text",
response_data.fields[3]);
} else {
ExpectFilledField("Address Line 1", "addr1", "3734 Elvis Presley Blvd.",
"text", response_data.fields[3]);
}
}
// Test that credit card fields are filled even if they have the autocomplete
// attribute set to off.
TEST_F(AutofillManagerTest, FillCreditCardForm_AutocompleteOff) {
// Set up our form data.
FormData form;
CreateTestCreditCardFormData(&form, true, false);
// Set the autocomplete=off on all fields.
for (FormFieldData field : form.fields)
field.should_autocomplete = false;
std::vector<FormData> forms(1, form);
FormsSeen(forms);
const char guid[] = "00000000-0000-0000-0000-000000000004";
int response_page_id = 0;
FormData response_data;
FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
MakeFrontendID(guid, std::string()),
&response_page_id, &response_data);
// All fields should be filled.
ExpectFilledCreditCardFormElvis(response_page_id, response_data,
kDefaultPageID, false);
}
// Test that non-focusable field is ignored while inferring boundaries between
// sections: http://crbug.com/231160
TEST_F(AutofillManagerTest, FillFormWithNonFocusableFields) {
......@@ -3870,4 +3954,72 @@ TEST_F(AutofillManagerTest, ShouldUploadForm) {
EXPECT_FALSE(autofill_manager_->ShouldUploadForm(form_structure_3));
}
// Verify that no suggestions are shown on desktop for non credit card related
// fields if the initiating field has the "autocomplete" attribute set to off.
TEST_F(AutofillManagerTest, DisplaySuggestions_AutocompleteOff_AddressField) {
// Set up an address form.
FormData mixed_form;
mixed_form.name = ASCIIToUTF16("MyForm");
mixed_form.origin = GURL("https://myform.com/form.html");
mixed_form.action = GURL("https://myform.com/submit.html");
FormFieldData field;
test::CreateTestFormField("First name", "firstname", "", "text", &field);
field.should_autocomplete = false;
mixed_form.fields.push_back(field);
test::CreateTestFormField("Last name", "lastname", "", "text", &field);
field.should_autocomplete = true;
mixed_form.fields.push_back(field);
test::CreateTestFormField("Address", "address", "", "text", &field);
field.should_autocomplete = true;
mixed_form.fields.push_back(field);
std::vector<FormData> mixed_forms(1, mixed_form);
FormsSeen(mixed_forms);
// Suggestions should not be displayed on desktop for this field.
GetAutofillSuggestions(mixed_form, mixed_form.fields[0]);
if (IsDesktopPlatform()) {
EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen());
} else {
EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen());
}
// Suggestions should always be displayed for all the other fields.
for (size_t i = 1U; i < mixed_form.fields.size(); ++i) {
GetAutofillSuggestions(mixed_form, mixed_form.fields[i]);
EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen());
}
}
// Verify that suggestions are shown on desktop for credit card related fields
// even if the initiating field field has the "autocomplete" attribute set to
// off.
TEST_F(AutofillManagerTest,
DisplaySuggestions_AutocompleteOff_CreditCardField) {
// Set up a credit card form.
FormData mixed_form;
mixed_form.name = ASCIIToUTF16("MyForm");
mixed_form.origin = GURL("https://myform.com/form.html");
mixed_form.action = GURL("https://myform.com/submit.html");
FormFieldData field;
test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field);
field.should_autocomplete = false;
mixed_form.fields.push_back(field);
test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
field.should_autocomplete = true;
mixed_form.fields.push_back(field);
test::CreateTestFormField("Expiration Month", "ccexpiresmonth", "", "text",
&field);
field.should_autocomplete = false;
mixed_form.fields.push_back(field);
mixed_form.fields.push_back(field);
std::vector<FormData> mixed_forms(1, mixed_form);
FormsSeen(mixed_forms);
// Suggestions should always be displayed.
for (const FormFieldData& field : mixed_form.fields) {
GetAutofillSuggestions(mixed_form, field);
EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen());
}
}
} // namespace autofill
......@@ -96,4 +96,12 @@ size_t GetTextSelectionStart(const base::string16& suggestion,
return base::string16::npos;
}
bool IsDesktopPlatform() {
#if defined(OS_ANDROID) || defined(OS_IOS)
return false;
#else
return true;
#endif
}
} // namespace autofill
......@@ -36,6 +36,10 @@ size_t GetTextSelectionStart(const base::string16& suggestion,
const base::string16& field_contents,
bool case_sensitive);
// Returns true if running on a desktop platform. Any platform that is not
// Android or iOS is considered desktop.
bool IsDesktopPlatform();
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_UTIL_H_
......@@ -83,6 +83,8 @@ bool WebFormControlElement::autoComplete() const
return constUnwrap<HTMLInputElement>()->shouldAutocomplete();
if (isHTMLTextAreaElement(*m_private))
return constUnwrap<HTMLTextAreaElement>()->shouldAutocomplete();
if (isHTMLSelectElement(*m_private))
return constUnwrap<HTMLSelectElement>()->shouldAutocomplete();
return false;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment