APZTestCommon.h (41886B)
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 #ifndef mozilla_layers_APZTestCommon_h 8 #define mozilla_layers_APZTestCommon_h 9 10 /** 11 * Defines a set of mock classes and utility functions/classes for 12 * writing APZ gtests. 13 */ 14 15 #include "gtest/gtest.h" 16 #include "gmock/gmock.h" 17 18 #include "mozilla/layers/GeckoContentController.h" 19 #include "mozilla/layers/CompositorBridgeParent.h" 20 #include "mozilla/layers/DoubleTapToZoom.h" 21 #include "mozilla/layers/APZThreadUtils.h" 22 #include "mozilla/layers/MatrixMessage.h" 23 #include "mozilla/StaticPrefs_layout.h" 24 #include "mozilla/TypedEnumBits.h" 25 #include "mozilla/UniquePtr.h" 26 #include "apz/src/APZCTreeManager.h" 27 #include "apz/src/AsyncPanZoomController.h" 28 #include "apz/src/HitTestingTreeNode.h" 29 #include "base/task.h" 30 #include "gfxPlatform.h" 31 #include "TestWRScrollData.h" 32 #include "UnitTransforms.h" 33 34 using namespace mozilla; 35 using namespace mozilla::gfx; 36 using namespace mozilla::layers; 37 using ::testing::_; 38 using ::testing::AtLeast; 39 using ::testing::AtMost; 40 using ::testing::InSequence; 41 using ::testing::MockFunction; 42 using ::testing::NiceMock; 43 typedef mozilla::layers::GeckoContentController::TapType TapType; 44 45 inline TimeStamp GetStartupTime() { 46 static TimeStamp sStartupTime = TimeStamp::Now(); 47 return sStartupTime; 48 } 49 50 inline uint32_t MillisecondsSinceStartup(TimeStamp aTime) { 51 return (aTime - GetStartupTime()).ToMilliseconds(); 52 } 53 54 // Some helper functions for constructing input event objects suitable to be 55 // passed either to an APZC (which expects an transformed point), or to an APZTM 56 // (which expects an untransformed point). We handle both cases by setting both 57 // the transformed and untransformed fields to the same value. 58 inline SingleTouchData CreateSingleTouchData(int32_t aIdentifier, 59 const ScreenIntPoint& aPoint) { 60 SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0); 61 touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y); 62 return touch; 63 } 64 65 // Convenience wrapper for CreateSingleTouchData() that takes loose coordinates. 66 inline SingleTouchData CreateSingleTouchData(int32_t aIdentifier, 67 ScreenIntCoord aX, 68 ScreenIntCoord aY) { 69 return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY)); 70 } 71 72 inline PinchGestureInput CreatePinchGestureInput( 73 PinchGestureInput::PinchGestureType aType, const ScreenPoint& aFocus, 74 float aCurrentSpan, float aPreviousSpan, TimeStamp timestamp) { 75 ParentLayerPoint localFocus(aFocus.x, aFocus.y); 76 PinchGestureInput result(aType, PinchGestureInput::UNKNOWN, timestamp, 77 ExternalPoint(0, 0), aFocus, aCurrentSpan, 78 aPreviousSpan, 0); 79 return result; 80 } 81 82 template <class SetArg, class Storage> 83 class ScopedGfxSetting { 84 public: 85 ScopedGfxSetting(const std::function<SetArg(void)>& aGetPrefFunc, 86 const std::function<void(SetArg)>& aSetPrefFunc, SetArg aVal) 87 : mSetPrefFunc(aSetPrefFunc) { 88 mOldVal = aGetPrefFunc(); 89 aSetPrefFunc(aVal); 90 } 91 92 ~ScopedGfxSetting() { mSetPrefFunc(mOldVal); } 93 94 private: 95 std::function<void(SetArg)> mSetPrefFunc; 96 Storage mOldVal; 97 }; 98 99 static inline constexpr auto kDefaultTouchBehavior = 100 AllowedTouchBehavior::VERTICAL_PAN | AllowedTouchBehavior::HORIZONTAL_PAN | 101 AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::ANIMATING_ZOOM; 102 103 #define FRESH_PREF_VAR_PASTE(id, line) id##line 104 #define FRESH_PREF_VAR_EXPAND(id, line) FRESH_PREF_VAR_PASTE(id, line) 105 #define FRESH_PREF_VAR FRESH_PREF_VAR_EXPAND(pref, __LINE__) 106 107 #define SCOPED_GFX_PREF_BOOL(prefName, prefValue) \ 108 ScopedGfxSetting<bool, bool> FRESH_PREF_VAR( \ 109 [=]() { return Preferences::GetBool(prefName); }, \ 110 [=](bool aPrefValue) { Preferences::SetBool(prefName, aPrefValue); }, \ 111 prefValue) 112 113 #define SCOPED_GFX_PREF_INT(prefName, prefValue) \ 114 ScopedGfxSetting<int32_t, int32_t> FRESH_PREF_VAR( \ 115 [=]() { return Preferences::GetInt(prefName); }, \ 116 [=](int32_t aPrefValue) { Preferences::SetInt(prefName, aPrefValue); }, \ 117 prefValue) 118 119 #define SCOPED_GFX_PREF_FLOAT(prefName, prefValue) \ 120 ScopedGfxSetting<float, float> FRESH_PREF_VAR( \ 121 [=]() { return Preferences::GetFloat(prefName); }, \ 122 [=](float aPrefValue) { Preferences::SetFloat(prefName, aPrefValue); }, \ 123 prefValue) 124 125 class MockContentController : public GeckoContentController { 126 public: 127 MOCK_METHOD1(NotifyLayerTransforms, void(nsTArray<MatrixMessage>&&)); 128 MOCK_METHOD1(RequestContentRepaint, void(const RepaintRequest&)); 129 MOCK_METHOD6(HandleTap, void(TapType, const LayoutDevicePoint&, Modifiers, 130 const ScrollableLayerGuid&, uint64_t, 131 const Maybe<DoubleTapToZoomMetrics>&)); 132 MOCK_METHOD5(NotifyPinchGesture, 133 void(PinchGestureInput::PinchGestureType, 134 const ScrollableLayerGuid&, const LayoutDevicePoint&, 135 LayoutDeviceCoord, Modifiers)); 136 // Can't use the macros with already_AddRefed :( 137 void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) { 138 RefPtr<Runnable> task = aTask; 139 } 140 bool IsRepaintThread() { return NS_IsMainThread(); } 141 void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) { 142 NS_DispatchToMainThread(std::move(aTask)); 143 } 144 MOCK_METHOD4(NotifyAPZStateChange, 145 void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, 146 int aArg, Maybe<uint64_t> aInputBlockId)); 147 MOCK_METHOD0(NotifyFlushComplete, void()); 148 MOCK_METHOD3(NotifyAsyncScrollbarDragInitiated, 149 void(uint64_t, const ScrollableLayerGuid::ViewID&, 150 ScrollDirection aDirection)); 151 MOCK_METHOD1(NotifyAsyncScrollbarDragRejected, 152 void(const ScrollableLayerGuid::ViewID&)); 153 MOCK_METHOD1(NotifyAsyncAutoscrollRejected, 154 void(const ScrollableLayerGuid::ViewID&)); 155 MOCK_METHOD1(CancelAutoscroll, void(const ScrollableLayerGuid&)); 156 MOCK_METHOD2(NotifyScaleGestureComplete, 157 void(const ScrollableLayerGuid&, float aScale)); 158 MOCK_METHOD4(UpdateOverscrollVelocity, 159 void(const ScrollableLayerGuid&, float, float, bool)); 160 MOCK_METHOD4(UpdateOverscrollOffset, 161 void(const ScrollableLayerGuid&, float, float, bool)); 162 }; 163 164 class MockContentControllerDelayed : public MockContentController { 165 public: 166 MockContentControllerDelayed() 167 : mTime(SampleTime::FromTest(GetStartupTime())) {} 168 169 const TimeStamp& Time() { return mTime.Time(); } 170 const SampleTime& GetSampleTime() { return mTime; } 171 172 void AdvanceByMillis(int aMillis) { 173 AdvanceBy(TimeDuration::FromMilliseconds(aMillis)); 174 } 175 176 void AdvanceBy(const TimeDuration& aIncrement) { 177 SampleTime target = mTime + aIncrement; 178 while (mTaskQueue.Length() > 0 && mTaskQueue[0].second <= target) { 179 RunNextDelayedTask(); 180 } 181 mTime = target; 182 } 183 184 void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) { 185 RefPtr<Runnable> task = aTask; 186 SampleTime runAtTime = mTime + TimeDuration::FromMilliseconds(aDelayMs); 187 int insIndex = mTaskQueue.Length(); 188 while (insIndex > 0) { 189 if (mTaskQueue[insIndex - 1].second <= runAtTime) { 190 break; 191 } 192 insIndex--; 193 } 194 mTaskQueue.InsertElementAt(insIndex, std::make_pair(task, runAtTime)); 195 } 196 197 // Run all the tasks in the queue, returning the number of tasks 198 // run. Note that if a task queues another task while running, that 199 // new task will not be run. Therefore, there may be still be tasks 200 // in the queue after this function is called. Only when the return 201 // value is 0 is the queue guaranteed to be empty. 202 int RunThroughDelayedTasks() { 203 nsTArray<std::pair<RefPtr<Runnable>, SampleTime>> runQueue = 204 std::move(mTaskQueue); 205 int numTasks = runQueue.Length(); 206 for (int i = 0; i < numTasks; i++) { 207 mTime = runQueue[i].second; 208 runQueue[i].first->Run(); 209 210 // Deleting the task is important in order to release the reference to 211 // the callee object. 212 runQueue[i].first = nullptr; 213 } 214 return numTasks; 215 } 216 217 private: 218 void RunNextDelayedTask() { 219 std::pair<RefPtr<Runnable>, SampleTime> next = mTaskQueue[0]; 220 mTaskQueue.RemoveElementAt(0); 221 mTime = next.second; 222 next.first->Run(); 223 // Deleting the task is important in order to release the reference to 224 // the callee object. 225 next.first = nullptr; 226 } 227 228 // The following array is sorted by timestamp (tasks are inserted in order by 229 // timestamp). 230 nsTArray<std::pair<RefPtr<Runnable>, SampleTime>> mTaskQueue; 231 SampleTime mTime; 232 }; 233 234 class TestAPZCTreeManager : public APZCTreeManager { 235 public: 236 explicit TestAPZCTreeManager(MockContentControllerDelayed* aMcc, 237 UniquePtr<IAPZHitTester> aHitTester = nullptr) 238 : APZCTreeManager(LayersId{0}, std::move(aHitTester)), mcc(aMcc) { 239 Init(); 240 } 241 242 RefPtr<InputQueue> GetInputQueue() const { return mInputQueue; } 243 244 void ClearContentController() { mcc = nullptr; } 245 246 /** 247 * This function is not currently implemented. 248 * See bug 1468804 for more information. 249 **/ 250 void CancelAnimation() { EXPECT_TRUE(false); } 251 252 bool AdvanceAnimations(const SampleTime& aSampleTime) { 253 MutexAutoLock lock(mMapLock); 254 return AdvanceAnimationsInternal(lock, aSampleTime); 255 } 256 257 APZEventResult ReceiveInputEvent( 258 InputData& aEvent, 259 InputBlockCallback&& aCallback = InputBlockCallback()) override { 260 APZEventResult result = 261 APZCTreeManager::ReceiveInputEvent(aEvent, std::move(aCallback)); 262 if (aEvent.mInputType == PANGESTURE_INPUT && 263 // In the APZCTreeManager::ReceiveInputEvent some type of pan gesture 264 // events are marked as `mHandledByAPZ = false` (e.g. with Ctrl key 265 // modifier which causes reflow zoom), in such cases the events will 266 // never be processed by InputQueue so we shouldn't try to invoke 267 // AllowsSwipe() here. 268 aEvent.AsPanGestureInput().mHandledByAPZ && 269 aEvent.AsPanGestureInput().AllowsSwipe()) { 270 SetBrowserGestureResponse(result.mInputBlockId, 271 BrowserGestureResponse::NotConsumed); 272 } 273 return result; 274 } 275 276 protected: 277 already_AddRefed<AsyncPanZoomController> NewAPZCInstance( 278 LayersId aLayersId, GeckoContentController* aController) override; 279 280 SampleTime GetFrameTime() override { return mcc->GetSampleTime(); } 281 282 RefPtr<MockContentControllerDelayed> mcc; 283 }; 284 285 class TestAsyncPanZoomController : public AsyncPanZoomController { 286 public: 287 TestAsyncPanZoomController(LayersId aLayersId, 288 MockContentControllerDelayed* aMcc, 289 TestAPZCTreeManager* aTreeManager, 290 GestureBehavior aBehavior = DEFAULT_GESTURES) 291 : AsyncPanZoomController(aLayersId, aTreeManager, 292 aTreeManager->GetInputQueue(), aMcc, aBehavior), 293 mWaitForMainThread(false), 294 mcc(aMcc) {} 295 296 APZEventResult ReceiveInputEvent( 297 InputData& aEvent, 298 const Maybe<nsTArray<uint32_t>>& aTouchBehaviors = Nothing()) { 299 // This is a function whose signature matches exactly the ReceiveInputEvent 300 // on APZCTreeManager. This allows us to templates for functions like 301 // TouchDown, TouchUp, etc so that we can reuse the code for dispatching 302 // events into both APZC and APZCTM. 303 APZEventResult result = GetInputQueue()->ReceiveInputEvent( 304 this, TargetConfirmationFlags{!mWaitForMainThread}, aEvent, 305 aTouchBehaviors); 306 307 if (aEvent.mInputType == PANGESTURE_INPUT && 308 aEvent.AsPanGestureInput().AllowsSwipe()) { 309 GetInputQueue()->SetBrowserGestureResponse( 310 result.mInputBlockId, BrowserGestureResponse::NotConsumed); 311 } 312 return result; 313 } 314 315 void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) { 316 GetInputQueue()->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); 317 } 318 319 void ConfirmTarget(uint64_t aInputBlockId) { 320 RefPtr<AsyncPanZoomController> target = this; 321 GetInputQueue()->SetConfirmedTargetApzc(aInputBlockId, target); 322 } 323 324 void SetAllowedTouchBehavior(uint64_t aInputBlockId, 325 const nsTArray<TouchBehaviorFlags>& aBehaviors) { 326 GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors); 327 } 328 329 void SetFrameMetrics(const FrameMetrics& metrics) { 330 RecursiveMutexAutoLock lock(mRecursiveMutex); 331 Metrics() = metrics; 332 } 333 334 void SetScrollMetadata(const ScrollMetadata& aMetadata) { 335 RecursiveMutexAutoLock lock(mRecursiveMutex); 336 mScrollMetadata = aMetadata; 337 } 338 339 FrameMetrics& GetFrameMetrics() { 340 RecursiveMutexAutoLock lock(mRecursiveMutex); 341 return mScrollMetadata.GetMetrics(); 342 } 343 344 ScrollMetadata& GetScrollMetadata() { 345 RecursiveMutexAutoLock lock(mRecursiveMutex); 346 return mScrollMetadata; 347 } 348 349 const FrameMetrics& GetFrameMetrics() const { 350 RecursiveMutexAutoLock lock(mRecursiveMutex); 351 return mScrollMetadata.GetMetrics(); 352 } 353 354 using AsyncPanZoomController::GetOverscrollAmount; 355 using AsyncPanZoomController::GetVelocityVector; 356 357 void AssertStateIsReset() const { 358 RecursiveMutexAutoLock lock(mRecursiveMutex); 359 EXPECT_EQ(NOTHING, mState); 360 } 361 362 void AssertStateIsFling() const { 363 RecursiveMutexAutoLock lock(mRecursiveMutex); 364 EXPECT_EQ(FLING, mState); 365 } 366 367 void AssertInSmoothScroll() const { 368 RecursiveMutexAutoLock lock(mRecursiveMutex); 369 EXPECT_TRUE(InScrollAnimation(ScrollAnimationKind::Smooth)); 370 } 371 372 void AssertInSmoothMsdScroll() const { 373 RecursiveMutexAutoLock lock(mRecursiveMutex); 374 EXPECT_TRUE(InScrollAnimation(ScrollAnimationKind::SmoothMsd)); 375 } 376 377 void AssertStateIsPanningLockedY() { 378 RecursiveMutexAutoLock lock(mRecursiveMutex); 379 EXPECT_EQ(PANNING_LOCKED_Y, mState); 380 } 381 382 void AssertStateIsPanningLockedX() { 383 RecursiveMutexAutoLock lock(mRecursiveMutex); 384 EXPECT_EQ(PANNING_LOCKED_X, mState); 385 } 386 387 void AssertStateIsPanning() { 388 RecursiveMutexAutoLock lock(mRecursiveMutex); 389 EXPECT_EQ(PANNING, mState); 390 } 391 392 void AssertStateIsPanMomentum() { 393 RecursiveMutexAutoLock lock(mRecursiveMutex); 394 EXPECT_EQ(PAN_MOMENTUM, mState); 395 } 396 397 void AssertInWheelScroll() { 398 RecursiveMutexAutoLock lock(mRecursiveMutex); 399 EXPECT_TRUE(InScrollAnimation(ScrollAnimationKind::Wheel)); 400 } 401 402 void AssertInKeyboardScroll() { 403 RecursiveMutexAutoLock lock(mRecursiveMutex); 404 EXPECT_TRUE(InScrollAnimation(ScrollAnimationKind::Keyboard)); 405 } 406 407 void AssertStateIsAutoscroll() { 408 RecursiveMutexAutoLock lock(mRecursiveMutex); 409 EXPECT_EQ(AUTOSCROLL, mState); 410 } 411 412 void SetAxisLocked(ScrollDirections aDirections, bool aLockValue) { 413 if (aDirections.contains(ScrollDirection::eVertical)) { 414 mY.SetAxisLocked(aLockValue); 415 } 416 if (aDirections.contains(ScrollDirection::eHorizontal)) { 417 mX.SetAxisLocked(aLockValue); 418 } 419 } 420 421 void AssertNotAxisLocked() const { 422 EXPECT_FALSE(mY.IsAxisLocked()); 423 EXPECT_FALSE(mX.IsAxisLocked()); 424 } 425 426 void AssertAxisLocked(ScrollDirection aDirection) const { 427 switch (aDirection) { 428 case ScrollDirection::eHorizontal: 429 EXPECT_TRUE(mY.IsAxisLocked()); 430 EXPECT_FALSE(mX.IsAxisLocked()); 431 break; 432 case ScrollDirection::eVertical: 433 EXPECT_TRUE(mX.IsAxisLocked()); 434 EXPECT_FALSE(mY.IsAxisLocked()); 435 break; 436 default: 437 FAIL() << "input direction must be either vertical or horizontal"; 438 } 439 } 440 441 void AdvanceAnimationsUntilEnd( 442 const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(10)) { 443 while (AdvanceAnimations(mcc->GetSampleTime())) { 444 mcc->AdvanceBy(aIncrement); 445 } 446 } 447 448 bool SampleContentTransformForFrame( 449 AsyncTransform* aOutTransform, ParentLayerPoint& aScrollOffset, 450 const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) { 451 mcc->AdvanceBy(aIncrement); 452 bool ret = AdvanceAnimations(mcc->GetSampleTime()); 453 if (aOutTransform) { 454 *aOutTransform = 455 GetCurrentAsyncTransform(AsyncPanZoomController::eForEventHandling); 456 } 457 aScrollOffset = 458 GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForEventHandling); 459 return ret; 460 } 461 462 CSSPoint GetCompositedScrollOffset() const { 463 return GetCurrentAsyncScrollOffset( 464 AsyncPanZoomController::eForCompositing) / 465 GetFrameMetrics().GetZoom(); 466 } 467 468 void SetWaitForMainThread() { mWaitForMainThread = true; } 469 470 bool IsOverscrollAnimationRunning() const { 471 return mState == PanZoomState::OVERSCROLL_ANIMATION; 472 } 473 474 bool IsWheelScrollAnimationRunning() const { 475 return InScrollAnimation(ScrollAnimationKind::Wheel); 476 } 477 478 private: 479 bool mWaitForMainThread; 480 MockContentControllerDelayed* mcc; 481 }; 482 483 class APZCTesterBase : public ::testing::Test { 484 public: 485 APZCTesterBase() { mcc = new NiceMock<MockContentControllerDelayed>(); } 486 487 void SetUp() override { 488 gfxPlatform::GetPlatform(); 489 // This pref is changed in Pan() without using SCOPED_GFX_PREF 490 // because the modified value needs to be in place until the touch 491 // events are processed, which may not happen until the input queue 492 // is flushed in TearDown(). So, we save and restore its value here. 493 mTouchStartTolerance = StaticPrefs::apz_touch_start_tolerance(); 494 } 495 496 void TearDown() override { 497 Preferences::SetFloat("apz.touch_start_tolerance", mTouchStartTolerance); 498 } 499 500 enum class PanOptions { 501 None = 0, 502 KeepFingerDown = 0x1, 503 /* 504 * Do not adjust the touch-start coordinates to overcome the touch-start 505 * tolerance threshold. If this option is passed, it's up to the caller 506 * to pass in coordinates that are sufficient to overcome the touch-start 507 * tolerance *and* cause the desired amount of scrolling. 508 */ 509 ExactCoordinates = 0x2, 510 NoFling = 0x4 511 }; 512 513 enum class PinchFlags { 514 None = 0, 515 LiftFinger1 = 0x1, 516 LiftFinger2 = 0x2, 517 /* 518 * The bitwise OR result of (LiftFinger1 | LiftFinger2). 519 * Defined explicitly here because it is used as the default 520 * argument for PinchWithTouchInput which is defined BEFORE the 521 * definition of operator| for this class. 522 */ 523 LiftBothFingers = 0x3 524 }; 525 526 template <class InputReceiver> 527 APZEventResult Tap(const RefPtr<InputReceiver>& aTarget, 528 const ScreenIntPoint& aPoint, TimeDuration aTapLength, 529 nsEventStatus (*aOutEventStatuses)[2] = nullptr); 530 531 template <class InputReceiver> 532 void TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget, 533 const ScreenIntPoint& aPoint, TimeDuration aTapLength); 534 535 template <class InputReceiver> 536 void Pan(const RefPtr<InputReceiver>& aTarget, 537 const ScreenIntPoint& aTouchStart, const ScreenIntPoint& aTouchEnd, 538 PanOptions aOptions = PanOptions::None, 539 nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr, 540 nsEventStatus (*aOutEventStatuses)[4] = nullptr, 541 uint64_t* aOutInputBlockId = nullptr); 542 543 /* 544 * A version of Pan() that only takes y coordinates rather than (x, y) points 545 * for the touch start and end points, and uses 10 for the x coordinates. 546 * This is for convenience, as most tests only need to pan in one direction. 547 */ 548 template <class InputReceiver> 549 void Pan(const RefPtr<InputReceiver>& aTarget, int aTouchStartY, 550 int aTouchEndY, PanOptions aOptions = PanOptions::None, 551 nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr, 552 nsEventStatus (*aOutEventStatuses)[4] = nullptr, 553 uint64_t* aOutInputBlockId = nullptr); 554 555 /* 556 * Dispatches mock touch events to the apzc and checks whether apzc properly 557 * consumed them and triggered scrolling behavior. 558 */ 559 template <class InputReceiver> 560 void PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aTouchStartY, 561 int aTouchEndY, bool aExpectConsumed, 562 nsTArray<uint32_t>* aAllowedTouchBehaviors, 563 uint64_t* aOutInputBlockId = nullptr); 564 565 template <class InputReceiver> 566 void DoubleTap(const RefPtr<InputReceiver>& aTarget, 567 const ScreenIntPoint& aPoint, 568 nsEventStatus (*aOutEventStatuses)[4] = nullptr, 569 uint64_t (*aOutInputBlockIds)[2] = nullptr); 570 571 template <class InputReceiver> 572 void DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget, 573 const ScreenIntPoint& aPoint, 574 uint64_t (*aOutInputBlockIds)[2] = nullptr); 575 576 struct PinchOptions { 577 nsTArray<uint32_t>* mAllowedTouchBehaviors = nullptr; 578 nsEventStatus (*mOutEventStatuses)[4] = nullptr; 579 uint64_t* mOutInputBlockId = nullptr; 580 PinchFlags mFlags = PinchFlags::LiftBothFingers; 581 bool mVertical = false; 582 int* mInputId = nullptr; 583 Maybe<ScreenIntPoint> mSecondFocus; 584 TimeDuration mTimeBetweenTouchEvents = TimeDuration::FromMilliseconds(20); 585 586 // Workaround for https://github.com/llvm/llvm-project/issues/36032 587 PinchOptions() {} 588 589 // Fluent interface 590 PinchOptions& AllowedTouchBehaviors( 591 nsTArray<uint32_t>* aAllowedTouchBehaviors) { 592 mAllowedTouchBehaviors = aAllowedTouchBehaviors; 593 return *this; 594 } 595 PinchOptions& OutEventStatuses(nsEventStatus (*aOutEventStatuses)[4]) { 596 mOutEventStatuses = aOutEventStatuses; 597 return *this; 598 } 599 PinchOptions& OutInputBlockId(uint64_t* aOutInputBlockId) { 600 mOutInputBlockId = aOutInputBlockId; 601 return *this; 602 } 603 PinchOptions& Flags(PinchFlags aFlags) { 604 mFlags = aFlags; 605 return *this; 606 } 607 PinchOptions& Vertical(bool aVertical) { 608 mVertical = aVertical; 609 return *this; 610 } 611 PinchOptions& InputId(int& aInputId) { 612 mInputId = &aInputId; 613 return *this; 614 } 615 PinchOptions& SecondFocus(const ScreenIntPoint& aSecondFocus) { 616 mSecondFocus = Some(aSecondFocus); 617 return *this; 618 } 619 PinchOptions& TimeBetweenTouchEvents(const TimeDuration& aDuration) { 620 mTimeBetweenTouchEvents = aDuration; 621 return *this; 622 } 623 }; 624 625 // Pinch with one focus point. Zooms in place with no panning 626 template <class InputReceiver> 627 void PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget, 628 const ScreenIntPoint& aFocus, float aScale, 629 PinchOptions aOptions = PinchOptions()); 630 631 template <class InputReceiver> 632 void PinchWithTouchInputAndCheckStatus( 633 const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus, 634 float aScale, int& inputId, bool aShouldTriggerPinch, 635 nsTArray<uint32_t>* aAllowedTouchBehaviors); 636 637 template <class InputReceiver> 638 void PinchWithPinchInput(const RefPtr<InputReceiver>& aTarget, 639 const ScreenIntPoint& aFocus, 640 const ScreenIntPoint& aSecondFocus, float aScale, 641 nsEventStatus (*aOutEventStatuses)[3] = nullptr); 642 643 template <class InputReceiver> 644 void PinchWithPinchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget, 645 const ScreenIntPoint& aFocus, 646 float aScale, 647 bool aShouldTriggerPinch); 648 649 protected: 650 RefPtr<MockContentControllerDelayed> mcc; 651 652 private: 653 float mTouchStartTolerance; 654 }; 655 656 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PanOptions) 657 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PinchFlags) 658 659 template <class InputReceiver> 660 APZEventResult APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget, 661 const ScreenIntPoint& aPoint, 662 TimeDuration aTapLength, 663 nsEventStatus (*aOutEventStatuses)[2]) { 664 APZEventResult touchDownResult = TouchDown(aTarget, aPoint, mcc->Time()); 665 if (aOutEventStatuses) { 666 (*aOutEventStatuses)[0] = touchDownResult.GetStatus(); 667 } 668 mcc->AdvanceBy(aTapLength); 669 670 // If touch-action is enabled then simulate the allowed touch behaviour 671 // notification that the main thread is supposed to deliver. 672 if (touchDownResult.GetStatus() != nsEventStatus_eConsumeNoDefault) { 673 SetDefaultAllowedTouchBehavior(aTarget, touchDownResult.mInputBlockId); 674 } 675 676 APZEventResult touchUpResult = TouchUp(aTarget, aPoint, mcc->Time()); 677 if (aOutEventStatuses) { 678 (*aOutEventStatuses)[1] = touchUpResult.GetStatus(); 679 } 680 return touchDownResult; 681 } 682 683 template <class InputReceiver> 684 void APZCTesterBase::TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget, 685 const ScreenIntPoint& aPoint, 686 TimeDuration aTapLength) { 687 nsEventStatus statuses[2]; 688 Tap(aTarget, aPoint, aTapLength, &statuses); 689 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]); 690 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]); 691 } 692 693 template <class InputReceiver> 694 void APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget, 695 const ScreenIntPoint& aTouchStart, 696 const ScreenIntPoint& aTouchEnd, PanOptions aOptions, 697 nsTArray<uint32_t>* aAllowedTouchBehaviors, 698 nsEventStatus (*aOutEventStatuses)[4], 699 uint64_t* aOutInputBlockId) { 700 // Reduce the move tolerance to a tiny value. 701 // We can't use a scoped pref because this value might be read at some later 702 // time when the events are actually processed, rather than when we deliver 703 // them. 704 const float touchStartTolerance = 0.1f; 705 const float panThreshold = touchStartTolerance * aTarget->GetDPI(); 706 Preferences::SetFloat("apz.touch_start_tolerance", touchStartTolerance); 707 Preferences::SetFloat("apz.touch_move_tolerance", 0.0f); 708 int overcomeTouchToleranceX = 0; 709 int overcomeTouchToleranceY = 0; 710 if (!(aOptions & PanOptions::ExactCoordinates)) { 711 // Have the direction of the adjustment to overcome the touch tolerance 712 // match the direction of the entire gesture, otherwise we run into 713 // trouble such as accidentally activating the axis lock. 714 if (aTouchStart.x != aTouchEnd.x && aTouchStart.y != aTouchEnd.y) { 715 // Tests that need to avoid rounding error here can arrange for 716 // panThreshold to be 10 (by setting the DPI to 100), which makes sure 717 // that these are the legs in a Pythagorean triple where panThreshold is 718 // the hypotenuse. Watch out for changes of APZCPinchTester::mDPI. 719 overcomeTouchToleranceX = panThreshold / 10 * 6; 720 overcomeTouchToleranceY = panThreshold / 10 * 8; 721 } else if (aTouchStart.x != aTouchEnd.x) { 722 overcomeTouchToleranceX = panThreshold; 723 } else if (aTouchStart.y != aTouchEnd.y) { 724 overcomeTouchToleranceY = panThreshold; 725 } 726 } 727 728 const TimeDuration TIME_BETWEEN_TOUCH_EVENT = 729 TimeDuration::FromMilliseconds(20); 730 731 // Even if the caller doesn't care about the block id, we need it to set the 732 // allowed touch behaviour below, so make sure aOutInputBlockId is non-null. 733 uint64_t blockId; 734 if (!aOutInputBlockId) { 735 aOutInputBlockId = &blockId; 736 } 737 738 // Make sure the move is large enough to not be handled as a tap 739 APZEventResult result = 740 TouchDown(aTarget, 741 ScreenIntPoint(aTouchStart.x + overcomeTouchToleranceX, 742 aTouchStart.y + overcomeTouchToleranceY), 743 mcc->Time()); 744 if (aOutInputBlockId) { 745 *aOutInputBlockId = result.mInputBlockId; 746 } 747 if (aOutEventStatuses) { 748 (*aOutEventStatuses)[0] = result.GetStatus(); 749 } 750 751 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 752 753 // Allowed touch behaviours must be set after sending touch-start. 754 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 755 if (aAllowedTouchBehaviors) { 756 EXPECT_EQ(1UL, aAllowedTouchBehaviors->Length()); 757 aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, 758 *aAllowedTouchBehaviors); 759 } else { 760 SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId); 761 } 762 } 763 764 result = TouchMove(aTarget, aTouchStart, mcc->Time()); 765 if (aOutEventStatuses) { 766 (*aOutEventStatuses)[1] = result.GetStatus(); 767 } 768 769 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 770 771 const int numSteps = 3; 772 auto stepVector = (aTouchEnd - aTouchStart) / numSteps; 773 for (int k = 1; k < numSteps; k++) { 774 auto stepPoint = aTouchStart + stepVector * k; 775 (void)TouchMove(aTarget, stepPoint, mcc->Time()); 776 777 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 778 } 779 780 result = TouchMove(aTarget, aTouchEnd, mcc->Time()); 781 if (aOutEventStatuses) { 782 (*aOutEventStatuses)[2] = result.GetStatus(); 783 } 784 785 mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT); 786 787 if (!(aOptions & PanOptions::KeepFingerDown)) { 788 result = TouchUp(aTarget, aTouchEnd, mcc->Time()); 789 } else { 790 result.SetStatusAsIgnore(); 791 } 792 if (aOutEventStatuses) { 793 (*aOutEventStatuses)[3] = result.GetStatus(); 794 } 795 796 if ((aOptions & PanOptions::NoFling)) { 797 aTarget->CancelAnimation(); 798 } 799 800 // Don't increment the time here. Animations started on touch-up, such as 801 // flings, are affected by elapsed time, and we want to be able to sample 802 // them immediately after they start, without time having elapsed. 803 } 804 805 template <class InputReceiver> 806 void APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget, int aTouchStartY, 807 int aTouchEndY, PanOptions aOptions, 808 nsTArray<uint32_t>* aAllowedTouchBehaviors, 809 nsEventStatus (*aOutEventStatuses)[4], 810 uint64_t* aOutInputBlockId) { 811 Pan(aTarget, ScreenIntPoint(10, aTouchStartY), ScreenIntPoint(10, aTouchEndY), 812 aOptions, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId); 813 } 814 815 template <class InputReceiver> 816 void APZCTesterBase::PanAndCheckStatus( 817 const RefPtr<InputReceiver>& aTarget, int aTouchStartY, int aTouchEndY, 818 bool aExpectConsumed, nsTArray<uint32_t>* aAllowedTouchBehaviors, 819 uint64_t* aOutInputBlockId) { 820 nsEventStatus statuses[4]; // down, move, move, up 821 Pan(aTarget, aTouchStartY, aTouchEndY, PanOptions::None, 822 aAllowedTouchBehaviors, &statuses, aOutInputBlockId); 823 824 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]); 825 826 nsEventStatus touchMoveStatus; 827 if (aExpectConsumed) { 828 touchMoveStatus = nsEventStatus_eConsumeDoDefault; 829 } else { 830 touchMoveStatus = nsEventStatus_eIgnore; 831 } 832 EXPECT_EQ(touchMoveStatus, statuses[1]); 833 EXPECT_EQ(touchMoveStatus, statuses[2]); 834 } 835 836 template <class InputReceiver> 837 void APZCTesterBase::DoubleTap(const RefPtr<InputReceiver>& aTarget, 838 const ScreenIntPoint& aPoint, 839 nsEventStatus (*aOutEventStatuses)[4], 840 uint64_t (*aOutInputBlockIds)[2]) { 841 APZEventResult result = TouchDown(aTarget, aPoint, mcc->Time()); 842 if (aOutEventStatuses) { 843 (*aOutEventStatuses)[0] = result.GetStatus(); 844 } 845 if (aOutInputBlockIds) { 846 (*aOutInputBlockIds)[0] = result.mInputBlockId; 847 } 848 mcc->AdvanceByMillis(10); 849 850 // If touch-action is enabled then simulate the allowed touch behaviour 851 // notification that the main thread is supposed to deliver. 852 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 853 SetDefaultAllowedTouchBehavior(aTarget, result.mInputBlockId); 854 } 855 856 result = TouchUp(aTarget, aPoint, mcc->Time()); 857 if (aOutEventStatuses) { 858 (*aOutEventStatuses)[1] = result.GetStatus(); 859 } 860 mcc->AdvanceByMillis(10); 861 result = TouchDown(aTarget, aPoint, mcc->Time()); 862 if (aOutEventStatuses) { 863 (*aOutEventStatuses)[2] = result.GetStatus(); 864 } 865 if (aOutInputBlockIds) { 866 (*aOutInputBlockIds)[1] = result.mInputBlockId; 867 } 868 mcc->AdvanceByMillis(10); 869 870 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) { 871 SetDefaultAllowedTouchBehavior(aTarget, result.mInputBlockId); 872 } 873 874 result = TouchUp(aTarget, aPoint, mcc->Time()); 875 if (aOutEventStatuses) { 876 (*aOutEventStatuses)[3] = result.GetStatus(); 877 } 878 } 879 880 template <class InputReceiver> 881 void APZCTesterBase::DoubleTapAndCheckStatus( 882 const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint, 883 uint64_t (*aOutInputBlockIds)[2]) { 884 nsEventStatus statuses[4]; 885 DoubleTap(aTarget, aPoint, &statuses, aOutInputBlockIds); 886 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]); 887 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]); 888 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]); 889 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]); 890 } 891 892 template <class InputReceiver> 893 void APZCTesterBase::PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget, 894 const ScreenIntPoint& aFocus, 895 float aScale, PinchOptions aOptions) { 896 // Having pinch coordinates in float type may cause problems with 897 // high-precision scale values since SingleTouchData accepts integer value. 898 // But for trivial tests it should be ok. 899 const float pinchLength = 100.0; 900 const float pinchLengthScaled = pinchLength * aScale; 901 902 const float pinchLengthX = aOptions.mVertical ? 0 : pinchLength; 903 const float pinchLengthScaledX = aOptions.mVertical ? 0 : pinchLengthScaled; 904 const float pinchLengthY = aOptions.mVertical ? pinchLength : 0; 905 const float pinchLengthScaledY = aOptions.mVertical ? pinchLengthScaled : 0; 906 907 // Even if the caller doesn't care about the block id, we need it to set the 908 // allowed touch behaviour below, so make sure aOutInputBlockId is non-null. 909 uint64_t blockId; 910 if (!aOptions.mOutInputBlockId) { 911 aOptions.mOutInputBlockId = &blockId; 912 } 913 914 int inputId = aOptions.mInputId ? *aOptions.mInputId : 0; 915 916 // If a second focus point is not specified in the pinch options, use the 917 // same focus point throughout the gesture. 918 ScreenIntPoint secondFocus = 919 aOptions.mSecondFocus.isSome() ? *aOptions.mSecondFocus : aFocus; 920 921 MultiTouchInput mtiStart = 922 MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, mcc->Time(), 0); 923 mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus)); 924 mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus)); 925 APZEventResult result; 926 result = aTarget->ReceiveInputEvent(mtiStart); 927 if (aOptions.mOutInputBlockId) { 928 *aOptions.mOutInputBlockId = result.mInputBlockId; 929 } 930 if (aOptions.mOutEventStatuses) { 931 (*aOptions.mOutEventStatuses)[0] = result.GetStatus(); 932 } 933 934 if (aOptions.mAllowedTouchBehaviors) { 935 EXPECT_EQ(2UL, aOptions.mAllowedTouchBehaviors->Length()); 936 aTarget->SetAllowedTouchBehavior(*aOptions.mOutInputBlockId, 937 *aOptions.mAllowedTouchBehaviors); 938 } else { 939 SetDefaultAllowedTouchBehavior(aTarget, *aOptions.mOutInputBlockId, 2); 940 } 941 942 mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents); 943 944 ScreenIntPoint pinchStartPoint1(aFocus.x - int32_t(pinchLengthX), 945 aFocus.y - int32_t(pinchLengthY)); 946 ScreenIntPoint pinchStartPoint2(aFocus.x + int32_t(pinchLengthX), 947 aFocus.y + int32_t(pinchLengthY)); 948 949 MultiTouchInput mtiMove1 = 950 MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); 951 mtiMove1.mTouches.AppendElement( 952 CreateSingleTouchData(inputId, pinchStartPoint1)); 953 mtiMove1.mTouches.AppendElement( 954 CreateSingleTouchData(inputId + 1, pinchStartPoint2)); 955 result = aTarget->ReceiveInputEvent(mtiMove1); 956 if (aOptions.mOutEventStatuses) { 957 (*aOptions.mOutEventStatuses)[1] = result.GetStatus(); 958 } 959 960 mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents); 961 962 // Pinch instantly but move in steps. 963 const int numSteps = 3; 964 auto stepVector = (secondFocus - aFocus) / numSteps; 965 for (int k = 1; k < numSteps; k++) { 966 ScreenIntPoint stepFocus = aFocus + stepVector * k; 967 ScreenIntPoint stepPoint1(stepFocus.x - int32_t(pinchLengthScaledX), 968 stepFocus.y - int32_t(pinchLengthScaledY)); 969 ScreenIntPoint stepPoint2(stepFocus.x + int32_t(pinchLengthScaledX), 970 stepFocus.y + int32_t(pinchLengthScaledY)); 971 MultiTouchInput mtiMoveStep = 972 MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); 973 mtiMoveStep.mTouches.AppendElement( 974 CreateSingleTouchData(inputId, stepPoint1)); 975 mtiMoveStep.mTouches.AppendElement( 976 CreateSingleTouchData(inputId + 1, stepPoint2)); 977 (void)aTarget->ReceiveInputEvent(mtiMoveStep); 978 979 mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents); 980 } 981 982 ScreenIntPoint pinchEndPoint1(secondFocus.x - int32_t(pinchLengthScaledX), 983 secondFocus.y - int32_t(pinchLengthScaledY)); 984 ScreenIntPoint pinchEndPoint2(secondFocus.x + int32_t(pinchLengthScaledX), 985 secondFocus.y + int32_t(pinchLengthScaledY)); 986 987 MultiTouchInput mtiMove2 = 988 MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0); 989 mtiMove2.mTouches.AppendElement( 990 CreateSingleTouchData(inputId, pinchEndPoint1)); 991 mtiMove2.mTouches.AppendElement( 992 CreateSingleTouchData(inputId + 1, pinchEndPoint2)); 993 result = aTarget->ReceiveInputEvent(mtiMove2); 994 if (aOptions.mOutEventStatuses) { 995 (*aOptions.mOutEventStatuses)[2] = result.GetStatus(); 996 } 997 998 if (aOptions.mFlags & (PinchFlags::LiftFinger1 | PinchFlags::LiftFinger2)) { 999 mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents); 1000 1001 MultiTouchInput mtiEnd = 1002 MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, mcc->Time(), 0); 1003 if (aOptions.mFlags & PinchFlags::LiftFinger1) { 1004 mtiEnd.mTouches.AppendElement( 1005 CreateSingleTouchData(inputId, pinchEndPoint1)); 1006 } 1007 if (aOptions.mFlags & PinchFlags::LiftFinger2) { 1008 mtiEnd.mTouches.AppendElement( 1009 CreateSingleTouchData(inputId + 1, pinchEndPoint2)); 1010 } 1011 result = aTarget->ReceiveInputEvent(mtiEnd); 1012 if (aOptions.mOutEventStatuses) { 1013 (*aOptions.mOutEventStatuses)[3] = result.GetStatus(); 1014 } 1015 } 1016 1017 inputId += 2; 1018 1019 if (aOptions.mInputId) { 1020 *aOptions.mInputId = inputId; 1021 } 1022 } 1023 1024 template <class InputReceiver> 1025 void APZCTesterBase::PinchWithTouchInputAndCheckStatus( 1026 const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus, 1027 float aScale, int& inputId, bool aShouldTriggerPinch, 1028 nsTArray<uint32_t>* aAllowedTouchBehaviors) { 1029 nsEventStatus statuses[4]; // down, move, move, up 1030 PinchWithTouchInput(aTarget, aFocus, aScale, 1031 PinchOptions() 1032 .AllowedTouchBehaviors(aAllowedTouchBehaviors) 1033 .OutEventStatuses(&statuses) 1034 .InputId(inputId)); 1035 1036 nsEventStatus expectedMoveStatus = aShouldTriggerPinch 1037 ? nsEventStatus_eConsumeDoDefault 1038 : nsEventStatus_eIgnore; 1039 EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]); 1040 EXPECT_EQ(expectedMoveStatus, statuses[1]); 1041 EXPECT_EQ(expectedMoveStatus, statuses[2]); 1042 } 1043 1044 template <class InputReceiver> 1045 void APZCTesterBase::PinchWithPinchInput( 1046 const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus, 1047 const ScreenIntPoint& aSecondFocus, float aScale, 1048 nsEventStatus (*aOutEventStatuses)[3]) { 1049 const TimeDuration TIME_BETWEEN_PINCH_INPUT = 1050 TimeDuration::FromMilliseconds(50); 1051 1052 auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START, 1053 aFocus, 10.0, 10.0, mcc->Time()); 1054 APZEventResult actual = aTarget->ReceiveInputEvent(event); 1055 if (aOutEventStatuses) { 1056 (*aOutEventStatuses)[0] = actual.GetStatus(); 1057 } 1058 mcc->AdvanceBy(TIME_BETWEEN_PINCH_INPUT); 1059 1060 event = 1061 CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE, 1062 aSecondFocus, 10.0 * aScale, 10.0, mcc->Time()); 1063 actual = aTarget->ReceiveInputEvent(event); 1064 if (aOutEventStatuses) { 1065 (*aOutEventStatuses)[1] = actual.GetStatus(); 1066 } 1067 mcc->AdvanceBy(TIME_BETWEEN_PINCH_INPUT); 1068 1069 event = 1070 CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_END, aSecondFocus, 1071 10.0 * aScale, 10.0 * aScale, mcc->Time()); 1072 actual = aTarget->ReceiveInputEvent(event); 1073 if (aOutEventStatuses) { 1074 (*aOutEventStatuses)[2] = actual.GetStatus(); 1075 } 1076 } 1077 1078 template <class InputReceiver> 1079 void APZCTesterBase::PinchWithPinchInputAndCheckStatus( 1080 const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus, 1081 float aScale, bool aShouldTriggerPinch) { 1082 nsEventStatus statuses[3]; // scalebegin, scale, scaleend 1083 PinchWithPinchInput(aTarget, aFocus, aFocus, aScale, &statuses); 1084 1085 nsEventStatus expectedStatus = aShouldTriggerPinch 1086 ? nsEventStatus_eConsumeDoDefault 1087 : nsEventStatus_eIgnore; 1088 EXPECT_EQ(expectedStatus, statuses[0]); 1089 EXPECT_EQ(expectedStatus, statuses[1]); 1090 } 1091 1092 inline FrameMetrics TestFrameMetrics() { 1093 FrameMetrics fm; 1094 1095 fm.SetDisplayPort(CSSRect(0, 0, 10, 10)); 1096 fm.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10)); 1097 fm.SetScrollableRect(CSSRect(0, 0, 100, 100)); 1098 1099 return fm; 1100 } 1101 1102 #endif // mozilla_layers_APZTestCommon_h