Commit e30da482 authored by Joel Einbinder's avatar Joel Einbinder Committed by Commit Bot

DevTools: Wait for a frame before sending touch and wheel events

Some user input (wheel and touch events) can be discarded by the
compositor if nothing appears to be listening. When automating the
browser, the input could be sent immediately after configuring the
DOM to listen for it.

Change-Id: I680a802051b7843f2de93f4aed2b24315aa24d53
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2437695
Commit-Queue: Joel Einbinder <einbinder@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814533}
parent fb2a6cbc
...@@ -714,13 +714,34 @@ void InputHandler::DispatchMouseEvent( ...@@ -714,13 +714,34 @@ void InputHandler::DispatchMouseEvent(
callback->sendFailure(Response::InternalError()); callback->sendFailure(Response::InternalError());
return; return;
} }
widget_host->delegate()
->GetInputEventRouter() auto findWidgetAndDispatchEvent = base::BindOnce(
->GetRenderWidgetHostAtPointAsynchronously( [](base::WeakPtr<InputHandler> self, RenderWidgetHostImpl* widget_host,
widget_host->GetView(), CssPixelsToPointF(x, y, page_scale_factor_), double x, double y, std::unique_ptr<blink::WebMouseEvent> mouse_event,
base::BindOnce(&InputHandler::OnWidgetForDispatchMouseEvent, blink::WebMouseWheelEvent* wheel_event,
weak_factory_.GetWeakPtr(), std::move(callback), std::unique_ptr<DispatchMouseEventCallback> callback, bool success) {
std::move(mouse_event), wheel_event)); if (!self.get())
return;
widget_host->delegate()
->GetInputEventRouter()
->GetRenderWidgetHostAtPointAsynchronously(
widget_host->GetView(),
CssPixelsToPointF(x, y, self->page_scale_factor_),
base::BindOnce(&InputHandler::OnWidgetForDispatchMouseEvent,
self, std::move(callback),
std::move(mouse_event), wheel_event));
},
weak_factory_.GetWeakPtr(), widget_host, x, y, std::move(mouse_event),
wheel_event, std::move(callback));
// We make sure the compositor is up to date before
// sending a wheel event. Otherwise it wont be
// picked up by newly added event listeners on the main thread.
if (wheel_event) {
widget_host->InsertVisualStateCallback(
std::move(findWidgetAndDispatchEvent));
} else {
std::move(findWidgetAndDispatchEvent).Run(true);
}
} }
void InputHandler::OnWidgetForDispatchMouseEvent( void InputHandler::OnWidgetForDispatchMouseEvent(
...@@ -884,14 +905,23 @@ void InputHandler::DispatchWebTouchEvent( ...@@ -884,14 +905,23 @@ void InputHandler::DispatchWebTouchEvent(
return; return;
} }
gfx::PointF original(events[0].touches[0].PositionInWidget()); // We make sure the compositor is up to date before
widget_host->delegate() // sending a touch event. Otherwise it wont be
->GetInputEventRouter() // picked up by newly added event listeners on the main thread.
->GetRenderWidgetHostAtPointAsynchronously( widget_host->InsertVisualStateCallback(base::BindOnce(
widget_host->GetView(), original, [](base::WeakPtr<InputHandler> self, RenderWidgetHostImpl* widget_host,
base::BindOnce(&InputHandler::OnWidgetForDispatchWebTouchEvent, std::vector<blink::WebTouchEvent> events,
weak_factory_.GetWeakPtr(), std::move(callback), std::unique_ptr<DispatchTouchEventCallback> callback, bool success) {
std::move(events))); gfx::PointF original(events[0].touches[0].PositionInWidget());
widget_host->delegate()
->GetInputEventRouter()
->GetRenderWidgetHostAtPointAsynchronously(
widget_host->GetView(), original,
base::BindOnce(&InputHandler::OnWidgetForDispatchWebTouchEvent,
self, std::move(callback), std::move(events)));
},
weak_factory_.GetWeakPtr(), widget_host, std::move(events),
std::move(callback)));
} }
void InputHandler::OnWidgetForDispatchWebTouchEvent( void InputHandler::OnWidgetForDispatchWebTouchEvent(
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host.h"
#include "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h" #include "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h"
#include "third_party/blink/renderer/platform/widget/widget_base.h" #include "third_party/blink/renderer/platform/widget/widget_base.h"
#include "third_party/blink/renderer/platform/widget/widget_base_client.h"
namespace blink { namespace blink {
...@@ -97,6 +98,11 @@ void WidgetCompositor::CreateQueueSwapPromise( ...@@ -97,6 +98,11 @@ void WidgetCompositor::CreateQueueSwapPromise(
// be aborted early and the swap promises will be broken (see // be aborted early and the swap promises will be broken (see
// EarlyOut_NoUpdates). // EarlyOut_NoUpdates).
LayerTreeHost()->SetNeedsAnimateIfNotInsideMainFrame(); LayerTreeHost()->SetNeedsAnimateIfNotInsideMainFrame();
// In web tests the request does not actually cause a commit, because the
// compositor is scheduled by the test runner to avoid flakiness. So for
// this case we must request a main frame.
widget_base_->client()->ScheduleAnimationForWebTests();
} else if (compositor_task_runner_) { } else if (compositor_task_runner_) {
// Delete callbacks on the compositor thread. // Delete callbacks on the compositor thread.
compositor_task_runner_->PostTask( compositor_task_runner_->PostTask(
......
Tests that Input.dispatchTouchEvent waits for a frame before comitting.
Dispatching event
touchEventPromise for has not resolved yet
Paused on debugger statement
Resumed
Recieved ack
Order of events:
requestAnimationFrame
setTimeout
touchstart
(async function(testRunner) {
let {session, dp} = await testRunner.startBlank(`Tests that Input.dispatchTouchEvent waits for a frame before comitting.`);
function dumpError(message) {
if (message.error)
testRunner.log('Error: ' + message.error.message);
}
let resolved = false;
await dp.Debugger.enable();
testRunner.log('Dispatching event');
await session.evaluate(`
window.logs = [];
requestAnimationFrame(() => {
logs.push('requestAnimationFrame');
setTimeout(() => logs.push('setTimeout'), 0);
debugger;
});
window.addEventListener('touchstart', () => logs.push('touchstart'));
`);
let touchEventPromise = dp.Input.dispatchTouchEvent({
type: 'touchStart',
touchPoints: [{
x: 100,
y: 100
}]
});
touchEventPromise.then(() => resolved = true);
await dp.Debugger.oncePaused();
await new Promise(x => setTimeout(x, 100)); // wait a bit to see if the touchEventPromise will resolve early
testRunner.log(resolved ? `touchEventPromise was resolved too early` : `touchEventPromise for has not resolved yet`)
testRunner.log('Paused on debugger statement');
await dp.Debugger.resume();
testRunner.log('Resumed');
dumpError(await touchEventPromise);
testRunner.log(`Recieved ack`);
testRunner.log(`Order of events:`);
testRunner.log(await session.evaluate(`window.logs.join('\\n')`));
testRunner.completeTest();
})
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