tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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