Commit c9d15e1b authored by gogerald's avatar gogerald Committed by Commit Bot

[Autofill Assistant] Wait for the document to become interactive before issuing click

The target element may be there but may not be interactive.

Bug: 806868
Change-Id: Ia89f7e52421cf61ffa87271565b6a5ed75603d42
Reviewed-on: https://chromium-review.googlesource.com/c/1476544
Commit-Queue: Ganggui Tang <gogerald@chromium.org>
Reviewed-by: default avatarStephane Zermatten <szermatt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#633751}
parent e692b51d
...@@ -34,12 +34,12 @@ namespace autofill_assistant { ...@@ -34,12 +34,12 @@ namespace autofill_assistant {
using autofill::ContentAutofillDriver; using autofill::ContentAutofillDriver;
namespace { namespace {
// Time between two periodic box model checks. // Time between two periodic box model and document ready state checks.
static constexpr base::TimeDelta kPeriodicBoxModelCheckInterval = static constexpr base::TimeDelta kPeriodicCheckInterval =
base::TimeDelta::FromMilliseconds(200); base::TimeDelta::FromMilliseconds(200);
// Timeout after roughly 10 seconds (50*200ms). // Timeout after roughly 10 seconds (50*200ms).
constexpr int kPeriodicBoxModelCheckRounds = 50; constexpr int kPeriodicCheckRounds = 50;
// Expiration time for the Autofill Assistant cookie. // Expiration time for the Autofill Assistant cookie.
constexpr int kCookieExpiresSeconds = 600; constexpr int kCookieExpiresSeconds = 600;
...@@ -153,6 +153,13 @@ const char* const kQuerySelectorAll = ...@@ -153,6 +153,13 @@ const char* const kQuerySelectorAll =
return undefined; return undefined;
})"; })";
// Javascript code to query whether the document is ready for interact.
const char* const kIsDocumentReadyForInteract =
R"(function () {
return document.readyState == 'interactive'
|| document.readyState == 'complete';
})";
bool ConvertPseudoType(const PseudoType pseudo_type, bool ConvertPseudoType(const PseudoType pseudo_type,
dom::PseudoType* pseudo_type_output) { dom::PseudoType* pseudo_type_output) {
switch (pseudo_type) { switch (pseudo_type) {
...@@ -220,7 +227,7 @@ void WebController::ElementPositionGetter::Start( ...@@ -220,7 +227,7 @@ void WebController::ElementPositionGetter::Start(
devtools_client_ = devtools_client; devtools_client_ = devtools_client;
object_id_ = element_object_id; object_id_ = element_object_id;
callback_ = std::move(callback); callback_ = std::move(callback);
remaining_rounds_ = kPeriodicBoxModelCheckRounds; remaining_rounds_ = kPeriodicCheckRounds;
// Wait for a roundtrips through the renderer and compositor pipeline, // Wait for a roundtrips through the renderer and compositor pipeline,
// otherwise touch event may be dropped because of missing handler. // otherwise touch event may be dropped because of missing handler.
...@@ -268,17 +275,15 @@ void WebController::ElementPositionGetter::OnGetBoxModelForStableCheck( ...@@ -268,17 +275,15 @@ void WebController::ElementPositionGetter::OnGetBoxModelForStableCheck(
round((round((*content_box)[3]) + round((*content_box)[5])) * 0.5); round((round((*content_box)[3]) + round((*content_box)[5])) * 0.5);
// Wait for at least three rounds (~600ms = // Wait for at least three rounds (~600ms =
// 3*kPeriodicBoxModelCheckInterval) for visual state update callback since // 3*kPeriodicCheckInterval) for visual state update callback since
// it might take longer time to return or never return if no updates. // it might take longer time to return or never return if no updates.
DCHECK(kPeriodicBoxModelCheckRounds > 2 && DCHECK(kPeriodicCheckRounds > 2 && kPeriodicCheckRounds >= remaining_rounds_);
kPeriodicBoxModelCheckRounds >= remaining_rounds_);
if (has_point_ && new_point_x == point_x_ && new_point_y == point_y_ && if (has_point_ && new_point_x == point_x_ && new_point_y == point_y_ &&
(visual_state_updated_ || (visual_state_updated_ || remaining_rounds_ + 2 < kPeriodicCheckRounds)) {
remaining_rounds_ + 2 < kPeriodicBoxModelCheckRounds)) {
// Note that there is still a chance that the element's position has been // Note that there is still a chance that the element's position has been
// changed after the last call of GetBoxModel, however, it might be safe // changed after the last call of GetBoxModel, however, it might be safe
// to assume the element's position will not be changed before issuing // to assume the element's position will not be changed before issuing
// click or tap event after stable for kPeriodicBoxModelCheckInterval. In // click or tap event after stable for kPeriodicCheckInterval. In
// addition, checking again after issuing click or tap event doesn't help // addition, checking again after issuing click or tap event doesn't help
// since the change may be expected. // since the change may be expected.
OnResult(new_point_x, new_point_y); OnResult(new_point_x, new_point_y);
...@@ -319,7 +324,7 @@ void WebController::ElementPositionGetter::OnGetBoxModelForStableCheck( ...@@ -319,7 +324,7 @@ void WebController::ElementPositionGetter::OnGetBoxModelForStableCheck(
base::BindOnce( base::BindOnce(
&WebController::ElementPositionGetter::GetAndWaitBoxModelStable, &WebController::ElementPositionGetter::GetAndWaitBoxModelStable,
weak_ptr_factory_.GetWeakPtr()), weak_ptr_factory_.GetWeakPtr()),
kPeriodicBoxModelCheckInterval); kPeriodicCheckInterval);
} }
void WebController::ElementPositionGetter::OnScrollIntoView( void WebController::ElementPositionGetter::OnScrollIntoView(
...@@ -336,7 +341,7 @@ void WebController::ElementPositionGetter::OnScrollIntoView( ...@@ -336,7 +341,7 @@ void WebController::ElementPositionGetter::OnScrollIntoView(
base::BindOnce( base::BindOnce(
&WebController::ElementPositionGetter::GetAndWaitBoxModelStable, &WebController::ElementPositionGetter::GetAndWaitBoxModelStable,
weak_ptr_factory_.GetWeakPtr()), weak_ptr_factory_.GetWeakPtr()),
kPeriodicBoxModelCheckInterval); kPeriodicCheckInterval);
} }
void WebController::ElementPositionGetter::OnResult(int x, int y) { void WebController::ElementPositionGetter::OnResult(int x, int y) {
...@@ -423,7 +428,26 @@ void WebController::OnFindElementForClickOrTap( ...@@ -423,7 +428,26 @@ void WebController::OnFindElementForClickOrTap(
return; return;
} }
ClickOrTapElement(std::move(result), is_a_click, std::move(callback)); std::string element_object_id = result->object_id;
WaitForDocumentToBecomeInteractive(
kPeriodicCheckRounds, element_object_id,
base::BindOnce(
&WebController::OnWaitDocumentToBecomeInteractiveForClickOrTap,
weak_ptr_factory_.GetWeakPtr(), std::move(callback), is_a_click,
std::move(result)));
}
void WebController::OnWaitDocumentToBecomeInteractiveForClickOrTap(
base::OnceCallback<void(bool)> callback,
bool is_a_click,
std::unique_ptr<FindElementResult> target_element,
bool result) {
if (!result) {
OnResult(false, std::move(callback));
return;
}
ClickOrTapElement(std::move(target_element), is_a_click, std::move(callback));
} }
void WebController::ClickOrTapElement( void WebController::ClickOrTapElement(
...@@ -1553,4 +1577,48 @@ void WebController::ClearCookie() { ...@@ -1553,4 +1577,48 @@ void WebController::ClearCookie() {
base::DoNothing()); base::DoNothing());
} }
void WebController::WaitForDocumentToBecomeInteractive(
int remaining_rounds,
std::string object_id,
base::OnceCallback<void(bool)> callback) {
devtools_client_->GetRuntime()->CallFunctionOn(
runtime::CallFunctionOnParams::Builder()
.SetObjectId(object_id)
.SetFunctionDeclaration(std::string(kIsDocumentReadyForInteract))
.SetReturnByValue(true)
.Build(),
base::BindOnce(&WebController::OnWaitForDocumentToBecomeInteractive,
weak_ptr_factory_.GetWeakPtr(), remaining_rounds,
object_id, std::move(callback)));
}
void WebController::OnWaitForDocumentToBecomeInteractive(
int remaining_rounds,
std::string object_id,
base::OnceCallback<void(bool)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result) {
if (!result || !result->GetResult() || result->HasExceptionDetails() ||
remaining_rounds <= 0) {
DVLOG(1) << __func__
<< " Failed to wait for the document to become interactive with "
"remaining_rounds: "
<< remaining_rounds;
std::move(callback).Run(false);
return;
}
DCHECK(result->GetResult()->GetValue()->is_bool());
if (result->GetResult()->GetValue()->GetBool()) {
std::move(callback).Run(true);
return;
}
base::PostDelayedTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&WebController::WaitForDocumentToBecomeInteractive,
weak_ptr_factory_.GetWeakPtr(), --remaining_rounds,
object_id, std::move(callback)),
kPeriodicCheckInterval);
}
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -267,6 +267,11 @@ class WebController { ...@@ -267,6 +267,11 @@ class WebController {
void OnFindElementForClickOrTap(base::OnceCallback<void(bool)> callback, void OnFindElementForClickOrTap(base::OnceCallback<void(bool)> callback,
bool is_a_click, bool is_a_click,
std::unique_ptr<FindElementResult> result); std::unique_ptr<FindElementResult> result);
void OnWaitDocumentToBecomeInteractiveForClickOrTap(
base::OnceCallback<void(bool)> callback,
bool is_a_click,
std::unique_ptr<FindElementResult> target_element,
bool result);
void OnFindElementForTap(base::OnceCallback<void(bool)> callback, void OnFindElementForTap(base::OnceCallback<void(bool)> callback,
std::unique_ptr<FindElementResult> result); std::unique_ptr<FindElementResult> result);
void ClickOrTapElement(std::unique_ptr<FindElementResult> target_element, void ClickOrTapElement(std::unique_ptr<FindElementResult> target_element,
...@@ -435,11 +440,9 @@ class WebController { ...@@ -435,11 +440,9 @@ class WebController {
void OnGetOuterHtml( void OnGetOuterHtml(
base::OnceCallback<void(bool, const std::string&)> callback, base::OnceCallback<void(bool, const std::string&)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result); std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnFindElementForPosition( void OnFindElementForPosition(
base::OnceCallback<void(bool, const RectF&)> callback, base::OnceCallback<void(bool, const RectF&)> callback,
std::unique_ptr<FindElementResult> result); std::unique_ptr<FindElementResult> result);
void OnGetElementPositionResult( void OnGetElementPositionResult(
base::OnceCallback<void(bool, const RectF&)> callback, base::OnceCallback<void(bool, const RectF&)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result); std::unique_ptr<runtime::CallFunctionOnResult> result);
...@@ -457,6 +460,17 @@ class WebController { ...@@ -457,6 +460,17 @@ class WebController {
void OnHasCookie(base::OnceCallback<void(bool)> callback, void OnHasCookie(base::OnceCallback<void(bool)> callback,
std::unique_ptr<network::GetCookiesResult> result); std::unique_ptr<network::GetCookiesResult> result);
// Waits for the document.readyState to be 'interactive' or 'complete'.
void WaitForDocumentToBecomeInteractive(
int remaining_rounds,
std::string object_id,
base::OnceCallback<void(bool)> callback);
void OnWaitForDocumentToBecomeInteractive(
int remaining_rounds,
std::string object_id,
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.
content::WebContents* web_contents_; content::WebContents* web_contents_;
......
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