blob: 91757d4bdd6bf969a4a27c93ab5c9d52b83afd23 [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 <tuple>
#include "base/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
#include "content/browser/web_contents/web_contents_impl.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/test/content_browser_test_utils_internal.h"
#include "net/dns/mock_host_resolver.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/base/ui_base_features.h"
#include "ui/events/base_event_utils.h"
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(const BrowserSideFlingBrowserTest&) = delete;
BrowserSideFlingBrowserTest& operator=(const BrowserSideFlingBrowserTest&) =
delete;
~BrowserSideFlingBrowserTest() override {}
void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
run_loop_->Quit();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
}
void SetUpCommandLine(base::CommandLine* command_line) override {
IsolateAllSitesForTesting(command_line);
}
protected:
RenderWidgetHostImpl* GetWidgetHost() {
return RenderWidgetHostImpl::From(shell()
->web_contents()
->GetMainFrame()
->GetRenderViewHost()
->GetWidget());
}
void SynchronizeThreads() {
MainThreadFrameObserver observer(GetWidgetHost());
observer.Wait();
}
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();
host->GetView()->SetSize(gfx::Size(400, 400));
std::u16string ready_title(u"ready");
TitleWatcher watcher(shell()->web_contents(), ready_title);
std::ignore = watcher.WaitAndGetTitle();
SynchronizeThreads();
}
void LoadPageWithOOPIF() {
// navigate main frame to URL.
GURL main_url(embedded_test_server()->GetURL(
"a.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
ASSERT_EQ(1U, root->child_count());
// Navigate oopif to URL.
FrameTreeNode* iframe_node = root->child_at(0);
GURL iframe_url(embedded_test_server()->GetURL("b.com", "/tall_page.html"));
{
RenderFrameDeletedObserver deleted_observer(
iframe_node->current_frame_host());
EXPECT_TRUE(NavigateToURLFromRenderer(iframe_node, iframe_url));
deleted_observer.WaitUntilDeleted();
}
// TODO(szager): This is a speculative fix for test flakiness caused by
// changes to render throttling (crbug.com/1158644). The hypothesis is that
// the test code might be initiating a scroll gesture before
// LocalFrameView::BeginLifecycleUpdates() is called in the child frame. By
// the time EvalJsAfterLifecycleUpdate() returns, BeginLifecycleUpdates() is
// guaranteed to have run.
ASSERT_TRUE(
EvalJsAfterLifecycleUpdate(iframe_node->current_frame_host(), "", "")
.error.empty());
WaitForHitTestData(iframe_node->current_frame_host());
ASSERT_EQ(
" Site A ------------ proxies for B\n"
" +--Site B ------- proxies for A\n"
"Where A = http://a.com/\n"
" B = http://b.com/",
DepictFrameTree(*root));
root_view_ = static_cast<RenderWidgetHostViewBase*>(
root->current_frame_host()->GetRenderWidgetHost()->GetView());
child_view_ = static_cast<RenderWidgetHostViewBase*>(
iframe_node->current_frame_host()->GetRenderWidgetHost()->GetView());
}
void SimulateTouchscreenFling(
RenderWidgetHostImpl* render_widget_host,
RenderWidgetHostImpl* parent_render_widget_host = nullptr,
const gfx::Vector2dF& fling_velocity = gfx::Vector2dF(0.f, -2000.f)) {
DCHECK(render_widget_host);
// Send a GSB to start scrolling sequence. In case of scroll bubbling wait
// for the parent to receive the GSB before sending the GFS.
auto input_msg_watcher =
parent_render_widget_host
? std::make_unique<InputMsgWatcher>(
parent_render_widget_host,
blink::WebInputEvent::Type::kGestureScrollBegin)
: std::make_unique<InputMsgWatcher>(
render_widget_host,
blink::WebInputEvent::Type::kGestureScrollBegin);
blink::WebGestureEvent gesture_scroll_begin(
blink::WebGestureEvent::Type::kGestureScrollBegin,
blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
gesture_scroll_begin.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
gesture_scroll_begin.data.scroll_begin.delta_hint_units =
ui::ScrollGranularity::kScrollByPrecisePixel;
gesture_scroll_begin.data.scroll_begin.delta_x_hint = fling_velocity.x();
gesture_scroll_begin.data.scroll_begin.delta_y_hint = fling_velocity.y();
const gfx::PointF scroll_location_in_widget(1, 1);
const gfx::PointF scroll_location_in_root =
child_view_ ? child_view_->TransformPointToRootCoordSpaceF(
scroll_location_in_widget)
: scroll_location_in_widget;
const gfx::PointF scroll_location_in_screen =
child_view_ ? scroll_location_in_root +
root_view_->GetViewBounds().OffsetFromOrigin()
: scroll_location_in_widget;
gesture_scroll_begin.SetPositionInWidget(scroll_location_in_widget);
gesture_scroll_begin.SetPositionInScreen(scroll_location_in_screen);
render_widget_host->ForwardGestureEvent(gesture_scroll_begin);
input_msg_watcher->GetAckStateWaitIfNecessary();
// Send a GFS.
blink::WebGestureEvent gesture_fling_start(
blink::WebGestureEvent::Type::kGestureFlingStart,
blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
gesture_fling_start.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
gesture_fling_start.data.fling_start.velocity_x = fling_velocity.x();
gesture_fling_start.data.fling_start.velocity_y = fling_velocity.y();
gesture_fling_start.SetPositionInWidget(scroll_location_in_widget);
gesture_fling_start.SetPositionInScreen(scroll_location_in_screen);
render_widget_host->ForwardGestureEvent(gesture_fling_start);
}
void SimulateTouchpadFling(
RenderWidgetHostImpl* render_widget_host,
RenderWidgetHostImpl* parent_render_widget_host = nullptr,
const gfx::Vector2dF& fling_velocity = gfx::Vector2dF(0.f, -2000.f)) {
DCHECK(render_widget_host);
// Send a wheel event to start scrolling sequence. In case of scroll
// bubbling wait for the parent to receive the GSB before sending the GFS.
auto input_msg_watcher =
parent_render_widget_host
? std::make_unique<InputMsgWatcher>(
parent_render_widget_host,
blink::WebInputEvent::Type::kGestureScrollBegin)
: std::make_unique<InputMsgWatcher>(
render_widget_host,
blink::WebInputEvent::Type::kGestureScrollBegin);
blink::WebMouseWheelEvent wheel_event =
blink::SyntheticWebMouseWheelEventBuilder::Build(
10, 10, fling_velocity.x() / 1000, fling_velocity.y() / 1000, 0,
ui::ScrollGranularity::kScrollByPrecisePixel);
wheel_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
const gfx::PointF position_in_widget(1, 1);
const gfx::PointF position_in_root =
child_view_
? child_view_->TransformPointToRootCoordSpaceF(position_in_widget)
: position_in_widget;
const gfx::PointF position_in_screen =
child_view_
? position_in_root + root_view_->GetViewBounds().OffsetFromOrigin()
: position_in_widget;
wheel_event.SetPositionInWidget(position_in_widget);
wheel_event.SetPositionInScreen(position_in_screen);
render_widget_host->ForwardWheelEvent(wheel_event);
input_msg_watcher->GetAckStateWaitIfNecessary();
// Send a GFS.
blink::WebGestureEvent gesture_fling_start(
blink::WebGestureEvent::Type::kGestureFlingStart,
blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
gesture_fling_start.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
gesture_fling_start.data.fling_start.velocity_x = fling_velocity.x();
gesture_fling_start.data.fling_start.velocity_y = fling_velocity.y();
gesture_fling_start.SetPositionInWidget(position_in_widget);
gesture_fling_start.SetPositionInScreen(position_in_screen);
render_widget_host->ForwardGestureEvent(gesture_fling_start);
}
void WaitForScroll() {
RenderFrameSubmissionObserver observer(
GetWidgetHost()->render_frame_metadata_provider());
gfx::PointF default_scroll_offset;
// scrollTop > 0 is not enough since the first progressFling is called from
// FlingController::ProcessGestureFlingStart. Wait for scrollTop to exceed
// 100 pixels to make sure that ProgressFling has been called through
// FlingScheduler at least once.
while (observer.LastRenderFrameMetadata()
.root_scroll_offset.value_or(default_scroll_offset)
.y() <= 100) {
observer.WaitForMetadataChange();
}
}
void GiveItSomeTime(int64_t time_delta_ms = 10) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(time_delta_ms));
run_loop.Run();
}
void WaitForFrameScroll(FrameTreeNode* frame_node,
int target_scroll_offset = 100,
bool upward = false) {
DCHECK(frame_node);
double scroll_top =
EvalJs(frame_node->current_frame_host(), "window.scrollY")
.ExtractDouble();
// scrollTop > 0 is not enough since the first progressFling is called from
// FlingController::ProcessGestureFlingStart. Wait for scrollTop to reach
// target_scroll_offset to make sure that ProgressFling has been called
// through FlingScheduler at least once.
while ((upward && scroll_top > target_scroll_offset) ||
(!upward && scroll_top < target_scroll_offset)) {
GiveItSomeTime();
scroll_top = EvalJs(frame_node->current_frame_host(), "window.scrollY")
.ExtractDouble();
}
}
FrameTreeNode* GetRootNode() {
return static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
}
FrameTreeNode* GetChildNode() {
FrameTreeNode* root = GetRootNode();
return root->child_at(0);
}
std::unique_ptr<base::RunLoop> run_loop_;
raw_ptr<RenderWidgetHostViewBase> child_view_ = nullptr;
raw_ptr<RenderWidgetHostViewBase> root_view_ = nullptr;
};
// On Mac we don't have any touchscreen/touchpad fling events (GFS/GFC).
// Instead, the OS keeps sending wheel events when the user lifts their fingers
// from touchpad.
#if !defined(OS_MAC)
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchscreenFling) {
LoadURL(kBrowserFlingDataURL);
SimulateTouchscreenFling(GetWidgetHost());
WaitForScroll();
}
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchpadFling) {
LoadURL(kBrowserFlingDataURL);
SimulateTouchpadFling(GetWidgetHost());
WaitForScroll();
}
IN_PROC_BROWSER_TEST_F(
BrowserSideFlingBrowserTest,
EarlyTouchscreenFlingCancelationOnInertialGSUAckNotConsumed) {
LoadURL(kBrowserFlingDataURL);
// Fling upward and wait for the generated GSE to arrive. Then check that the
// RWHV has stopped the fling.
auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::Type::kGestureScrollEnd);
gfx::Vector2d fling_velocity(0, 2000);
SimulateTouchscreenFling(
GetWidgetHost(), nullptr /*parent_render_widget_host*/, fling_velocity);
input_msg_watcher->GetAckStateWaitIfNecessary();
EXPECT_TRUE(GetWidgetHost()->GetView()->view_stopped_flinging_for_test());
}
IN_PROC_BROWSER_TEST_F(
BrowserSideFlingBrowserTest,
EarlyTouchpadFlingCancelationOnInertialGSUAckNotConsumed) {
LoadURL(kBrowserFlingDataURL);
// Fling upward and wait for the generated GSE to arrive. Then check that the
// RWHV has stopped the fling.
auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::Type::kGestureScrollEnd);
gfx::Vector2d fling_velocity(0, 2000);
SimulateTouchpadFling(GetWidgetHost(), nullptr /*parent_render_widget_host*/,
fling_velocity);
input_msg_watcher->GetAckStateWaitIfNecessary();
EXPECT_TRUE(GetWidgetHost()->GetView()->view_stopped_flinging_for_test());
}
// Tests that flinging does not continue after navigating to a page that uses
// the same renderer.
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
FlingingStopsAfterNavigation) {
GURL first_url(embedded_test_server()->GetURL(
"b.a.com", "/scrollable_page_with_iframe.html"));
EXPECT_TRUE(NavigateToURL(shell(), first_url));
// The test below only makes sense for same-site same-RFH navigations, so we
// need to ensure that we won't trigger a same-site cross-RFH navigation.
DisableProactiveBrowsingInstanceSwapFor(
shell()->web_contents()->GetMainFrame());
SynchronizeThreads();
SimulateTouchscreenFling(GetWidgetHost());
WaitForScroll();
// Navigate to a second page with the same domain.
GURL second_url(
embedded_test_server()->GetURL("a.com", "/scrollable_page.html"));
EXPECT_TRUE(NavigateToURL(shell(), second_url));
SynchronizeThreads();
// Wait for 100ms. Then check that the second page has not scrolled.
GiveItSomeTime(100);
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(
0, EvalJs(root->current_frame_host(), "window.scrollY").ExtractDouble());
}
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchscreenFlingInOOPIF) {
LoadPageWithOOPIF();
SimulateTouchscreenFling(child_view_->host());
WaitForFrameScroll(GetChildNode());
}
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest, TouchpadFlingInOOPIF) {
LoadPageWithOOPIF();
SimulateTouchpadFling(child_view_->host());
WaitForFrameScroll(GetChildNode());
}
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
TouchscreenInertialGSUsBubbleFromOOPIF) {
LoadPageWithOOPIF();
// Scroll the parent down so that it is scrollable upward.
EXPECT_TRUE(
ExecJs(GetRootNode()->current_frame_host(), "window.scrollTo(0, 20)"));
// We expect to have window.scrollY == 20 after scrolling but with zoom for
// dsf enabled on android we get window.scrollY == 19 (see
// https://crbug.com/891860).
WaitForFrameScroll(GetRootNode(), 19);
SynchronizeThreads();
// Fling and wait for the parent to scroll upward.
gfx::Vector2d fling_velocity(0, 2000);
SimulateTouchscreenFling(child_view_->host(), GetWidgetHost(),
fling_velocity);
WaitForFrameScroll(GetRootNode(), 15, true /* upward */);
}
// Touchpad fling only happens on ChromeOS.
#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
TouchpadInertialGSUsBubbleFromOOPIF) {
LoadPageWithOOPIF();
// Scroll the parent down so that it is scrollable upward.
EXPECT_TRUE(
ExecJs(GetRootNode()->current_frame_host(), "window.scrollTo(0, 20)"));
// We expect to have window.scrollY == 20 after scrolling but with zoom for
// dsf enabled on android we get window.scrollY == 19 (see
// https://crbug.com/891860).
WaitForFrameScroll(GetRootNode(), 19);
SynchronizeThreads();
// Fling and wait for the parent to scroll upward.
gfx::Vector2d fling_velocity(0, 2000);
SimulateTouchpadFling(child_view_->host(), GetWidgetHost(), fling_velocity);
WaitForFrameScroll(GetRootNode(), 15, true /* upward */);
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
InertialGSEGetsBubbledFromOOPIF) {
LoadPageWithOOPIF();
// Scroll the parent down so that it is scrollable upward.
EXPECT_TRUE(
ExecJs(GetRootNode()->current_frame_host(), "window.scrollTo(0, 20)"));
// We expect to have window.scrollY == 20 after scrolling but with zoom for
// dsf enabled on android we get window.scrollY == 19 (see
// https://crbug.com/891860).
WaitForFrameScroll(GetRootNode(), 19);
SynchronizeThreads();
// Fling and wait for the parent to scroll upward.
gfx::Vector2d fling_velocity(0, 2000);
SimulateTouchscreenFling(child_view_->host(), GetWidgetHost(),
fling_velocity);
WaitForFrameScroll(GetRootNode(), 15, true /* upward */);
// Send a GFC to the child and wait for the Generated GSE to get bubbled.
auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::Type::kGestureScrollEnd);
blink::WebGestureEvent gesture_fling_cancel(
blink::WebGestureEvent::Type::kGestureFlingCancel,
blink::WebInputEvent::kNoModifiers, ui::EventTimeForNow());
gesture_fling_cancel.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
const gfx::PointF location_in_widget(1, 1);
const gfx::PointF location_in_root =
child_view_->TransformPointToRootCoordSpaceF(location_in_widget);
const gfx::PointF location_in_screen =
location_in_root + root_view_->GetViewBounds().OffsetFromOrigin();
gesture_fling_cancel.SetPositionInWidget(location_in_widget);
gesture_fling_cancel.SetPositionInScreen(location_in_screen);
child_view_->host()->ForwardGestureEvent(gesture_fling_cancel);
input_msg_watcher->GetAckStateWaitIfNecessary();
}
// Checks that the fling controller of the oopif stops the fling when the
// bubbled inertial GSUs are not consumed by the parent's renderer.
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
InertialGSUBubblingStopsWhenParentCannotScroll) {
LoadPageWithOOPIF();
// Scroll the parent down so that it is scrollable upward.
// Initialize observer before scrolling changes the position of the OOPIF.
HitTestRegionObserver observer(child_view_->GetFrameSinkId());
observer.WaitForHitTestData();
EXPECT_TRUE(
ExecJs(GetRootNode()->current_frame_host(), "window.scrollTo(0, 20)"));
// We expect to have window.scrollY == 20 after scrolling but with zoom for
// dsf enabled on android we get window.scrollY == 19 (see
// https://crbug.com/891860).
WaitForFrameScroll(GetRootNode(), 19);
SynchronizeThreads();
observer.WaitForHitTestDataChange();
// Fling and wait for the parent to scroll up.
auto input_msg_watcher = std::make_unique<InputMsgWatcher>(
GetWidgetHost(), blink::WebInputEvent::Type::kGestureScrollEnd);
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = content::mojom::GestureSourceType::kTouchInput;
const gfx::PointF location_in_widget(10, 10);
const gfx::PointF location_in_root =
child_view_->TransformPointToRootCoordSpaceF(location_in_widget);
const gfx::PointF location_in_screen =
location_in_root + root_view_->GetViewBounds().OffsetFromOrigin();
params.anchor = location_in_screen;
params.distances.push_back(gfx::Vector2d(0, 100));
params.prevent_fling = false;
run_loop_ = std::make_unique<base::RunLoop>();
GetWidgetHost()->QueueSyntheticGesture(
std::make_unique<SyntheticSmoothScrollGesture>(params),
base::BindOnce(&BrowserSideFlingBrowserTest::OnSyntheticGestureCompleted,
base::Unretained(this)));
// Runs until we get the OnSyntheticGestureCompleted callback.
run_loop_->Run();
// Wait for the Generated GSE to get bubbled.
input_msg_watcher->GetAckStateWaitIfNecessary();
// Check that the router has forced the last fling start target to stop
// flinging.
RenderWidgetHostInputEventRouter* router =
static_cast<WebContentsImpl*>(shell()->web_contents())
->GetInputEventRouter();
EXPECT_TRUE(
router->forced_last_fling_start_target_to_stop_flinging_for_test());
}
// Check that fling controller does not generate fling curve when view is
// destroyed.
IN_PROC_BROWSER_TEST_F(BrowserSideFlingBrowserTest,
NoFlingWhenViewIsDestroyed) {
LoadURL(kBrowserFlingDataURL);
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
GetWidgetHost()->GetView()->Destroy();
SimulateTouchscreenFling(GetWidgetHost());
// As the view is destroyed, there shouldn't be any active fling.
EXPECT_FALSE(static_cast<InputRouterImpl*>(GetWidgetHost()->input_router())
->IsFlingActiveForTest());
EXPECT_EQ(
0, EvalJs(root->current_frame_host(), "window.scrollY").ExtractDouble());
}
#endif // !defined(OS_MAC)
class PhysicsBasedFlingCurveBrowserTest : public BrowserSideFlingBrowserTest {
public:
PhysicsBasedFlingCurveBrowserTest() {}
PhysicsBasedFlingCurveBrowserTest(const PhysicsBasedFlingCurveBrowserTest&) =
delete;
PhysicsBasedFlingCurveBrowserTest& operator=(
const PhysicsBasedFlingCurveBrowserTest&) = delete;
~PhysicsBasedFlingCurveBrowserTest() override {}
void SetUpCommandLine(base::CommandLine* command_line) override {
scoped_feature_list_.InitWithFeatures(
{features::kExperimentalFlingAnimation}, {});
IsolateAllSitesForTesting(command_line);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(PhysicsBasedFlingCurveBrowserTest,
TargetScrollOffsetForFlingAnimation) {
LoadPageWithOOPIF();
// Higher value of fling velocity will make sure that the scroll distance
// calculated exceeds the upper bound.
gfx::Vector2d fling_velocity(0, -6000.0);
// Simulate fling on OOPIF
SimulateTouchscreenFling(child_view_->host(), nullptr, fling_velocity);
// If the viewport size required for fling curve generation
// (PhysicsBasedFlingCurveBrowser) is based on RenderWidget, test will time
// out as upper bound calculated will be 3 * size of iframe window(3 * 100)
// and thus it will not scroll beyond it. Viewport size should be based on
// root RenderWidgetHost.
WaitForFrameScroll(GetChildNode(), 400);
}
} // namespace content