Commit b18f54c0 authored by Mathias Carlen's avatar Mathias Carlen Committed by Commit Bot

[Autofill Assistant] Implement focus on element.

R=gogerald@chromium.org

Bug: 806868
Change-Id: I6e2b277d3083645c7967cc3a1a481c8bf645eebd
Reviewed-on: https://chromium-review.googlesource.com/1233739Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Commit-Queue: Ganggui Tang <gogerald@chromium.org>
Cr-Commit-Position: refs/heads/master@{#592433}
parent 6b1291f7
...@@ -84,7 +84,7 @@ void WebController::OnScrollIntoView( ...@@ -84,7 +84,7 @@ void WebController::OnScrollIntoView(
std::unique_ptr<runtime::CallFunctionOnResult> result) { std::unique_ptr<runtime::CallFunctionOnResult> result) {
devtools_client_->GetRuntime()->Disable(); devtools_client_->GetRuntime()->Disable();
if (!result || result->HasExceptionDetails()) { if (!result || result->HasExceptionDetails()) {
DLOG(ERROR) << "Failed to scroll the element into view to click."; DLOG(ERROR) << "Failed to scroll the element.";
OnResult(false, std::move(callback)); OnResult(false, std::move(callback));
return; return;
} }
...@@ -295,6 +295,42 @@ void WebController::OnResult(bool result, ...@@ -295,6 +295,42 @@ void WebController::OnResult(bool result,
std::move(callback).Run(result); std::move(callback).Run(result);
} }
void WebController::OnFindElementForFocusElement(
base::OnceCallback<void(bool)> callback,
std::string object_id) {
if (object_id.empty()) {
DLOG(ERROR) << "Failed to find the element to focus on.";
OnResult(false, std::move(callback));
return;
}
std::vector<std::unique_ptr<runtime::CallArgument>> argument;
argument.emplace_back(
runtime::CallArgument::Builder().SetObjectId(object_id).Build());
devtools_client_->GetRuntime()->Enable();
devtools_client_->GetRuntime()->CallFunctionOn(
runtime::CallFunctionOnParams::Builder()
.SetObjectId(object_id)
.SetArguments(std::move(argument))
.SetFunctionDeclaration(std::string(kScrollIntoViewScript))
.SetReturnByValue(true)
.Build(),
base::BindOnce(&WebController::OnFocusElement,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void WebController::OnFocusElement(
base::OnceCallback<void(bool)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result) {
devtools_client_->GetRuntime()->Disable();
if (!result || result->HasExceptionDetails()) {
DLOG(ERROR) << "Failed to focus on element.";
OnResult(false, std::move(callback));
return;
}
OnResult(true, std::move(callback));
}
void WebController::FillAddressForm(const std::string& guid, void WebController::FillAddressForm(const std::string& guid,
const std::vector<std::string>& selectors, const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) { base::OnceCallback<void(bool)> callback) {
...@@ -318,8 +354,11 @@ void WebController::SelectOption(const std::vector<std::string>& selectors, ...@@ -318,8 +354,11 @@ void WebController::SelectOption(const std::vector<std::string>& selectors,
void WebController::FocusElement(const std::vector<std::string>& selectors, void WebController::FocusElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) { base::OnceCallback<void(bool)> callback) {
// TODO(crbug.com/806868): Implement focus on element operation. DCHECK(!selectors.empty());
std::move(callback).Run(true); FindElement(
selectors,
base::BindOnce(&WebController::OnFindElementForFocusElement,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
} }
void WebController::GetFieldsValue( void WebController::GetFieldsValue(
......
...@@ -122,6 +122,10 @@ class WebController { ...@@ -122,6 +122,10 @@ class WebController {
base::OnceCallback<void(std::string)> callback, base::OnceCallback<void(std::string)> callback,
std::unique_ptr<dom::PushNodesByBackendIdsToFrontendResult> result); std::unique_ptr<dom::PushNodesByBackendIdsToFrontendResult> result);
void OnResult(bool result, base::OnceCallback<void(bool)> callback); void OnResult(bool result, base::OnceCallback<void(bool)> callback);
void OnFindElementForFocusElement(base::OnceCallback<void(bool)> callback,
std::string object_id);
void OnFocusElement(base::OnceCallback<void(bool)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result);
// Weak pointer is fine here since it must outlive this web controller, which // Weak pointer is fine here since it must outlive this web controller, which
// is guaranteed by the owner of this object. // is guaranteed by the owner of this object.
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "components/autofill_assistant/browser/web_controller.h" #include "components/autofill_assistant/browser/web_controller.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h" #include "content/shell/browser/shell.h"
...@@ -83,6 +84,20 @@ class WebControllerBrowserTest : public content::ContentBrowserTest { ...@@ -83,6 +84,20 @@ class WebControllerBrowserTest : public content::ContentBrowserTest {
} }
} }
void FocusElement(const std::vector<std::string>& selectors) {
base::RunLoop run_loop;
web_controller_->FocusElement(
selectors,
base::BindOnce(&WebControllerBrowserTest::OnFocusElement,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
}
void OnFocusElement(base::Closure done_callback, bool result) {
ASSERT_TRUE(result);
std::move(done_callback).Run();
}
private: private:
std::unique_ptr<net::EmbeddedTestServer> http_server_; std::unique_ptr<net::EmbeddedTestServer> http_server_;
std::unique_ptr<autofill_assistant::WebController> web_controller_; std::unique_ptr<autofill_assistant::WebController> web_controller_;
...@@ -141,4 +156,52 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ...@@ -141,4 +156,52 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
selectors.emplace_back("#button"); selectors.emplace_back("#button");
WaitForElementRemove(selectors); WaitForElementRemove(selectors);
} }
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, FocusElement) {
std::vector<std::string> selectors;
selectors.emplace_back("#iframe");
selectors.emplace_back("#focus");
// The element is not visible initially.
const std::string checkNotVisibleScript = R"(
let iframe = document.querySelector("#iframe");
let div = iframe.contentDocument.querySelector("#focus");
let iframeRect = iframe.getBoundingClientRect();
let divRect = div.getBoundingClientRect();
iframeRect.y + divRect.y > window.innerHeight;
)";
EXPECT_EQ(true, content::EvalJs(shell(), checkNotVisibleScript));
FocusElement(selectors);
// Verify that the scroll moved the div in the iframe into view.
const std::string checkVisibleScript = R"(
const scrollTimeoutMs = 500;
var timer = null;
function check() {
let iframe = document.querySelector("#iframe");
let div = iframe.contentDocument.querySelector("#focus");
let iframeRect = iframe.getBoundingClientRect();
let divRect = div.getBoundingClientRect();
return iframeRect.y + divRect.y < window.innerHeight;
}
function onScrollDone() {
window.removeEventListener("scroll", onScroll);
domAutomationController.send(check());
}
function onScroll(e) {
if (timer != null) {
clearTimeout(timer);
}
timer = setTimeout(onScrollDone, scrollTimeoutMs);
}
if (check()) {
// Scrolling finished before this script started. Just return the result.
domAutomationController.send(true);
} else {
window.addEventListener("scroll", onScroll);
}
)";
EXPECT_EQ(true, content::EvalJsWithManualReply(shell(), checkVisibleScript));
}
} // namespace } // namespace
...@@ -25,6 +25,12 @@ found in the LICENSE file. ...@@ -25,6 +25,12 @@ found in the LICENSE file.
button.parentNode.removeChild(button); button.parentNode.removeChild(button);
} }
</script> </script>
<style>
#full_height_section {
height: 100vh;
}
</style>
</head> </head>
<body onload="createShadowDom()"> <body onload="createShadowDom()">
...@@ -58,5 +64,15 @@ found in the LICENSE file. ...@@ -58,5 +64,15 @@ found in the LICENSE file.
<hr> <hr>
<div id="shadowsection"></div> <div id="shadowsection"></div>
<!--
Intentionally make this section has the full height of the window
to force scroll when operating on the elements below not in this
section.
-->
<div id="full_height_section">
<p>Blank Section
</div>
<div id="focus">Hidden Text</div>
</body> </body>
</html> </html>
\ No newline at end of file
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