Commit 6a3f8d9a authored by rouslan's avatar rouslan Committed by Commit bot

[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}
parent 992492e6
......@@ -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()
......
......@@ -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;
......
......@@ -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,
......
......@@ -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()));
}
......
......@@ -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.
......
......@@ -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);
......
......@@ -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,
......
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