Skip to content

Commit

Permalink
Rewrite hr-timestamp/input-events.html to a browser test
Browse files Browse the repository at this point in the history
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: David Bokan <bokan@chromium.org>
Reviewed-by: Majid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#790868}
  • Loading branch information
LanWei22 authored and Commit Bot committed Jul 22, 2020
1 parent 6f8564e commit 4f6da93
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 50 deletions.
319 changes: 319 additions & 0 deletions content/browser/renderer_host/input/input_event_browsertest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
// 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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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());
Expand Down
1 change: 1 addition & 0 deletions content/test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 10 additions & 0 deletions third_party/blink/renderer/core/loader/document_load_timing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions third_party/blink/renderer/core/testing/internals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
4 changes: 4 additions & 0 deletions third_party/blink/renderer/core/testing/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
Loading

0 comments on commit 4f6da93

Please sign in to comment.