diff options
Diffstat (limited to 'gfx/layers/apz/test/gtest/TestEventResult.cpp')
-rw-r--r-- | gfx/layers/apz/test/gtest/TestEventResult.cpp | 476 |
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})); +} |