Commit 4f6da930 authored by Lan Wei's avatar Lan Wei Committed by Commit Bot

Rewrite hr-timestamp/input-events.html to a browser test

hr-timestamp/input-events.htmlEvent timestamp tests that the received
events on the page should be equal to the timestamp provided by the
eventSender's recorded last event. But we should deprecate eventSender in
the future, and GpuBenchmarking sends events based on the current system
time, which cannot be equal to the received events' timestamp.


Bug: 1047176
Change-Id: I225dec07b1928ae254d61e8a8384ebfc7dab75e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2173421
Commit-Queue: Lan Wei <lanwei@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Reviewed-by: default avatarMajid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#790868}
parent 6f8564e7
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/input/synthetic_gesture_controller.h"
#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
#include "content/browser/renderer_host/input/synthetic_pointer_driver.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_base.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/hit_test_region_observer.h"
#include "content/shell/browser/shell.h"
#include "content/shell/common/shell_switches.h"
#include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "ui/events/blink/blink_features.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
namespace {
const std::string kEventListenerDataURL = R"HTML(
<!DOCTYPE html>
<meta name='viewport' content='width=device-width'/>
<style>
html, body {
margin: 0;
}
.spacer { height: 10000px; }
</style>
<div class=spacer></div>
<script type="text/javascript">
window.eventCounts =
{mousedown: 0, keydown: 0, touchstart: 0, click: 0, wheel: 0};
window.eventTimeStamp =
{mousedown: 0, keydown: 0, touchstart: 0, click: 0, wheel: 0};
function recordEvent(e) {
eventCounts[e.type]++;
if (eventCounts[e.type] == 1)
eventTimeStamp[e.type] = e.timeStamp;
}
for (var evt in eventCounts) {
document.addEventListener(evt, recordEvent);
}
document.title='ready';
</script>)HTML";
} // namespace
namespace content {
class InputEventBrowserTest : public ContentBrowserTest {
public:
InputEventBrowserTest() = default;
~InputEventBrowserTest() override = default;
RenderWidgetHostImpl* GetWidgetHost() {
return RenderWidgetHostImpl::From(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
ContentBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kExposeInternalsForTesting);
}
void LoadURL(const std::string& page_data) {
const GURL data_url("data:text/html," + page_data);
EXPECT_TRUE(NavigateToURL(shell(), data_url));
RenderWidgetHostImpl* host = GetWidgetHost();
frame_observer_ = std::make_unique<RenderFrameSubmissionObserver>(
host->render_frame_metadata_provider());
host->GetView()->SetSize(gfx::Size(400, 400));
base::string16 ready_title(base::ASCIIToUTF16("ready"));
TitleWatcher watcher(shell()->web_contents(), ready_title);
ignore_result(watcher.WaitAndGetTitle());
// We need to wait until hit test data is available. We use our own
// HitTestRegionObserver here because we have the RenderWidgetHostImpl
// available.
HitTestRegionObserver observer(host->GetFrameSinkId());
observer.WaitForHitTestData();
}
// ContentBrowserTest:
void PostRunTestOnMainThread() override {
// Delete this before the WebContents is destroyed.
frame_observer_.reset();
ContentBrowserTest::PostRunTestOnMainThread();
}
bool URLLoaded() {
base::string16 ready_title(base::ASCIIToUTF16("ready"));
TitleWatcher watcher(shell()->web_contents(), ready_title);
const base::string16 title = watcher.WaitAndGetTitle();
return title == ready_title;
}
int ExecuteScriptAndExtractInt(const std::string& script) {
int value = 0;
EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
shell(), "domAutomationController.send(" + script + ")", &value));
return value;
}
double ExecuteScriptAndExtractDouble(const std::string& script) {
double value = 0;
EXPECT_TRUE(content::ExecuteScriptAndExtractDouble(
shell(), "domAutomationController.send(" + script + ")", &value));
return value;
}
void SimulateSyntheticMousePressAt(base::TimeTicks event_time) {
DCHECK(URLLoaded());
std::unique_ptr<SyntheticPointerDriver> synthetic_pointer_driver =
SyntheticPointerDriver::Create(SyntheticGestureParams::MOUSE_INPUT);
RenderWidgetHostImpl* render_widget_host = GetWidgetHost();
auto* root_view = render_widget_host->GetView()->GetRootView();
std::unique_ptr<SyntheticGestureTarget> synthetic_gesture_target;
if (root_view)
synthetic_gesture_target = root_view->CreateSyntheticGestureTarget();
else
synthetic_gesture_target =
render_widget_host->GetView()->CreateSyntheticGestureTarget();
synthetic_pointer_driver->Press(50, 50, 0,
SyntheticPointerActionParams::Button::LEFT);
synthetic_pointer_driver->DispatchEvent(synthetic_gesture_target.get(),
event_time);
synthetic_pointer_driver->Release(
0, SyntheticPointerActionParams::Button::LEFT);
synthetic_pointer_driver->DispatchEvent(synthetic_gesture_target.get(),
event_time);
}
void SimulateSyntheticKeyDown(base::TimeTicks event_time) {
DCHECK(URLLoaded());
content::NativeWebKeyboardEvent event(
blink::WebKeyboardEvent::Type::kRawKeyDown,
blink::WebInputEvent::kNoModifiers, event_time);
event.windows_key_code = ui::VKEY_DOWN;
event.native_key_code =
ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::ARROW_DOWN);
event.dom_code = static_cast<int>(ui::DomCode::ARROW_DOWN);
event.dom_key = ui::DomKey::ARROW_DOWN;
GetWidgetHost()->ForwardKeyboardEvent(event);
}
void SimulateSyntheticTouchTapAt(base::TimeTicks event_time) {
DCHECK(URLLoaded());
std::unique_ptr<SyntheticPointerDriver> synthetic_pointer_driver =
SyntheticPointerDriver::Create(SyntheticGestureParams::TOUCH_INPUT);
RenderWidgetHostImpl* render_widget_host = GetWidgetHost();
auto* root_view = render_widget_host->GetView()->GetRootView();
std::unique_ptr<SyntheticGestureTarget> synthetic_gesture_target;
if (root_view)
synthetic_gesture_target = root_view->CreateSyntheticGestureTarget();
else
synthetic_gesture_target =
render_widget_host->GetView()->CreateSyntheticGestureTarget();
synthetic_pointer_driver->Press(50, 50, 0,
SyntheticPointerActionParams::Button::LEFT);
synthetic_pointer_driver->DispatchEvent(synthetic_gesture_target.get(),
event_time);
synthetic_pointer_driver->Release(
0, SyntheticPointerActionParams::Button::LEFT);
synthetic_pointer_driver->DispatchEvent(synthetic_gesture_target.get(),
event_time);
}
void SimulateSyntheticWheelScroll(base::TimeTicks event_time) {
DCHECK(URLLoaded());
double x = 50;
double y = 50;
blink::WebMouseWheelEvent wheel_event =
blink::SyntheticWebMouseWheelEventBuilder::Build(
x, y, x, y, 20, 20, 0,
ui::ScrollGranularity::kScrollByPrecisePixel);
wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
wheel_event.SetTimeStamp(event_time);
GetWidgetHost()->ForwardWheelEvent(wheel_event);
}
private:
std::unique_ptr<RenderFrameSubmissionObserver> frame_observer_;
DISALLOW_COPY_AND_ASSIGN(InputEventBrowserTest);
};
#if defined(OS_ANDROID)
// Android does not support synthetic mouse events.
// TODO(lanwei): support dispatching WebMouseEvent in
// SyntheticGestureTargetAndroid.
#define MAYBE_MouseDownEventTimeStamp DISABLED_MouseDownEventTimeStamp
#else
#define MAYBE_MouseDownEventTimeStamp MouseDownEventTimeStamp
#endif
IN_PROC_BROWSER_TEST_F(InputEventBrowserTest, MAYBE_MouseDownEventTimeStamp) {
LoadURL(kEventListenerDataURL);
MainThreadFrameObserver frame_observer(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
base::TimeTicks event_time = base::TimeTicks::Now();
int64_t event_time_ms = event_time.since_origin().InMilliseconds();
SimulateSyntheticMousePressAt(event_time);
while (ExecuteScriptAndExtractInt("eventCounts.mousedown") == 0)
frame_observer.Wait();
int64_t monotonic_time = ExecuteScriptAndExtractDouble(
"internals.zeroBasedDocumentTimeToMonotonicTime(eventTimeStamp."
"mousedown)");
EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.mousedown"));
EXPECT_EQ(event_time_ms, monotonic_time);
}
IN_PROC_BROWSER_TEST_F(InputEventBrowserTest, KeyDownEventTimeStamp) {
LoadURL(kEventListenerDataURL);
MainThreadFrameObserver frame_observer(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
base::TimeTicks event_time = base::TimeTicks::Now();
int64_t event_time_ms = event_time.since_origin().InMilliseconds();
SimulateSyntheticKeyDown(event_time);
while (ExecuteScriptAndExtractInt("eventCounts.keydown") == 0)
frame_observer.Wait();
int64_t monotonic_time = ExecuteScriptAndExtractDouble(
"internals.zeroBasedDocumentTimeToMonotonicTime(eventTimeStamp."
"keydown)");
EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.keydown"));
EXPECT_EQ(event_time_ms, monotonic_time);
}
IN_PROC_BROWSER_TEST_F(InputEventBrowserTest, TouchStartEventTimeStamp) {
LoadURL(kEventListenerDataURL);
MainThreadFrameObserver frame_observer(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
base::TimeTicks event_time = base::TimeTicks::Now();
int64_t event_time_ms = event_time.since_origin().InMilliseconds();
SimulateSyntheticTouchTapAt(event_time);
while (ExecuteScriptAndExtractInt("eventCounts.touchstart") == 0)
frame_observer.Wait();
int64_t monotonic_time = ExecuteScriptAndExtractDouble(
"internals.zeroBasedDocumentTimeToMonotonicTime(eventTimeStamp."
"touchstart)");
EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.touchstart"));
EXPECT_EQ(event_time_ms, monotonic_time);
}
IN_PROC_BROWSER_TEST_F(InputEventBrowserTest, ClickEventTimeStamp) {
LoadURL(kEventListenerDataURL);
MainThreadFrameObserver frame_observer(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
base::TimeTicks event_time = base::TimeTicks::Now();
int64_t event_time_ms = event_time.since_origin().InMilliseconds();
SimulateSyntheticTouchTapAt(event_time);
while (ExecuteScriptAndExtractInt("eventCounts.click") == 0)
frame_observer.Wait();
int64_t monotonic_time = ExecuteScriptAndExtractDouble(
"internals.zeroBasedDocumentTimeToMonotonicTime(eventTimeStamp."
"click)");
EXPECT_EQ(1, ExecuteScriptAndExtractInt("eventCounts.click"));
EXPECT_EQ(event_time_ms, monotonic_time);
}
IN_PROC_BROWSER_TEST_F(InputEventBrowserTest, WheelEventTimeStamp) {
LoadURL(kEventListenerDataURL);
MainThreadFrameObserver frame_observer(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
base::TimeTicks event_time = base::TimeTicks::Now();
int64_t event_time_ms = event_time.since_origin().InMilliseconds();
SimulateSyntheticWheelScroll(event_time);
while (ExecuteScriptAndExtractInt("eventCounts.wheel") == 0)
frame_observer.Wait();
int64_t monotonic_time = ExecuteScriptAndExtractDouble(
"internals.zeroBasedDocumentTimeToMonotonicTime(eventTimeStamp."
"wheel)");
EXPECT_GE(ExecuteScriptAndExtractInt("eventCounts.wheel"), 1);
EXPECT_EQ(event_time_ms, monotonic_time);
}
} // namespace content
......@@ -134,7 +134,7 @@ void SyntheticGestureTargetAura::DispatchWebGestureEventToPlatform(
: ui::EventMomentumPhase::END;
ui::ScrollEvent scroll_event(event_type, web_gesture.PositionInWidget(),
web_gesture.PositionInWidget(),
ui::EventTimeForNow(), flags,
web_gesture.TimeStamp(), flags,
web_gesture.data.fling_start.velocity_x,
web_gesture.data.fling_start.velocity_y, 0, 0, 2,
momentum_phase, ui::ScrollEventPhase::kNone);
......@@ -157,8 +157,8 @@ void SyntheticGestureTargetAura::DispatchWebMouseEventToPlatform(
}
ui::MouseEvent mouse_event(event_type, web_mouse_event.PositionInWidget(),
web_mouse_event.PositionInWidget(),
ui::EventTimeForNow(), flags, changed_button_flags,
pointer_details);
web_mouse_event.TimeStamp(), flags,
changed_button_flags, pointer_details);
aura::Window* window = GetWindow();
mouse_event.ConvertLocationToTarget(window, window->GetRootWindow());
......
......@@ -1036,6 +1036,7 @@ test("content_browsertests") {
"../browser/renderer_host/input/compositor_event_ack_browsertest.cc",
"../browser/renderer_host/input/event_latency_aura_browsertest.cc",
"../browser/renderer_host/input/fling_browsertest.cc",
"../browser/renderer_host/input/input_event_browsertest.cc",
"../browser/renderer_host/input/interaction_mq_dynamic_browsertest.cc",
"../browser/renderer_host/input/main_thread_event_queue_browsertest.cc",
"../browser/renderer_host/input/mouse_latency_browsertest.cc",
......
......@@ -90,6 +90,16 @@ base::TimeDelta DocumentLoadTiming::MonotonicTimeToPseudoWallTime(
return monotonic_time + reference_wall_time_ - reference_monotonic_time_;
}
int64_t DocumentLoadTiming::ZeroBasedDocumentTimeToMonotonicTime(
double dom_event_time) const {
if (reference_monotonic_time_.is_null())
return 0;
base::TimeTicks monotonic_time =
reference_monotonic_time_ +
base::TimeDelta::FromMillisecondsD(dom_event_time);
return monotonic_time.since_origin().InMilliseconds();
}
void DocumentLoadTiming::MarkNavigationStart() {
// Allow the embedder to override navigationStart before we record it if
// they have a more accurate timestamp.
......
......@@ -50,6 +50,7 @@ class CORE_EXPORT DocumentLoadTiming final {
base::TimeDelta MonotonicTimeToZeroBasedDocumentTime(base::TimeTicks) const;
base::TimeDelta MonotonicTimeToPseudoWallTime(base::TimeTicks) const;
int64_t ZeroBasedDocumentTimeToMonotonicTime(double dom_event_time) const;
void MarkNavigationStart();
void SetNavigationStart(base::TimeTicks);
......
......@@ -3317,6 +3317,11 @@ double Internals::monotonicTimeToZeroBasedDocumentTime(
.InSecondsF();
}
int64_t Internals::zeroBasedDocumentTimeToMonotonicTime(double dom_event_time) {
return document_->Loader()->GetTiming().ZeroBasedDocumentTimeToMonotonicTime(
dom_event_time);
}
int64_t Internals::currentTimeTicks() {
return base::TimeTicks::Now().since_origin().InMicroseconds();
}
......
......@@ -543,6 +543,10 @@ class Internals final : public ScriptWrappable {
// document time in seconds
double monotonicTimeToZeroBasedDocumentTime(double, ExceptionState&);
// Translate an event's DOMHighResTimeStamp in seconds into a monotonic time
// in milliseconds.
int64_t zeroBasedDocumentTimeToMonotonicTime(double dom_event_time);
// Returns the current time ticks (in microseconds).
int64_t currentTimeTicks();
......
......@@ -366,6 +366,11 @@
boolean setScrollbarVisibilityInScrollableArea(Node node, boolean visible);
[RaisesException] double monotonicTimeToZeroBasedDocumentTime(double platformTime);
// Translate an event's DOMHighResTimeStamp in seconds into a monotonic time
// in milliseconds.
long long zeroBasedDocumentTimeToMonotonicTime(double domHighResTimeStamp);
long long currentTimeTicks();
DOMString getScrollAnimationState(Node node);
......
<!DOCTYPE html>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script type="text/javascript">
'use strict';
const testCases = {
'mousedown': () => eventSender.mouseDown(),
'keydown': () => eventSender.keyDown('x'),
'touchstart': () => {
eventSender.addTouchPoint(1, 1);
eventSender.touchStart();
},
'click': () => eventSender.gestureTap(1, 1),
'wheel': () => eventSender.mouseScrollBy(0, -50),
};
let receivedEvents = [];
for (let eventName in testCases)
createTest(eventName, testCases[eventName]);
function createTest(eventName, dispatchEventFn) {
async_test(function(t) {
document.addEventListener(eventName, t.step_func(function(e) {
receivedEvents.push(eventName);
// Prevent default to ensure contextmenu is not shown which can
// potentially hijack other events.
e.preventDefault();
const platformTimestamp = eventSender.lastEventTimestamp(); // in seconds
const expectedUnclampedTimestamp = internals.monotonicTimeToZeroBasedDocumentTime(platformTimestamp) * 1000; // in milliseconds
// Time clamping logic in Blink can introduce at most 2*100us of
// difference. Use 0.200001 instead of 0.2 to deal with floating
// point comparison issues.
assert_approx_equals(e.timeStamp, expectedUnclampedTimestamp, 0.200001);
t.done();
}));
dispatchEventFn();
t.step_timeout(function() {
assert_unreached("timeout with received events: " + receivedEvents.join(', '));
}, 5000);
}, "Event timestamp should be equal to the timestamp provided by the platform for " + eventName);
}
</script>
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