/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCBasicTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
static ScrollGenerationCounter sGenerationCounter;
TEST_F(APZCBasicTester, Overzoom) {
// the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
FrameMetrics fm;
fm.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
fm.SetScrollableRect(CSSRect(
0,
0,
125,
150));
fm.SetVisualScrollOffset(CSSPoint(
10,
0));
fm.SetZoom(CSSToParentLayerScale(
1.
0));
fm.SetIsRootContent(
true);
apzc->SetFrameMetrics(fm);
MakeApzcZoomable();
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(
1);
PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(
50,
50),
0.
5,
true);
fm = apzc->GetFrameMetrics();
EXPECT_EQ(
0.
8f, fm.GetZoom().scale);
// bug 936721 - PGO builds introduce rounding error so
// use a fuzzy match instead
EXPECT_LT(std::abs(fm.GetVisualScrollOffset().x),
1e-
5);
EXPECT_LT(std::abs(fm.GetVisualScrollOffset().y),
1e-
5);
}
TEST_F(APZCBasicTester, ZoomLimits) {
SCOPED_GFX_PREF_FLOAT(
"apz.min_zoom",
0.
9f);
SCOPED_GFX_PREF_FLOAT(
"apz.max_zoom",
2.
0f);
// the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
FrameMetrics fm;
fm.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
fm.SetScrollableRect(CSSRect(
0,
0,
125,
150));
fm.SetZoom(CSSToParentLayerScale(
1.
0));
fm.SetIsRootContent(
true);
apzc->SetFrameMetrics(fm);
MakeApzcZoomable();
// This should take the zoom scale to 0.8, but we've capped it at 0.9.
PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(
50,
50),
0.
5,
true);
fm = apzc->GetFrameMetrics();
EXPECT_EQ(
0.
9f, fm.GetZoom().scale);
// This should take the zoom scale to 2.7, but we've capped it at 2.
PinchWithPinchInputAndCheckStatus(apzc, ScreenIntPoint(
50,
50),
3,
true);
fm = apzc->GetFrameMetrics();
EXPECT_EQ(
2.
0f, fm.GetZoom().scale);
}
TEST_F(APZCBasicTester, SimpleTransform) {
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(ParentLayerPoint(), pointOut);
EXPECT_EQ(AsyncTransform(), viewTransformOut);
}
TEST_F(APZCBasicTester, ComplexTransform) {
// This test assumes there is a page that gets rendered to
// two layers. In CSS pixels, the first layer is 50x50 and
// the second layer is 25x50. The widget scale factor is 3.0
// and the presShell resolution is 2.0. Therefore, these layers
// end up being 300x300 and 150x300 in layer pixels.
//
// The second (child) layer has an additional CSS transform that
// stretches it by 2.0 on the x-axis. Therefore, after applying
// CSS transforms, the two layers are the same size in screen
// pixels.
//
// The screen itself is 24x24 in screen pixels (therefore 4x4 in
// CSS pixels). The displayport is 1 extra CSS pixel on all
// sides.
RefPtr<TestAsyncPanZoomController> childApzc =
new TestAsyncPanZoomController(LayersId{
0}, mcc, tm);
const char* treeShape =
"x(x)";
// LayerID 0 1
LayerIntRect layerVisibleRect[] = {
LayerIntRect(
0,
0,
300,
300),
LayerIntRect(
0,
0,
150,
300),
};
Matrix4x4 transforms[] = {
Matrix4x4(),
Matrix4x4(),
};
transforms[
0].PostScale(
0.
5f,
0.
5f,
1.
0f);
// this results from the 2.0 resolution on the root layer
transforms[
1].PostScale(
2.
0f,
1.
0f,
1.
0f);
// this is the 2.0 x-axis CSS transform on the child layer
auto layers = TestWRScrollData::Create(treeShape, *updater, layerVisibleRect,
transforms);
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
24,
24));
metrics.SetDisplayPort(CSSRect(-
1, -
1,
6,
6));
metrics.SetVisualScrollOffset(CSSPoint(
10,
10));
metrics.SetLayoutViewport(CSSRect(
10,
10,
8,
8));
metrics.SetScrollableRect(CSSRect(
0,
0,
50,
50));
metrics.SetCumulativeResolution(LayoutDeviceToLayerScale(
2));
metrics.SetPresShellResolution(
2.
0f);
metrics.SetZoom(CSSToParentLayerScale(
6));
metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(
3));
metrics.SetScrollId(ScrollableLayerGuid::START_SCROLL_ID);
ScrollMetadata childMetadata = metadata;
FrameMetrics& childMetrics = childMetadata.GetMetrics();
childMetrics.SetScrollId(ScrollableLayerGuid::START_SCROLL_ID +
1);
layers[
0]->AppendScrollMetadata(layers, metadata);
layers[
1]->AppendScrollMetadata(layers, childMetadata);
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
// Both the parent and child layer should behave exactly the same here,
// because the CSS transform on the child layer does not affect the
// SampleContentTransformForFrame code
// initial transform
apzc->SetFrameMetrics(metrics);
apzc->NotifyLayersUpdated(metadata,
true,
true);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(
1), ParentLayerPoint()),
viewTransformOut);
EXPECT_EQ(ParentLayerPoint(
60,
60), pointOut);
childApzc->SetFrameMetrics(childMetrics);
childApzc->NotifyLayersUpdated(childMetadata,
true,
true);
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(
1), ParentLayerPoint()),
viewTransformOut);
EXPECT_EQ(ParentLayerPoint(
60,
60), pointOut);
// do an async scroll by 5 pixels and check the transform
metrics.ScrollBy(CSSPoint(
5,
0));
apzc->SetFrameMetrics(metrics);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(
AsyncTransform(LayerToParentLayerScale(
1), ParentLayerPoint(-
30,
0)),
viewTransformOut);
EXPECT_EQ(ParentLayerPoint(
90,
60), pointOut);
childMetrics.ScrollBy(CSSPoint(
5,
0));
childApzc->SetFrameMetrics(childMetrics);
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(
AsyncTransform(LayerToParentLayerScale(
1), ParentLayerPoint(-
30,
0)),
viewTransformOut);
EXPECT_EQ(ParentLayerPoint(
90,
60), pointOut);
// do an async zoom of 1.5x and check the transform
metrics.ZoomBy(
1.
5f);
apzc->SetFrameMetrics(metrics);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(
AsyncTransform(LayerToParentLayerScale(
1.
5), ParentLayerPoint(-
45,
0)),
viewTransformOut);
EXPECT_EQ(ParentLayerPoint(
135,
90), pointOut);
childMetrics.ZoomBy(
1.
5f);
childApzc->SetFrameMetrics(childMetrics);
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(
AsyncTransform(LayerToParentLayerScale(
1.
5), ParentLayerPoint(-
45,
0)),
viewTransformOut);
EXPECT_EQ(ParentLayerPoint(
135,
90), pointOut);
childApzc->Destroy();
}
TEST_F(APZCBasicTester, Fling) {
SCOPED_GFX_PREF_FLOAT(
"apz.fling_min_velocity_threshold",
0.
0f);
int touchStart =
50;
int touchEnd =
10;
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
// Fling down. Each step scroll further down
Pan(apzc, touchStart, touchEnd);
ParentLayerPoint lastPoint;
for (
int i =
1; i <
50; i +=
1) {
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut,
TimeDuration::FromMilliseconds(
1));
EXPECT_GT(pointOut.y, lastPoint.y);
lastPoint = pointOut;
}
}
#ifndef MOZ_WIDGET_ANDROID
// Maybe fails on Android
TEST_F(APZCBasicTester, ResumeInterruptedTouchDrag_Bug1592435) {
// Start a touch-drag and scroll some amount, not lifting the finger.
SCOPED_GFX_PREF_FLOAT(
"apz.touch_start_tolerance",
1.
0f /
1000.
0f);
ScreenIntPoint touchPos(
10,
50);
uint64_t touchBlock = TouchDown(apzc, touchPos, mcc->Time()).mInputBlockId;
SetDefaultAllowedTouchBehavior(apzc, touchBlock);
for (
int i =
0; i <
20; ++i) {
touchPos.y -=
1;
mcc->AdvanceByMillis(
1);
TouchMove(apzc, touchPos, mcc->Time());
}
// Take note of the scroll offset before the interruption.
CSSPoint scrollOffsetBeforeInterruption =
apzc->GetFrameMetrics().GetVisualScrollOffset();
// Have the main thread interrupt the touch-drag by sending
// a main thread scroll update to a nearby location.
CSSPoint mainThreadOffset = scrollOffsetBeforeInterruption;
mainThreadOffset.y -=
5;
ScrollMetadata metadata = apzc->GetScrollMetadata();
metadata.GetMetrics().SetLayoutScrollOffset(mainThreadOffset);
nsTArray<ScrollPositionUpdate> scrollUpdates;
scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll(
ScrollOrigin::Other, CSSPoint::ToAppUnits(mainThreadOffset)));
metadata.SetScrollUpdates(scrollUpdates);
metadata.GetMetrics().SetScrollGeneration(
scrollUpdates.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(metadata,
false,
true);
// Continue and finish the touch-drag gesture.
for (
int i =
0; i <
20; ++i) {
touchPos.y -=
1;
mcc->AdvanceByMillis(
1);
TouchMove(apzc, touchPos, mcc->Time());
}
// Check that the portion of the touch-drag that occurred after
// the interruption caused additional scrolling.
CSSPoint finalScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset();
EXPECT_GT(finalScrollOffset.y, scrollOffsetBeforeInterruption.y);
// Now do the same thing, but for a visual scroll update.
scrollOffsetBeforeInterruption =
apzc->GetFrameMetrics().GetVisualScrollOffset();
mainThreadOffset = scrollOffsetBeforeInterruption;
mainThreadOffset.y -=
5;
metadata = apzc->GetScrollMetadata();
metadata.GetMetrics().SetVisualDestination(mainThreadOffset);
metadata.GetMetrics().SetScrollGeneration(
sGenerationCounter.NewMainThreadGeneration());
metadata.GetMetrics().SetVisualScrollUpdateType(FrameMetrics::eMainThread);
scrollUpdates.Clear();
metadata.SetScrollUpdates(scrollUpdates);
apzc->NotifyLayersUpdated(metadata,
false,
true);
for (
int i =
0; i <
20; ++i) {
touchPos.y -=
1;
mcc->AdvanceByMillis(
1);
TouchMove(apzc, touchPos, mcc->Time());
}
finalScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset();
EXPECT_GT(finalScrollOffset.y, scrollOffsetBeforeInterruption.y);
// Clean up by ending the touch gesture.
mcc->AdvanceByMillis(
1);
TouchUp(apzc, touchPos, mcc->Time());
}
#endif
TEST_F(APZCBasicTester, RelativeScrollOffset) {
// Set up initial conditions: zoomed in, layout offset at (100, 100),
// visual offset at (120, 120); the relative offset is therefore (20, 20).
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetScrollableRect(CSSRect(
0,
0,
1000,
1000));
metrics.SetLayoutViewport(CSSRect(
100,
100,
100,
100));
metrics.SetZoom(CSSToParentLayerScale(
2.
0));
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
metrics.SetVisualScrollOffset(CSSPoint(
120,
120));
metrics.SetIsRootContent(
true);
apzc->SetFrameMetrics(metrics);
// Scroll the layout viewport to (200, 200).
ScrollMetadata mainThreadMetadata = metadata;
FrameMetrics& mainThreadMetrics = mainThreadMetadata.GetMetrics();
mainThreadMetrics.SetLayoutScrollOffset(CSSPoint(
200,
200));
nsTArray<ScrollPositionUpdate> scrollUpdates;
scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll(
ScrollOrigin::Other, CSSPoint::ToAppUnits(CSSPoint(
200,
200))));
mainThreadMetadata.SetScrollUpdates(scrollUpdates);
mainThreadMetrics.SetScrollGeneration(
scrollUpdates.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(mainThreadMetadata,
/*isFirstPaint=*/false,
/*thisLayerTreeUpdated=*/true);
// Check that the relative offset has been preserved.
metrics = apzc->GetFrameMetrics();
EXPECT_EQ(metrics.GetLayoutScrollOffset(), CSSPoint(
200,
200));
EXPECT_EQ(metrics.GetVisualScrollOffset(), CSSPoint(
220,
220));
}
TEST_F(APZCBasicTester, MultipleSmoothScrollsSmooth) {
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll",
true);
// We want to test that if we send multiple smooth scroll requests that we
// still smoothly animate, ie that we get non-zero change every frame while
// the animation is running.
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetScrollableRect(CSSRect(
0,
0,
100,
10000));
metrics.SetLayoutViewport(CSSRect(
0,
0,
100,
100));
metrics.SetZoom(CSSToParentLayerScale(
1.
0));
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
metrics.SetVisualScrollOffset(CSSPoint(
0,
0));
metrics.SetIsRootContent(
true);
apzc->SetFrameMetrics(metrics);
// Structure of this test.
// -send a pure relative smooth scroll request via NotifyLayersUpdated
// -advance animations a few times, check that scroll offset is increasing
// after the first few advances
// -send a pure relative smooth scroll request via NotifyLayersUpdated
// -advance animations a few times, check that scroll offset is increasing
// -send a pure relative smooth scroll request via NotifyLayersUpdated
// -advance animations a few times, check that scroll offset is increasing
ScrollMetadata metadata2 = metadata;
nsTArray<ScrollPositionUpdate> scrollUpdates2;
scrollUpdates2.AppendElement(ScrollPositionUpdate::NewPureRelativeScroll(
ScrollOrigin::Other, ScrollMode::Smooth,
CSSPoint::ToAppUnits(CSSPoint(
0,
200))));
metadata2.SetScrollUpdates(scrollUpdates2);
metadata2.GetMetrics().SetScrollGeneration(
scrollUpdates2.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(metadata2,
/*isFirstPaint=*/false,
/*thisLayerTreeUpdated=*/true);
// Get the animation going
for (uint32_t i =
0; i <
3; i++) {
SampleAnimationOneFrame();
}
float offset =
apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing)
.y;
ASSERT_GT(offset,
0);
float lastOffset = offset;
for (uint32_t i =
0; i <
2; i++) {
for (uint32_t j =
0; j <
3; j++) {
SampleAnimationOneFrame();
offset = apzc->GetCurrentAsyncScrollOffset(
AsyncPanZoomController::eForCompositing)
.y;
ASSERT_GT(offset, lastOffset);
lastOffset = offset;
}
ScrollMetadata metadata3 = metadata;
nsTArray<ScrollPositionUpdate> scrollUpdates3;
scrollUpdates3.AppendElement(ScrollPositionUpdate::NewPureRelativeScroll(
ScrollOrigin::Other, ScrollMode::Smooth,
CSSPoint::ToAppUnits(CSSPoint(
0,
200))));
metadata3.SetScrollUpdates(scrollUpdates3);
metadata3.GetMetrics().SetScrollGeneration(
scrollUpdates3.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(metadata3,
/*isFirstPaint=*/false,
/*thisLayerTreeUpdated=*/true);
}
for (uint32_t j =
0; j <
7; j++) {
SampleAnimationOneFrame();
offset = apzc->GetCurrentAsyncScrollOffset(
AsyncPanZoomController::eForCompositing)
.y;
ASSERT_GT(offset, lastOffset);
lastOffset = offset;
}
}
class APZCSmoothScrollTester :
public APZCBasicTester {
public:
// Test that a smooth scroll animation correctly handles its destination
// being updated by a relative scroll delta from the main thread (a "content
// shift").
void TestContentShift() {
// Set up scroll frame. Starting scroll position is (0, 0).
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetScrollableRect(CSSRect(
0,
0,
100,
10000));
metrics.SetLayoutViewport(CSSRect(
0,
0,
100,
100));
metrics.SetZoom(CSSToParentLayerScale(
1.
0));
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
metrics.SetVisualScrollOffset(CSSPoint(
0,
0));
metrics.SetIsRootContent(
true);
apzc->SetFrameMetrics(metrics);
// Start smooth scroll via main-thread request.
nsTArray<ScrollPositionUpdate> scrollUpdates;
scrollUpdates.AppendElement(ScrollPositionUpdate::NewPureRelativeScroll(
ScrollOrigin::Other, ScrollMode::Smooth,
CSSPoint::ToAppUnits(CSSPoint(
0,
1000))));
metadata.SetScrollUpdates(scrollUpdates);
metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(metadata,
false,
true);
// Sample the smooth scroll animation until we get past y=500.
apzc->AssertStateIsSmoothScroll();
float y =
0;
while (y <
500) {
SampleAnimationOneFrame();
y = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
}
// Send a relative scroll of y = -400.
scrollUpdates.Clear();
scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll(
CSSPoint::ToAppUnits(CSSPoint(
0,
500)),
CSSPoint::ToAppUnits(CSSPoint(
0,
100))));
metadata.SetScrollUpdates(scrollUpdates);
metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(metadata,
false,
false);
// Verify the relative scroll was applied but didn't cancel the animation.
float y2 = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
ASSERT_EQ(y2, y -
400);
apzc->AssertStateIsSmoothScroll();
// Sample the animation again and check that it respected the relative
// scroll.
SampleAnimationOneFrame();
float y3 = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
ASSERT_GT(y3, y2);
ASSERT_LT(y3,
500);
// Continue animation until done and check that it ended up at a correctly
// adjusted destination.
apzc->AdvanceAnimationsUntilEnd();
float y4 = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
ASSERT_EQ(y4,
600);
// 1000 (initial destination) - 400 (relative scroll)
}
// Test that a smooth scroll animation correctly handles a content
// shift, followed by an UpdateDelta due to a new input event.
void TestContentShiftThenUpdateDelta() {
// Set up scroll frame. Starting position is (0, 0).
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetScrollableRect(CSSRect(
0,
0,
1000,
10000));
metrics.SetLayoutViewport(CSSRect(
0,
0,
1000,
1000));
metrics.SetZoom(CSSToParentLayerScale(
1.
0));
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
1000,
1000));
metrics.SetVisualScrollOffset(CSSPoint(
0,
0));
metrics.SetIsRootContent(
true);
// Set the line scroll amount to 100 pixels. Note that SmoothWheel() takes
// a delta denominated in lines.
metadata.SetLineScrollAmount({
100,
100});
// The page scroll amount also needs to be set, otherwise the wheel handling
// code will get confused by things like the "don't scroll more than one
// page" check.
metadata.SetPageScrollAmount({
1000,
1000});
apzc->SetScrollMetadata(metadata);
// Send a wheel event to trigger smooth scrolling by 5 lines (= 500 pixels).
SmoothWheel(apzc, ScreenIntPoint(
50,
50), ScreenPoint(
0,
5), mcc->Time());
apzc->AssertStateIsWheelScroll();
// Sample the wheel scroll animation until we get past y=200.
float y =
0;
while (y <
200) {
SampleAnimationOneFrame();
y = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
}
// Apply a content shift of y=100.
nsTArray<ScrollPositionUpdate> scrollUpdates;
scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll(
CSSPoint::ToAppUnits(CSSPoint(
0,
200)),
CSSPoint::ToAppUnits(CSSPoint(
0,
300))));
metadata.SetScrollUpdates(scrollUpdates);
metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(metadata,
false,
true);
// Check that the content shift was applied but didn't cancel the animation.
// At this point, the animation's internal state should be targeting a
// destination of y=600.
float y2 = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
ASSERT_EQ(y2, y +
100);
apzc->AssertStateIsWheelScroll();
// Sample the animation until we get past y=400.
while (y <
400) {
SampleAnimationOneFrame();
y = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
}
// Send another wheel event to trigger smooth scrolling by another 5 lines
// (=500 pixels). This should update the animation to target a destination
// of y=1100.
SmoothWheel(apzc, ScreenIntPoint(
50,
50), ScreenPoint(
0,
5), mcc->Time());
// Continue the animation until done and check that it ended up at y=1100.
apzc->AdvanceAnimationsUntilEnd();
float yEnd = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
ASSERT_EQ(yEnd,
1100);
}
// Test that a content shift does not cause a smooth scroll animation to
// overshoot its (updated) destination.
void TestContentShiftDoesNotCauseOvershoot() {
// Follow the same steps as in TestContentShiftThenUpdateDelta(),
// except use a content shift of y=1000.
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetScrollableRect(CSSRect(
0,
0,
1000,
10000));
metrics.SetLayoutViewport(CSSRect(
0,
0,
1000,
1000));
metrics.SetZoom(CSSToParentLayerScale(
1.
0));
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
1000,
1000));
metrics.SetVisualScrollOffset(CSSPoint(
0,
0));
metrics.SetIsRootContent(
true);
metadata.SetLineScrollAmount({
100,
100});
metadata.SetPageScrollAmount({
1000,
1000});
apzc->SetScrollMetadata(metadata);
// First wheel event, smooth scroll destination is y=500.
SmoothWheel(apzc, ScreenIntPoint(
50,
50), ScreenPoint(
0,
5), mcc->Time());
apzc->AssertStateIsWheelScroll();
// Sample until we get past y=200.
float y =
0;
while (y <
200) {
SampleAnimationOneFrame();
y = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
}
// Apply a content shift of y=1000. The current scroll position is now
// y>1200, and the updated destination is y=1500.
nsTArray<ScrollPositionUpdate> scrollUpdates;
scrollUpdates.AppendElement(ScrollPositionUpdate::NewRelativeScroll(
CSSPoint::ToAppUnits(CSSPoint(
0,
200)),
CSSPoint::ToAppUnits(CSSPoint(
0,
1200))));
metadata.SetScrollUpdates(scrollUpdates);
metrics.SetScrollGeneration(scrollUpdates.LastElement().GetGeneration());
apzc->NotifyLayersUpdated(metadata,
false,
true);
float y2 = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
ASSERT_EQ(y2, y +
1000);
apzc->AssertStateIsWheelScroll();
// Sample until we get past y=1300.
while (y <
1300) {
SampleAnimationOneFrame();
y = apzc->GetFrameMetrics().GetVisualScrollOffset().y;
}
// Second wheel event, destination is now y=2000.
// MSD physics has a bug where the UpdateDelta() causes the content shift
// to be applied in duplicate on the next sample, causing the scroll
// position to be y>2000!
SmoothWheel(apzc, ScreenIntPoint(
50,
50), ScreenPoint(
0,
5), mcc->Time());
// Check that the scroll position remains <= 2000 until the end of the
// animation.
while (apzc->IsWheelScrollAnimationRunning()) {
SampleAnimationOneFrame();
ASSERT_LE(apzc->GetFrameMetrics().GetVisualScrollOffset().y,
2000);
}
ASSERT_EQ(
2000, apzc->GetFrameMetrics().GetVisualScrollOffset().y);
}
};
TEST_F(APZCSmoothScrollTester, ContentShiftBezier) {
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll",
true);
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll.msdPhysics.enabled",
false);
TestContentShift();
}
TEST_F(APZCSmoothScrollTester, ContentShiftMsd) {
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll",
true);
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll.msdPhysics.enabled",
true);
TestContentShift();
}
TEST_F(APZCSmoothScrollTester, ContentShiftThenUpdateDeltaBezier) {
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll",
true);
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll.msdPhysics.enabled",
false);
TestContentShiftThenUpdateDelta();
}
TEST_F(APZCSmoothScrollTester, ContentShiftThenUpdateDeltaMsd) {
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll",
true);
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll.msdPhysics.enabled",
true);
TestContentShiftThenUpdateDelta();
}
TEST_F(APZCSmoothScrollTester, ContentShiftDoesNotCauseOvershootBezier) {
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll",
true);
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll.msdPhysics.enabled",
false);
TestContentShiftDoesNotCauseOvershoot();
}
TEST_F(APZCSmoothScrollTester, ContentShiftDoesNotCauseOvershootMsd) {
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll",
true);
SCOPED_GFX_PREF_BOOL(
"general.smoothScroll.msdPhysics.enabled",
true);
TestContentShiftDoesNotCauseOvershoot();
}
TEST_F(APZCBasicTester, ZoomAndScrollableRectChangeAfterZoomChange) {
// We want to check that a small scrollable rect change (which causes us to
// reclamp our scroll position, including in the sampled state) does not move
// the scroll offset in the sample state based the zoom in the apzc, only
// based on the zoom in the sampled state.
// First we zoom in to the right hand side. Then start zooming out, then send
// a scrollable rect change and check that it doesn't change the sampled state
// scroll offset.
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
metrics.SetScrollableRect(CSSRect(
0,
0,
100,
1000));
metrics.SetLayoutViewport(CSSRect(
0,
0,
100,
100));
metrics.SetVisualScrollOffset(CSSPoint(
0,
0));
metrics.SetZoom(CSSToParentLayerScale(
1.
0));
metrics.SetIsRootContent(
true);
apzc->SetFrameMetrics(metrics);
MakeApzcZoomable();
// Zoom to right side.
ZoomTarget zoomTarget{CSSRect(
75,
25,
25,
25)};
apzc->ZoomToRect(zoomTarget,
0);
// Run the animation to completion, should take 250ms/16.67ms = 15 frames, but
// do extra to make sure.
for (uint32_t i =
0; i <
30; i++) {
SampleAnimationOneFrame();
}
EXPECT_FALSE(apzc->IsAsyncZooming());
// Zoom out.
ZoomTarget zoomTarget2{CSSRect(
0,
0,
100,
100)};
apzc->ZoomToRect(zoomTarget2,
0);
// Run the animation a few times to get it going.
for (uint32_t i =
0; i <
2; i++) {
SampleAnimationOneFrame();
}
// Check that it is decreasing in scale.
float prevScale =
apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing)
.scale;
for (uint32_t i =
0; i <
2; i++) {
SampleAnimationOneFrame();
float scale =
apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing)
.scale;
ASSERT_GT(prevScale, scale);
prevScale = scale;
}
float offset =
apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing)
.x;
// Change the scrollable rect slightly to trigger a reclamp.
ScrollMetadata metadata2 = metadata;
metadata2.GetMetrics().SetScrollableRect(CSSRect(
0,
0,
100,
1000.
2));
apzc->NotifyLayersUpdated(metadata2,
/*isFirstPaint=*/false,
/*thisLayerTreeUpdated=*/true);
float newOffset =
apzc->GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForCompositing)
.x;
ASSERT_EQ(newOffset, offset);
}
TEST_F(APZCBasicTester, ZoomToRectAndCompositionBoundsChange) {
// We want to check that content sending a composition bounds change (due to
// addition of scrollbars) during a zoom animation does not cause us to take
// the out of date content resolution.
ScrollMetadata metadata;
FrameMetrics& metrics = metadata.GetMetrics();
metrics.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
metrics.SetCompositionBoundsWidthIgnoringScrollbars(ParentLayerCoord{
100});
metrics.SetScrollableRect(CSSRect(
0,
0,
100,
1000));
metrics.SetLayoutViewport(CSSRect(
0,
0,
100,
100));
metrics.SetVisualScrollOffset(CSSPoint(
0,
0));
metrics.SetZoom(CSSToParentLayerScale(
1.
0));
metrics.SetIsRootContent(
true);
apzc->SetFrameMetrics(metrics);
MakeApzcZoomable();
// Start a zoom to a rect.
ZoomTarget zoomTarget{CSSRect(
25,
25,
25,
25)};
apzc->ZoomToRect(zoomTarget,
0);
// Run the animation a few times to get it going.
// Check that it is increasing in scale.
float prevScale =
apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing)
.scale;
for (uint32_t i =
0; i <
3; i++) {
SampleAnimationOneFrame();
float scale =
apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing)
.scale;
ASSERT_GE(scale, prevScale);
prevScale = scale;
}
EXPECT_TRUE(apzc->IsAsyncZooming());
// Simulate the appearance of a scrollbar by reducing the width of
// the composition bounds, while keeping
// mCompositionBoundsWidthIgnoringScrollbars unchanged.
ScrollMetadata metadata2 = metadata;
metadata2.GetMetrics().SetCompositionBounds(ParentLayerRect(
0,
0,
90,
100));
apzc->NotifyLayersUpdated(metadata2,
/*isFirstPaint=*/false,
/*thisLayerTreeUpdated=*/true);
float scale =
apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing)
.scale;
ASSERT_EQ(scale, prevScale);
// Run the rest of the animation to completion, should take 250ms/16.67ms = 15
// frames total, but do extra to make sure.
for (uint32_t i =
0; i <
30; i++) {
SampleAnimationOneFrame();
scale =
apzc->GetCurrentPinchZoomScale(AsyncPanZoomController::eForCompositing)
.scale;
ASSERT_GE(scale, prevScale);
prevScale = scale;
}
EXPECT_FALSE(apzc->IsAsyncZooming());
}
TEST_F(APZCBasicTester, StartTolerance) {
SCOPED_GFX_PREF_FLOAT(
"apz.touch_start_tolerance",
10 / tm->GetDPI());
FrameMetrics fm;
fm.SetCompositionBounds(ParentLayerRect(
0,
0,
100,
100));
fm.SetScrollableRect(CSSRect(
0,
0,
100,
300));
fm.SetVisualScrollOffset(CSSPoint(
0,
50));
fm.SetIsRootContent(
true);
apzc->SetFrameMetrics(fm);
uint64_t touchBlock = TouchDown(apzc, {
50,
50}, mcc->Time()).mInputBlockId;
SetDefaultAllowedTouchBehavior(apzc, touchBlock);
CSSPoint initialScrollOffset =
apzc->GetFrameMetrics().GetVisualScrollOffset();
mcc->AdvanceByMillis(
1);
TouchMove(apzc, {
50,
70}, mcc->Time());
// Expect 10 pixels of scrolling: the distance from (50,50) to (50,70)
// minus the 10-pixel touch start tolerance.
ASSERT_EQ(initialScrollOffset.y -
10,
apzc->GetFrameMetrics().GetVisualScrollOffset().y);
mcc->AdvanceByMillis(
1);
TouchMove(apzc, {
50,
90}, mcc->Time());
// Expect 30 pixels of scrolling: the distance from (50,50) to (50,90)
// minus the 10-pixel touch start tolerance.
ASSERT_EQ(initialScrollOffset.y -
30,
apzc->GetFrameMetrics().GetVisualScrollOffset().y);
// Clean up by ending the touch gesture.
mcc->AdvanceByMillis(
1);
TouchUp(apzc, {
50,
90}, mcc->Time());
}