Commit 58e39354 authored by Hui(Andy) Wu's avatar Hui(Andy) Wu Committed by Commit Bot

Reland "[autofill assistant] Introduce small delay between key presses."

This is a reland of 012bea0a, with flacky
test deleted.

Original change's description:
> [autofill assistant] Introduce small delay between key presses.
>
> Introduce a small delay between key presses while simulate_key_presses
> is true. This hopefully will allow any JS from the website to have time
> to process the events.
>
> Bug: b/124505757
> Change-Id: I3ab90bde3434043ae9988a9e0b7ff43fdf6e86fb
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1567920
> Commit-Queue: Hui Wu <wuandy@chromium.org>
> Reviewed-by: Clemens Arbesser <arbesser@google.com>
> Cr-Commit-Position: refs/heads/master@{#652013}

Bug: b/124505757
Change-Id: Ieb9153190ec7c80fc4debde93b3159df6f70ce88
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1573642Reviewed-by: default avatarClemens Arbesser <arbesser@google.com>
Commit-Queue: Hui Wu <wuandy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#652249}
parent e24dc38a
......@@ -169,6 +169,7 @@ class ActionDelegate {
const Selector& selector,
const std::string& value,
bool simulate_key_presses,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) = 0;
// Set the |value| of the |attribute| of the element given by |selector|.
......@@ -183,6 +184,7 @@ class ActionDelegate {
virtual void SendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) = 0;
// Return the outerHTML of an element given by |selector|.
......
......@@ -252,6 +252,7 @@ void AutofillAction::SetFallbackFieldValuesSequentially(
Selector(required_fields.Get(required_fields_index).element()),
fallback_value,
required_fields.Get(required_fields_index).simulate_key_presses(),
required_fields.Get(required_fields_index).delay_in_millisecond(),
base::BindOnce(&AutofillAction::OnSetFallbackFieldValue,
weak_ptr_factory_.GetWeakPtr(), delegate,
required_fields_index));
......
......@@ -116,6 +116,7 @@ class MockActionDelegate : public ActionDelegate {
void SetFieldValue(const Selector& selector,
const std::string& value,
bool ignored_simulate_key_presses,
int ignored_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
OnSetFieldValue(selector, value, callback);
}
......@@ -131,9 +132,10 @@ class MockActionDelegate : public ActionDelegate {
const std::string& value,
base::OnceCallback<void(const ClientStatus&)> callback));
MOCK_METHOD3(SendKeyboardInput,
MOCK_METHOD4(SendKeyboardInput,
void(const Selector& selector,
const std::vector<UChar32>& codepoints,
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback));
MOCK_METHOD2(GetOuterHtml,
void(const Selector& selector,
......
......@@ -69,6 +69,7 @@ void SetFormFieldValueAction::OnSetFieldValue(ActionDelegate* delegate,
const auto& key_field = proto_.set_form_value().value(next);
bool simulate_key_presses = proto_.set_form_value().simulate_key_presses();
int delay_in_millisecond = proto_.set_form_value().delay_in_millisecond();
switch (key_field.keypress_case()) {
case SetFormFieldValueProto_KeyPress::kText:
if (simulate_key_presses || key_field.text().empty()) {
......@@ -77,6 +78,7 @@ void SetFormFieldValueAction::OnSetFieldValue(ActionDelegate* delegate,
// to next value after |SetFieldValue| is done.
delegate->SetFieldValue(
selector, key_field.text(), simulate_key_presses,
delay_in_millisecond,
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), delegate,
std::move(callback), selector,
......@@ -85,6 +87,7 @@ void SetFormFieldValueAction::OnSetFieldValue(ActionDelegate* delegate,
// Trigger a check for keyboard fallback when |SetFieldValue| is done.
delegate->SetFieldValue(
selector, key_field.text(), simulate_key_presses,
delay_in_millisecond,
base::BindOnce(
&SetFormFieldValueAction::OnSetFieldValueAndCheckFallback,
weak_ptr_factory_.GetWeakPtr(), delegate, std::move(callback),
......@@ -98,7 +101,7 @@ void SetFormFieldValueAction::OnSetFieldValue(ActionDelegate* delegate,
// You should use the `keyboard_input' field instead.
if (key_field.keycode() < 128) { // US-ASCII
delegate->SendKeyboardInput(
selector, {key_field.keycode()},
selector, {key_field.keycode()}, delay_in_millisecond,
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), delegate,
std::move(callback), selector,
......@@ -115,6 +118,7 @@ void SetFormFieldValueAction::OnSetFieldValue(ActionDelegate* delegate,
case SetFormFieldValueProto_KeyPress::kKeyboardInput:
delegate->SendKeyboardInput(
selector, UTF8ToUnicode(key_field.keyboard_input()),
delay_in_millisecond,
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), delegate,
std::move(callback), selector,
......@@ -171,6 +175,7 @@ void SetFormFieldValueAction::OnGetFieldValue(ActionDelegate* delegate,
// afterwards.
delegate->SetFieldValue(
selector, key_field.text(), /*simulate_key_presses = */ true,
proto_.set_form_value().delay_in_millisecond(),
base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
weak_ptr_factory_.GetWeakPtr(), delegate,
std::move(callback), selector,
......
......@@ -349,9 +349,11 @@ void ScriptExecutor::SetFieldValue(
const Selector& selector,
const std::string& value,
bool simulate_key_presses,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
delegate_->GetWebController()->SetFieldValue(
selector, value, simulate_key_presses, std::move(callback));
selector, value, simulate_key_presses, key_press_delay_in_millisecond,
std::move(callback));
}
void ScriptExecutor::SetAttribute(
......@@ -366,9 +368,11 @@ void ScriptExecutor::SetAttribute(
void ScriptExecutor::SendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
delegate_->GetWebController()->SendKeyboardInput(selector, codepoints,
std::move(callback));
delegate_->GetWebController()->SendKeyboardInput(
selector, codepoints, key_press_delay_in_millisecond,
std::move(callback));
}
void ScriptExecutor::GetOuterHtml(
......
......@@ -144,6 +144,7 @@ class ScriptExecutor : public ActionDelegate,
const Selector& selector,
const std::string& value,
bool simulate_key_presses,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void SetAttribute(
const Selector& selector,
......@@ -153,6 +154,7 @@ class ScriptExecutor : public ActionDelegate,
void SendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) override;
void GetOuterHtml(
const Selector& selector,
......
......@@ -691,6 +691,9 @@ message UseAddressProto {
// Whether we should simulate actual key presses when filling |element| with
// its corresponding value.
optional bool simulate_key_presses = 3;
// Delay between two key presses when simlulating.
optional int32 delay_in_millisecond = 4 [default = 20];
}
// An optional name to allow to handle multiple addresses selection (for
......@@ -1027,6 +1030,9 @@ message SetFormFieldValueProto {
// Whether to send key press events when setting values to HTML fields.
optional bool simulate_key_presses = 5;
// Delay between two key presses when simlulating.
optional int32 delay_in_millisecond = 6 [default = 20];
}
// Set an element attribute to a specific value.
......
......@@ -1450,6 +1450,7 @@ void WebController::SetFieldValue(
const Selector& selector,
const std::string& value,
bool simulate_key_presses,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
DVLOG(3) << __func__ << " " << selector << ", value=" << value
<< ", simulate_key_presses=" << simulate_key_presses;
......@@ -1461,7 +1462,8 @@ void WebController::SetFieldValue(
selector, "",
base::BindOnce(&WebController::OnClearFieldForSendKeyboardInput,
weak_ptr_factory_.GetWeakPtr(), selector,
UTF8ToUnicode(value), std::move(callback)));
UTF8ToUnicode(value), key_press_delay_in_millisecond,
std::move(callback)));
return;
}
InternalSetFieldValue(selector, value, std::move(callback));
......@@ -1481,47 +1483,65 @@ void WebController::InternalSetFieldValue(
void WebController::OnClearFieldForSendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& clear_status) {
if (!clear_status.ok()) {
std::move(callback).Run(clear_status);
return;
}
SendKeyboardInput(selector, codepoints, std::move(callback));
SendKeyboardInput(selector, codepoints, key_press_delay_in_millisecond,
std::move(callback));
}
void WebController::OnClickElementForSendKeyboardInput(
const std::vector<UChar32>& codepoints,
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& click_status) {
if (!click_status.ok()) {
std::move(callback).Run(click_status);
return;
}
DispatchKeyboardTextDownEvent(codepoints, 0, std::move(callback));
DispatchKeyboardTextDownEvent(codepoints, 0, /*delay=*/false,
delay_in_millisecond, std::move(callback));
}
void WebController::DispatchKeyboardTextDownEvent(
const std::vector<UChar32>& codepoints,
size_t index,
bool delay,
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
if (index >= codepoints.size()) {
std::move(callback).Run(OkClientStatus());
return;
}
if (delay && delay_in_millisecond > 0) {
base::PostDelayedTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&WebController::DispatchKeyboardTextDownEvent,
weak_ptr_factory_.GetWeakPtr(), codepoints, index,
/*delay=*/false, delay_in_millisecond,
std::move(callback)),
base::TimeDelta::FromMilliseconds(delay_in_millisecond));
return;
}
devtools_client_->GetInput()->DispatchKeyEvent(
CreateKeyEventParamsForCharacter(
autofill_assistant::input::DispatchKeyEventType::KEY_DOWN,
codepoints[index]),
base::BindOnce(&WebController::DispatchKeyboardTextUpEvent,
weak_ptr_factory_.GetWeakPtr(), codepoints, index,
std::move(callback)));
delay_in_millisecond, std::move(callback)));
}
void WebController::DispatchKeyboardTextUpEvent(
const std::vector<UChar32>& codepoints,
size_t index,
int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
DCHECK_LT(index, codepoints.size());
devtools_client_->GetInput()->DispatchKeyEvent(
......@@ -1530,6 +1550,7 @@ void WebController::DispatchKeyboardTextUpEvent(
codepoints[index]),
base::BindOnce(&WebController::DispatchKeyboardTextDownEvent,
weak_ptr_factory_.GetWeakPtr(), codepoints, index + 1,
/*delay=*/true, delay_in_millisecond,
std::move(callback)));
}
......@@ -1651,6 +1672,7 @@ void WebController::OnSetAttribute(
void WebController::SendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
const int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback) {
if (VLOG_IS_ON(3)) {
std::string input_str;
......@@ -1661,16 +1683,18 @@ void WebController::SendKeyboardInput(
}
DCHECK(!selector.empty());
FindElement(selector,
/* strict_mode= */ true,
base::BindOnce(&WebController::OnFindElementForSendKeyboardInput,
weak_ptr_factory_.GetWeakPtr(), selector,
codepoints, std::move(callback)));
FindElement(
selector,
/* strict_mode= */ true,
base::BindOnce(&WebController::OnFindElementForSendKeyboardInput,
weak_ptr_factory_.GetWeakPtr(), selector, codepoints,
delay_in_millisecond, std::move(callback)));
}
void WebController::OnFindElementForSendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
const int delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status,
std::unique_ptr<FindElementResult> element_result) {
......@@ -1681,7 +1705,7 @@ void WebController::OnFindElementForSendKeyboardInput(
ClickElement(selector, base::BindOnce(
&WebController::OnClickElementForSendKeyboardInput,
weak_ptr_factory_.GetWeakPtr(), codepoints,
std::move(callback)));
delay_in_millisecond, std::move(callback)));
}
void WebController::GetOuterHtml(
......
......@@ -121,6 +121,7 @@ class WebController {
const Selector& selector,
const std::string& value,
bool simulate_key_presses,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback);
// Set the |value| of the |attribute| of the element given by |selector|.
......@@ -131,11 +132,13 @@ class WebController {
base::OnceCallback<void(const ClientStatus&)> callback);
// Sets the keyboard focus to |selector| and inputs |codepoints|, one
// character at a time.
// character at a time. Key presses will have a delay of |delay_in_milli|
// between them.
// Returns the result through |callback|.
virtual void SendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int delay_in_milli,
base::OnceCallback<void(const ClientStatus&)> callback);
// Return the outerHTML of |selector|.
......@@ -345,19 +348,24 @@ class WebController {
void OnClearFieldForSendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int key_press_delay_in_millisecond,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status);
void OnClickElementForSendKeyboardInput(
const std::vector<UChar32>& codepoints,
int delay_in_milli,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& click_status);
void DispatchKeyboardTextDownEvent(
const std::vector<UChar32>& codepoints,
size_t index,
bool delay,
int delay_in_milli,
base::OnceCallback<void(const ClientStatus&)> callback);
void DispatchKeyboardTextUpEvent(
const std::vector<UChar32>& codepoints,
size_t index,
int delay_in_milli,
base::OnceCallback<void(const ClientStatus&)> callback);
void OnFindElementForSetAttribute(
const std::vector<std::string>& attribute,
......@@ -370,6 +378,7 @@ class WebController {
void OnFindElementForSendKeyboardInput(
const Selector& selector,
const std::vector<UChar32>& codepoints,
int delay_in_milli,
base::OnceCallback<void(const ClientStatus&)> callback,
const ClientStatus& status,
std::unique_ptr<FindElementResult> element_result);
......
......@@ -332,7 +332,7 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
base::RunLoop run_loop;
ClientStatus result;
web_controller_->SetFieldValue(
selector, value, simulate_key_presses,
selector, value, simulate_key_presses, 0,
base::BindOnce(&WebControllerBrowserTest::OnSetFieldValue,
base::Unretained(this), run_loop.QuitClosure(),
&result));
......@@ -348,11 +348,12 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
}
ClientStatus SendKeyboardInput(const Selector& selector,
const std::vector<UChar32>& codepoints) {
const std::vector<UChar32>& codepoints,
int delay_in_milli) {
base::RunLoop run_loop;
ClientStatus result;
web_controller_->SendKeyboardInput(
selector, codepoints,
selector, codepoints, delay_in_milli,
base::BindOnce(&WebControllerBrowserTest::OnSendKeyboardInput,
base::Unretained(this), run_loop.QuitClosure(),
&result));
......@@ -360,6 +361,11 @@ class WebControllerBrowserTest : public content::ContentBrowserTest,
return result;
}
ClientStatus SendKeyboardInput(const Selector& selector,
const std::vector<UChar32>& codepoints) {
return SendKeyboardInput(selector, codepoints, -1);
}
void OnSendKeyboardInput(const base::Closure& done_callback,
ClientStatus* result_output,
const ClientStatus& status) {
......@@ -928,6 +934,25 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
GetFieldsValue(selectors, {expected_output});
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
SendKeyboardInputSetsKeyPropertyWithTimeout) {
// Sends input keys to a field where JS will intercept KeyDown and
// at index 3 it will set a timeout whick inserts an space after the
// timeout. If key press delay is enabled, it should handle this
// correctly.
auto input = UTF8ToUnicode("012345");
std::string expected_output = "012 345";
std::vector<Selector> selectors;
Selector a_selector;
a_selector.selectors.emplace_back("#input_js_event_with_timeout");
selectors.emplace_back(a_selector);
EXPECT_EQ(ACTION_APPLIED,
SendKeyboardInput(a_selector, input, /*delay_in_milli*/ 100)
.proto_status());
GetFieldsValue(selectors, {expected_output});
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SetAttribute) {
Selector selector;
std::vector<std::string> attribute;
......
......@@ -174,6 +174,21 @@ found in the LICENSE file.
</script>
</div>
<div>
<input id="input_js_event_with_timeout" type="text">
<!-- Uses the 'key' property of a keydown event. -->
<script>
var t = document.getElementById("input_js_event_with_timeout");
t.addEventListener('keydown', e => {
var value = e.target.value;
if (value.length === 3) {
e.preventDefault();
setTimeout(() => {e.target.value = value + ' ' + e.key}, 10);
}
});
</script>
</div>
<div id="testOuterHtml"><span>Span</span><p>Paragraph</p></div>
<div id="hidden" style="display: none;">This text is hidden</div>
......
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