/* -*- 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 "gtest/gtest.h" #include "APZCTreeManagerTester.h" #include "APZTestCommon.h" #include "InputUtils.h" #include "apz/src/AsyncPanZoomController.h" #include "apz/src/InputBlockState.h" #include "apz/src/OverscrollHandoffState.h" #include "mozilla/layers/IAPZCTreeManager.h" class APZCArePointerEventsConsumable : public APZCTreeManagerTester { public: APZCArePointerEventsConsumable() { CreateMockHitTester(); } void CreateSingleElementTree() { const char* treeShape = "x"; LayerIntRect layerVisibleRect[] = { LayerIntRect(0, 0, 100, 100), }; CreateScrollData(treeShape, layerVisibleRect); SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, CSSRect(0, 0, 500, 500)); registration = MakeUnique(LayersId{0}, mcc); UpdateHitTestingTree(); ApzcOf(root)->GetFrameMetrics().SetIsRootContent(true); } void CreateScrollHandoffTree() { const char* treeShape = "x(x)"; LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 200, 200), LayerIntRect(50, 50, 100, 100)}; CreateScrollData(treeShape, layerVisibleRect); SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID, CSSRect(0, 0, 300, 300)); SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 200)); SetScrollHandoff(layers[1], root); registration = MakeUnique(LayersId{0}, mcc); UpdateHitTestingTree(); ApzcOf(root)->GetFrameMetrics().SetIsRootContent(true); } RefPtr CreateTouchBlockStateForApzc( const RefPtr& aApzc) { TouchCounter counter{}; TargetConfirmationFlags flags{true}; return new TouchBlockState(aApzc, flags, counter); } void UpdateOverscrollBehavior(ScrollableLayerGuid::ViewID aScrollId, OverscrollBehavior aX, OverscrollBehavior aY) { auto* layer = layers[aScrollId - ScrollableLayerGuid::START_SCROLL_ID]; ModifyFrameMetrics(layer, [aX, aY](ScrollMetadata& sm, FrameMetrics& _) { OverscrollBehaviorInfo overscroll; overscroll.mBehaviorX = aX; overscroll.mBehaviorY = aY; sm.SetOverscrollBehavior(overscroll); }); UpdateHitTestingTree(); } UniquePtr registration; }; TEST_F(APZCArePointerEventsConsumable, EmptyInput) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); RefPtr blockState = CreateTouchBlockStateForApzc(apzc); MultiTouchInput touchInput = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); const PointerEventsConsumableFlags expected{false, false}; const PointerEventsConsumableFlags actual = apzc->ArePointerEventsConsumable(blockState, touchInput); EXPECT_EQ(expected, actual); } TEST_F(APZCArePointerEventsConsumable, ScrollHorizontally) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); RefPtr blockState = CreateTouchBlockStateForApzc(apzc); // Create touch with horizontal 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(30, 10), ScreenSize(0, 0), 0, 0)); blockState->UpdateSlopState(touchStart, false); PointerEventsConsumableFlags actual{}; PointerEventsConsumableFlags expected{}; // Scroll area 500x500, room to pan x, room to pan y expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Scroll area 100x100, no room to pan x, no room to pan y apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 100, 100}); expected = {false, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Scroll area 500x100, room to pan x, no room to pan y apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 500, 100}); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Scroll area 100x500, no room to pan x, room to pan y apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 100, 500}); expected = {false, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } TEST_F(APZCArePointerEventsConsumable, ScrollVertically) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); RefPtr blockState = CreateTouchBlockStateForApzc(apzc); // Create touch with vertical 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(10, 10), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(10, 30), ScreenSize(0, 0), 0, 0)); blockState->UpdateSlopState(touchStart, false); PointerEventsConsumableFlags actual{}; PointerEventsConsumableFlags expected{}; // Scroll area 500x500, room to pan x, room to pan y expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Scroll area 100x100, no room to pan x, no room to pan y apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 100, 100}); expected = {false, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Scroll area 500x100, room to pan x, no room to pan y apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 500, 100}); expected = {false, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Scroll area 100x500, no room to pan x, room to pan y apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 100, 500}); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } TEST_F(APZCArePointerEventsConsumable, NestedElementCanScroll) { CreateScrollHandoffTree(); RefPtr apzc = ApzcOf(layers[1]); RefPtr blockState = CreateTouchBlockStateForApzc(apzc); // Create touch with vertical 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 60), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 80), ScreenSize(0, 0), 0, 0)); blockState->UpdateSlopState(touchStart, false); const PointerEventsConsumableFlags expected{true, true}; const PointerEventsConsumableFlags actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } TEST_F(APZCArePointerEventsConsumable, NestedElementCannotScroll) { CreateScrollHandoffTree(); RefPtr apzc = ApzcOf(layers[1]); RefPtr blockState = CreateTouchBlockStateForApzc(apzc); // Create touch with vertical 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 60), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 80), ScreenSize(0, 0), 0, 0)); blockState->UpdateSlopState(touchStart, false); PointerEventsConsumableFlags actual{}; PointerEventsConsumableFlags expected{}; // Set the nested element to have no room to scroll. // Because of the overscroll handoff, we still have room to scroll // in the parent element. apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 100, 100}); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Set overscroll handoff for the nested element to none. // Because no handoff will happen, we are not able to use the parent's // room to scroll. // Bug 1814886: Once fixed, change expected value to {false, true}. UpdateOverscrollBehavior(ScrollableLayerGuid::START_SCROLL_ID + 1, OverscrollBehavior::None, OverscrollBehavior::None); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } TEST_F(APZCArePointerEventsConsumable, NotScrollableButZoomable) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); RefPtr blockState = CreateTouchBlockStateForApzc(apzc); // Create touch with vertical 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 60), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 80), ScreenSize(0, 0), 0, 0)); blockState->UpdateSlopState(touchStart, false); // Make the root have no room to scroll apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 100, 100}); // Make zoomable apzc->UpdateZoomConstraints(ZoomConstraints( true, true, CSSToParentLayerScale(0.25f), CSSToParentLayerScale(4.0f))); PointerEventsConsumableFlags actual{}; PointerEventsConsumableFlags expected{}; expected = {false, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); // Add a second touch point and therefore make the APZC consider // zoom use cases as well. touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 90), ScreenSize(0, 0), 0, 0)); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } TEST_F(APZCArePointerEventsConsumable, TouchActionsProhibitAll) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); // Create touch with vertical 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 60), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 80), ScreenSize(0, 0), 0, 0)); PointerEventsConsumableFlags expected{}; PointerEventsConsumableFlags actual{}; { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors({AllowedTouchBehavior::NONE}); expected = {true, false}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } // Convert touch input to two-finger pinch touchStart.mTouches.AppendElement( SingleTouchData(1, ScreenIntPoint(80, 80), ScreenSize(0, 0), 0, 0)); touchMove.mTouches.AppendElement( SingleTouchData(1, ScreenIntPoint(90, 90), ScreenSize(0, 0), 0, 0)); { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors({AllowedTouchBehavior::NONE}); expected = {true, false}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } } TEST_F(APZCArePointerEventsConsumable, TouchActionsAllowVerticalScrolling) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); // Create touch with vertical 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 60), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 80), ScreenSize(0, 0), 0, 0)); PointerEventsConsumableFlags expected{}; PointerEventsConsumableFlags actual{}; { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors({AllowedTouchBehavior::NONE}); expected = {true, false}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors({AllowedTouchBehavior::VERTICAL_PAN}); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } } TEST_F(APZCArePointerEventsConsumable, TouchActionsAllowHorizontalScrolling) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); // Create touch with horizontal 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 60), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(80, 60), ScreenSize(0, 0), 0, 0)); PointerEventsConsumableFlags expected{}; PointerEventsConsumableFlags actual{}; { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors({AllowedTouchBehavior::NONE}); expected = {true, false}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors( {AllowedTouchBehavior::HORIZONTAL_PAN}); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } } TEST_F(APZCArePointerEventsConsumable, TouchActionsAllowPinchZoom) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); // Create two-finger pinch MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 60), ScreenSize(0, 0), 0, 0)); touchStart.mTouches.AppendElement( SingleTouchData(1, ScreenIntPoint(80, 80), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(50, 50), ScreenSize(0, 0), 0, 0)); touchMove.mTouches.AppendElement( SingleTouchData(1, ScreenIntPoint(90, 90), ScreenSize(0, 0), 0, 0)); PointerEventsConsumableFlags expected{}; PointerEventsConsumableFlags actual{}; { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors({AllowedTouchBehavior::NONE}); expected = {true, false}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } { RefPtr blockState = CreateTouchBlockStateForApzc(apzc); blockState->UpdateSlopState(touchStart, false); blockState->SetAllowedTouchBehaviors({AllowedTouchBehavior::PINCH_ZOOM}); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); } } TEST_F(APZCArePointerEventsConsumable, DynamicToolbar) { CreateSingleElementTree(); RefPtr apzc = ApzcOf(root); RefPtr blockState = CreateTouchBlockStateForApzc(apzc); // Create touch with vertical 20 unit scroll MultiTouchInput touchStart = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); touchStart.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 30), ScreenSize(0, 0), 0, 0)); MultiTouchInput touchMove = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); touchMove.mTouches.AppendElement( SingleTouchData(0, ScreenIntPoint(60, 40), ScreenSize(0, 0), 0, 0)); blockState->UpdateSlopState(touchStart, false); // Restrict size of scrollable area: No room to pan X, no room to pan Y apzc->GetFrameMetrics().SetScrollableRect(CSSRect{0, 0, 100, 100}); PointerEventsConsumableFlags actual{}; PointerEventsConsumableFlags expected{}; expected = {false, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); apzc->GetFrameMetrics().SetCompositionSizeWithoutDynamicToolbar( ParentLayerSize{100, 90}); UpdateHitTestingTree(); expected = {true, true}; actual = apzc->ArePointerEventsConsumable(blockState, touchMove); EXPECT_EQ(expected, actual); }