blob: f63cdee678d7ff1676ed0dfb0b09e1fc7c2f74ad [file] [log] [blame]
// 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_NEAR(event_time_ms, monotonic_time, 1);
}
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_NEAR(event_time_ms, monotonic_time, 1);
}
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_NEAR(event_time_ms, monotonic_time, 1);
}
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_NEAR(event_time_ms, monotonic_time, 1);
}
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_NEAR(event_time_ms, monotonic_time, 1);
}
} // namespace content