Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  APZTestCommon.h   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef mozilla_layers_APZTestCommon_h
#define mozilla_layers_APZTestCommon_h

/**
 * Defines a set of mock classes and utility functions/classes for
 * writing APZ gtests.
 */


#include "gtest/gtest.h"
#include "gmock/gmock.h"

#include "mozilla/Attributes.h"
#include "mozilla/layers/GeckoContentController.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/DoubleTapToZoom.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/MatrixMessage.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/TypedEnumBits.h"
#include "mozilla/UniquePtr.h"
#include "apz/src/APZCTreeManager.h"
#include "apz/src/AsyncPanZoomController.h"
#include "apz/src/HitTestingTreeNode.h"
#include "base/task.h"
#include "gfxPlatform.h"
#include "TestWRScrollData.h"
#include "UnitTransforms.h"

using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::AtMost;
using ::testing::InSequence;
using ::testing::MockFunction;
using ::testing::NiceMock;
typedef mozilla::layers::GeckoContentController::TapType TapType;

inline TimeStamp GetStartupTime() {
  static TimeStamp sStartupTime = TimeStamp::Now();
  return sStartupTime;
}

inline uint32_t MillisecondsSinceStartup(TimeStamp aTime) {
  return (aTime - GetStartupTime()).ToMilliseconds();
}

// Some helper functions for constructing input event objects suitable to be
// passed either to an APZC (which expects an transformed point), or to an APZTM
// (which expects an untransformed point). We handle both cases by setting both
// the transformed and untransformed fields to the same value.
inline SingleTouchData CreateSingleTouchData(int32_t aIdentifier,
                                             const ScreenIntPoint& aPoint) {
  SingleTouchData touch(aIdentifier, aPoint, ScreenSize(0, 0), 0, 0);
  touch.mLocalScreenPoint = ParentLayerPoint(aPoint.x, aPoint.y);
  return touch;
}

// Convenience wrapper for CreateSingleTouchData() that takes loose coordinates.
inline SingleTouchData CreateSingleTouchData(int32_t aIdentifier,
                                             ScreenIntCoord aX,
                                             ScreenIntCoord aY) {
  return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY));
}

inline PinchGestureInput CreatePinchGestureInput(
    PinchGestureInput::PinchGestureType aType, const ScreenPoint& aFocus,
    float aCurrentSpan, float aPreviousSpan, TimeStamp timestamp) {
  ParentLayerPoint localFocus(aFocus.x, aFocus.y);
  PinchGestureInput result(aType, PinchGestureInput::UNKNOWN, timestamp,
                           ExternalPoint(0, 0), aFocus, aCurrentSpan,
                           aPreviousSpan, 0);
  return result;
}

template <class SetArg, class Storage>
class ScopedGfxSetting {
 public:
  ScopedGfxSetting(const std::function<SetArg(void)>& aGetPrefFunc,
                   const std::function<void(SetArg)>& aSetPrefFunc, SetArg aVal)
      : mSetPrefFunc(aSetPrefFunc) {
    mOldVal = aGetPrefFunc();
    aSetPrefFunc(aVal);
  }

  ~ScopedGfxSetting() { mSetPrefFunc(mOldVal); }

 private:
  std::function<void(SetArg)> mSetPrefFunc;
  Storage mOldVal;
};

static inline constexpr auto kDefaultTouchBehavior =
    AllowedTouchBehavior::VERTICAL_PAN | AllowedTouchBehavior::HORIZONTAL_PAN |
    AllowedTouchBehavior::PINCH_ZOOM | AllowedTouchBehavior::ANIMATING_ZOOM;

#define FRESH_PREF_VAR_PASTE(id, line) id##line
#define FRESH_PREF_VAR_EXPAND(id, line) FRESH_PREF_VAR_PASTE(id, line)
#define FRESH_PREF_VAR FRESH_PREF_VAR_EXPAND(pref, __LINE__)

#define SCOPED_GFX_PREF_BOOL(prefName, prefValue)                           \
  ScopedGfxSetting<boolbool> FRESH_PREF_VAR(                              \
      [=]() { return Preferences::GetBool(prefName); },                     \
      [=](bool aPrefValue) { Preferences::SetBool(prefName, aPrefValue); }, \
      prefValue)

#define SCOPED_GFX_PREF_INT(prefName, prefValue)                              \
  ScopedGfxSetting<int32_t, int32_t> FRESH_PREF_VAR(                          \
      [=]() { return Preferences::GetInt(prefName); },                        \
      [=](int32_t aPrefValue) { Preferences::SetInt(prefName, aPrefValue); }, \
      prefValue)

#define SCOPED_GFX_PREF_FLOAT(prefName, prefValue)                            \
  ScopedGfxSetting<floatfloat> FRESH_PREF_VAR(                              \
      [=]() { return Preferences::GetFloat(prefName); },                      \
      [=](float aPrefValue) { Preferences::SetFloat(prefName, aPrefValue); }, \
      prefValue)

class MockContentController : public GeckoContentController {
 public:
  MOCK_METHOD1(NotifyLayerTransforms, void(nsTArray<MatrixMessage>&&));
  MOCK_METHOD1(RequestContentRepaint, void(const RepaintRequest&));
  MOCK_METHOD6(HandleTap, void(TapType, const LayoutDevicePoint&, Modifiers,
                               const ScrollableLayerGuid&, uint64_t,
                               const Maybe<DoubleTapToZoomMetrics>&));
  MOCK_METHOD5(NotifyPinchGesture,
               void(PinchGestureInput::PinchGestureType,
                    const ScrollableLayerGuid&, const LayoutDevicePoint&,
                    LayoutDeviceCoord, Modifiers));
  // Can't use the macros with already_AddRefed :(
  void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
    RefPtr<Runnable> task = aTask;
  }
  bool IsRepaintThread() { return NS_IsMainThread(); }
  void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) {
    NS_DispatchToMainThread(std::move(aTask));
  }
  MOCK_METHOD4(NotifyAPZStateChange,
               void(const ScrollableLayerGuid& aGuid, APZStateChange aChange,
                    int aArg, Maybe<uint64_t> aInputBlockId));
  MOCK_METHOD0(NotifyFlushComplete, void());
  MOCK_METHOD3(NotifyAsyncScrollbarDragInitiated,
               void(uint64_t, const ScrollableLayerGuid::ViewID&,
                    ScrollDirection aDirection));
  MOCK_METHOD1(NotifyAsyncScrollbarDragRejected,
               void(const ScrollableLayerGuid::ViewID&));
  MOCK_METHOD1(NotifyAsyncAutoscrollRejected,
               void(const ScrollableLayerGuid::ViewID&));
  MOCK_METHOD1(CancelAutoscroll, void(const ScrollableLayerGuid&));
  MOCK_METHOD2(NotifyScaleGestureComplete,
               void(const ScrollableLayerGuid&, float aScale));
  MOCK_METHOD4(UpdateOverscrollVelocity,
               void(const ScrollableLayerGuid&, floatfloatbool));
  MOCK_METHOD4(UpdateOverscrollOffset,
               void(const ScrollableLayerGuid&, floatfloatbool));
};

class MockContentControllerDelayed : public MockContentController {
 public:
  MockContentControllerDelayed()
      : mTime(SampleTime::FromTest(GetStartupTime())) {}

  const TimeStamp& Time() { return mTime.Time(); }
  const SampleTime& GetSampleTime() { return mTime; }

  void AdvanceByMillis(int aMillis) {
    AdvanceBy(TimeDuration::FromMilliseconds(aMillis));
  }

  void AdvanceBy(const TimeDuration& aIncrement) {
    SampleTime target = mTime + aIncrement;
    while (mTaskQueue.Length() > 0 && mTaskQueue[0].second <= target) {
      RunNextDelayedTask();
    }
    mTime = target;
  }

  void PostDelayedTask(already_AddRefed<Runnable> aTask, int aDelayMs) {
    RefPtr<Runnable> task = aTask;
    SampleTime runAtTime = mTime + TimeDuration::FromMilliseconds(aDelayMs);
    int insIndex = mTaskQueue.Length();
    while (insIndex > 0) {
      if (mTaskQueue[insIndex - 1].second <= runAtTime) {
        break;
      }
      insIndex--;
    }
    mTaskQueue.InsertElementAt(insIndex, std::make_pair(task, runAtTime));
  }

  // Run all the tasks in the queue, returning the number of tasks
  // run. Note that if a task queues another task while running, that
  // new task will not be run. Therefore, there may be still be tasks
  // in the queue after this function is called. Only when the return
  // value is 0 is the queue guaranteed to be empty.
  int RunThroughDelayedTasks() {
    nsTArray<std::pair<RefPtr<Runnable>, SampleTime>> runQueue =
        std::move(mTaskQueue);
    int numTasks = runQueue.Length();
    for (int i = 0; i < numTasks; i++) {
      mTime = runQueue[i].second;
      runQueue[i].first->Run();

      // Deleting the task is important in order to release the reference to
      // the callee object.
      runQueue[i].first = nullptr;
    }
    return numTasks;
  }

 private:
  void RunNextDelayedTask() {
    std::pair<RefPtr<Runnable>, SampleTime> next = mTaskQueue[0];
    mTaskQueue.RemoveElementAt(0);
    mTime = next.second;
    next.first->Run();
    // Deleting the task is important in order to release the reference to
    // the callee object.
    next.first = nullptr;
  }

  // The following array is sorted by timestamp (tasks are inserted in order by
  // timestamp).
  nsTArray<std::pair<RefPtr<Runnable>, SampleTime>> mTaskQueue;
  SampleTime mTime;
};

class TestAPZCTreeManager : public APZCTreeManager {
 public:
  explicit TestAPZCTreeManager(MockContentControllerDelayed* aMcc,
                               UniquePtr<IAPZHitTester> aHitTester = nullptr)
      : APZCTreeManager(LayersId{0}, std::move(aHitTester)), mcc(aMcc) {
    Init();
  }

  RefPtr<InputQueue> GetInputQueue() const { return mInputQueue; }

  void ClearContentController() { mcc = nullptr; }

  /**
   * This function is not currently implemented.
   * See bug 1468804 for more information.
   **/

  void CancelAnimation() { EXPECT_TRUE(false); }

  bool AdvanceAnimations(const SampleTime& aSampleTime) {
    MutexAutoLock lock(mMapLock);
    return AdvanceAnimationsInternal(lock, aSampleTime);
  }

  APZEventResult ReceiveInputEvent(
      InputData& aEvent,
      InputBlockCallback&& aCallback = InputBlockCallback()) override {
    APZEventResult result =
        APZCTreeManager::ReceiveInputEvent(aEvent, std::move(aCallback));
    if (aEvent.mInputType == PANGESTURE_INPUT &&
        // In the APZCTreeManager::ReceiveInputEvent some type of pan gesture
        // events are marked as `mHandledByAPZ = false` (e.g. with Ctrl key
        // modifier which causes reflow zoom), in such cases the events will
        // never be processed by InputQueue so we shouldn't try to invoke
        // AllowsSwipe() here.
        aEvent.AsPanGestureInput().mHandledByAPZ &&
        aEvent.AsPanGestureInput().AllowsSwipe()) {
      SetBrowserGestureResponse(result.mInputBlockId,
                                BrowserGestureResponse::NotConsumed);
    }
    return result;
  }

 protected:
  already_AddRefed<AsyncPanZoomController> NewAPZCInstance(
      LayersId aLayersId, GeckoContentController* aController) override;

  SampleTime GetFrameTime() override { return mcc->GetSampleTime(); }

 private:
  RefPtr<MockContentControllerDelayed> mcc;
};

class TestAsyncPanZoomController : public AsyncPanZoomController {
 public:
  TestAsyncPanZoomController(LayersId aLayersId,
                             MockContentControllerDelayed* aMcc,
                             TestAPZCTreeManager* aTreeManager,
                             GestureBehavior aBehavior = DEFAULT_GESTURES)
      : AsyncPanZoomController(aLayersId, aTreeManager,
                               aTreeManager->GetInputQueue(), aMcc, aBehavior),
        mWaitForMainThread(false),
        mcc(aMcc) {}

  APZEventResult ReceiveInputEvent(
      InputData& aEvent,
      const Maybe<nsTArray<uint32_t>>& aTouchBehaviors = Nothing()) {
    // This is a function whose signature matches exactly the ReceiveInputEvent
    // on APZCTreeManager. This allows us to templates for functions like
    // TouchDown, TouchUp, etc so that we can reuse the code for dispatching
    // events into both APZC and APZCTM.
    APZEventResult result = GetInputQueue()->ReceiveInputEvent(
        this, TargetConfirmationFlags{!mWaitForMainThread}, aEvent,
        aTouchBehaviors);

    if (aEvent.mInputType == PANGESTURE_INPUT &&
        aEvent.AsPanGestureInput().AllowsSwipe()) {
      GetInputQueue()->SetBrowserGestureResponse(
          result.mInputBlockId, BrowserGestureResponse::NotConsumed);
    }
    return result;
  }

  void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
    GetInputQueue()->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
  }

  void ConfirmTarget(uint64_t aInputBlockId) {
    RefPtr<AsyncPanZoomController> target = this;
    GetInputQueue()->SetConfirmedTargetApzc(aInputBlockId, target);
  }

  void SetAllowedTouchBehavior(uint64_t aInputBlockId,
                               const nsTArray<TouchBehaviorFlags>& aBehaviors) {
    GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors);
  }

  void SetFrameMetrics(const FrameMetrics& metrics) {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    Metrics() = metrics;
  }

  void SetScrollMetadata(const ScrollMetadata& aMetadata) {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    mScrollMetadata = aMetadata;
  }

  FrameMetrics& GetFrameMetrics() {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    return mScrollMetadata.GetMetrics();
  }

  ScrollMetadata& GetScrollMetadata() {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    return mScrollMetadata;
  }

  const FrameMetrics& GetFrameMetrics() const {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    return mScrollMetadata.GetMetrics();
  }

  using AsyncPanZoomController::GetOverscrollAmount;
  using AsyncPanZoomController::GetVelocityVector;

  void AssertStateIsReset() const {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(NOTHING, mState);
  }

  void AssertStateIsFling() const {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(FLING, mState);
  }

  void AssertStateIsSmoothScroll() const {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(SMOOTH_SCROLL, mState);
  }

  void AssertStateIsSmoothMsdScroll() const {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(SMOOTHMSD_SCROLL, mState);
  }

  void AssertStateIsPanningLockedY() {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(PANNING_LOCKED_Y, mState);
  }

  void AssertStateIsPanningLockedX() {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(PANNING_LOCKED_X, mState);
  }

  void AssertStateIsPanning() {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(PANNING, mState);
  }

  void AssertStateIsPanMomentum() {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(PAN_MOMENTUM, mState);
  }

  void AssertStateIsWheelScroll() {
    RecursiveMutexAutoLock lock(mRecursiveMutex);
    EXPECT_EQ(WHEEL_SCROLL, mState);
  }

  void SetAxisLocked(ScrollDirections aDirections, bool aLockValue) {
    if (aDirections.contains(ScrollDirection::eVertical)) {
      mY.SetAxisLocked(aLockValue);
    }
    if (aDirections.contains(ScrollDirection::eHorizontal)) {
      mX.SetAxisLocked(aLockValue);
    }
  }

  void AssertNotAxisLocked() const {
    EXPECT_FALSE(mY.IsAxisLocked());
    EXPECT_FALSE(mX.IsAxisLocked());
  }

  void AssertAxisLocked(ScrollDirection aDirection) const {
    switch (aDirection) {
      case ScrollDirection::eHorizontal:
        EXPECT_TRUE(mY.IsAxisLocked());
        EXPECT_FALSE(mX.IsAxisLocked());
        break;
      case ScrollDirection::eVertical:
        EXPECT_TRUE(mX.IsAxisLocked());
        EXPECT_FALSE(mY.IsAxisLocked());
        break;
      default:
        FAIL() << "input direction must be either vertical or horizontal";
    }
  }

  void AdvanceAnimationsUntilEnd(
      const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(10)) {
    while (AdvanceAnimations(mcc->GetSampleTime())) {
      mcc->AdvanceBy(aIncrement);
    }
  }

  bool SampleContentTransformForFrame(
      AsyncTransform* aOutTransform, ParentLayerPoint& aScrollOffset,
      const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) {
    mcc->AdvanceBy(aIncrement);
    bool ret = AdvanceAnimations(mcc->GetSampleTime());
    if (aOutTransform) {
      *aOutTransform =
          GetCurrentAsyncTransform(AsyncPanZoomController::eForEventHandling);
    }
    aScrollOffset =
        GetCurrentAsyncScrollOffset(AsyncPanZoomController::eForEventHandling);
    return ret;
  }

  CSSPoint GetCompositedScrollOffset() const {
    return GetCurrentAsyncScrollOffset(
               AsyncPanZoomController::eForCompositing) /
           GetFrameMetrics().GetZoom();
  }

  void SetWaitForMainThread() { mWaitForMainThread = true; }

  bool IsOverscrollAnimationRunning() const {
    return mState == PanZoomState::OVERSCROLL_ANIMATION;
  }

  bool IsWheelScrollAnimationRunning() const {
    return mState == PanZoomState::WHEEL_SCROLL;
  }

 private:
  bool mWaitForMainThread;
  MockContentControllerDelayed* mcc;
};

class APZCTesterBase : public ::testing::Test {
 public:
  APZCTesterBase() { mcc = new NiceMock<MockContentControllerDelayed>(); }

  void SetUp() override {
    gfxPlatform::GetPlatform();
    // This pref is changed in Pan() without using SCOPED_GFX_PREF
    // because the modified value needs to be in place until the touch
    // events are processed, which may not happen until the input queue
    // is flushed in TearDown(). So, we save and restore its value here.
    mTouchStartTolerance = StaticPrefs::apz_touch_start_tolerance();
  }

  void TearDown() override {
    Preferences::SetFloat("apz.touch_start_tolerance", mTouchStartTolerance);
  }

  enum class PanOptions {
    None = 0,
    KeepFingerDown = 0x1,
    /*
     * Do not adjust the touch-start coordinates to overcome the touch-start
     * tolerance threshold. If this option is passed, it's up to the caller
     * to pass in coordinates that are sufficient to overcome the touch-start
     * tolerance *and* cause the desired amount of scrolling.
     */

    ExactCoordinates = 0x2,
    NoFling = 0x4
  };

  enum class PinchFlags {
    None = 0,
    LiftFinger1 = 0x1,
    LiftFinger2 = 0x2,
    /*
     * The bitwise OR result of (LiftFinger1 | LiftFinger2).
     * Defined explicitly here because it is used as the default
     * argument for PinchWithTouchInput which is defined BEFORE the
     * definition of operator| for this class.
     */

    LiftBothFingers = 0x3
  };

  template <class InputReceiver>
  APZEventResult Tap(const RefPtr<InputReceiver>& aTarget,
                     const ScreenIntPoint& aPoint, TimeDuration aTapLength,
                     nsEventStatus (*aOutEventStatuses)[2] = nullptr);

  template <class InputReceiver>
  void TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
                         const ScreenIntPoint& aPoint, TimeDuration aTapLength);

  template <class InputReceiver>
  void Pan(const RefPtr<InputReceiver>& aTarget,
           const ScreenIntPoint& aTouchStart, const ScreenIntPoint& aTouchEnd,
           PanOptions aOptions = PanOptions::None,
           nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
           nsEventStatus (*aOutEventStatuses)[4] = nullptr,
           uint64_t* aOutInputBlockId = nullptr);

  /*
   * A version of Pan() that only takes y coordinates rather than (x, y) points
   * for the touch start and end points, and uses 10 for the x coordinates.
   * This is for convenience, as most tests only need to pan in one direction.
   */

  template <class InputReceiver>
  void Pan(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
           int aTouchEndY, PanOptions aOptions = PanOptions::None,
           nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
           nsEventStatus (*aOutEventStatuses)[4] = nullptr,
           uint64_t* aOutInputBlockId = nullptr);

  /*
   * Dispatches mock touch events to the apzc and checks whether apzc properly
   * consumed them and triggered scrolling behavior.
   */

  template <class InputReceiver>
  void PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
                         int aTouchEndY, bool aExpectConsumed,
                         nsTArray<uint32_t>* aAllowedTouchBehaviors,
                         uint64_t* aOutInputBlockId = nullptr);

  template <class InputReceiver>
  void DoubleTap(const RefPtr<InputReceiver>& aTarget,
                 const ScreenIntPoint& aPoint,
                 nsEventStatus (*aOutEventStatuses)[4] = nullptr,
                 uint64_t (*aOutInputBlockIds)[2] = nullptr);

  template <class InputReceiver>
  void DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
                               const ScreenIntPoint& aPoint,
                               uint64_t (*aOutInputBlockIds)[2] = nullptr);

  struct PinchOptions {
    nsTArray<uint32_t>* mAllowedTouchBehaviors = nullptr;
    nsEventStatus (*mOutEventStatuses)[4] = nullptr;
    uint64_t* mOutInputBlockId = nullptr;
    PinchFlags mFlags = PinchFlags::LiftBothFingers;
    bool mVertical = false;
    int* mInputId = nullptr;
    Maybe<ScreenIntPoint> mSecondFocus;
    TimeDuration mTimeBetweenTouchEvents = TimeDuration::FromMilliseconds(20);

    // Workaround for https://github.com/llvm/llvm-project/issues/36032
    PinchOptions() {}

    // Fluent interface
    PinchOptions& AllowedTouchBehaviors(
        nsTArray<uint32_t>* aAllowedTouchBehaviors) {
      mAllowedTouchBehaviors = aAllowedTouchBehaviors;
      return *this;
    }
    PinchOptions& OutEventStatuses(nsEventStatus (*aOutEventStatuses)[4]) {
      mOutEventStatuses = aOutEventStatuses;
      return *this;
    }
    PinchOptions& OutInputBlockId(uint64_t* aOutInputBlockId) {
      mOutInputBlockId = aOutInputBlockId;
      return *this;
    }
    PinchOptions& Flags(PinchFlags aFlags) {
      mFlags = aFlags;
      return *this;
    }
    PinchOptions& Vertical(bool aVertical) {
      mVertical = aVertical;
      return *this;
    }
    PinchOptions& InputId(int& aInputId) {
      mInputId = &aInputId;
      return *this;
    }
    PinchOptions& SecondFocus(const ScreenIntPoint& aSecondFocus) {
      mSecondFocus = Some(aSecondFocus);
      return *this;
    }
    PinchOptions& TimeBetweenTouchEvents(const TimeDuration& aDuration) {
      mTimeBetweenTouchEvents = aDuration;
      return *this;
    }
  };

  // Pinch with one focus point. Zooms in place with no panning
  template <class InputReceiver>
  void PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
                           const ScreenIntPoint& aFocus, float aScale,
                           PinchOptions aOptions = PinchOptions());

  template <class InputReceiver>
  void PinchWithTouchInputAndCheckStatus(
      const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus,
      float aScale, int& inputId, bool aShouldTriggerPinch,
      nsTArray<uint32_t>* aAllowedTouchBehaviors);

  template <class InputReceiver>
  void PinchWithPinchInput(const RefPtr<InputReceiver>& aTarget,
                           const ScreenIntPoint& aFocus,
                           const ScreenIntPoint& aSecondFocus, float aScale,
                           nsEventStatus (*aOutEventStatuses)[3] = nullptr);

  template <class InputReceiver>
  void PinchWithPinchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
                                         const ScreenIntPoint& aFocus,
                                         float aScale,
                                         bool aShouldTriggerPinch);

 protected:
  RefPtr<MockContentControllerDelayed> mcc;

 private:
  float mTouchStartTolerance;
};

MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PanOptions)
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(APZCTesterBase::PinchFlags)

template <class InputReceiver>
APZEventResult APZCTesterBase::Tap(const RefPtr<InputReceiver>& aTarget,
                                   const ScreenIntPoint& aPoint,
                                   TimeDuration aTapLength,
                                   nsEventStatus (*aOutEventStatuses)[2]) {
  APZEventResult touchDownResult = TouchDown(aTarget, aPoint, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[0] = touchDownResult.GetStatus();
  }
  mcc->AdvanceBy(aTapLength);

  // If touch-action is enabled then simulate the allowed touch behaviour
  // notification that the main thread is supposed to deliver.
  if (touchDownResult.GetStatus() != nsEventStatus_eConsumeNoDefault) {
    SetDefaultAllowedTouchBehavior(aTarget, touchDownResult.mInputBlockId);
  }

  APZEventResult touchUpResult = TouchUp(aTarget, aPoint, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[1] = touchUpResult.GetStatus();
  }
  return touchDownResult;
}

template <class InputReceiver>
void APZCTesterBase::TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
                                       const ScreenIntPoint& aPoint,
                                       TimeDuration aTapLength) {
  nsEventStatus statuses[2];
  Tap(aTarget, aPoint, aTapLength, &statuses);
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
}

template <class InputReceiver>
void APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget,
                         const ScreenIntPoint& aTouchStart,
                         const ScreenIntPoint& aTouchEnd, PanOptions aOptions,
                         nsTArray<uint32_t>* aAllowedTouchBehaviors,
                         nsEventStatus (*aOutEventStatuses)[4],
                         uint64_t* aOutInputBlockId) {
  // Reduce the move tolerance to a tiny value.
  // We can't use a scoped pref because this value might be read at some later
  // time when the events are actually processed, rather than when we deliver
  // them.
  const float touchStartTolerance = 0.1f;
  const float panThreshold = touchStartTolerance * aTarget->GetDPI();
  Preferences::SetFloat("apz.touch_start_tolerance", touchStartTolerance);
  Preferences::SetFloat("apz.touch_move_tolerance", 0.0f);
  int overcomeTouchToleranceX = 0;
  int overcomeTouchToleranceY = 0;
  if (!(aOptions & PanOptions::ExactCoordinates)) {
    // Have the direction of the adjustment to overcome the touch tolerance
    // match the direction of the entire gesture, otherwise we run into
    // trouble such as accidentally activating the axis lock.
    if (aTouchStart.x != aTouchEnd.x && aTouchStart.y != aTouchEnd.y) {
      // Tests that need to avoid rounding error here can arrange for
      // panThreshold to be 10 (by setting the DPI to 100), which makes sure
      // that these are the legs in a Pythagorean triple where panThreshold is
      // the hypotenuse. Watch out for changes of APZCPinchTester::mDPI.
      overcomeTouchToleranceX = panThreshold / 10 * 6;
      overcomeTouchToleranceY = panThreshold / 10 * 8;
    } else if (aTouchStart.x != aTouchEnd.x) {
      overcomeTouchToleranceX = panThreshold;
    } else if (aTouchStart.y != aTouchEnd.y) {
      overcomeTouchToleranceY = panThreshold;
    }
  }

  const TimeDuration TIME_BETWEEN_TOUCH_EVENT =
      TimeDuration::FromMilliseconds(20);

  // Even if the caller doesn't care about the block id, we need it to set the
  // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
  uint64_t blockId;
  if (!aOutInputBlockId) {
    aOutInputBlockId = &blockId;
  }

  // Make sure the move is large enough to not be handled as a tap
  APZEventResult result =
      TouchDown(aTarget,
                ScreenIntPoint(aTouchStart.x + overcomeTouchToleranceX,
                               aTouchStart.y + overcomeTouchToleranceY),
                mcc->Time());
  if (aOutInputBlockId) {
    *aOutInputBlockId = result.mInputBlockId;
  }
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[0] = result.GetStatus();
  }

  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);

  // Allowed touch behaviours must be set after sending touch-start.
  if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) {
    if (aAllowedTouchBehaviors) {
      EXPECT_EQ(1UL, aAllowedTouchBehaviors->Length());
      aTarget->SetAllowedTouchBehavior(*aOutInputBlockId,
                                       *aAllowedTouchBehaviors);
    } else {
      SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
    }
  }

  result = TouchMove(aTarget, aTouchStart, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[1] = result.GetStatus();
  }

  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);

  const int numSteps = 3;
  auto stepVector = (aTouchEnd - aTouchStart) / numSteps;
  for (int k = 1; k < numSteps; k++) {
    auto stepPoint = aTouchStart + stepVector * k;
    Unused << TouchMove(aTarget, stepPoint, mcc->Time());

    mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
  }

  result = TouchMove(aTarget, aTouchEnd, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[2] = result.GetStatus();
  }

  mcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);

  if (!(aOptions & PanOptions::KeepFingerDown)) {
    result = TouchUp(aTarget, aTouchEnd, mcc->Time());
  } else {
    result.SetStatusAsIgnore();
  }
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[3] = result.GetStatus();
  }

  if ((aOptions & PanOptions::NoFling)) {
    aTarget->CancelAnimation();
  }

  // Don't increment the time here. Animations started on touch-up, such as
  // flings, are affected by elapsed time, and we want to be able to sample
  // them immediately after they start, without time having elapsed.
}

template <class InputReceiver>
void APZCTesterBase::Pan(const RefPtr<InputReceiver>& aTarget, int aTouchStartY,
                         int aTouchEndY, PanOptions aOptions,
                         nsTArray<uint32_t>* aAllowedTouchBehaviors,
                         nsEventStatus (*aOutEventStatuses)[4],
                         uint64_t* aOutInputBlockId) {
  Pan(aTarget, ScreenIntPoint(10, aTouchStartY), ScreenIntPoint(10, aTouchEndY),
      aOptions, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId);
}

template <class InputReceiver>
void APZCTesterBase::PanAndCheckStatus(
    const RefPtr<InputReceiver>& aTarget, int aTouchStartY, int aTouchEndY,
    bool aExpectConsumed, nsTArray<uint32_t>* aAllowedTouchBehaviors,
    uint64_t* aOutInputBlockId) {
  nsEventStatus statuses[4];  // down, move, move, up
  Pan(aTarget, aTouchStartY, aTouchEndY, PanOptions::None,
      aAllowedTouchBehaviors, &statuses, aOutInputBlockId);

  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);

  nsEventStatus touchMoveStatus;
  if (aExpectConsumed) {
    touchMoveStatus = nsEventStatus_eConsumeDoDefault;
  } else {
    touchMoveStatus = nsEventStatus_eIgnore;
  }
  EXPECT_EQ(touchMoveStatus, statuses[1]);
  EXPECT_EQ(touchMoveStatus, statuses[2]);
}

template <class InputReceiver>
void APZCTesterBase::DoubleTap(const RefPtr<InputReceiver>& aTarget,
                               const ScreenIntPoint& aPoint,
                               nsEventStatus (*aOutEventStatuses)[4],
                               uint64_t (*aOutInputBlockIds)[2]) {
  APZEventResult result = TouchDown(aTarget, aPoint, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[0] = result.GetStatus();
  }
  if (aOutInputBlockIds) {
    (*aOutInputBlockIds)[0] = result.mInputBlockId;
  }
  mcc->AdvanceByMillis(10);

  // If touch-action is enabled then simulate the allowed touch behaviour
  // notification that the main thread is supposed to deliver.
  if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) {
    SetDefaultAllowedTouchBehavior(aTarget, result.mInputBlockId);
  }

  result = TouchUp(aTarget, aPoint, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[1] = result.GetStatus();
  }
  mcc->AdvanceByMillis(10);
  result = TouchDown(aTarget, aPoint, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[2] = result.GetStatus();
  }
  if (aOutInputBlockIds) {
    (*aOutInputBlockIds)[1] = result.mInputBlockId;
  }
  mcc->AdvanceByMillis(10);

  if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) {
    SetDefaultAllowedTouchBehavior(aTarget, result.mInputBlockId);
  }

  result = TouchUp(aTarget, aPoint, mcc->Time());
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[3] = result.GetStatus();
  }
}

template <class InputReceiver>
void APZCTesterBase::DoubleTapAndCheckStatus(
    const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aPoint,
    uint64_t (*aOutInputBlockIds)[2]) {
  nsEventStatus statuses[4];
  DoubleTap(aTarget, aPoint, &statuses, aOutInputBlockIds);
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
}

template <class InputReceiver>
void APZCTesterBase::PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
                                         const ScreenIntPoint& aFocus,
                                         float aScale, PinchOptions aOptions) {
  // Having pinch coordinates in float type may cause problems with
  // high-precision scale values since SingleTouchData accepts integer value.
  // But for trivial tests it should be ok.
  const float pinchLength = 100.0;
  const float pinchLengthScaled = pinchLength * aScale;

  const float pinchLengthX = aOptions.mVertical ? 0 : pinchLength;
  const float pinchLengthScaledX = aOptions.mVertical ? 0 : pinchLengthScaled;
  const float pinchLengthY = aOptions.mVertical ? pinchLength : 0;
  const float pinchLengthScaledY = aOptions.mVertical ? pinchLengthScaled : 0;

  // Even if the caller doesn't care about the block id, we need it to set the
  // allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
  uint64_t blockId;
  if (!aOptions.mOutInputBlockId) {
    aOptions.mOutInputBlockId = &blockId;
  }

  int inputId = aOptions.mInputId ? *aOptions.mInputId : 0;

  // If a second focus point is not specified in the pinch options, use the
  // same focus point throughout the gesture.
  ScreenIntPoint secondFocus =
      aOptions.mSecondFocus.isSome() ? *aOptions.mSecondFocus : aFocus;

  MultiTouchInput mtiStart =
      MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, mcc->Time(), 0);
  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocus));
  mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocus));
  APZEventResult result;
  result = aTarget->ReceiveInputEvent(mtiStart);
  if (aOptions.mOutInputBlockId) {
    *aOptions.mOutInputBlockId = result.mInputBlockId;
  }
  if (aOptions.mOutEventStatuses) {
    (*aOptions.mOutEventStatuses)[0] = result.GetStatus();
  }

  if (aOptions.mAllowedTouchBehaviors) {
    EXPECT_EQ(2UL, aOptions.mAllowedTouchBehaviors->Length());
    aTarget->SetAllowedTouchBehavior(*aOptions.mOutInputBlockId,
                                     *aOptions.mAllowedTouchBehaviors);
  } else {
    SetDefaultAllowedTouchBehavior(aTarget, *aOptions.mOutInputBlockId, 2);
  }

  mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents);

  ScreenIntPoint pinchStartPoint1(aFocus.x - int32_t(pinchLengthX),
                                  aFocus.y - int32_t(pinchLengthY));
  ScreenIntPoint pinchStartPoint2(aFocus.x + int32_t(pinchLengthX),
                                  aFocus.y + int32_t(pinchLengthY));

  MultiTouchInput mtiMove1 =
      MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0);
  mtiMove1.mTouches.AppendElement(
      CreateSingleTouchData(inputId, pinchStartPoint1));
  mtiMove1.mTouches.AppendElement(
      CreateSingleTouchData(inputId + 1, pinchStartPoint2));
  result = aTarget->ReceiveInputEvent(mtiMove1);
  if (aOptions.mOutEventStatuses) {
    (*aOptions.mOutEventStatuses)[1] = result.GetStatus();
  }

  mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents);

  // Pinch instantly but move in steps.
  const int numSteps = 3;
  auto stepVector = (secondFocus - aFocus) / numSteps;
  for (int k = 1; k < numSteps; k++) {
    ScreenIntPoint stepFocus = aFocus + stepVector * k;
    ScreenIntPoint stepPoint1(stepFocus.x - int32_t(pinchLengthScaledX),
                              stepFocus.y - int32_t(pinchLengthScaledY));
    ScreenIntPoint stepPoint2(stepFocus.x + int32_t(pinchLengthScaledX),
                              stepFocus.y + int32_t(pinchLengthScaledY));
    MultiTouchInput mtiMoveStep =
        MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0);
    mtiMoveStep.mTouches.AppendElement(
        CreateSingleTouchData(inputId, stepPoint1));
    mtiMoveStep.mTouches.AppendElement(
        CreateSingleTouchData(inputId + 1, stepPoint2));
    Unused << aTarget->ReceiveInputEvent(mtiMoveStep);

    mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents);
  }

  ScreenIntPoint pinchEndPoint1(secondFocus.x - int32_t(pinchLengthScaledX),
                                secondFocus.y - int32_t(pinchLengthScaledY));
  ScreenIntPoint pinchEndPoint2(secondFocus.x + int32_t(pinchLengthScaledX),
                                secondFocus.y + int32_t(pinchLengthScaledY));

  MultiTouchInput mtiMove2 =
      MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, mcc->Time(), 0);
  mtiMove2.mTouches.AppendElement(
      CreateSingleTouchData(inputId, pinchEndPoint1));
  mtiMove2.mTouches.AppendElement(
      CreateSingleTouchData(inputId + 1, pinchEndPoint2));
  result = aTarget->ReceiveInputEvent(mtiMove2);
  if (aOptions.mOutEventStatuses) {
    (*aOptions.mOutEventStatuses)[2] = result.GetStatus();
  }

  if (aOptions.mFlags & (PinchFlags::LiftFinger1 | PinchFlags::LiftFinger2)) {
    mcc->AdvanceBy(aOptions.mTimeBetweenTouchEvents);

    MultiTouchInput mtiEnd =
        MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, mcc->Time(), 0);
    if (aOptions.mFlags & PinchFlags::LiftFinger1) {
      mtiEnd.mTouches.AppendElement(
          CreateSingleTouchData(inputId, pinchEndPoint1));
    }
    if (aOptions.mFlags & PinchFlags::LiftFinger2) {
      mtiEnd.mTouches.AppendElement(
          CreateSingleTouchData(inputId + 1, pinchEndPoint2));
    }
    result = aTarget->ReceiveInputEvent(mtiEnd);
    if (aOptions.mOutEventStatuses) {
      (*aOptions.mOutEventStatuses)[3] = result.GetStatus();
    }
  }

  inputId += 2;

  if (aOptions.mInputId) {
    *aOptions.mInputId = inputId;
  }
}

template <class InputReceiver>
void APZCTesterBase::PinchWithTouchInputAndCheckStatus(
    const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus,
    float aScale, int& inputId, bool aShouldTriggerPinch,
    nsTArray<uint32_t>* aAllowedTouchBehaviors) {
  nsEventStatus statuses[4];  // down, move, move, up
  PinchWithTouchInput(aTarget, aFocus, aScale,
                      PinchOptions()
                          .AllowedTouchBehaviors(aAllowedTouchBehaviors)
                          .OutEventStatuses(&statuses)
                          .InputId(inputId));

  nsEventStatus expectedMoveStatus = aShouldTriggerPinch
                                         ? nsEventStatus_eConsumeDoDefault
                                         : nsEventStatus_eIgnore;
  EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
  EXPECT_EQ(expectedMoveStatus, statuses[1]);
  EXPECT_EQ(expectedMoveStatus, statuses[2]);
}

template <class InputReceiver>
void APZCTesterBase::PinchWithPinchInput(
    const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus,
    const ScreenIntPoint& aSecondFocus, float aScale,
    nsEventStatus (*aOutEventStatuses)[3]) {
  const TimeDuration TIME_BETWEEN_PINCH_INPUT =
      TimeDuration::FromMilliseconds(50);

  auto event = CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
                                       aFocus, 10.0, 10.0, mcc->Time());
  APZEventResult actual = aTarget->ReceiveInputEvent(event);
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[0] = actual.GetStatus();
  }
  mcc->AdvanceBy(TIME_BETWEEN_PINCH_INPUT);

  event =
      CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
                              aSecondFocus, 10.0 * aScale, 10.0, mcc->Time());
  actual = aTarget->ReceiveInputEvent(event);
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[1] = actual.GetStatus();
  }
  mcc->AdvanceBy(TIME_BETWEEN_PINCH_INPUT);

  event =
      CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_END, aSecondFocus,
                              10.0 * aScale, 10.0 * aScale, mcc->Time());
  actual = aTarget->ReceiveInputEvent(event);
  if (aOutEventStatuses) {
    (*aOutEventStatuses)[2] = actual.GetStatus();
  }
}

template <class InputReceiver>
void APZCTesterBase::PinchWithPinchInputAndCheckStatus(
    const RefPtr<InputReceiver>& aTarget, const ScreenIntPoint& aFocus,
    float aScale, bool aShouldTriggerPinch) {
  nsEventStatus statuses[3];  // scalebegin, scale, scaleend
  PinchWithPinchInput(aTarget, aFocus, aFocus, aScale, &statuses);

  nsEventStatus expectedStatus = aShouldTriggerPinch
                                     ? nsEventStatus_eConsumeDoDefault
                                     : nsEventStatus_eIgnore;
  EXPECT_EQ(expectedStatus, statuses[0]);
  EXPECT_EQ(expectedStatus, statuses[1]);
}

inline FrameMetrics TestFrameMetrics() {
  FrameMetrics fm;

  fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
  fm.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10));
  fm.SetScrollableRect(CSSRect(0, 0, 100, 100));

  return fm;
}

#endif  // mozilla_layers_APZTestCommon_h

Messung V0.5
C=88 H=94 G=90

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge