summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/test/gtest/TestEventResult.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/apz/test/gtest/TestEventResult.cpp')
-rw-r--r--gfx/layers/apz/test/gtest/TestEventResult.cpp476
1 files changed, 476 insertions, 0 deletions
diff --git a/gfx/layers/apz/test/gtest/TestEventResult.cpp b/gfx/layers/apz/test/gtest/TestEventResult.cpp
new file mode 100644
index 0000000000..90d17ee511
--- /dev/null
+++ b/gfx/layers/apz/test/gtest/TestEventResult.cpp
@@ -0,0 +1,476 @@
+/* -*- 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 "APZCTreeManagerTester.h"
+#include "APZTestCommon.h"
+#include "InputUtils.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/layers/LayersTypes.h"
+#include <tuple>
+
+class APZEventResultTester : public APZCTreeManagerTester {
+ protected:
+ UniquePtr<ScopedLayerTreeRegistration> registration;
+
+ void UpdateOverscrollBehavior(OverscrollBehavior aX, OverscrollBehavior aY) {
+ ModifyFrameMetrics(root, [aX, aY](ScrollMetadata& sm, FrameMetrics& _) {
+ OverscrollBehaviorInfo overscroll;
+ overscroll.mBehaviorX = aX;
+ overscroll.mBehaviorY = aY;
+ sm.SetOverscrollBehavior(overscroll);
+ });
+ UpdateHitTestingTree();
+ }
+
+ void SetScrollOffsetOnMainThread(const CSSPoint& aPoint) {
+ RefPtr<TestAsyncPanZoomController> apzc = ApzcOf(root);
+
+ ScrollMetadata metadata = apzc->GetScrollMetadata();
+ metadata.GetMetrics().SetLayoutScrollOffset(aPoint);
+ nsTArray<ScrollPositionUpdate> scrollUpdates;
+ scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll(
+ ScrollOrigin::Other, CSSPoint::ToAppUnits(aPoint)));
+ metadata.SetScrollUpdates(scrollUpdates);
+ metadata.GetMetrics().SetScrollGeneration(
+ scrollUpdates.LastElement().GetGeneration());
+ apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
+ /*aThisLayerTreeUpdated=*/true);
+ }
+
+ void CreateScrollableRootLayer() {
+ const char* treeShape = "x";
+ LayerIntRegion layerVisibleRegions[] = {
+ LayerIntRect(0, 0, 100, 100),
+ };
+ CreateScrollData(treeShape, layerVisibleRegions);
+ SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
+ CSSRect(0, 0, 200, 200));
+ ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
+ metrics.SetIsRootContent(true);
+ });
+ registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
+ UpdateHitTestingTree();
+ }
+
+ enum class PreventDefaultFlag { No, Yes };
+ std::tuple<APZEventResult, APZHandledResult> TapDispatchToContent(
+ const ScreenIntPoint& aPoint, PreventDefaultFlag aPreventDefaultFlag) {
+ APZEventResult result =
+ Tap(manager, aPoint, TimeDuration::FromMilliseconds(100));
+
+ APZHandledResult delayedAnswer{APZHandledPlace::Invalid, SideBits::eNone,
+ ScrollDirections()};
+ manager->AddInputBlockCallback(
+ result.mInputBlockId,
+ {result.GetStatus(), [&](uint64_t id, const APZHandledResult& answer) {
+ EXPECT_EQ(id, result.mInputBlockId);
+ delayedAnswer = answer;
+ }});
+ manager->SetAllowedTouchBehavior(result.mInputBlockId,
+ {AllowedTouchBehavior::VERTICAL_PAN});
+ manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
+ manager->ContentReceivedInputBlock(
+ result.mInputBlockId, aPreventDefaultFlag == PreventDefaultFlag::Yes);
+ return {result, delayedAnswer};
+ }
+
+ void OverscrollDirectionsWithEventHandlerTest(
+ PreventDefaultFlag aPreventDefaultFlag) {
+ UpdateHitTestingTree();
+
+ APZHandledPlace expectedPlace =
+ aPreventDefaultFlag == PreventDefaultFlag::No
+ ? APZHandledPlace::HandledByRoot
+ : APZHandledPlace::HandledByContent;
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
+ EitherScrollDirection}));
+ }
+
+ // overscroll-behavior: contain, contain.
+ UpdateOverscrollBehavior(OverscrollBehavior::Contain,
+ OverscrollBehavior::Contain);
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
+ ScrollDirections()}));
+ }
+
+ // overscroll-behavior: none, none.
+ UpdateOverscrollBehavior(OverscrollBehavior::None,
+ OverscrollBehavior::None);
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
+ ScrollDirections()}));
+ }
+
+ // overscroll-behavior: auto, none.
+ UpdateOverscrollBehavior(OverscrollBehavior::Auto,
+ OverscrollBehavior::None);
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
+ HorizontalScrollDirection}));
+ }
+
+ // overscroll-behavior: none, auto.
+ UpdateOverscrollBehavior(OverscrollBehavior::None,
+ OverscrollBehavior::Auto);
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
+ VerticalScrollDirection}));
+ }
+ }
+
+ void ScrollableDirectionsWithEventHandlerTest(
+ PreventDefaultFlag aPreventDefaultFlag) {
+ UpdateHitTestingTree();
+
+ APZHandledPlace expectedPlace =
+ aPreventDefaultFlag == PreventDefaultFlag::No
+ ? APZHandledPlace::HandledByRoot
+ : APZHandledPlace::HandledByContent;
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eBottom | SideBits::eRight,
+ EitherScrollDirection}));
+ }
+
+ // scroll down a bit.
+ SetScrollOffsetOnMainThread(CSSPoint(0, 10));
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(delayedHandledResult,
+ (APZHandledResult{
+ expectedPlace,
+ SideBits::eTop | SideBits::eBottom | SideBits::eRight,
+ EitherScrollDirection}));
+ }
+
+ // scroll to the bottom edge
+ SetScrollOffsetOnMainThread(CSSPoint(0, 100));
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eRight | SideBits::eTop,
+ EitherScrollDirection}));
+ }
+
+ // scroll to right a bit.
+ SetScrollOffsetOnMainThread(CSSPoint(10, 100));
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace,
+ SideBits::eLeft | SideBits::eRight | SideBits::eTop,
+ EitherScrollDirection}));
+ }
+
+ // scroll to the right edge.
+ SetScrollOffsetOnMainThread(CSSPoint(100, 100));
+ {
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ auto [result, delayedHandledResult] =
+ TapDispatchToContent(ScreenIntPoint(50, 50), aPreventDefaultFlag);
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ EXPECT_EQ(
+ delayedHandledResult,
+ (APZHandledResult{expectedPlace, SideBits::eTop | SideBits::eLeft,
+ EitherScrollDirection}));
+ }
+ }
+};
+
+TEST_F(APZEventResultTester, OverscrollDirections) {
+ CreateScrollableRootLayer();
+
+ TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
+
+ // The default value of overscroll-behavior is auto.
+ APZEventResult result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
+ EitherScrollDirection);
+
+ // overscroll-behavior: contain, contain.
+ UpdateOverscrollBehavior(OverscrollBehavior::Contain,
+ OverscrollBehavior::Contain);
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
+ ScrollDirections());
+
+ // overscroll-behavior: none, none.
+ UpdateOverscrollBehavior(OverscrollBehavior::None, OverscrollBehavior::None);
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
+ ScrollDirections());
+
+ // overscroll-behavior: auto, none.
+ UpdateOverscrollBehavior(OverscrollBehavior::Auto, OverscrollBehavior::None);
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
+ HorizontalScrollDirection);
+
+ // overscroll-behavior: none, auto.
+ UpdateOverscrollBehavior(OverscrollBehavior::None, OverscrollBehavior::Auto);
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mOverscrollDirections,
+ VerticalScrollDirection);
+}
+
+TEST_F(APZEventResultTester, ScrollableDirections) {
+ CreateScrollableRootLayer();
+
+ TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
+
+ APZEventResult result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ // scrollable to down/right.
+ EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
+ SideBits::eBottom | SideBits::eRight);
+
+ // scroll down a bit.
+ SetScrollOffsetOnMainThread(CSSPoint(0, 10));
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ // also scrollable toward top.
+ EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
+ SideBits::eTop | SideBits::eBottom | SideBits::eRight);
+
+ // scroll to the bottom edge
+ SetScrollOffsetOnMainThread(CSSPoint(0, 100));
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
+ SideBits::eRight | SideBits::eTop);
+
+ // scroll to right a bit.
+ SetScrollOffsetOnMainThread(CSSPoint(10, 100));
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
+ SideBits::eLeft | SideBits::eRight | SideBits::eTop);
+
+ // scroll to the right edge.
+ SetScrollOffsetOnMainThread(CSSPoint(100, 100));
+ result = Tap(manager, ScreenIntPoint(50, 50), tapDuration);
+ EXPECT_EQ(result.GetHandledResult()->mScrollableDirections,
+ SideBits::eLeft | SideBits::eTop);
+}
+
+class APZEventResultTesterMock : public APZEventResultTester {
+ public:
+ APZEventResultTesterMock() { CreateMockHitTester(); }
+};
+
+TEST_F(APZEventResultTesterMock, OverscrollDirectionsWithEventHandler) {
+ CreateScrollableRootLayer();
+
+ OverscrollDirectionsWithEventHandlerTest(PreventDefaultFlag::No);
+}
+
+TEST_F(APZEventResultTesterMock,
+ OverscrollDirectionsWithPreventDefaultEventHandler) {
+ CreateScrollableRootLayer();
+
+ OverscrollDirectionsWithEventHandlerTest(PreventDefaultFlag::Yes);
+}
+
+TEST_F(APZEventResultTesterMock, ScrollableDirectionsWithEventHandler) {
+ CreateScrollableRootLayer();
+
+ ScrollableDirectionsWithEventHandlerTest(PreventDefaultFlag::No);
+}
+
+TEST_F(APZEventResultTesterMock,
+ ScrollableDirectionsWithPreventDefaultEventHandler) {
+ CreateScrollableRootLayer();
+
+ ScrollableDirectionsWithEventHandlerTest(PreventDefaultFlag::Yes);
+}
+
+// Test that APZEventResult::GetHandledResult() is correctly
+// populated.
+TEST_F(APZEventResultTesterMock, HandledByRootApzcFlag) {
+ // Create simple layer tree containing a dispatch-to-content region
+ // that covers part but not all of its area.
+ const char* treeShape = "x";
+ LayerIntRegion layerVisibleRegions[] = {
+ LayerIntRect(0, 0, 100, 100),
+ };
+ CreateScrollData(treeShape, layerVisibleRegions);
+ SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
+ CSSRect(0, 0, 100, 200));
+ ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
+ metrics.SetIsRootContent(true);
+ });
+ // away from the scrolling container layer.
+ registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
+ UpdateHitTestingTree();
+
+ // Tap the top half and check that we report that the event was
+ // handled by the root APZC.
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
+ APZEventResult result =
+ TouchDown(manager, ScreenIntPoint(50, 25), mcc->Time());
+ TouchUp(manager, ScreenIntPoint(50, 25), mcc->Time());
+ EXPECT_EQ(result.GetHandledResult(),
+ Some(APZHandledResult{APZHandledPlace::HandledByRoot,
+ SideBits::eBottom, EitherScrollDirection}));
+
+ // Tap the bottom half and check that we report that we're not
+ // sure whether the event was handled by the root APZC.
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
+ TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+
+ // Register an input block callback that will tell us the
+ // delayed answer.
+ APZHandledResult delayedAnswer{APZHandledPlace::Invalid, SideBits::eNone,
+ ScrollDirections()};
+ manager->AddInputBlockCallback(
+ result.mInputBlockId,
+ {result.GetStatus(), [&](uint64_t id, const APZHandledResult& answer) {
+ EXPECT_EQ(id, result.mInputBlockId);
+ delayedAnswer = answer;
+ }});
+
+ // Send APZ the relevant notifications to allow it to process the
+ // input block.
+ manager->SetAllowedTouchBehavior(result.mInputBlockId,
+ {AllowedTouchBehavior::VERTICAL_PAN});
+ manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
+ manager->ContentReceivedInputBlock(result.mInputBlockId,
+ /*aPreventDefault=*/false);
+
+ // Check that we received the delayed answer and it is what we expect.
+ EXPECT_EQ(delayedAnswer,
+ (APZHandledResult{APZHandledPlace::HandledByRoot, SideBits::eBottom,
+ EitherScrollDirection}));
+
+ // Now repeat the tap on the bottom half, but simulate a prevent-default.
+ // This time, we expect a delayed answer of `HandledByContent`.
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
+ TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ manager->AddInputBlockCallback(
+ result.mInputBlockId,
+ {result.GetStatus(), [&](uint64_t id, const APZHandledResult& answer) {
+ EXPECT_EQ(id, result.mInputBlockId);
+ delayedAnswer = answer;
+ }});
+ manager->SetAllowedTouchBehavior(result.mInputBlockId,
+ {AllowedTouchBehavior::VERTICAL_PAN});
+ manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
+ manager->ContentReceivedInputBlock(result.mInputBlockId,
+ /*aPreventDefault=*/true);
+ EXPECT_EQ(delayedAnswer,
+ (APZHandledResult{APZHandledPlace::HandledByContent,
+ SideBits::eBottom, EitherScrollDirection}));
+
+ // Shrink the scrollable area, now it's no longer scrollable.
+ ModifyFrameMetrics(root, [](ScrollMetadata& sm, FrameMetrics& metrics) {
+ metrics.SetScrollableRect(CSSRect(0, 0, 100, 100));
+ });
+ UpdateHitTestingTree();
+ // Now repeat the tap on the bottom half with an event handler.
+ // This time, we expect a delayed answer of `Unhandled`.
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
+ {CompositorHitTestFlags::eVisibleToHitTest,
+ CompositorHitTestFlags::eIrregularArea});
+ result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
+ TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
+ EXPECT_EQ(result.GetHandledResult(), Nothing());
+ manager->AddInputBlockCallback(
+ result.mInputBlockId,
+ {result.GetStatus(), [&](uint64_t id, const APZHandledResult& answer) {
+ EXPECT_EQ(id, result.mInputBlockId);
+ delayedAnswer = answer;
+ }});
+ manager->SetAllowedTouchBehavior(result.mInputBlockId,
+ {AllowedTouchBehavior::VERTICAL_PAN});
+ manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
+ manager->ContentReceivedInputBlock(result.mInputBlockId,
+ /*aPreventDefault=*/false);
+ EXPECT_EQ(delayedAnswer,
+ (APZHandledResult{APZHandledPlace::Unhandled, SideBits::eNone,
+ ScrollDirections()}));
+
+ // Repeat the tap on the bottom half, with no event handler.
+ // Make sure we get an eager answer of `Unhandled`.
+ QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
+ result = TouchDown(manager, ScreenIntPoint(50, 75), mcc->Time());
+ TouchUp(manager, ScreenIntPoint(50, 75), mcc->Time());
+ EXPECT_EQ(result.GetStatus(), nsEventStatus_eIgnore);
+ EXPECT_EQ(result.GetHandledResult(),
+ Some(APZHandledResult{APZHandledPlace::Unhandled, SideBits::eNone,
+ EitherScrollDirection}));
+}