TestGestureDetector.cpp (45203B)
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 "gtest/gtest.h" 8 #include "gmock/gmock.h" 9 10 #include "APZCBasicTester.h" 11 #include "APZTestCommon.h" 12 #include "InputUtils.h" 13 #include "apz/src/InputBlockState.h" 14 #include "mozilla/StaticPrefs_apz.h" 15 16 // Note: There are additional tests that test gesture detection behaviour 17 // with multiple APZCs in TestTreeManager.cpp. 18 19 class APZCGestureDetectorTester : public APZCBasicTester { 20 public: 21 APZCGestureDetectorTester() 22 : APZCBasicTester(AsyncPanZoomController::USE_GESTURE_DETECTOR) {} 23 24 protected: 25 FrameMetrics GetPinchableFrameMetrics(float aZoom = 2.0f) { 26 FrameMetrics fm; 27 fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200)); 28 fm.SetScrollableRect(CSSRect(0, 0, 980, 1000)); 29 fm.SetVisualScrollOffset(CSSPoint(300, 300)); 30 fm.SetZoom(CSSToParentLayerScale(aZoom)); 31 // APZC only allows zooming on the root scrollable frame. 32 fm.SetIsRootContent(true); 33 // the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100 34 return fm; 35 } 36 }; 37 38 #ifndef MOZ_WIDGET_ANDROID // Currently fails on Android 39 TEST_F(APZCGestureDetectorTester, Pan_After_Pinch) { 40 SCOPED_GFX_PREF_INT("apz.axis_lock.mode", 2); 41 SCOPED_GFX_PREF_FLOAT("apz.axis_lock.lock_angle", M_PI / 6.0f); 42 SCOPED_GFX_PREF_FLOAT("apz.axis_lock.breakout_angle", M_PI / 8.0f); 43 44 FrameMetrics originalMetrics = GetPinchableFrameMetrics(); 45 apzc->SetFrameMetrics(originalMetrics); 46 47 MakeApzcZoomable(); 48 49 // Test parameters 50 float zoomAmount = 1.25; 51 float pinchLength = 100.0; 52 float pinchLengthScaled = pinchLength * zoomAmount; 53 int focusX = 250; 54 int focusY = 300; 55 int panDistance = 20; 56 const TimeDuration TIME_BETWEEN_TOUCH_EVENT = 57 TimeDuration::FromMilliseconds(50); 58 59 int firstFingerId = 0; 60 int secondFingerId = firstFingerId + 1; 61 62 // Put fingers down 63 MultiTouchInput mti = 64 MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, mcc->Time(), 0); 65 mti.mTouches.AppendElement( 66 CreateSingleTouchData(firstFingerId, focusX, focusY)); 67 mti.mTouches.AppendElement( 68 CreateSingleTouchData(secondFingerId, focusX, focusY)); 69 apzc->ReceiveInputEvent(mti, Some(nsTArray<uint32_t>{kDefaultTouchBehavior})); 70 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 71 72 // Spread fingers out to enter the pinch state 73 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); 74 mti.mTouches.AppendElement(CreateSingleTouchData( 75 firstFingerId, static_cast<int32_t>(focusX - pinchLength), focusY)); 76 mti.mTouches.AppendElement(CreateSingleTouchData( 77 secondFingerId, static_cast<int32_t>(focusX + pinchLength), focusY)); 78 apzc->ReceiveInputEvent(mti); 79 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 80 81 // Do the actual pinch of 1.25x 82 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); 83 mti.mTouches.AppendElement(CreateSingleTouchData( 84 firstFingerId, static_cast<int32_t>(focusX - pinchLengthScaled), focusY)); 85 mti.mTouches.AppendElement(CreateSingleTouchData( 86 secondFingerId, static_cast<int32_t>(focusX + pinchLengthScaled), 87 focusY)); 88 apzc->ReceiveInputEvent(mti); 89 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 90 91 // Verify that the zoom changed, just to make sure our code above did what it 92 // was supposed to. 93 FrameMetrics zoomedMetrics = apzc->GetFrameMetrics(); 94 float newZoom = zoomedMetrics.GetZoom().scale; 95 EXPECT_EQ(originalMetrics.GetZoom().scale * zoomAmount, newZoom); 96 97 // Now we lift one finger... 98 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, mcc->Time(), 0); 99 mti.mTouches.AppendElement(CreateSingleTouchData( 100 secondFingerId, static_cast<int32_t>(focusX + pinchLengthScaled), 101 focusY)); 102 apzc->ReceiveInputEvent(mti); 103 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 104 105 // ... and pan with the remaining finger. This pan just breaks through the 106 // distance threshold. 107 focusY += StaticPrefs::apz_touch_start_tolerance() * tm->GetDPI(); 108 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); 109 mti.mTouches.AppendElement(CreateSingleTouchData( 110 firstFingerId, static_cast<int32_t>(focusX - pinchLengthScaled), focusY)); 111 apzc->ReceiveInputEvent(mti); 112 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 113 114 // This one does an actual pan of 20 pixels 115 focusY += panDistance; 116 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); 117 mti.mTouches.AppendElement(CreateSingleTouchData( 118 firstFingerId, static_cast<int32_t>(focusX - pinchLengthScaled), focusY)); 119 apzc->ReceiveInputEvent(mti); 120 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 121 122 // Lift the remaining finger 123 mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, mcc->Time(), 0); 124 mti.mTouches.AppendElement(CreateSingleTouchData( 125 firstFingerId, static_cast<int32_t>(focusX - pinchLengthScaled), focusY)); 126 apzc->ReceiveInputEvent(mti); 127 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 128 129 // Verify that we scrolled 130 FrameMetrics finalMetrics = apzc->GetFrameMetrics(); 131 EXPECT_EQ(zoomedMetrics.GetVisualScrollOffset().y - (panDistance / newZoom), 132 finalMetrics.GetVisualScrollOffset().y); 133 134 // Clear out any remaining fling animation and pending tasks 135 apzc->AdvanceAnimationsUntilEnd(); 136 while (mcc->RunThroughDelayedTasks()); 137 apzc->AssertStateIsReset(); 138 } 139 #endif 140 141 TEST_F(APZCGestureDetectorTester, Pan_With_Tap) { 142 SCOPED_GFX_PREF_FLOAT("apz.touch_start_tolerance", 0.1); 143 144 FrameMetrics originalMetrics = GetPinchableFrameMetrics(); 145 apzc->SetFrameMetrics(originalMetrics); 146 147 // Making the APZC zoomable isn't really needed for the correct operation of 148 // this test, but it could help catch regressions where we accidentally enter 149 // a pinch state. 150 MakeApzcZoomable(); 151 152 // Test parameters 153 int touchX = 250; 154 int touchY = 300; 155 int panDistance = 20; 156 157 int firstFingerId = 0; 158 int secondFingerId = firstFingerId + 1; 159 160 const float panThreshold = 161 StaticPrefs::apz_touch_start_tolerance() * tm->GetDPI(); 162 163 // Put finger down 164 MultiTouchInput mti = 165 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); 166 mti.mTouches.AppendElement( 167 CreateSingleTouchData(firstFingerId, touchX, touchY)); 168 apzc->ReceiveInputEvent(mti, Some(nsTArray<uint32_t>{kDefaultTouchBehavior})); 169 170 // Start a pan, break through the threshold 171 touchY += panThreshold; 172 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); 173 mti.mTouches.AppendElement( 174 CreateSingleTouchData(firstFingerId, touchX, touchY)); 175 apzc->ReceiveInputEvent(mti); 176 177 // Do an actual pan for a bit 178 touchY += panDistance; 179 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); 180 mti.mTouches.AppendElement( 181 CreateSingleTouchData(firstFingerId, touchX, touchY)); 182 apzc->ReceiveInputEvent(mti); 183 184 // Put a second finger down 185 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); 186 mti.mTouches.AppendElement( 187 CreateSingleTouchData(firstFingerId, touchX, touchY)); 188 mti.mTouches.AppendElement( 189 CreateSingleTouchData(secondFingerId, touchX + 10, touchY)); 190 apzc->ReceiveInputEvent(mti, Some(nsTArray<uint32_t>{kDefaultTouchBehavior})); 191 192 // Lift the second finger 193 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time()); 194 mti.mTouches.AppendElement( 195 CreateSingleTouchData(secondFingerId, touchX + 10, touchY)); 196 apzc->ReceiveInputEvent(mti); 197 198 // Bust through the threshold again 199 touchY += panThreshold; 200 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); 201 mti.mTouches.AppendElement( 202 CreateSingleTouchData(firstFingerId, touchX, touchY)); 203 apzc->ReceiveInputEvent(mti); 204 205 // Do some more actual panning 206 touchY += panDistance; 207 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); 208 mti.mTouches.AppendElement( 209 CreateSingleTouchData(firstFingerId, touchX, touchY)); 210 apzc->ReceiveInputEvent(mti); 211 212 // Lift the first finger 213 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time()); 214 mti.mTouches.AppendElement( 215 CreateSingleTouchData(firstFingerId, touchX, touchY)); 216 apzc->ReceiveInputEvent(mti); 217 218 // Verify that we scrolled 219 FrameMetrics finalMetrics = apzc->GetFrameMetrics(); 220 float zoom = finalMetrics.GetZoom().scale; 221 EXPECT_EQ( 222 originalMetrics.GetVisualScrollOffset().y - (panDistance * 2 / zoom), 223 finalMetrics.GetVisualScrollOffset().y); 224 225 // Clear out any remaining fling animation and pending tasks 226 apzc->AdvanceAnimationsUntilEnd(); 227 while (mcc->RunThroughDelayedTasks()); 228 apzc->AssertStateIsReset(); 229 } 230 231 TEST_F(APZCGestureDetectorTester, SecondTapIsFar_Bug1586496) { 232 // Test that we receive two single-tap events when two tap gestures are 233 // close in time but far in distance. 234 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) 235 .Times(2); 236 237 TimeDuration brief = 238 TimeDuration::FromMilliseconds(StaticPrefs::apz_max_tap_time() / 10.0); 239 240 ScreenIntPoint point(10, 10); 241 Tap(apzc, point, brief); 242 243 mcc->AdvanceBy(brief); 244 245 point.x += static_cast<int32_t>(apzc->GetSecondTapTolerance() * 2); 246 point.y += static_cast<int32_t>(apzc->GetSecondTapTolerance() * 2); 247 248 Tap(apzc, point, brief); 249 } 250 251 class APZCFlingStopTester : public APZCGestureDetectorTester { 252 protected: 253 // Start a fling, and then tap while the fling is ongoing. When 254 // aSlow is false, the tap will happen while the fling is at a 255 // high velocity, and we check that the tap doesn't trigger sending a tap 256 // to content. If aSlow is true, the tap will happen while the fling 257 // is at a slow velocity, and we check that the tap does trigger sending 258 // a tap to content. See bug 1022956. 259 void DoFlingStopTest(bool aSlow) { 260 int touchStart = 50; 261 int touchEnd = 10; 262 263 // Start the fling down. 264 Pan(apzc, touchStart, touchEnd); 265 // The touchstart from the pan will leave some cancelled tasks in the queue, 266 // clear them out 267 268 // If we want to tap while the fling is fast, let the fling advance for 10ms 269 // only. If we want the fling to slow down more, advance to 2000ms. These 270 // numbers may need adjusting if our friction and threshold values change, 271 // but they should be deterministic at least. 272 int timeDelta = aSlow ? 2000 : 10; 273 int tapCallsExpected = aSlow ? 2 : 1; 274 275 // Advance the fling animation by timeDelta milliseconds. 276 ParentLayerPoint pointOut; 277 AsyncTransform viewTransformOut; 278 apzc->SampleContentTransformForFrame( 279 &viewTransformOut, pointOut, TimeDuration::FromMilliseconds(timeDelta)); 280 281 // Deliver a tap to abort the fling. Ensure that we get a SingleTap 282 // call out of it if and only if the fling is slow. 283 EXPECT_CALL(*mcc, 284 HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) 285 .Times(tapCallsExpected); 286 Tap(apzc, ScreenIntPoint(10, 10), 0); 287 while (mcc->RunThroughDelayedTasks()); 288 289 // Deliver another tap, to make sure that taps are flowing properly once 290 // the fling is aborted. 291 Tap(apzc, ScreenIntPoint(100, 100), 0); 292 while (mcc->RunThroughDelayedTasks()); 293 294 // Verify that we didn't advance any further after the fling was aborted, in 295 // either case. 296 ParentLayerPoint finalPointOut; 297 apzc->SampleContentTransformForFrame(&viewTransformOut, finalPointOut); 298 EXPECT_EQ(pointOut.x, finalPointOut.x); 299 EXPECT_EQ(pointOut.y, finalPointOut.y); 300 301 apzc->AssertStateIsReset(); 302 } 303 304 void DoFlingStopWithSlowListener(bool aPreventDefault) { 305 MakeApzcWaitForMainThread(); 306 307 int touchStart = 50; 308 int touchEnd = 10; 309 uint64_t blockId = 0; 310 311 // Start the fling down. 312 Pan(apzc, touchStart, touchEnd, PanOptions::None, nullptr, nullptr, 313 &blockId); 314 apzc->ConfirmTarget(blockId); 315 apzc->ContentReceivedInputBlock(blockId, false); 316 317 // Sample the fling a couple of times to ensure it's going. 318 ParentLayerPoint point, finalPoint; 319 AsyncTransform viewTransform; 320 apzc->SampleContentTransformForFrame(&viewTransform, point, 321 TimeDuration::FromMilliseconds(10)); 322 apzc->SampleContentTransformForFrame(&viewTransform, finalPoint, 323 TimeDuration::FromMilliseconds(10)); 324 EXPECT_GT(finalPoint.y, point.y); 325 326 // Now we put our finger down to stop the fling 327 blockId = 328 TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()).mInputBlockId; 329 330 // Re-sample to make sure it hasn't moved 331 apzc->SampleContentTransformForFrame(&viewTransform, point, 332 TimeDuration::FromMilliseconds(10)); 333 EXPECT_EQ(finalPoint.x, point.x); 334 EXPECT_EQ(finalPoint.y, point.y); 335 336 // respond to the touchdown that stopped the fling. 337 // even if we do a prevent-default on it, the animation should remain 338 // stopped. 339 apzc->ContentReceivedInputBlock(blockId, aPreventDefault); 340 341 // Verify the page hasn't moved 342 apzc->SampleContentTransformForFrame(&viewTransform, point, 343 TimeDuration::FromMilliseconds(70)); 344 EXPECT_EQ(finalPoint.x, point.x); 345 EXPECT_EQ(finalPoint.y, point.y); 346 347 // clean up 348 TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time()); 349 350 apzc->AssertStateIsReset(); 351 } 352 }; 353 354 TEST_F(APZCFlingStopTester, FlingStop) { 355 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 356 DoFlingStopTest(false); 357 } 358 359 TEST_F(APZCFlingStopTester, FlingStopTap) { 360 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 361 DoFlingStopTest(true); 362 } 363 364 TEST_F(APZCFlingStopTester, FlingStopSlowListener) { 365 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 366 DoFlingStopWithSlowListener(false); 367 } 368 369 TEST_F(APZCFlingStopTester, FlingStopPreventDefault) { 370 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f); 371 DoFlingStopWithSlowListener(true); 372 } 373 374 TEST_F(APZCGestureDetectorTester, ShortPress) { 375 MakeApzcUnzoomable(); 376 377 MockFunction<void(std::string checkPointName)> check; 378 { 379 InSequence s; 380 // This verifies that the single tap notification is sent after the 381 // touchup is fully processed. The ordering here is important. 382 EXPECT_CALL(check, Call("pre-tap")); 383 EXPECT_CALL(check, Call("post-tap")); 384 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 385 0, apzc->GetGuid(), _, _)) 386 .Times(1); 387 } 388 389 check.Call("pre-tap"); 390 TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), 391 TimeDuration::FromMilliseconds(100)); 392 check.Call("post-tap"); 393 394 apzc->AssertStateIsReset(); 395 } 396 397 TEST_F(APZCGestureDetectorTester, MediumPress) { 398 MakeApzcUnzoomable(); 399 400 MockFunction<void(std::string checkPointName)> check; 401 { 402 InSequence s; 403 // This verifies that the single tap notification is sent after the 404 // touchup is fully processed. The ordering here is important. 405 EXPECT_CALL(check, Call("pre-tap")); 406 EXPECT_CALL(check, Call("post-tap")); 407 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 408 0, apzc->GetGuid(), _, _)) 409 .Times(1); 410 } 411 412 check.Call("pre-tap"); 413 TapAndCheckStatus(apzc, ScreenIntPoint(10, 10), 414 TimeDuration::FromMilliseconds(400)); 415 check.Call("post-tap"); 416 417 apzc->AssertStateIsReset(); 418 } 419 420 class APZCLongPressTester : public APZCGestureDetectorTester { 421 protected: 422 void DoLongPressTest(uint32_t aBehavior) { 423 MakeApzcUnzoomable(); 424 425 APZEventResult result = 426 TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 427 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, result.GetStatus()); 428 uint64_t blockId = result.mInputBlockId; 429 430 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 431 // SetAllowedTouchBehavior() must be called after sending touch-start. 432 nsTArray<uint32_t> allowedTouchBehaviors; 433 allowedTouchBehaviors.AppendElement(aBehavior); 434 apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors); 435 } 436 // Have content "respond" to the touchstart 437 apzc->ContentReceivedInputBlock(blockId, false); 438 439 MockFunction<void(std::string checkPointName)> check; 440 441 { 442 InSequence s; 443 444 EXPECT_CALL(check, Call("preHandleLongTap")); 445 blockId++; 446 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 447 0, apzc->GetGuid(), blockId, _)) 448 .Times(1); 449 EXPECT_CALL(check, Call("postHandleLongTap")); 450 451 EXPECT_CALL(check, Call("preHandleLongTapUp")); 452 EXPECT_CALL(*mcc, 453 HandleTap(TapType::eLongTapUp, LayoutDevicePoint(10, 10), 0, 454 apzc->GetGuid(), _, _)) 455 .Times(1); 456 EXPECT_CALL(check, Call("postHandleLongTapUp")); 457 } 458 459 // Manually invoke the longpress while the touch is currently down. 460 check.Call("preHandleLongTap"); 461 mcc->RunThroughDelayedTasks(); 462 check.Call("postHandleLongTap"); 463 464 // Dispatching the longpress event starts a new touch block, which 465 // needs a new content response and also has a pending timeout task 466 // in the queue. Deal with those here. We do the content response first 467 // with preventDefault=false, and then we run the timeout task which 468 // "loses the race" and does nothing. 469 apzc->ContentReceivedInputBlock(blockId, false); 470 mcc->AdvanceByMillis(1000); 471 472 // Finally, simulate lifting the finger. Since the long-press wasn't 473 // prevent-defaulted, we should get a long-tap-up event. 474 check.Call("preHandleLongTapUp"); 475 result = TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time()); 476 mcc->RunThroughDelayedTasks(); 477 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, result.GetStatus()); 478 check.Call("postHandleLongTapUp"); 479 480 apzc->AssertStateIsReset(); 481 } 482 483 void DoLongPressPreventDefaultTest(uint32_t aBehavior) { 484 MakeApzcUnzoomable(); 485 486 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0); 487 488 int touchX = 10, touchStartY = 50, touchEndY = 10; 489 490 APZEventResult result = 491 TouchDown(apzc, ScreenIntPoint(touchX, touchStartY), mcc->Time()); 492 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, result.GetStatus()); 493 uint64_t blockId = result.mInputBlockId; 494 495 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 496 // SetAllowedTouchBehavior() must be called after sending touch-start. 497 nsTArray<uint32_t> allowedTouchBehaviors; 498 allowedTouchBehaviors.AppendElement(aBehavior); 499 apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors); 500 } 501 // Have content "respond" to the touchstart 502 apzc->ContentReceivedInputBlock(blockId, false); 503 504 MockFunction<void(std::string checkPointName)> check; 505 506 { 507 InSequence s; 508 509 EXPECT_CALL(check, Call("preHandleLongTap")); 510 blockId++; 511 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, 512 LayoutDevicePoint(touchX, touchStartY), 0, 513 apzc->GetGuid(), blockId, _)) 514 .Times(1); 515 EXPECT_CALL(check, Call("postHandleLongTap")); 516 } 517 518 // Manually invoke the longpress while the touch is currently down. 519 check.Call("preHandleLongTap"); 520 mcc->RunThroughDelayedTasks(); 521 check.Call("postHandleLongTap"); 522 523 // There should be a TimeoutContentResponse task in the queue still, 524 // waiting for the response from the longtap event dispatched above. 525 // Send the signal that content has handled the long-tap, and then run 526 // the timeout task (it will be a no-op because the content "wins" the 527 // race. This takes the place of the "contextmenu" event. 528 apzc->ContentReceivedInputBlock(blockId, true); 529 mcc->AdvanceByMillis(1000); 530 531 MultiTouchInput mti = 532 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time()); 533 mti.mTouches.AppendElement(CreateSingleTouchData(0, touchX, touchEndY)); 534 result = apzc->ReceiveInputEvent(mti); 535 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, result.GetStatus()); 536 537 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, 538 LayoutDevicePoint(touchX, touchEndY), 0, 539 apzc->GetGuid(), _, _)) 540 .Times(0); 541 result = TouchUp(apzc, ScreenIntPoint(touchX, touchEndY), mcc->Time()); 542 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, result.GetStatus()); 543 544 ParentLayerPoint pointOut; 545 AsyncTransform viewTransformOut; 546 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); 547 548 EXPECT_EQ(ParentLayerPoint(), pointOut); 549 EXPECT_EQ(AsyncTransform(), viewTransformOut); 550 551 apzc->AssertStateIsReset(); 552 } 553 554 // Tests a scenario that after a long-press event happened the original touch 555 // block initiated by a touch-start event and the touch block initiated by a 556 // long-tap event have been discarded when a new touch-start event happens. 557 void DoLongPressDiscardTouchBlockTest(bool aWithTouchMove) { 558 // Set apz.content_response_timeout > ui.click_hold_context_menus.delay and 559 // apz.touch_start_tolerance explicitly to match Android preferences. 560 SCOPED_GFX_PREF_INT("apz.content_response_timeout", 60); 561 SCOPED_GFX_PREF_INT("ui.click_hold_context_menus.delay", 30); 562 SCOPED_GFX_PREF_FLOAT("apz.touch_start_tolerance", 0.06); 563 564 MockFunction<void(std::string checkPointName)> check; 565 { 566 InSequence s; 567 EXPECT_CALL(check, Call("pre long-tap dispatch")); 568 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 569 0, apzc->GetGuid(), _, _)) 570 .Times(1); 571 EXPECT_CALL(check, Call("post long-tap dispatch")); 572 573 // If a touch-move happens while long-tap is happening, there's no 574 // eLongTapUp event. 575 if (!aWithTouchMove) { 576 EXPECT_CALL(*mcc, 577 HandleTap(TapType::eLongTapUp, LayoutDevicePoint(10, 20), 0, 578 apzc->GetGuid(), _, _)) 579 .Times(1); 580 } 581 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 582 0, apzc->GetGuid(), _, _)) 583 .Times(1); 584 } 585 586 // Keep touching for a while to trigger a long tap event. 587 uint64_t firstTouchBlockId = 588 TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()).mInputBlockId; 589 TouchBlockState* firstTouchBlock = 590 tm->GetInputQueue()->GetCurrentTouchBlock(); 591 EXPECT_NE(firstTouchBlock, nullptr); 592 EXPECT_EQ(tm->GetInputQueue()->GetBlockForId(firstTouchBlockId), 593 firstTouchBlock); 594 595 // Wait for a long tap. 596 check.Call("pre long-tap dispatch"); 597 mcc->AdvanceByMillis(30); 598 check.Call("post long-tap dispatch"); 599 600 // Now the current touch block is not the first touch block, it should be 601 // a new touch block for the long tap event. 602 TouchBlockState* secondTouchBlock = 603 tm->GetInputQueue()->GetCurrentTouchBlock(); 604 EXPECT_NE(secondTouchBlock, firstTouchBlock); 605 EXPECT_TRUE(secondTouchBlock->ForLongTap()); 606 uint64_t secondTouchBlockId = secondTouchBlock->GetBlockId(); 607 608 if (aWithTouchMove) { 609 mcc->AdvanceByMillis(10); 610 TouchMove(apzc, ScreenIntPoint(10, 20), mcc->Time()); 611 } 612 613 // Finish the first touch block. 614 mcc->AdvanceByMillis(10); 615 TouchUp(apzc, ScreenIntPoint(10, 20), mcc->Time()); 616 617 // And start a new touch block. 618 mcc->AdvanceByMillis(10); 619 uint64_t newTouchBlockId = 620 TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()).mInputBlockId; 621 622 mcc->AdvanceByMillis(10); 623 // Now the original touch block and the touch block for long-tap should have 624 // been discarded from the input queue. 625 EXPECT_EQ(tm->GetInputQueue()->GetBlockForId(firstTouchBlockId), nullptr); 626 EXPECT_EQ(tm->GetInputQueue()->GetBlockForId(secondTouchBlockId), nullptr); 627 EXPECT_EQ(tm->GetInputQueue()->GetBlockForId(newTouchBlockId), 628 tm->GetInputQueue()->GetCurrentBlock()); 629 } 630 }; 631 632 TEST_F(APZCLongPressTester, LongPress) { 633 DoLongPressTest(kDefaultTouchBehavior); 634 } 635 636 TEST_F(APZCLongPressTester, LongPressPreventDefault) { 637 DoLongPressPreventDefaultTest(kDefaultTouchBehavior); 638 } 639 640 TEST_F(APZCLongPressTester, LongPressDiscardBlock) { 641 DoLongPressDiscardTouchBlockTest(true /* with touch-move */); 642 } 643 644 // Similar to above LongPressDiscardBlock but APZ is waiting for responses from 645 // the content. 646 TEST_F(APZCLongPressTester, LongPressDiscardBlock2) { 647 MakeApzcWaitForMainThread(); 648 DoLongPressDiscardTouchBlockTest(true /* with touch-move */); 649 } 650 651 // Similar to above LongPressDiscardBlock/LongPressDiscardBlock2 without 652 // touch-move events. 653 TEST_F(APZCLongPressTester, LongPressDiscardBlock3) { 654 DoLongPressDiscardTouchBlockTest(false /* without touch-move */); 655 } 656 657 TEST_F(APZCLongPressTester, LongPressDiscardBlock4) { 658 MakeApzcWaitForMainThread(); 659 DoLongPressDiscardTouchBlockTest(false /* without touch-move */); 660 } 661 662 TEST_F(APZCGestureDetectorTester, DoubleTap) { 663 MakeApzcWaitForMainThread(); 664 MakeApzcZoomable(); 665 666 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, 667 apzc->GetGuid(), _, _)) 668 .Times(0); 669 EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, 670 apzc->GetGuid(), _, _)) 671 .Times(1); 672 673 uint64_t blockIds[2]; 674 DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds); 675 676 // responses to the two touchstarts 677 apzc->ContentReceivedInputBlock(blockIds[0], false); 678 apzc->ContentReceivedInputBlock(blockIds[1], false); 679 680 apzc->AssertStateIsReset(); 681 } 682 683 TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) { 684 MakeApzcWaitForMainThread(); 685 MakeApzcUnzoomable(); 686 687 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, 688 apzc->GetGuid(), _, _)) 689 .Times(1); 690 EXPECT_CALL(*mcc, HandleTap(TapType::eSecondTap, LayoutDevicePoint(10, 10), 0, 691 apzc->GetGuid(), _, _)) 692 .Times(1); 693 EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, 694 apzc->GetGuid(), _, _)) 695 .Times(0); 696 697 uint64_t blockIds[2]; 698 DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds); 699 700 // responses to the two touchstarts 701 apzc->ContentReceivedInputBlock(blockIds[0], false); 702 apzc->ContentReceivedInputBlock(blockIds[1], false); 703 704 apzc->AssertStateIsReset(); 705 } 706 707 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) { 708 MakeApzcWaitForMainThread(); 709 MakeApzcZoomable(); 710 711 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, 712 apzc->GetGuid(), _, _)) 713 .Times(1); 714 EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, 715 apzc->GetGuid(), _, _)) 716 .Times(0); 717 718 uint64_t blockIds[2]; 719 DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds); 720 721 // responses to the two touchstarts 722 apzc->ContentReceivedInputBlock(blockIds[0], true); 723 apzc->ContentReceivedInputBlock(blockIds[1], false); 724 725 apzc->AssertStateIsReset(); 726 } 727 728 TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) { 729 MakeApzcWaitForMainThread(); 730 MakeApzcZoomable(); 731 732 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, 733 apzc->GetGuid(), _, _)) 734 .Times(0); 735 EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, LayoutDevicePoint(10, 10), 0, 736 apzc->GetGuid(), _, _)) 737 .Times(0); 738 739 uint64_t blockIds[2]; 740 DoubleTapAndCheckStatus(apzc, ScreenIntPoint(10, 10), &blockIds); 741 742 // responses to the two touchstarts 743 apzc->ContentReceivedInputBlock(blockIds[0], true); 744 apzc->ContentReceivedInputBlock(blockIds[1], true); 745 746 apzc->AssertStateIsReset(); 747 } 748 749 // Test for bug 947892 750 // We test whether we dispatch tap event when the tap is followed by pinch. 751 // Additionally test that the pinch gesture successfully results in zooming. 752 TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) { 753 MakeApzcZoomable(); 754 755 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, 756 apzc->GetGuid(), _, _)) 757 .Times(1); 758 759 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100)); 760 761 PinchWithTouchInput( 762 apzc, ScreenIntPoint(15, 15), 1.5, 763 PinchOptions().TimeBetweenTouchEvents( 764 // Time it so that the max tap timer expires while the fingers are 765 // down for the pinch but haven't started to move yet. 766 TimeDuration::FromMilliseconds(StaticPrefs::apz_max_tap_time() - 767 90))); 768 769 EXPECT_GT(apzc->GetFrameMetrics().GetZoom().scale, 1.0f); 770 apzc->AssertStateIsReset(); 771 } 772 773 TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) { 774 MakeApzcZoomable(); 775 776 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, 777 apzc->GetGuid(), _, _)) 778 .Times(1); 779 780 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(100)); 781 782 int inputId = 0; 783 MultiTouchInput mti; 784 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); 785 mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), 786 ScreenSize(0, 0), 0, 0)); 787 apzc->ReceiveInputEvent(mti, Some(nsTArray<uint32_t>{kDefaultTouchBehavior})); 788 789 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time()); 790 mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), 791 ScreenSize(0, 0), 0, 0)); 792 mti.mTouches.AppendElement(SingleTouchData( 793 inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0)); 794 apzc->ReceiveInputEvent(mti, Some(nsTArray<uint32_t>{kDefaultTouchBehavior})); 795 796 mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time()); 797 mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), 798 ScreenSize(0, 0), 0, 0)); 799 mti.mTouches.AppendElement(SingleTouchData( 800 inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0)); 801 apzc->ReceiveInputEvent(mti); 802 803 apzc->AssertStateIsReset(); 804 } 805 806 TEST_F(APZCGestureDetectorTester, LongPressInterruptedByWheel) { 807 // Since we try to allow concurrent input blocks of different types to 808 // co-exist, the wheel block shouldn't interrupt the long-press detection. 809 // But more importantly, this shouldn't crash, which is what it did at one 810 // point in time. 811 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _, _)).Times(1); 812 813 APZEventResult result = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 814 uint64_t touchBlockId = result.mInputBlockId; 815 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 816 SetDefaultAllowedTouchBehavior(apzc, touchBlockId); 817 } 818 mcc->AdvanceByMillis(10); 819 uint64_t wheelBlockId = 820 Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time()) 821 .mInputBlockId; 822 EXPECT_NE(touchBlockId, wheelBlockId); 823 mcc->AdvanceByMillis(1000); 824 } 825 826 TEST_F(APZCGestureDetectorTester, TapTimeoutInterruptedByWheel) { 827 // In this test, even though the wheel block comes right after the tap, the 828 // tap should still be dispatched because it completes fully before the wheel 829 // block arrived. 830 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(10, 10), 0, 831 apzc->GetGuid(), _, _)) 832 .Times(1); 833 834 // We make the APZC zoomable so the gesture detector needs to wait to 835 // distinguish between tap and double-tap. During that timeout is when we 836 // insert the wheel event. 837 MakeApzcZoomable(); 838 839 APZEventResult result = Tap(apzc, ScreenIntPoint(10, 10), 840 TimeDuration::FromMilliseconds(100), nullptr); 841 mcc->AdvanceByMillis(10); 842 uint64_t wheelBlockId = 843 Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time()) 844 .mInputBlockId; 845 EXPECT_NE(result.mInputBlockId, wheelBlockId); 846 while (mcc->RunThroughDelayedTasks()); 847 } 848 849 TEST_F(APZCGestureDetectorTester, LongPressWithInputQueueDelay) { 850 // In this test, we ensure that any time spent waiting in the input queue for 851 // the content response is subtracted from the long-press timeout in the 852 // GestureEventListener. In this test the content response timeout is longer 853 // than the long-press timeout. 854 SCOPED_GFX_PREF_INT("apz.content_response_timeout", 60); 855 SCOPED_GFX_PREF_INT("ui.click_hold_context_menus.delay", 30); 856 857 MakeApzcWaitForMainThread(); 858 859 MockFunction<void(std::string checkPointName)> check; 860 861 { 862 InSequence s; 863 EXPECT_CALL(check, Call("pre long-tap dispatch")); 864 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 0, 865 apzc->GetGuid(), _, _)) 866 .Times(1); 867 EXPECT_CALL(check, Call("post long-tap dispatch")); 868 } 869 870 // Touch down 871 APZEventResult result = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 872 uint64_t touchBlockId = result.mInputBlockId; 873 // Simulate content response after 10ms 874 mcc->AdvanceByMillis(10); 875 apzc->ContentReceivedInputBlock(touchBlockId, false); 876 apzc->SetAllowedTouchBehavior(touchBlockId, {kDefaultTouchBehavior}); 877 apzc->ConfirmTarget(touchBlockId); 878 // Ensure long-tap event happens within 20ms after that 879 check.Call("pre long-tap dispatch"); 880 mcc->AdvanceByMillis(20); 881 check.Call("post long-tap dispatch"); 882 } 883 884 TEST_F(APZCGestureDetectorTester, LongPressWithInputQueueDelay2) { 885 // Similar to the previous test, except this time we don't simulate the 886 // content response at all, and still expect the long-press to happen on 887 // schedule. 888 SCOPED_GFX_PREF_INT("apz.content_response_timeout", 60); 889 SCOPED_GFX_PREF_INT("ui.click_hold_context_menus.delay", 30); 890 891 MakeApzcWaitForMainThread(); 892 893 MockFunction<void(std::string checkPointName)> check; 894 895 { 896 InSequence s; 897 EXPECT_CALL(check, Call("pre long-tap dispatch")); 898 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 0, 899 apzc->GetGuid(), _, _)) 900 .Times(1); 901 EXPECT_CALL(check, Call("post long-tap dispatch")); 902 } 903 904 // Touch down 905 TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 906 // Ensure the long-tap happens within 30ms even though there's no content 907 // response. 908 check.Call("pre long-tap dispatch"); 909 mcc->AdvanceByMillis(30); 910 check.Call("post long-tap dispatch"); 911 } 912 913 TEST_F(APZCGestureDetectorTester, LongPressWithInputQueueDelay3) { 914 // Similar to the previous test, except now we have the long-press delay 915 // being longer than the content response timeout. 916 SCOPED_GFX_PREF_INT("apz.content_response_timeout", 30); 917 SCOPED_GFX_PREF_INT("ui.click_hold_context_menus.delay", 60); 918 919 MakeApzcWaitForMainThread(); 920 921 MockFunction<void(std::string checkPointName)> check; 922 923 { 924 InSequence s; 925 EXPECT_CALL(check, Call("pre long-tap dispatch")); 926 EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, LayoutDevicePoint(10, 10), 0, 927 apzc->GetGuid(), _, _)) 928 .Times(1); 929 EXPECT_CALL(check, Call("post long-tap dispatch")); 930 } 931 932 // Touch down 933 TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 934 // Ensure the long-tap happens at the 60ms mark even though the input event 935 // waits in the input queue for the full content response timeout of 30ms 936 mcc->AdvanceByMillis(59); 937 check.Call("pre long-tap dispatch"); 938 mcc->AdvanceByMillis(1); 939 check.Call("post long-tap dispatch"); 940 } 941 942 TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureShort) { 943 // Take less than StaticPrefs::apz_max_tap_time() until second touch down, 944 // hold second touch down for a very short time, then move 945 // and expect a successful one touch pinch gesture 946 SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); 947 948 MakeApzcZoomable(); 949 apzc->SetFrameMetrics(GetPinchableFrameMetrics()); 950 const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; 951 952 const auto tapResult = 953 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); 954 apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, 955 {kDefaultTouchBehavior}); 956 957 mcc->AdvanceByMillis(10); 958 const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 959 apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, 960 {kDefaultTouchBehavior}); 961 962 // We should be able to hold down the second touch as long as we like 963 // before beginning to move 964 mcc->AdvanceByMillis(10); 965 TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); 966 967 mcc->AdvanceByMillis(10); 968 TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); 969 970 mcc->AdvanceByMillis(10); 971 TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); 972 973 const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; 974 EXPECT_NE(newZoom, oldZoom); 975 } 976 977 TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureLong) { 978 // Take less than StaticPrefs::apz_max_tap_time() until second touch down, 979 // hold second touch down for a long time, then move 980 // and expect a successful one touch pinch gesture 981 SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); 982 983 MakeApzcZoomable(); 984 apzc->SetFrameMetrics(GetPinchableFrameMetrics()); 985 const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; 986 987 const auto tapResult = 988 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); 989 apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, 990 {kDefaultTouchBehavior}); 991 992 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); 993 const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 994 apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, 995 {kDefaultTouchBehavior}); 996 997 // We should be able to hold down the second touch as long as we like 998 // before beginning to move 999 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); 1000 TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); 1001 1002 mcc->AdvanceByMillis(10); 1003 TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); 1004 1005 mcc->AdvanceByMillis(10); 1006 TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); 1007 1008 const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; 1009 EXPECT_NE(newZoom, oldZoom); 1010 } 1011 1012 TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureNoMoveTriggersDoubleTap) { 1013 // Take less than StaticPrefs::apz_max_tap_time() until second touch down, 1014 // then wait longer than StaticPrefs::apz_max_tap_time(), lift finger up 1015 // and expect a successful double tap. No zooming should be performed 1016 // by the one-touch pinch codepath. 1017 SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); 1018 1019 apzc->SetFrameMetrics(GetPinchableFrameMetrics()); 1020 const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; 1021 1022 MakeApzcZoomable(); 1023 1024 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) 1025 .Times(0); 1026 EXPECT_CALL(*mcc, 1027 HandleTap(TapType::eDoubleTap, _, 0, apzc->GetGuid(), _, _)); 1028 1029 const auto tapResult = 1030 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); 1031 apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, 1032 {kDefaultTouchBehavior}); 1033 1034 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); 1035 const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 1036 apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, 1037 {kDefaultTouchBehavior}); 1038 1039 // We should be able to hold down the second touch as long as we like 1040 // before lifting the finger 1041 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); 1042 TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time()); 1043 1044 const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; 1045 EXPECT_EQ(newZoom, oldZoom); 1046 } 1047 1048 TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureNonZoomablePage) { 1049 // Use a non-zoomable page. Perform a tap and a touch-drag 1050 // which on a zoomable page trigger a one touch pinch gesture, 1051 // and expect a single tap followed by a touch-scroll 1052 SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); 1053 1054 apzc->SetFrameMetrics(GetPinchableFrameMetrics(1.0f)); 1055 const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; 1056 const auto oldScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); 1057 MakeApzcUnzoomable(); 1058 1059 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) 1060 .Times(1); 1061 EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, _, 0, apzc->GetGuid(), _, _)) 1062 .Times(0); 1063 1064 const auto tapResult = 1065 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); 1066 apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, 1067 {kDefaultTouchBehavior}); 1068 1069 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); 1070 const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 1071 apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, 1072 {kDefaultTouchBehavior}); 1073 1074 // We should be able to hold down the second touch as long as we like 1075 // before beginning to move 1076 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); 1077 TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); 1078 1079 mcc->AdvanceByMillis(10); 1080 TouchMove(apzc, ScreenIntPoint(10, 100), mcc->Time()); 1081 1082 mcc->AdvanceByMillis(10); 1083 TouchUp(apzc, ScreenIntPoint(10, 100), mcc->Time()); 1084 1085 const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; 1086 EXPECT_EQ(newZoom, oldZoom); 1087 1088 const auto newScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); 1089 EXPECT_NE(newScrollOffset, oldScrollOffset); 1090 } 1091 1092 TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureTimeout) { 1093 // Take longer than StaticPrefs::apz_max_tap_time() until second touch down 1094 // and expect no one touch pinch gesture being performed 1095 SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", true); 1096 1097 MakeApzcZoomable(); 1098 apzc->SetFrameMetrics(GetPinchableFrameMetrics()); 1099 const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; 1100 1101 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, _)) 1102 .Times(1); 1103 1104 const auto tapResult = 1105 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); 1106 apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, 1107 {kDefaultTouchBehavior}); 1108 1109 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time()); 1110 const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 1111 apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, 1112 {kDefaultTouchBehavior}); 1113 1114 mcc->AdvanceByMillis(10); 1115 TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); 1116 1117 mcc->AdvanceByMillis(10); 1118 TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); 1119 1120 mcc->AdvanceByMillis(10); 1121 TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); 1122 1123 const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; 1124 EXPECT_EQ(newZoom, oldZoom); 1125 } 1126 1127 TEST_F(APZCGestureDetectorTester, OneTouchPinchGestureDisabled) { 1128 // With apz.one_touch_pinch disabled, 1129 // perform one touch pinch gesture within the time threshold, 1130 // and expect no zooming. 1131 SCOPED_GFX_PREF_BOOL("apz.one_touch_pinch.enabled", false); 1132 1133 MakeApzcZoomable(); 1134 apzc->SetFrameMetrics(GetPinchableFrameMetrics()); 1135 const auto oldZoom = apzc->GetFrameMetrics().GetZoom().scale; 1136 const auto oldScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); 1137 1138 // todo: enable following EXPECT_CALLs when fixing bug 1881794 1139 // EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, 0, apzc->GetGuid(), _, 1140 // _)) 1141 // .Times(1); 1142 // EXPECT_CALL(*mcc, HandleTap(TapType::eDoubleTap, _, 0, apzc->GetGuid(), _, 1143 // _)) 1144 // .Times(0); 1145 1146 const auto tapResult = 1147 Tap(apzc, ScreenIntPoint(10, 10), TimeDuration::FromMilliseconds(10)); 1148 apzc->SetAllowedTouchBehavior(tapResult.mInputBlockId, 1149 {kDefaultTouchBehavior}); 1150 1151 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() - 20); 1152 const auto touchResult = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time()); 1153 apzc->SetAllowedTouchBehavior(touchResult.mInputBlockId, 1154 {kDefaultTouchBehavior}); 1155 1156 // We should be able to hold down the second touch as long as we like 1157 // before beginning to move 1158 mcc->AdvanceByMillis(StaticPrefs::apz_max_tap_time() + 100); 1159 TouchMove(apzc, ScreenIntPoint(10, 50), mcc->Time()); 1160 1161 mcc->AdvanceByMillis(10); 1162 TouchMove(apzc, ScreenIntPoint(10, 150), mcc->Time()); 1163 1164 mcc->AdvanceByMillis(10); 1165 TouchUp(apzc, ScreenIntPoint(10, 150), mcc->Time()); 1166 1167 const auto newZoom = apzc->GetFrameMetrics().GetZoom().scale; 1168 EXPECT_EQ(newZoom, oldZoom); 1169 1170 const auto newScrollOffset = apzc->GetFrameMetrics().GetVisualScrollOffset(); 1171 EXPECT_NE(newScrollOffset, oldScrollOffset); 1172 }