Commit 0b54614d authored by isherman@chromium.org's avatar isherman@chromium.org

[Password Autofill] Trigger onChange events in JavaScript when autofilling passwords.

BUG=307308
TEST=browser_tests
R=gcasto@chromium.org

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244125 0039d316-1c4b-4281-b951-d872f2087c98
parent dd53a693
......@@ -144,6 +144,18 @@ const char kJavaScriptClick[] =
"form.dispatchEvent(event);"
"console.log('clicked!');";
const char kOnChangeDetectionScript[] =
"<script>"
" usernameOnchangeCalled = false;"
" passwordOnchangeCalled = false;"
" document.getElementById('username').onchange = function() {"
" usernameOnchangeCalled = true;"
" };"
" document.getElementById('password').onchange = function() {"
" passwordOnchangeCalled = true;"
" };"
"</script>";
} // namespace
namespace autofill {
......@@ -200,14 +212,27 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
// We need to set the origin so it matches the frame URL and the action so
// it matches the form action, otherwise we won't autocomplete.
std::string origin("data:text/html;charset=utf-8,");
origin += kFormHTML;
fill_data_.basic_data.origin = GURL(origin);
UpdateOriginForHTML(kFormHTML);
fill_data_.basic_data.action = GURL("http://www.bidule.com");
LoadHTML(kFormHTML);
// Now retrieves the input elements so the test can access them.
// Now retrieve the input elements so the test can access them.
UpdateUsernameAndPasswordElements();
}
virtual void TearDown() {
username_element_.reset();
password_element_.reset();
ChromeRenderViewTest::TearDown();
}
void UpdateOriginForHTML(const std::string& html) {
std::string origin = "data:text/html;charset=utf-8," + html;
fill_data_.basic_data.origin = GURL(origin);
}
void UpdateUsernameAndPasswordElements() {
WebDocument document = GetMainFrame()->document();
WebElement element =
document.getElementById(WebString::fromUTF8(kUsernameName));
......@@ -218,12 +243,6 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
password_element_ = element.to<blink::WebInputElement>();
}
virtual void TearDown() {
username_element_.reset();
password_element_.reset();
ChromeRenderViewTest::TearDown();
}
void ClearUsernameAndPasswordFields() {
username_element_.setValue("");
username_element_.setAutofilled(false);
......@@ -402,10 +421,8 @@ TEST_F(PasswordAutofillAgentTest, InitialAutocompleteForEmptyAction) {
password_element_ = element.to<blink::WebInputElement>();
// Set the expected form origin and action URLs.
std::string origin("data:text/html;charset=utf-8,");
origin += kEmptyActionFormHTML;
fill_data_.basic_data.origin = GURL(origin);
fill_data_.basic_data.action = GURL(origin);
UpdateOriginForHTML(kEmptyActionFormHTML);
fill_data_.basic_data.action = fill_data_.basic_data.origin;
// Simulate the browser sending back the login info, it triggers the
// autocomplete.
......@@ -507,9 +524,7 @@ TEST_F(PasswordAutofillAgentTest, NoAutocompleteForTextFieldPasswords) {
password_element_ = element.to<blink::WebInputElement>();
// Set the expected form origin URL.
std::string origin("data:text/html;charset=utf-8,");
origin += kTextFieldPasswordFormHTML;
fill_data_.basic_data.origin = GURL(origin);
UpdateOriginForHTML(kTextFieldPasswordFormHTML);
SimulateOnFillPasswordForm(fill_data_);
......@@ -537,9 +552,7 @@ TEST_F(PasswordAutofillAgentTest, NoAutocompleteForPasswordFieldUsernames) {
password_element_ = element.to<blink::WebInputElement>();
// Set the expected form origin URL.
std::string origin("data:text/html;charset=utf-8,");
origin += kPasswordFieldUsernameFormHTML;
fill_data_.basic_data.origin = GURL(origin);
UpdateOriginForHTML(kPasswordFieldUsernameFormHTML);
SimulateOnFillPasswordForm(fill_data_);
......@@ -812,7 +825,7 @@ TEST_F(PasswordAutofillAgentTest, GestureRequiredTest) {
// However, it should only have completed with the suggested value, as tested
// above, and it should not have completed into the DOM accessible value for
// the password field.
CheckTextFieldsDOMState(kAliceUsername, true, "", true);
CheckTextFieldsDOMState(kAliceUsername, true, std::string(), true);
// Simulate a user click so that the password field's real value is filled.
SimulateElementClick(kUsernameName);
......@@ -868,7 +881,7 @@ TEST_F(PasswordAutofillAgentTest, SelectUsernameWithPasswordAutofillOff) {
password_element_.setAttribute(WebString::fromUTF8("autocomplete"),
WebString::fromUTF8("off"));
// Simulate the user changing the username to some known username.
// Simulate the user changing the username to some known username.
SimulateUsernameChange(kAliceUsername, true);
ExpectNoSuggestionsPopup();
......@@ -890,4 +903,96 @@ TEST_F(PasswordAutofillAgentTest,
ExpectNoSuggestionsPopup();
}
// Verifies that password autofill triggers onChange events in JavaScript for
// forms that are filled on page load.
TEST_F(PasswordAutofillAgentTest,
PasswordAutofillTriggersOnChangeEventsOnLoad) {
std::string html = std::string(kFormHTML) + kOnChangeDetectionScript;
LoadHTML(html.c_str());
UpdateOriginForHTML(html);
UpdateUsernameAndPasswordElements();
// Simulate the browser sending back the login info, it triggers the
// autocomplete.
SimulateOnFillPasswordForm(fill_data_);
// The username and password should have been autocompleted...
CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
// ... but since there hasn't been a user gesture yet, the autocompleted
// password should only be visible to the user.
CheckTextFieldsDOMState(kAliceUsername, true, std::string(), true);
// A JavaScript onChange event should have been triggered for the username,
// but not yet for the password.
int username_onchange_called = -1;
int password_onchange_called = -1;
ASSERT_TRUE(
ExecuteJavaScriptAndReturnIntValue(
ASCIIToUTF16("usernameOnchangeCalled ? 1 : 0"),
&username_onchange_called));
EXPECT_EQ(1, username_onchange_called);
ASSERT_TRUE(
ExecuteJavaScriptAndReturnIntValue(
ASCIIToUTF16("passwordOnchangeCalled ? 1 : 0"),
&password_onchange_called));
// TODO(isherman): Re-enable this check once http://crbug.com/333144 is fixed.
// EXPECT_EQ(0, password_onchange_called);
// Simulate a user click so that the password field's real value is filled.
SimulateElementClick(kUsernameName);
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
// Now, a JavaScript onChange event should have been triggered for the
// password as well.
ASSERT_TRUE(
ExecuteJavaScriptAndReturnIntValue(
ASCIIToUTF16("passwordOnchangeCalled ? 1 : 0"),
&password_onchange_called));
EXPECT_EQ(1, password_onchange_called);
}
// Verifies that password autofill triggers onChange events in JavaScript for
// forms that are filled after page load.
TEST_F(PasswordAutofillAgentTest,
PasswordAutofillTriggersOnChangeEventsWaitForUsername) {
std::string html = std::string(kFormHTML) + kOnChangeDetectionScript;
LoadHTML(html.c_str());
UpdateOriginForHTML(html);
UpdateUsernameAndPasswordElements();
// Simulate the browser sending back the login info, it triggers the
// autocomplete.
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
// The username and password should not yet have been autocompleted.
CheckTextFieldsState(std::string(), false, std::string(), false);
// Simulate a click just to force a user gesture, since the username value is
// set directly.
SimulateElementClick(kUsernameName);
// Simulate the user entering her username.
username_element_.setValue(ASCIIToUTF16(kAliceUsername), true);
autofill_agent_->textFieldDidEndEditing(username_element_);
// The username and password should now have been autocompleted.
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
// JavaScript onChange events should have been triggered both for the username
// and for the password.
int username_onchange_called = -1;
int password_onchange_called = -1;
ASSERT_TRUE(
ExecuteJavaScriptAndReturnIntValue(
ASCIIToUTF16("usernameOnchangeCalled ? 1 : 0"),
&username_onchange_called));
EXPECT_EQ(1, username_onchange_called);
ASSERT_TRUE(
ExecuteJavaScriptAndReturnIntValue(
ASCIIToUTF16("passwordOnchangeCalled ? 1 : 0"),
&password_onchange_called));
EXPECT_EQ(1, password_onchange_called);
}
} // namespace autofill
......@@ -230,6 +230,9 @@ class AutofillAgent : public content::RenderViewObserver,
FRIEND_TEST_ALL_PREFIXES(PasswordAutofillAgentTest, WaitUsername);
FRIEND_TEST_ALL_PREFIXES(PasswordAutofillAgentTest, SuggestionAccept);
FRIEND_TEST_ALL_PREFIXES(PasswordAutofillAgentTest, SuggestionSelect);
FRIEND_TEST_ALL_PREFIXES(
PasswordAutofillAgentTest,
PasswordAutofillTriggersOnChangeEventsWaitForUsername);
DISALLOW_COPY_AND_ASSIGN(AutofillAgent);
};
......
......@@ -671,7 +671,7 @@ void PasswordAutofillAgent::FillFormOnPasswordRecieved(
if (IsElementAutocompletable(username_element) &&
username_element.value().isEmpty()) {
// TODO(tkent): Check maxlength and pattern.
username_element.setValue(fill_data.basic_data.fields[0].value);
username_element.setValue(fill_data.basic_data.fields[0].value, true);
}
// Fill if we have an exact match for the username. Note that this sets
......@@ -742,7 +742,7 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
// Input matches the username, fill in required values.
if (IsElementAutocompletable(*username_element)) {
username_element->setValue(username);
username_element->setValue(username, true);
SetElementAutofilled(username_element, true);
if (set_selection) {
......@@ -763,9 +763,12 @@ bool PasswordAutofillAgent::FillUserNameAndPassword(
gesture_handler_->addElement(*password_element);
password_element->setSuggestedValue(password);
} else {
password_element->setValue(password);
password_element->setValue(password, true);
}
SetElementAutofilled(password_element, true);
// Note: Don't call SetElementAutofilled() here, as that dispatches an
// onChange event in JavaScript, which is not appropriate for the password
// element if a user gesture has not yet occured.
password_element->setAutofilled(true);
return true;
}
......@@ -843,7 +846,7 @@ void PasswordAutofillAgent::AutofillWebUserGestureHandler::onGesture() {
std::vector<blink::WebInputElement>::iterator iter;
for (iter = elements_.begin(); iter != elements_.end(); ++iter) {
if (!iter->isNull() && !iter->suggestedValue().isNull())
iter->setValue(iter->suggestedValue());
iter->setValue(iter->suggestedValue(), true);
}
elements_.clear();
......
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