/* -*- 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/layers/LayersTypes.h" class APZEventRegionsTester : public APZCTreeManagerTester { protected: UniquePtr registration; TestAsyncPanZoomController* rootApzc; void CreateEventRegionsLayerTree1() { const char* treeShape = "x(xx)"; LayerIntRegion layerVisibleRegions[] = { LayerIntRect(0, 0, 200, 200), // root LayerIntRect(0, 0, 100, 200), // left half LayerIntRect(0, 100, 200, 100), // bottom half }; CreateScrollData(treeShape, layerVisibleRegions); SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID); SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1); SetScrollableFrameMetrics(layers[2], ScrollableLayerGuid::START_SCROLL_ID + 2); SetScrollHandoff(layers[1], root); SetScrollHandoff(layers[2], root); registration = MakeUnique(LayersId{0}, mcc); UpdateHitTestingTree(); rootApzc = ApzcOf(root); } void CreateEventRegionsLayerTree2() { const char* treeShape = "x(x)"; LayerIntRegion layerVisibleRegions[] = { LayerIntRect(0, 0, 100, 500), LayerIntRect(0, 150, 100, 100), }; CreateScrollData(treeShape, layerVisibleRegions); SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID); registration = MakeUnique(LayersId{0}, mcc); UpdateHitTestingTree(); rootApzc = ApzcOf(root); } void CreateBug1117712LayerTree() { const char* treeShape = "x(x(x)x)"; // LayerID 0 1 2 3 // 0 is the root // 1 is a container layer whose sole purpose to make a non-empty ancestor // transform for 2, so that 2's screen-to-apzc and apzc-to-gecko // transforms are different from 3's. // 2 is a small layer that is the actual target // 3 is a big layer obscuring 2 with a dispatch-to-content region LayerIntRegion layerVisibleRegions[] = { LayerIntRect(0, 0, 100, 100), LayerIntRect(0, 0, 0, 0), LayerIntRect(0, 0, 10, 10), LayerIntRect(0, 0, 100, 100), }; Matrix4x4 layerTransforms[] = { Matrix4x4(), Matrix4x4::Translation(50, 0, 0), Matrix4x4(), Matrix4x4(), }; CreateScrollData(treeShape, layerVisibleRegions, layerTransforms); SetScrollableFrameMetrics(layers[2], ScrollableLayerGuid::START_SCROLL_ID, CSSRect(0, 0, 10, 10)); SetScrollableFrameMetrics(layers[3], ScrollableLayerGuid::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100)); SetScrollHandoff(layers[3], layers[2]); registration = MakeUnique(LayersId{0}, mcc); UpdateHitTestingTree(); } }; class APZEventRegionsTesterMock : public APZEventRegionsTester { public: APZEventRegionsTesterMock() { CreateMockHitTester(); } }; TEST_F(APZEventRegionsTesterMock, HitRegionImmediateResponse) { CreateEventRegionsLayerTree1(); TestAsyncPanZoomController* root = ApzcOf(layers[0]); TestAsyncPanZoomController* left = ApzcOf(layers[1]); TestAsyncPanZoomController* bottom = ApzcOf(layers[2]); MockFunction check; { InSequence s; EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)) .Times(1); EXPECT_CALL(check, Call("Tapped on left")); EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)) .Times(1); EXPECT_CALL(check, Call("Tapped on bottom")); EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _)) .Times(1); EXPECT_CALL(check, Call("Tapped on root")); EXPECT_CALL(check, Call("Tap pending on d-t-c region")); EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)) .Times(1); EXPECT_CALL(check, Call("Tapped on bottom again")); EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)) .Times(1); EXPECT_CALL(check, Call("Tapped on left this time")); } TimeDuration tapDuration = TimeDuration::FromMilliseconds(100); // Tap in the exposed hit regions of each of the layers once and ensure // the clicks are dispatched right away QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1); Tap(manager, ScreenIntPoint(10, 10), tapDuration); mcc->RunThroughDelayedTasks(); // this runs the tap event check.Call("Tapped on left"); QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 2); Tap(manager, ScreenIntPoint(110, 110), tapDuration); mcc->RunThroughDelayedTasks(); // this runs the tap event check.Call("Tapped on bottom"); QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); Tap(manager, ScreenIntPoint(110, 10), tapDuration); mcc->RunThroughDelayedTasks(); // this runs the tap event check.Call("Tapped on root"); // Now tap on the dispatch-to-content region where the layers overlap QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 2, {CompositorHitTestFlags::eVisibleToHitTest, CompositorHitTestFlags::eIrregularArea}); Tap(manager, ScreenIntPoint(10, 110), tapDuration); mcc->RunThroughDelayedTasks(); // this runs the main-thread timeout check.Call("Tap pending on d-t-c region"); mcc->RunThroughDelayedTasks(); // this runs the tap event check.Call("Tapped on bottom again"); // Now let's do that again, but simulate a main-thread response uint64_t inputBlockId = 0; QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 2, {CompositorHitTestFlags::eVisibleToHitTest, CompositorHitTestFlags::eIrregularArea}); Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId); nsTArray targets; targets.AppendElement(left->GetGuid()); manager->SetTargetAPZC(inputBlockId, targets); while (mcc->RunThroughDelayedTasks()) ; // this runs the tap event check.Call("Tapped on left this time"); } TEST_F(APZEventRegionsTesterMock, HitRegionAccumulatesChildren) { CreateEventRegionsLayerTree2(); // Tap in the area of the child layer that's not directly included in the // parent layer's hit region. Verify that it comes out of the APZC's // content controller, which indicates the input events got routed correctly // to the APZC. EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)) .Times(1); QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID); Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100)); } TEST_F(APZEventRegionsTesterMock, Bug1117712) { CreateBug1117712LayerTree(); TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]); // These touch events should hit the dispatch-to-content region of layers[3] // and so get queued with that APZC as the tentative target. uint64_t inputBlockId = 0; QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1, {CompositorHitTestFlags::eVisibleToHitTest, CompositorHitTestFlags::eIrregularArea}); Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId); // But now we tell the APZ that really it hit layers[2], and expect the tap // to be delivered at the correct coordinates. EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0, apzc2->GetGuid(), _)) .Times(1); nsTArray targets; targets.AppendElement(apzc2->GetGuid()); manager->SetTargetAPZC(inputBlockId, targets); }