diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/layers/apz/test/gtest/TestBasic.cpp | 639 |
1 files changed, 639 insertions, 0 deletions
diff --git a/gfx/layers/apz/test/gtest/TestBasic.cpp b/gfx/layers/apz/test/gtest/TestBasic.cpp new file mode 100644 index 0000000000..52cebfccd4 --- /dev/null +++ b/gfx/layers/apz/test/gtest/TestBasic.cpp @@ -0,0 +1,639 @@ +/* -*- 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 + LayerIntRegion layerVisibleRegion[] = { + 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, + layerVisibleRegion, 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. + void TestSmoothScrollDestinationUpdate() { + // 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_F(APZCSmoothScrollTester, SmoothScrollDestinationUpdateBezier) { + SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); + SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", false); + TestSmoothScrollDestinationUpdate(); +} + +TEST_F(APZCSmoothScrollTester, SmoothScrollDestinationUpdateMsd) { + SCOPED_GFX_PREF_BOOL("general.smoothScroll", true); + SCOPED_GFX_PREF_BOOL("general.smoothScroll.msdPhysics.enabled", true); + TestSmoothScrollDestinationUpdate(); +} + +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()); +} |