summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrouslan <rouslan@chromium.org>2015-04-27 18:00:01 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-28 01:00:02 +0000
commit6a3f8d9afcbb397f562e3a359ffbe39d59d278b9 (patch)
treea00e18a07a30bf38e399277e81d7a2fb0db40bfb
parent992492e66abc07139da9b20aaccf405ca8866586 (diff)
downloadchromium_src-6a3f8d9afcbb397f562e3a359ffbe39d59d278b9.zip
chromium_src-6a3f8d9afcbb397f562e3a359ffbe39d59d278b9.tar.gz
chromium_src-6a3f8d9afcbb397f562e3a359ffbe39d59d278b9.tar.bz2
[autofill] Allow only a user gesture to trigger autofill.
If a script inserts text into an input field without a user gesture, then do not show the autofill popup. TEST=AutofillRendererTest.IgnoreNonUserGestureTextFieldChanges BUG=353001 Review URL: https://codereview.chromium.org/1026493002 Cr-Commit-Position: refs/heads/master@{#327204}
-rw-r--r--chrome/renderer/autofill/autofill_renderer_browsertest.cc26
-rw-r--r--chrome/renderer/autofill/password_autofill_agent_browsertest.cc213
-rw-r--r--chrome/renderer/autofill/password_generation_agent_browsertest.cc59
-rw-r--r--components/autofill/content/renderer/autofill_agent.cc13
-rw-r--r--components/autofill/content/renderer/autofill_agent.h3
-rw-r--r--content/public/test/render_view_test.cc81
-rw-r--r--content/public/test/render_view_test.h15
7 files changed, 207 insertions, 203 deletions
diff --git a/chrome/renderer/autofill/autofill_renderer_browsertest.cc b/chrome/renderer/autofill/autofill_renderer_browsertest.cc
index 0ff5d27..e6f4ec1 100644
--- a/chrome/renderer/autofill/autofill_renderer_browsertest.cc
+++ b/chrome/renderer/autofill/autofill_renderer_browsertest.cc
@@ -22,6 +22,7 @@
#include "third_party/WebKit/public/web/WebFormElement.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebView.h"
using base::ASCIIToUTF16;
using blink::WebDocument;
@@ -245,6 +246,31 @@ TEST_F(AutofillRendererTest, DynamicallyAddedUnownedFormElements) {
EXPECT_FORM_FIELD_DATA_EQUALS(expected, forms[0].fields[8]);
}
+TEST_F(AutofillRendererTest, IgnoreNonUserGestureTextFieldChanges) {
+ LoadHTML("<form method='post'>"
+ " <input type='text' id='full_name'/>"
+ "</form>");
+
+ blink::WebInputElement full_name =
+ GetMainFrame()->document().getElementById("full_name")
+ .to<blink::WebInputElement>();
+ while (!full_name.focused())
+ GetMainFrame()->view()->advanceFocus(false);
+
+ // Not a user gesture, so no IPC message to browser.
+ full_name.setValue("Alice", true);
+ GetMainFrame()->toWebLocalFrame()->autofillClient()->textFieldDidChange(
+ full_name);
+ base::MessageLoop::current()->RunUntilIdle();
+ ASSERT_EQ(nullptr, render_thread_->sink().GetFirstMessageMatching(
+ AutofillHostMsg_TextFieldDidChange::ID));
+
+ // A user gesture will send a message to the browser.
+ SimulateUserInputChangeForElement(&full_name, "Alice");
+ ASSERT_NE(nullptr, render_thread_->sink().GetFirstMessageMatching(
+ AutofillHostMsg_TextFieldDidChange::ID));
+}
+
class RequestAutocompleteRendererTest : public AutofillRendererTest {
public:
RequestAutocompleteRendererTest()
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 0b57f44..7ff1938 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -297,40 +297,6 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
->textFieldDidEndEditing(input);
}
- void SimulateInputChangeForElement(const std::string& new_value,
- bool move_caret_to_end,
- WebFrame* input_frame,
- WebInputElement& input,
- bool is_user_input) {
- input.setValue(WebString::fromUTF8(new_value), is_user_input);
- // The field must have focus or AutofillAgent will think the
- // change should be ignored.
- while (!input.focused())
- input_frame->document().frame()->view()->advanceFocus(false);
- if (move_caret_to_end)
- input.setSelectionRange(new_value.length(), new_value.length());
- if (is_user_input) {
- AutofillMsg_FirstUserGestureObservedInTab msg(0);
- content::RenderFrame::FromWebFrame(input_frame)->OnMessageReceived(msg);
-
- // Also pass the message to the testing object.
- if (input_frame == GetMainFrame())
- password_autofill_agent_->FirstUserGestureObserved();
- }
- input_frame->toWebLocalFrame()->autofillClient()->textFieldDidChange(input);
- // Processing is delayed because of a Blink bug:
- // https://bugs.webkit.org/show_bug.cgi?id=16976
- // See PasswordAutofillAgent::TextDidChangeInTextField() for details.
-
- // Autocomplete will trigger a style recalculation when we put up the next
- // frame, but we don't want to wait that long. Instead, trigger a style
- // recalcuation manually after TextFieldDidChangeImpl runs.
- base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
- &PasswordAutofillAgentTest::LayoutMainFrame, base::Unretained(this)));
-
- base::MessageLoop::current()->RunUntilIdle();
- }
-
void SimulateSuggestionChoice(WebInputElement& username_input) {
base::string16 username(base::ASCIIToUTF16(kAliceUsername));
base::string16 password(base::ASCIIToUTF16(kAlicePassword));
@@ -353,26 +319,12 @@ class PasswordAutofillAgentTest : public ChromeRenderViewTest {
->OnMessageReceived(msg);
}
- void LayoutMainFrame() {
- GetMainFrame()->view()->layout();
+ void SimulateUsernameChange(const std::string& username) {
+ SimulateUserInputChangeForElement(&username_element_, username);
}
- void SimulateUsernameChange(const std::string& username,
- bool move_caret_to_end,
- bool is_user_input = false) {
- SimulateInputChangeForElement(username,
- move_caret_to_end,
- GetMainFrame(),
- username_element_,
- is_user_input);
- }
-
- void SimulateKeyDownEvent(const WebInputElement& element,
- ui::KeyboardCode key_code) {
- blink::WebKeyboardEvent key_event;
- key_event.windowsKeyCode = key_code;
- static_cast<blink::WebAutofillClient*>(autofill_agent_)
- ->textFieldDidReceiveKeyDown(element, key_event);
+ void SimulatePasswordChange(const std::string& password) {
+ SimulateUserInputChangeForElement(&password_element_, password);
}
void CheckTextFieldsStateForElements(const WebInputElement& username_element,
@@ -688,7 +640,7 @@ TEST_F(PasswordAutofillAgentTest, PasswordClearOnEdit) {
SimulateOnFillPasswordForm(fill_data_);
// Simulate the user changing the username to some unknown username.
- SimulateUsernameChange("alicia", true);
+ SimulateUsernameChange("alicia");
// The password should have been cleared.
CheckTextFieldsState("alicia", false, std::string(), false);
@@ -705,31 +657,31 @@ TEST_F(PasswordAutofillAgentTest, WaitUsername) {
CheckTextFieldsState(std::string(), false, std::string(), false);
// No autocomplete should happen when text is entered in the username.
- SimulateUsernameChange("a", true);
+ SimulateUsernameChange("a");
CheckTextFieldsState("a", false, std::string(), false);
- SimulateUsernameChange("al", true);
+ SimulateUsernameChange("al");
CheckTextFieldsState("al", false, std::string(), false);
- SimulateUsernameChange(kAliceUsername, true);
+ SimulateUsernameChange(kAliceUsername);
CheckTextFieldsState(kAliceUsername, false, std::string(), false);
// Autocomplete should happen only when the username textfield is blurred with
// a full match.
- username_element_.setValue("a");
+ SimulateUsernameChange("a");
static_cast<blink::WebAutofillClient*>(autofill_agent_)
->textFieldDidEndEditing(username_element_);
CheckTextFieldsState("a", false, std::string(), false);
- username_element_.setValue("al");
+ SimulateUsernameChange("al");
static_cast<blink::WebAutofillClient*>(autofill_agent_)
->textFieldDidEndEditing(username_element_);
CheckTextFieldsState("al", false, std::string(), false);
- username_element_.setValue("alices");
+ SimulateUsernameChange("alices");
static_cast<blink::WebAutofillClient*>(autofill_agent_)
->textFieldDidEndEditing(username_element_);
CheckTextFieldsState("alices", false, std::string(), false);
- username_element_.setValue(ASCIIToUTF16(kAliceUsername));
+ SimulateUsernameChange(kAliceUsername);
static_cast<blink::WebAutofillClient*>(autofill_agent_)
->textFieldDidEndEditing(username_element_);
- CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
+ CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
}
// Tests that inline autocompletion works properly.
@@ -741,57 +693,55 @@ TEST_F(PasswordAutofillAgentTest, InlineAutocomplete) {
// Simulate the user typing in the first letter of 'alice', a stored
// username.
- SimulateUsernameChange("a", true);
+ SimulateUsernameChange("a");
// Both the username and password text fields should reflect selection of the
// stored login.
- CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
+ CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
// And the selection should have been set to 'lice', the last 4 letters.
CheckUsernameSelection(1, 5);
// Now the user types the next letter of the same username, 'l'.
- SimulateUsernameChange("al", true);
+ SimulateUserTypingASCIICharacter('l', true);
// Now the fields should have the same value, but the selection should have a
// different start value.
- CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
+ CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
CheckUsernameSelection(2, 5);
- // Test that deleting does not trigger autocomplete.
- SimulateKeyDownEvent(username_element_, ui::VKEY_BACK);
- SimulateUsernameChange("alic", true);
- CheckTextFieldsState("alic", false, std::string(), false);
- CheckUsernameSelection(4, 4); // No selection.
- // Reset the last pressed key to something other than backspace.
- SimulateKeyDownEvent(username_element_, ui::VKEY_A);
+ // Test that backspace will erase the selection and will stop autocompletion.
+ SimulateUserTypingASCIICharacter(ui::VKEY_BACK, true);
+ CheckTextFieldsState("al", false, std::string(), false);
+ CheckUsernameSelection(2, 2); // No selection.
// Now lets say the user goes astray from the stored username and types the
// letter 'f', spelling 'alf'. We don't know alf (that's just sad), so in
// practice the username should no longer be 'alice' and the selected range
// should be empty.
- SimulateUsernameChange("alf", true);
+ SimulateUserTypingASCIICharacter('f', true);
CheckTextFieldsState("alf", false, std::string(), false);
CheckUsernameSelection(3, 3); // No selection.
// Ok, so now the user removes all the text and enters the letter 'b'.
- SimulateUsernameChange("b", true);
+ SimulateUsernameChange("b");
// The username and password fields should match the 'bob' entry.
- CheckTextFieldsState(kBobUsername, true, kBobPassword, true);
+ CheckTextFieldsDOMState(kBobUsername, true, kBobPassword, true);
CheckUsernameSelection(1, 3);
// Then, the user again removes all the text and types an uppercase 'C'.
- SimulateUsernameChange("C", true);
+ SimulateUsernameChange("C");
// The username and password fields should match the 'Carol' entry.
- CheckTextFieldsState(kCarolUsername, true, kCarolPassword, true);
+ CheckTextFieldsDOMState(kCarolUsername, true, kCarolPassword, true);
CheckUsernameSelection(1, 5);
+
// The user removes all the text and types a lowercase 'c'. We only
// want case-sensitive autocompletion, so the username and the selected range
// should be empty.
- SimulateUsernameChange("c", true);
+ SimulateUsernameChange("c");
CheckTextFieldsState("c", false, std::string(), false);
CheckUsernameSelection(1, 1);
// Check that we complete other_possible_usernames as well.
- SimulateUsernameChange("R", true);
- CheckTextFieldsState(kCarolAlternateUsername, true, kCarolPassword, true);
+ SimulateUsernameChange("R");
+ CheckTextFieldsDOMState(kCarolAlternateUsername, true, kCarolPassword, true);
CheckUsernameSelection(1, 17);
}
@@ -920,8 +870,9 @@ TEST_F(PasswordAutofillAgentTest, IframeNoFillTest) {
// Simulate the user typing in the username in the iframe which should cause
// an autofill.
- SimulateInputChangeForElement(
- kAliceUsername, true, iframe, username_input, true);
+ content::RenderFrame::FromWebFrame(iframe)
+ ->OnMessageReceived(AutofillMsg_FirstUserGestureObservedInTab(0));
+ SimulateUserInputChangeForElement(&username_input, kAliceUsername);
CheckTextFieldsStateForElements(username_input,
kAliceUsername,
@@ -1029,9 +980,9 @@ TEST_F(PasswordAutofillAgentTest,
// set directly.
SimulateElementClick(kUsernameName);
- // Simulate the user entering her username and selecting the matching autofill
- // from the dropdown.
- SimulateUsernameChange(kAliceUsername, true, true);
+ // Simulate the user entering the first letter of her username and selecting
+ // the matching autofill from the dropdown.
+ SimulateUsernameChange("a");
SimulateSuggestionChoice(username_element_);
// The username and password should now have been autocompleted.
@@ -1289,10 +1240,10 @@ TEST_F(PasswordAutofillAgentTest, ClearPreviewWithInlineAutocompletedUsername) {
ClearUsernameAndPasswordFields();
// Simulate the user typing in the first letter of 'alice', a stored username.
- SimulateUsernameChange("a", true);
+ SimulateUsernameChange("a");
// Both the username and password text fields should reflect selection of the
// stored login.
- CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
+ CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
// The selection should have been set to 'lice', the last 4 letters.
CheckUsernameSelection(1, 5);
@@ -1314,7 +1265,7 @@ TEST_F(PasswordAutofillAgentTest, ClearPreviewWithInlineAutocompletedUsername) {
EXPECT_EQ(kAliceUsername, username_element_.value().utf8());
EXPECT_TRUE(username_element_.suggestedValue().isEmpty());
EXPECT_TRUE(username_element_.isAutofilled());
- EXPECT_TRUE(password_element_.value().isEmpty());
+ EXPECT_EQ(kAlicePassword, password_element_.value().utf8());
EXPECT_TRUE(password_element_.suggestedValue().isEmpty());
EXPECT_TRUE(password_element_.isAutofilled());
CheckUsernameSelection(1, 5);
@@ -1414,17 +1365,18 @@ TEST_F(PasswordAutofillAgentTest, CredentialsOnClick) {
// Now simulate a user typing in an unrecognized username and then
// clicking on the username element. This should also produce a message with
// all the usernames.
- SimulateUsernameChange("baz", true);
+ SimulateUsernameChange("baz");
render_thread_->sink().ClearMessages();
static_cast<PageClickListener*>(autofill_agent_)
->FormControlElementClicked(username_element_, true);
CheckSuggestions("baz", true);
+ ClearUsernameAndPasswordFields();
// Now simulate a user typing in the first letter of the username and then
// clicking on the username element. While the typing of the first letter will
// inline autocomplete, clicking on the element should still produce a full
// suggestion list.
- SimulateUsernameChange("a", true);
+ SimulateUsernameChange("a");
render_thread_->sink().ClearMessages();
static_cast<PageClickListener*>(autofill_agent_)
->FormControlElementClicked(username_element_, true);
@@ -1520,10 +1472,8 @@ TEST_F(PasswordAutofillAgentTest, NoCredentialsOnPasswordClick) {
// typed by the user.
TEST_F(PasswordAutofillAgentTest,
RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared) {
- SimulateInputChangeForElement(
- "temp", true, GetMainFrame(), username_element_, true);
- SimulateInputChangeForElement(
- "random", true, GetMainFrame(), password_element_, true);
+ SimulateUsernameChange("temp");
+ SimulatePasswordChange("random");
// Simulate that the username and the password value was cleared by the
// site's JavaScript before submit.
@@ -1543,16 +1493,12 @@ TEST_F(PasswordAutofillAgentTest,
// remembered.
TEST_F(PasswordAutofillAgentTest,
RememberLastNonEmptyUsernameAndPasswordOnSubmit_UserCleared) {
- SimulateInputChangeForElement(
- "temp", true, GetMainFrame(), username_element_, true);
- SimulateInputChangeForElement(
- "random", true, GetMainFrame(), password_element_, true);
+ SimulateUsernameChange("temp");
+ SimulatePasswordChange("random");
// Simulate that the user actually cleared the username and password again.
- SimulateInputChangeForElement("", true, GetMainFrame(), username_element_,
- true);
- SimulateInputChangeForElement(
- "", true, GetMainFrame(), password_element_, true);
+ SimulateUsernameChange("");
+ SimulatePasswordChange("");
static_cast<content::RenderFrameObserver*>(password_autofill_agent_)
->WillSubmitForm(username_element_.form());
@@ -1574,10 +1520,8 @@ TEST_F(PasswordAutofillAgentTest,
LoadHTML(kNewPasswordFormHTML);
UpdateUsernameAndPasswordElements();
- SimulateInputChangeForElement(
- "temp", true, GetMainFrame(), username_element_, true);
- SimulateInputChangeForElement(
- "random", true, GetMainFrame(), password_element_, true);
+ SimulateUsernameChange("temp");
+ SimulatePasswordChange("random");
// Simulate that the username and the password value was cleared by
// the site's JavaScript before submit.
@@ -1602,22 +1546,14 @@ TEST_F(PasswordAutofillAgentTest,
fill_data_.wait_for_username = true;
SimulateOnFillPasswordForm(fill_data_);
// Simulate that the user typed her name to make the autofill work.
- SimulateInputChangeForElement(kAliceUsername,
- /*move_caret_to_end=*/true,
- GetMainFrame(),
- username_element_,
- /*is_user_input=*/true);
+ SimulateUsernameChange(kAliceUsername);
SimulateDidEndEditing(GetMainFrame(), username_element_);
const std::string old_username(username_element_.value().utf8());
const std::string old_password(password_element_.value().utf8());
const std::string new_password(old_password + "modify");
// The user changes the password.
- SimulateInputChangeForElement(new_password,
- /*move_caret_to_end=*/true,
- GetMainFrame(),
- password_element_,
- /*is_user_input=*/true);
+ SimulatePasswordChange(new_password);
// The user switches back into the username field, but leaves that without
// changes.
@@ -1637,14 +1573,10 @@ TEST_F(PasswordAutofillAgentTest,
ClearUsernameAndPasswordFields();
// The user enters a password
- SimulateInputChangeForElement("someOtherPassword",
- /*move_caret_to_end=*/true,
- GetMainFrame(),
- password_element_,
- /*is_user_input=*/true);
+ SimulatePasswordChange("someOtherPassword");
// Simulate the user typing a stored username.
- SimulateUsernameChange(kAliceUsername, true);
+ SimulateUsernameChange(kAliceUsername);
// The autofileld password should replace the typed one.
CheckTextFieldsDOMState(kAliceUsername, true, kAlicePassword, true);
}
@@ -1655,10 +1587,8 @@ TEST_F(PasswordAutofillAgentTest,
// typed by the user.
TEST_F(PasswordAutofillAgentTest,
RememberLastTypedUsernameAndPasswordOnSubmit_ScriptChanged) {
- SimulateInputChangeForElement("temp", true, GetMainFrame(), username_element_,
- true);
- SimulateInputChangeForElement("random", true, GetMainFrame(),
- password_element_, true);
+ SimulateUsernameChange("temp");
+ SimulatePasswordChange("random");
// Simulate that the username and the password value was changed by the
// site's JavaScript before submit.
@@ -1705,10 +1635,8 @@ TEST_F(
RememberLastTypedAfterAutofilledUsernameAndPasswordOnSubmit_ScriptChanged) {
SimulateOnFillPasswordForm(fill_data_);
- SimulateInputChangeForElement("temp", true, GetMainFrame(), username_element_,
- true);
- SimulateInputChangeForElement("random", true, GetMainFrame(),
- password_element_, true);
+ SimulateUsernameChange("temp");
+ SimulatePasswordChange("random");
// Simulate that the username and the password value was changed by the
// site's JavaScript before submit.
@@ -1728,12 +1656,10 @@ TEST_F(
// PasswordAutofillAgent should remember the username that was autofilled,
// not last typed.
TEST_F(PasswordAutofillAgentTest, RememberAutofilledUsername) {
- SimulateInputChangeForElement("Te", true, GetMainFrame(), username_element_,
- true);
+ SimulateUsernameChange("Te");
// Simulate that the username was changed by autofilling.
username_element_.setValue(WebString("temp"));
- SimulateInputChangeForElement("random", true, GetMainFrame(),
- password_element_, true);
+ SimulatePasswordChange("random");
static_cast<content::RenderFrameObserver*>(password_autofill_agent_)
->WillSendSubmitEvent(username_element_.form());
@@ -1878,12 +1804,9 @@ TEST_F(PasswordAutofillAgentTest, FindingUsernameWithoutAutofillPredictions) {
LoadHTML(kFormHTMLWithTwoTextFields);
UpdateUsernameAndPasswordElements();
blink::WebInputElement email_element = GetInputElementByID(kEmailName);
- SimulateInputChangeForElement("temp", true, GetMainFrame(), username_element_,
- true);
- SimulateInputChangeForElement("temp@google.com", true, GetMainFrame(),
- email_element, true);
- SimulateInputChangeForElement("random", true, GetMainFrame(),
- password_element_, true);
+ SimulateUsernameChange("temp");
+ SimulateUserInputChangeForElement(&email_element, "temp@google.com");
+ SimulatePasswordChange("random");
static_cast<content::RenderFrameObserver*>(password_autofill_agent_)
->WillSendSubmitEvent(username_element_.form());
static_cast<content::RenderFrameObserver*>(password_autofill_agent_)
@@ -1900,13 +1823,9 @@ TEST_F(PasswordAutofillAgentTest, FindingUsernameWithAutofillPredictions) {
LoadHTML(kFormHTMLWithTwoTextFields);
UpdateUsernameAndPasswordElements();
blink::WebInputElement email_element = GetInputElementByID(kEmailName);
- SimulateInputChangeForElement("temp", true, GetMainFrame(), username_element_,
- true);
- SimulateInputChangeForElement("temp@google.com", true, GetMainFrame(),
- email_element, true);
- SimulateInputChangeForElement("random", true, GetMainFrame(),
- password_element_, true);
-
+ SimulateUsernameChange("temp");
+ SimulateUserInputChangeForElement(&email_element, "temp@google.com");
+ SimulatePasswordChange("random");
// Find FormData for visible password form.
blink::WebFormElement form_element = username_element_.form();
FormData form_data;
diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
index 3d0a401..801f0d2 100644
--- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
@@ -19,6 +19,7 @@
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebWidget.h"
+#include "ui/events/keycodes/keyboard_codes.h"
using blink::WebDocument;
using blink::WebElement;
@@ -309,29 +310,18 @@ TEST_F(PasswordGenerationAgentTest, EditingTest) {
EXPECT_EQ(password, second_password_element.value());
// After editing the first field they are still the same.
- base::string16 edited_password = base::ASCIIToUTF16("edited_password");
- first_password_element.setValue(edited_password);
- // Cast to WebAutofillClient where textFieldDidChange() is public.
- static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
- first_password_element);
- // textFieldDidChange posts a task, so we need to wait until it's been
- // processed.
- base::MessageLoop::current()->RunUntilIdle();
+ std::string edited_password_ascii = "edited_password";
+ SimulateUserInputChangeForElement(&first_password_element,
+ edited_password_ascii);
+ base::string16 edited_password = base::ASCIIToUTF16(edited_password_ascii);
EXPECT_EQ(edited_password, first_password_element.value());
EXPECT_EQ(edited_password, second_password_element.value());
// Verify that password mirroring works correctly even when the password
// is deleted.
- base::string16 empty_password;
- first_password_element.setValue(empty_password);
- // Cast to WebAutofillClient where textFieldDidChange() is public.
- static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
- first_password_element);
- // textFieldDidChange posts a task, so we need to wait until it's been
- // processed.
- base::MessageLoop::current()->RunUntilIdle();
- EXPECT_EQ(empty_password, first_password_element.value());
- EXPECT_EQ(empty_password, second_password_element.value());
+ SimulateUserInputChangeForElement(&first_password_element, std::string());
+ EXPECT_EQ(base::string16(), first_password_element.value());
+ EXPECT_EQ(base::string16(), second_password_element.value());
}
TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
@@ -394,15 +384,9 @@ TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) {
WebInputElement first_password_element = element.to<WebInputElement>();
// Make a password just under maximum offer size.
- first_password_element.setValue(
- base::ASCIIToUTF16(
- std::string(password_generation_->kMaximumOfferSize - 1, 'a')));
- // Cast to WebAutofillClient where textFieldDidChange() is public.
- static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
- first_password_element);
- // textFieldDidChange posts a task, so we need to wait until it's been
- // processed.
- base::MessageLoop::current()->RunUntilIdle();
+ SimulateUserInputChangeForElement(
+ &first_password_element,
+ std::string(password_generation_->kMaximumOfferSize - 1, 'a'));
// There should now be a message to show the UI.
ASSERT_EQ(1u, password_generation_->messages().size());
EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
@@ -410,15 +394,8 @@ TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) {
password_generation_->clear_messages();
// Simulate a user typing a password just over maximum offer size.
- first_password_element.setValue(
- base::ASCIIToUTF16(
- std::string(password_generation_->kMaximumOfferSize + 1, 'a')));
- // Cast to WebAutofillClient where textFieldDidChange() is public.
- static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
- first_password_element);
- // textFieldDidChange posts a task, so we need to wait until it's been
- // processed.
- base::MessageLoop::current()->RunUntilIdle();
+ SimulateUserTypingASCIICharacter('a', false);
+ SimulateUserTypingASCIICharacter('a', true);
// There should now be a message to hide the UI.
ASSERT_EQ(1u, password_generation_->messages().size());
EXPECT_EQ(AutofillHostMsg_HidePasswordGenerationPopup::ID,
@@ -427,15 +404,7 @@ TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) {
// Simulate the user deleting characters. The generation popup should be shown
// again.
- first_password_element.setValue(
- base::ASCIIToUTF16(
- std::string(password_generation_->kMaximumOfferSize, 'a')));
- // Cast to WebAutofillClient where textFieldDidChange() is public.
- static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
- first_password_element);
- // textFieldDidChange posts a task, so we need to wait until it's been
- // processed.
- base::MessageLoop::current()->RunUntilIdle();
+ SimulateUserTypingASCIICharacter(ui::VKEY_BACK, true);
// There should now be a message to show the UI.
ASSERT_EQ(1u, password_generation_->messages().size());
EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 3106833..16099c0 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -4,6 +4,7 @@
#include "components/autofill/content/renderer/autofill_agent.h"
+#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
@@ -43,6 +44,7 @@
#include "third_party/WebKit/public/web/WebNode.h"
#include "third_party/WebKit/public/web/WebOptionElement.h"
#include "third_party/WebKit/public/web/WebTextAreaElement.h"
+#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/keycodes/keyboard_codes.h"
@@ -62,6 +64,7 @@ using blink::WebNode;
using blink::WebOptionElement;
using blink::WebString;
using blink::WebTextAreaElement;
+using blink::WebUserGestureIndicator;
using blink::WebVector;
namespace autofill {
@@ -149,7 +152,6 @@ AutofillAgent::AutofillAgent(content::RenderFrame* render_frame,
autofill_query_id_(0),
was_query_node_autofilled_(false),
has_shown_autofill_popup_for_current_edit_(false),
- did_set_node_text_(false),
ignore_text_changes_(false),
is_popup_possibly_visible_(false),
weak_ptr_factory_(this) {
@@ -378,15 +380,12 @@ void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) {
}
void AutofillAgent::textFieldDidChange(const WebFormControlElement& element) {
+ DCHECK(toWebInputElement(&element) || IsTextAreaElement(element));
if (ignore_text_changes_)
return;
- DCHECK(toWebInputElement(&element) || IsTextAreaElement(element));
-
- if (did_set_node_text_) {
- did_set_node_text_ = false;
+ if (!WebUserGestureIndicator::isProcessingUserGesture())
return;
- }
// We post a task for doing the Autofill as the caret position is not set
// properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and
@@ -714,7 +713,7 @@ void AutofillAgent::QueryAutofillSuggestions(
void AutofillAgent::FillFieldWithValue(const base::string16& value,
WebInputElement* node) {
- did_set_node_text_ = true;
+ base::AutoReset<bool> auto_reset(&ignore_text_changes_, true);
node->setEditingValue(value.substr(0, node->maxLength()));
}
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h
index 4ec475c..39d6573 100644
--- a/components/autofill/content/renderer/autofill_agent.h
+++ b/components/autofill/content/renderer/autofill_agent.h
@@ -252,9 +252,6 @@ class AutofillAgent : public content::RenderFrameObserver,
// currently editing? Used to keep track of state for metrics logging.
bool has_shown_autofill_popup_for_current_edit_;
- // If true we just set the node text so we shouldn't show the popup.
- bool did_set_node_text_;
-
// Whether or not to ignore text changes. Useful for when we're committing
// a composition when we are defocusing the WebView and we don't want to
// trigger an autofill popup to show.
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 1e1a048..626b887 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -4,6 +4,8 @@
#include "content/public/test/render_view_test.h"
+#include <cctype>
+
#include "base/run_loop.h"
#include "components/scheduler/renderer/renderer_scheduler.h"
#include "content/common/dom_storage/dom_storage_types.h"
@@ -27,13 +29,16 @@
#include "content/test/test_content_client.h"
#include "third_party/WebKit/public/platform/WebScreenInfo.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"
+#include "third_party/WebKit/public/web/WebInputElement.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "third_party/WebKit/public/web/WebKit.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/base/resource/resource_bundle.h"
+#include "ui/events/keycodes/keyboard_codes.h"
#include "v8/include/v8.h"
#if defined(OS_MACOSX)
@@ -49,6 +54,7 @@ using blink::WebString;
using blink::WebURLRequest;
namespace {
+
const int32 kOpenerId = -2;
const int32 kRouteId = 5;
const int32 kMainFrameRouteId = 6;
@@ -56,6 +62,32 @@ const int32 kNewWindowRouteId = 7;
const int32 kNewFrameRouteId = 10;
const int32 kSurfaceId = 42;
+// Converts |ascii_character| into |key_code| and returns true on success.
+// Handles only the characters needed by tests.
+bool GetWindowsKeyCode(char ascii_character, int* key_code) {
+ if (isalnum(ascii_character)) {
+ *key_code = base::ToUpperASCII(ascii_character);
+ return true;
+ }
+
+ switch (ascii_character) {
+ case '@':
+ *key_code = '2';
+ return true;
+ case '_':
+ *key_code = ui::VKEY_OEM_MINUS;
+ return true;
+ case '.':
+ *key_code = ui::VKEY_OEM_PERIOD;
+ return true;
+ case ui::VKEY_BACK:
+ *key_code = ui::VKEY_BACK;
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace
namespace content {
@@ -395,6 +427,55 @@ void RenderViewTest::Resize(gfx::Size new_size,
OnMessageReceived(*resize_message);
}
+void RenderViewTest::SimulateUserTypingASCIICharacter(char ascii_character,
+ bool flush_message_loop) {
+ blink::WebKeyboardEvent event;
+ event.text[0] = ascii_character;
+ ASSERT_TRUE(GetWindowsKeyCode(ascii_character, &event.windowsKeyCode));
+ if (isupper(ascii_character) || ascii_character == '@' ||
+ ascii_character == '_') {
+ event.modifiers = blink::WebKeyboardEvent::ShiftKey;
+ }
+
+ event.type = blink::WebKeyboardEvent::RawKeyDown;
+ SendWebKeyboardEvent(event);
+
+ event.type = blink::WebKeyboardEvent::Char;
+ SendWebKeyboardEvent(event);
+
+ event.type = blink::WebKeyboardEvent::KeyUp;
+ SendWebKeyboardEvent(event);
+
+ if (flush_message_loop) {
+ // Processing is delayed because of a Blink bug:
+ // https://bugs.webkit.org/show_bug.cgi?id=16976 See
+ // PasswordAutofillAgent::TextDidChangeInTextField() for details.
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+}
+
+void RenderViewTest::SimulateUserInputChangeForElement(
+ blink::WebInputElement* input,
+ const std::string& new_value) {
+ ASSERT_TRUE(base::IsStringASCII(new_value));
+ while (!input->focused())
+ input->document().frame()->view()->advanceFocus(false);
+
+ size_t previous_length = input->value().length();
+ for (size_t i = 0; i < previous_length; ++i)
+ SimulateUserTypingASCIICharacter(ui::VKEY_BACK, false);
+
+ EXPECT_TRUE(input->value().utf8().empty());
+ for (size_t i = 0; i < new_value.size(); ++i)
+ SimulateUserTypingASCIICharacter(new_value[i], false);
+
+ // Compare only beginning, because autocomplete may have filled out the
+ // form.
+ EXPECT_EQ(new_value, input->value().utf8().substr(0, new_value.length()));
+
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
bool RenderViewTest::OnMessageReceived(const IPC::Message& msg) {
RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
return impl->OnMessageReceived(msg);
diff --git a/content/public/test/render_view_test.h b/content/public/test/render_view_test.h
index d3fd776..91d0df6 100644
--- a/content/public/test/render_view_test.h
+++ b/content/public/test/render_view_test.h
@@ -22,6 +22,7 @@
struct ViewMsg_Resize_Params;
namespace blink {
+class WebInputElement;
class WebWidget;
}
@@ -99,7 +100,7 @@ class RenderViewTest : public testing::Test {
void SendWebKeyboardEvent(const blink::WebKeyboardEvent& key_event);
// Send a raw mouse event to the renderer.
- void SendWebMouseEvent(const blink::WebMouseEvent& key_event);
+ void SendWebMouseEvent(const blink::WebMouseEvent& mouse_event);
// Returns the bounds (coordinates and size) of the element with id
// |element_id|. Returns an empty rect if such an element was not found.
@@ -130,6 +131,18 @@ class RenderViewTest : public testing::Test {
gfx::Rect resizer_rect,
bool is_fullscreen);
+ // Simulates typing the |ascii_character| into this render view. Also accepts
+ // ui::VKEY_BACK for backspace. Will flush the message loop if
+ // |flush_message_loop| is true.
+ void SimulateUserTypingASCIICharacter(char ascii_character,
+ bool flush_message_loop);
+
+ // Simulates user focusing |input|, erasing all text, and typing the
+ // |new_value| instead. Will process input events for autofill. This is a user
+ // gesture.
+ void SimulateUserInputChangeForElement(blink::WebInputElement* input,
+ const std::string& new_value);
+
// These are all methods from RenderViewImpl that we expose to testing code.
bool OnMessageReceived(const IPC::Message& msg);
void DidNavigateWithinPage(blink::WebLocalFrame* frame,