blob: b8371dc58c6a0a80fcf70f62a5fe2cbb533a9f14 [file] [log] [blame]
// Copyright 2018 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 "build/build_config.h"
#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
#include "content/browser/web_contents/web_contents_impl.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/shell/browser/shell.h"
#include "third_party/blink/public/platform/web_input_event.h"
#include "ui/events/base_event_utils.h"
using blink::WebInputEvent;
namespace {
const std::string kBrowserFlingDataURL = 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>
document.title='ready';
</script>)HTML";
const std::string kTouchActionFilterDataURL = R"HTML(
<!DOCTYPE html>
<meta name='viewport' content='width=device-width'/>
<style>
body {
height: 10000px;
touch-action: pan-y;
}
</style>
<script>
document.title='ready';
</script>)HTML";
} // namespace
namespace content {
class BrowserSideFlingBrowserTest : public ContentBrowserTest {
public:
BrowserSideFlingBrowserTest() {}
~BrowserSideFlingBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitchASCII("--enable-blink-features",
"MiddleClickAutoscroll");
}
void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
run_loop_->Quit();
}
protected:
RenderWidgetHostImpl* GetWidgetHost() {
return RenderWidgetHostImpl::From(
shell()->web_contents()->GetRenderViewHost()->GetWidget());
}
void LoadURL(const std::string& page_data) {
const GURL data_url("data:text/html," + page_data);
NavigateToURL(shell(), data_url);
RenderWidgetHostImpl* host = GetWidgetHost();
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());
MainThreadFrameObserver main_thread_sync(host);
main_thread_sync.Wait();
}
void SimulateMiddleClick(int x, int y, int modifiers) {
// Simulate and send middle click mouse down.
blink::WebMouseEvent down_event = SyntheticWebMouseEventBuilder::Build(
blink::WebInputEvent::kMouseDown, x, y, modifiers);
down_event.button = blink::WebMouseEvent::Button::kMiddle;
down_event.SetTimeStamp(ui::EventTimeForNow());
down_event.SetPositionInScreen(x, y);
GetWidgetHost()->ForwardMouseEvent(down_event);
// Simulate and send middle click mouse up.
blink::WebMouseEvent up_event = SyntheticWebMouseEventBuilder::Build(
blink::WebInputEvent::kMouseUp, x, y, modifiers);
up_event.button = blink::WebMouseEvent::Button::kMiddle;
up_event.SetTimeStamp(ui::EventTimeForNow());
up_event.SetPositionInScreen(x, y);
GetWidgetHost()->ForwardMouseEvent(up_event);
}
std::unique_ptr<base::RunLoop> run_loop_;
private:
DISALLOW_COPY_AND_ASSIGN(BrowserSideFlingBrowserTest);
};
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchscreenFling) {
LoadURL(kBrowserFlingDataURL);
// Send a GSB to start scrolling sequence.
blink::WebGestureEvent gesture_scroll_begin(
blink::WebGestureEvent::kGestureScrollBegin,
blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
gesture_scroll_begin.SetSourceDevice(blink::kWebGestureDeviceTouchscreen);
gesture_scroll_begin.data.scroll_begin.delta_hint_units =
blink::WebGestureEvent::ScrollUnits::kPrecisePixels;
gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
gesture_scroll_begin.data.scroll_begin.delta_y_hint = -5.f;
GetWidgetHost()->ForwardGestureEvent(gesture_scroll_begin);
// Send a GFS and wait for the page to scroll making sure that fling progress
// has started.
blink::WebGestureEvent gesture_fling_start(
blink::WebGestureEvent::kGestureFlingStart,
blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
gesture_fling_start.SetSourceDevice(blink::kWebGestureDeviceTouchscreen);
gesture_fling_start.data.fling_start.velocity_x = 0.f;
gesture_fling_start.data.fling_start.velocity_y = -2000.f;
GetWidgetHost()->ForwardGestureEvent(gesture_fling_start);
RenderFrameSubmissionObserver observer(
GetWidgetHost()->render_frame_metadata_provider());
gfx::Vector2dF default_scroll_offset;
while (observer.LastRenderFrameMetadata()
.root_scroll_offset.value_or(default_scroll_offset)
.y() <= 0) {
observer.WaitForMetadataChange();
}
}
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchpadFling) {
LoadURL(kBrowserFlingDataURL);
// Send a wheel event to start scrolling sequence.
auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::kMouseWheel);
blink::WebMouseWheelEvent wheel_event =
SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, -53, 0, true);
wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
GetWidgetHost()->ForwardWheelEvent(wheel_event);
input_msg_watcher->WaitForAck();
// Send a GFS and wait for the page to scroll more than 60 pixels making sure
// that fling progress has started.
blink::WebGestureEvent gesture_fling_start(
blink::WebGestureEvent::kGestureFlingStart,
blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
gesture_fling_start.SetSourceDevice(blink::kWebGestureDeviceTouchpad);
gesture_fling_start.data.fling_start.velocity_x = 0.f;
gesture_fling_start.data.fling_start.velocity_y = -2000.f;
GetWidgetHost()->ForwardGestureEvent(gesture_fling_start);
RenderFrameSubmissionObserver observer(
GetWidgetHost()->render_frame_metadata_provider());
gfx::Vector2dF default_scroll_offset;
while (observer.LastRenderFrameMetadata()
.root_scroll_offset.value_or(default_scroll_offset)
.y() <= 60) {
observer.WaitForMetadataChange();
}
}
// TODO(sahel): This test is flaky https://crbug.com/838769
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, DISABLED_AutoscrollFling) {
LoadURL(kBrowserFlingDataURL);
// Start autoscroll with middle click.
auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::kGestureScrollBegin);
SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
input_msg_watcher->WaitForAck();
// The page should start scrolling with mouse move.
RenderFrameSubmissionObserver observer(
GetWidgetHost()->render_frame_metadata_provider());
blink::WebMouseEvent move_event = SyntheticWebMouseEventBuilder::Build(
blink::WebInputEvent::kMouseMove, 30, 30,
blink::WebInputEvent::kNoModifiers);
move_event.SetTimeStamp(ui::EventTimeForNow());
move_event.SetPositionInScreen(30, 30);
GetWidgetHost()->ForwardMouseEvent(move_event);
gfx::Vector2dF default_scroll_offset;
while (observer.LastRenderFrameMetadata()
.root_scroll_offset.value_or(default_scroll_offset)
.y() <= 0) {
observer.WaitForMetadataChange();
}
}
#if !defined(OS_ANDROID)
#define MAYBE_WheelScrollingWorksAfterAutoscrollCancel \
WheelScrollingWorksAfterAutoscrollCancel
#else
#define MAYBE_WheelScrollingWorksAfterAutoscrollCancel \
DISABLED_WheelScrollingWorksAfterAutoscrollCancel
#endif
// Checks that wheel scrolling works after autoscroll cancelation.
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
MAYBE_WheelScrollingWorksAfterAutoscrollCancel) {
LoadURL(kBrowserFlingDataURL);
// Start autoscroll with middle click.
auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::kGestureScrollBegin);
SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
input_msg_watcher->WaitForAck();
// Without moving the mouse cancel the autoscroll fling with another click.
input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::kGestureScrollEnd);
SimulateMiddleClick(10, 10, blink::WebInputEvent::kNoModifiers);
input_msg_watcher->WaitForAck();
// The mouse wheel scrolling must work after autoscroll cancellation.
RenderFrameSubmissionObserver observer(
GetWidgetHost()->render_frame_metadata_provider());
blink::WebMouseWheelEvent wheel_event =
SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, -53, 0, true);
wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
GetWidgetHost()->ForwardWheelEvent(wheel_event);
gfx::Vector2dF default_scroll_offset;
while (observer.LastRenderFrameMetadata()
.root_scroll_offset.value_or(default_scroll_offset)
.y() <= 0) {
observer.WaitForMetadataChange();
}
}
// Disabled on MacOS because it doesn't support touchscreen scroll.
#if defined(OS_MACOSX)
#define MAYBE_ScrollEndGeneratedForFilteredFling \
DISABLED_ScrollEndGeneratedForFilteredFling
#else
// Flaky, see https://crbug.com/850455
#define MAYBE_ScrollEndGeneratedForFilteredFling \
DISABLED_ScrollEndGeneratedForFilteredFling
#endif
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
MAYBE_ScrollEndGeneratedForFilteredFling) {
LoadURL(kTouchActionFilterDataURL);
// Necessary for checking the ACK source of the sent events. The events are
// filtered when the Browser is the source.
auto scroll_begin_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::kGestureScrollBegin);
auto fling_start_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::kGestureFlingStart);
auto scroll_end_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::kGestureScrollEnd);
// Do a horizontal touchscreen scroll followed by a fling. The GFS must get
// filtered since the GSB is filtered.
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
params.anchor = gfx::PointF(10, 10);
params.distances.push_back(gfx::Vector2d(-60, 0));
params.prevent_fling = false;
run_loop_ = std::make_unique<base::RunLoop>();
std::unique_ptr<SyntheticSmoothScrollGesture> gesture(
new SyntheticSmoothScrollGesture(params));
GetWidgetHost()->QueueSyntheticGesture(
std::move(gesture),
base::BindOnce(&BrowserSideFlingBrowserTest::OnSyntheticGestureCompleted,
base::Unretained(this)));
// Runs until we get the OnSyntheticGestureCompleted callback.
run_loop_->Run();
scroll_begin_watcher->GetAckStateWaitIfNecessary();
EXPECT_EQ(InputEventAckSource::BROWSER,
scroll_begin_watcher->last_event_ack_source());
fling_start_watcher->GetAckStateWaitIfNecessary();
EXPECT_EQ(InputEventAckSource::BROWSER,
fling_start_watcher->last_event_ack_source());
// Since the GFS is filtered. the input_router_impl will generate and forward
// a GSE to make sure that the scrolling sequence and the touch action filter
// state get reset properly. The generated GSE will also get filtered since
// its equivalent GSB is filtered. The test will timeout if the GSE is not
// generated.
scroll_end_watcher->GetAckStateWaitIfNecessary();
EXPECT_EQ(InputEventAckSource::BROWSER,
scroll_end_watcher->last_event_ack_source());
}
} // namespace content