TestHitTesting.cpp (14493B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "APZCTreeManagerTester.h" 8 #include "APZTestCommon.h" 9 10 #include "InputUtils.h" 11 12 class APZHitTestingTester : public APZCTreeManagerTester { 13 protected: 14 ScreenToParentLayerMatrix4x4 transformToApzc; 15 ParentLayerToScreenMatrix4x4 transformToGecko; 16 17 already_AddRefed<AsyncPanZoomController> GetTargetAPZC( 18 const ScreenPoint& aPoint) { 19 RefPtr<AsyncPanZoomController> hit = 20 manager->GetTargetAPZC(aPoint).mTargetApzc; 21 if (hit) { 22 transformToApzc = manager->GetScreenToApzcTransform(hit.get()); 23 transformToGecko = 24 manager->GetApzcToGeckoTransform(hit.get(), LayoutAndVisual); 25 } 26 return hit.forget(); 27 } 28 29 protected: 30 void DisableApzOn(WebRenderLayerScrollData* aLayer) { 31 ModifyFrameMetrics(aLayer, [](ScrollMetadata& aSm, FrameMetrics&) { 32 aSm.SetForceDisableApz(true); 33 }); 34 } 35 36 void CreateComplexMultiLayerTree() { 37 const char* treeShape = "x(xx(x)xx(x(x)xx))"; 38 // LayerID 0 12 3 45 6 7 89 39 LayerIntRect layerVisibleRect[] = { 40 LayerIntRect(0, 0, 300, 400), // root(0) 41 LayerIntRect(0, 0, 100, 100), // layer(1) in top-left 42 LayerIntRect(50, 50, 200, 300), // layer(2) centered in root(0) 43 LayerIntRect(50, 50, 200, 44 300), // layer(3) fully occupying parent layer(2) 45 LayerIntRect(0, 200, 100, 100), // layer(4) in bottom-left 46 LayerIntRect(200, 0, 100, 47 400), // layer(5) along the right 100px of root(0) 48 LayerIntRect(200, 0, 100, 200), // layer(6) taking up the top 49 // half of parent layer(5) 50 LayerIntRect(200, 0, 100, 51 200), // layer(7) fully occupying parent layer(6) 52 LayerIntRect(200, 200, 100, 53 100), // layer(8) in bottom-right (below (6)) 54 LayerIntRect(200, 300, 100, 55 100), // layer(9) in bottom-right (below (8)) 56 }; 57 CreateScrollData(treeShape, layerVisibleRect); 58 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID); 59 SetScrollableFrameMetrics(layers[2], ScrollableLayerGuid::START_SCROLL_ID); 60 SetScrollableFrameMetrics(layers[4], 61 ScrollableLayerGuid::START_SCROLL_ID + 1); 62 SetScrollableFrameMetrics(layers[6], 63 ScrollableLayerGuid::START_SCROLL_ID + 1); 64 SetScrollableFrameMetrics(layers[7], 65 ScrollableLayerGuid::START_SCROLL_ID + 2); 66 SetScrollableFrameMetrics(layers[8], 67 ScrollableLayerGuid::START_SCROLL_ID + 1); 68 SetScrollableFrameMetrics(layers[9], 69 ScrollableLayerGuid::START_SCROLL_ID + 3); 70 } 71 72 void CreateBug1148350LayerTree() { 73 const char* treeShape = "x(x)"; 74 // LayerID 0 1 75 LayerIntRect layerVisibleRect[] = { 76 LayerIntRect(0, 0, 200, 200), 77 LayerIntRect(0, 0, 200, 200), 78 }; 79 CreateScrollData(treeShape, layerVisibleRect); 80 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID); 81 } 82 }; 83 84 TEST_F(APZHitTestingTester, ComplexMultiLayerTree) { 85 CreateComplexMultiLayerTree(); 86 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 87 UpdateHitTestingTree(); 88 89 /* The layer tree looks like this: 90 91 0 92 |----|--+--|----| 93 1 2 4 5 94 | /|\ 95 3 6 8 9 96 | 97 7 98 99 Layers 1,2 have the same APZC 100 Layers 4,6,8 have the same APZC 101 Layer 7 has an APZC 102 Layer 9 has an APZC 103 */ 104 105 TestAsyncPanZoomController* nullAPZC = nullptr; 106 // Ensure all the scrollable layers have an APZC 107 108 EXPECT_FALSE(HasScrollableFrameMetrics(layers[0])); 109 EXPECT_NE(nullAPZC, ApzcOf(layers[1])); 110 EXPECT_NE(nullAPZC, ApzcOf(layers[2])); 111 EXPECT_FALSE(HasScrollableFrameMetrics(layers[3])); 112 EXPECT_NE(nullAPZC, ApzcOf(layers[4])); 113 EXPECT_FALSE(HasScrollableFrameMetrics(layers[5])); 114 EXPECT_NE(nullAPZC, ApzcOf(layers[6])); 115 EXPECT_NE(nullAPZC, ApzcOf(layers[7])); 116 EXPECT_NE(nullAPZC, ApzcOf(layers[8])); 117 EXPECT_NE(nullAPZC, ApzcOf(layers[9])); 118 // Ensure those that scroll together have the same APZCs 119 EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2])); 120 EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6])); 121 EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6])); 122 // Ensure those that don't scroll together have different APZCs 123 EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4])); 124 EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7])); 125 EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9])); 126 EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7])); 127 EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9])); 128 EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9])); 129 // Ensure the APZC parent chains are set up correctly 130 TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]); 131 TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]); 132 TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]); 133 TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]); 134 EXPECT_EQ(nullptr, layers1_2->GetParent()); 135 EXPECT_EQ(nullptr, layers4_6_8->GetParent()); 136 EXPECT_EQ(layers4_6_8, layer7->GetParent()); 137 EXPECT_EQ(nullptr, layer9->GetParent()); 138 // Ensure the hit-testing tree looks like the layer tree 139 RefPtr<HitTestingTreeNode> root = manager->GetRootNode(); 140 RefPtr<HitTestingTreeNode> node5 = root->GetLastChild(); 141 RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling(); 142 RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling(); 143 RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling(); 144 RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild(); 145 RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild(); 146 RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling(); 147 RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling(); 148 RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild(); 149 EXPECT_EQ(nullptr, node1->GetPrevSibling()); 150 EXPECT_EQ(nullptr, node3->GetPrevSibling()); 151 EXPECT_EQ(nullptr, node6->GetPrevSibling()); 152 EXPECT_EQ(nullptr, node7->GetPrevSibling()); 153 EXPECT_EQ(nullptr, node1->GetLastChild()); 154 EXPECT_EQ(nullptr, node3->GetLastChild()); 155 EXPECT_EQ(nullptr, node4->GetLastChild()); 156 EXPECT_EQ(nullptr, node7->GetLastChild()); 157 EXPECT_EQ(nullptr, node8->GetLastChild()); 158 EXPECT_EQ(nullptr, node9->GetLastChild()); 159 160 // Assertions about hit-testing have been ported to mochitest, 161 // in helper_hittest_bug1730606-4.html. 162 } 163 164 TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) { 165 // The main purpose of this test is to verify that touch-start events (or 166 // anything that starts a new input block) don't ever get untransformed. This 167 // should always hold because the APZ code should flush repaints when we start 168 // a new input block and the transform to gecko space should be empty. 169 170 CreateSimpleScrollingLayer(); 171 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 172 UpdateHitTestingTree(); 173 RefPtr<TestAsyncPanZoomController> apzcroot = ApzcOf(root); 174 175 // At this point, the following holds (all coordinates in screen pixels): 176 // layers[0] has content from (0,0)-(500,500), clipped by composition bounds 177 // (0,0)-(200,200) 178 179 MockFunction<void(std::string checkPointName)> check; 180 181 { 182 InSequence s; 183 184 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1)); 185 EXPECT_CALL(check, Call("post-first-touch-start")); 186 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1)); 187 EXPECT_CALL(check, Call("post-second-fling")); 188 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1)); 189 EXPECT_CALL(check, Call("post-second-touch-start")); 190 } 191 192 // This first pan will move the APZC by 50 pixels, and dispatch a paint 193 // request. 194 Pan(apzcroot, 100, 50, PanOptions::NoFling); 195 196 // Verify that a touch start doesn't get untransformed 197 ScreenIntPoint touchPoint(50, 50); 198 MultiTouchInput mti = 199 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); 200 mti.mTouches.AppendElement( 201 SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0)); 202 203 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, 204 manager->ReceiveInputEvent(mti).GetStatus()); 205 EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint); 206 check.Call("post-first-touch-start"); 207 208 // Send a touchend to clear state 209 mti.mType = MultiTouchInput::MULTITOUCH_END; 210 manager->ReceiveInputEvent(mti); 211 212 mcc->AdvanceByMillis(1000); 213 214 // Now do two pans. The first of these will dispatch a repaint request, as 215 // above. The second will get stuck in the paint throttler because the first 216 // one doesn't get marked as "completed", so this will result in a non-empty 217 // LD transform. (Note that any outstanding repaint requests from the first 218 // half of this test don't impact this half because we advance the time by 1 219 // second, which will trigger the max-wait-exceeded codepath in the paint 220 // throttler). 221 Pan(apzcroot, 100, 50, PanOptions::NoFling); 222 check.Call("post-second-fling"); 223 Pan(apzcroot, 100, 50, PanOptions::NoFling); 224 225 // Ensure that a touch start again doesn't get untransformed by flushing 226 // a repaint 227 mti.mType = MultiTouchInput::MULTITOUCH_START; 228 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, 229 manager->ReceiveInputEvent(mti).GetStatus()); 230 EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint); 231 check.Call("post-second-touch-start"); 232 233 mti.mType = MultiTouchInput::MULTITOUCH_END; 234 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, 235 manager->ReceiveInputEvent(mti).GetStatus()); 236 EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint); 237 } 238 239 TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) { 240 // The purpose of this test is to ensure that wheel events trigger a repaint 241 // flush as per bug 1166871, and that the wheel event untransform is a no-op. 242 243 CreateSimpleScrollingLayer(); 244 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 245 UpdateHitTestingTree(); 246 TestAsyncPanZoomController* apzcroot = ApzcOf(root); 247 248 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3)); 249 ScreenPoint origin(100, 50); 250 for (int i = 0; i < 3; i++) { 251 ScrollWheelInput swi(mcc->Time(), 0, ScrollWheelInput::SCROLLMODE_INSTANT, 252 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 10, 253 false, WheelDeltaAdjustmentStrategy::eNone); 254 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, 255 manager->ReceiveInputEvent(swi).GetStatus()); 256 EXPECT_EQ(origin, swi.mOrigin); 257 258 AsyncTransform viewTransform; 259 ParentLayerPoint point; 260 apzcroot->SampleContentTransformForFrame(&viewTransform, point); 261 EXPECT_EQ(0, point.x); 262 EXPECT_EQ((i + 1) * 10, point.y); 263 EXPECT_EQ(0, viewTransform.mTranslation.x); 264 EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y); 265 266 mcc->AdvanceByMillis(5); 267 } 268 } 269 270 TEST_F(APZHitTestingTester, TestForceDisableApz) { 271 CreateSimpleScrollingLayer(); 272 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 273 UpdateHitTestingTree(); 274 DisableApzOn(root); 275 TestAsyncPanZoomController* apzcroot = ApzcOf(root); 276 277 ScreenPoint origin(100, 50); 278 ScrollWheelInput swi(mcc->Time(), 0, ScrollWheelInput::SCROLLMODE_INSTANT, 279 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 10, 280 false, WheelDeltaAdjustmentStrategy::eNone); 281 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, 282 manager->ReceiveInputEvent(swi).GetStatus()); 283 EXPECT_EQ(origin, swi.mOrigin); 284 285 AsyncTransform viewTransform; 286 ParentLayerPoint point; 287 apzcroot->SampleContentTransformForFrame(&viewTransform, point); 288 // Since APZ is force-disabled, we expect to see the async transform via 289 // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode. 290 EXPECT_EQ(0, point.x); 291 EXPECT_EQ(10, point.y); 292 EXPECT_EQ(0, viewTransform.mTranslation.x); 293 EXPECT_EQ(-10, viewTransform.mTranslation.y); 294 viewTransform = apzcroot->GetCurrentAsyncTransform( 295 AsyncPanZoomController::eForCompositing); 296 point = apzcroot->GetCurrentAsyncScrollOffset( 297 AsyncPanZoomController::eForCompositing); 298 EXPECT_EQ(0, point.x); 299 EXPECT_EQ(0, point.y); 300 EXPECT_EQ(0, viewTransform.mTranslation.x); 301 EXPECT_EQ(0, viewTransform.mTranslation.y); 302 303 mcc->AdvanceByMillis(10); 304 305 // With untransforming events we should get normal behaviour (in this case, 306 // no noticeable untransform, because the repaint request already got 307 // flushed). 308 swi = ScrollWheelInput(mcc->Time(), 0, ScrollWheelInput::SCROLLMODE_INSTANT, 309 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 0, 310 false, WheelDeltaAdjustmentStrategy::eNone); 311 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, 312 manager->ReceiveInputEvent(swi).GetStatus()); 313 EXPECT_EQ(origin, swi.mOrigin); 314 } 315 316 TEST_F(APZHitTestingTester, Bug1148350) { 317 CreateBug1148350LayerTree(); 318 ScopedLayerTreeRegistration registration(LayersId{0}, mcc); 319 UpdateHitTestingTree(); 320 321 MockFunction<void(std::string checkPointName)> check; 322 { 323 InSequence s; 324 EXPECT_CALL(*mcc, 325 HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, 326 ApzcOf(layers[1])->GetGuid(), _, _)) 327 .Times(1); 328 EXPECT_CALL(check, Call("Tapped without transform")); 329 EXPECT_CALL(*mcc, 330 HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0, 331 ApzcOf(layers[1])->GetGuid(), _, _)) 332 .Times(1); 333 EXPECT_CALL(check, Call("Tapped with interleaved transform")); 334 } 335 336 Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100)); 337 mcc->RunThroughDelayedTasks(); 338 check.Call("Tapped without transform"); 339 340 uint64_t blockId = 341 TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time()).mInputBlockId; 342 SetDefaultAllowedTouchBehavior(manager, blockId); 343 mcc->AdvanceByMillis(100); 344 345 layers[0]->SetVisibleRect(LayerIntRect(0, 50, 200, 150)); 346 layers[0]->SetTransform(Matrix4x4::Translation(0, 50, 0)); 347 UpdateHitTestingTree(); 348 349 TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time()); 350 mcc->RunThroughDelayedTasks(); 351 check.Call("Tapped with interleaved transform"); 352 }