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


Quelle  TestOverscroll.cpp

  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/. */


#include "APZCBasicTester.h"
#include "APZCTreeManagerTester.h"
#include "APZTestCommon.h"
#include "mozilla/ScrollPositionUpdate.h"
#include "mozilla/layers/ScrollableLayerGuid.h"
#include "mozilla/layers/WebRenderScrollDataWrapper.h"

#include "InputUtils.h"

class APZCOverscrollTester : public APZCBasicTester {
 public:
  explicit APZCOverscrollTester(
      AsyncPanZoomController::GestureBehavior aGestureBehavior =
          AsyncPanZoomController::DEFAULT_GESTURES)
      : APZCBasicTester(aGestureBehavior) {}

 protected:
  UniquePtr<ScopedLayerTreeRegistration> registration;

  void TestOverscroll() {
    // Pan sufficiently to hit overscroll behavior
    PanIntoOverscroll();

    // Check that we recover from overscroll via an animation.
    ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
    SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
  }

  void PanIntoOverscroll() {
    int touchStart = 500;
    int touchEnd = 10;
    Pan(apzc, touchStart, touchEnd);
    EXPECT_TRUE(apzc->IsOverscrolled());
  }

  /**
   * Sample animations until we recover from overscroll.
   * @param aExpectedScrollOffset the expected reported scroll offset
   *                              throughout the animation
   */

  void SampleAnimationUntilRecoveredFromOverscroll(
      const ParentLayerPoint& aExpectedScrollOffset) {
    const TimeDuration increment = TimeDuration::FromMilliseconds(1);
    bool recoveredFromOverscroll = false;
    ParentLayerPoint pointOut;
    AsyncTransform viewTransformOut;
    while (apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut)) {
      // The reported scroll offset should be the same throughout.
      EXPECT_EQ(aExpectedScrollOffset, pointOut);

      // Trigger computation of the overscroll tranform, to make sure
      // no assetions fire during the calculation.
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);

      if (!apzc->IsOverscrolled()) {
        recoveredFromOverscroll = true;
      }

      mcc->AdvanceBy(increment);
    }
    EXPECT_TRUE(recoveredFromOverscroll);
    apzc->AssertStateIsReset();
  }

  ScrollableLayerGuid CreateSimpleRootScrollableForWebRender() {
    ScrollableLayerGuid guid;
    guid.mScrollId = ScrollableLayerGuid::START_SCROLL_ID;
    guid.mLayersId = LayersId{0};

    ScrollMetadata metadata;
    FrameMetrics& metrics = metadata.GetMetrics();
    metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
    metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
    metrics.SetScrollId(guid.mScrollId);
    metadata.SetIsLayersIdRoot(true);

    WebRenderLayerScrollData rootLayerScrollData;
    rootLayerScrollData.InitializeRoot(0);
    WebRenderScrollData scrollData;
    rootLayerScrollData.AppendScrollMetadata(scrollData, metadata);
    scrollData.AddLayerData(std::move(rootLayerScrollData));

    registration = MakeUnique<ScopedLayerTreeRegistration>(guid.mLayersId, mcc);
    tm->UpdateHitTestingTree(WebRenderScrollDataWrapper(*updater, &scrollData),
                             guid.mLayersId, 0);
    return guid;
  }
};

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, FlingIntoOverscroll) {
  // Enable overscrolling.
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);
  SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0f);

  // Scroll down by 25 px. Don't fling for simplicity.
  Pan(apzc, 50, 25, PanOptions::NoFling);

  // Now scroll back up by 20px, this time flinging after.
  // The fling should cover the remaining 5 px of room to scroll, then
  // go into overscroll, and finally snap-back to recover from overscroll.
  Pan(apzc, 25, 45);
  const TimeDuration increment = TimeDuration::FromMilliseconds(1);
  bool reachedOverscroll = false;
  bool recoveredFromOverscroll = false;
  while (apzc->AdvanceAnimations(mcc->GetSampleTime())) {
    if (!reachedOverscroll && apzc->IsOverscrolled()) {
      reachedOverscroll = true;
    }
    if (reachedOverscroll && !apzc->IsOverscrolled()) {
      recoveredFromOverscroll = true;
    }
    mcc->AdvanceBy(increment);
  }
  EXPECT_TRUE(reachedOverscroll);
  EXPECT_TRUE(recoveredFromOverscroll);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, OverScrollPanning) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  TestOverscroll();
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
// Tests that an overscroll animation doesn't trigger an assertion failure
// in the case where a sample has a velocity of zero.
TEST_F(APZCOverscrollTester, OverScroll_Bug1152051a) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Doctor the prefs to make the velocity zero at the end of the first sample.

  // This ensures our incoming velocity to the overscroll animation is
  // a round(ish) number, 4.9 (that being the distance of the pan before
  // overscroll, which is 500 - 10 = 490 pixels, divided by the duration of
  // the pan, which is 100 ms).
  SCOPED_GFX_PREF_FLOAT("apz.fling_friction", 0);

  TestOverscroll();
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
// Tests that ending an overscroll animation doesn't leave around state that
// confuses the next overscroll animation.
TEST_F(APZCOverscrollTester, OverScroll_Bug1152051b) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);
  SCOPED_GFX_PREF_FLOAT("apz.overscroll.stop_distance_threshold", 0.1f);

  // Pan sufficiently to hit overscroll behavior
  PanIntoOverscroll();

  // Sample animations once, to give the fling animation started on touch-up
  // a chance to realize it's overscrolled, and schedule a call to
  // HandleFlingOverscroll().
  SampleAnimationOnce();

  // This advances the time and runs the HandleFlingOverscroll task scheduled in
  // the previous call, which starts an overscroll animation. It then samples
  // the overscroll animation once, to get it to initialize the first overscroll
  // sample.
  SampleAnimationOnce();

  // Do a touch-down to cancel the overscroll animation, and then a touch-up
  // to schedule a new one since we're still overscrolled. We don't pan because
  // panning can trigger functions that clear the overscroll animation state
  // in other ways.
  APZEventResult result = TouchDown(apzc, ScreenIntPoint(10, 10), mcc->Time());
  if (result.GetStatus() != nsEventStatus_eConsumeNoDefault) {
    SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId);
  }
  TouchUp(apzc, ScreenIntPoint(10, 10), mcc->Time());

  // Sample the second overscroll animation to its end.
  // If the ending of the first overscroll animation fails to clear state
  // properly, this will assert.
  ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
// Tests that the page doesn't get stuck in an
// overscroll animation after a low-velocity pan.
TEST_F(APZCOverscrollTester, OverScrollAfterLowVelocityPan_Bug1343775) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Pan into overscroll with a velocity less than the
  // apz.fling_min_velocity_threshold preference.
  Pan(apzc, 10, 30);

  EXPECT_TRUE(apzc->IsOverscrolled());

  apzc->AdvanceAnimationsUntilEnd();

  // Check that we recovered from overscroll.
  EXPECT_FALSE(apzc->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, OverScrollAbort) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Pan sufficiently to hit overscroll behavior
  int touchStart = 500;
  int touchEnd = 10;
  Pan(apzc, touchStart, touchEnd);
  EXPECT_TRUE(apzc->IsOverscrolled());

  ParentLayerPoint pointOut;
  AsyncTransform viewTransformOut;

  // This sample call will run to the end of the fling animation
  // and will schedule the overscroll animation.
  apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut,
                                       TimeDuration::FromMilliseconds(10000));
  EXPECT_TRUE(apzc->IsOverscrolled());

  // At this point, we have an active overscroll animation.
  // Check that cancelling the animation clears the overscroll.
  apzc->CancelAnimation();
  EXPECT_FALSE(apzc->IsOverscrolled());
  apzc->AssertStateIsReset();
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, OverScrollPanningAbort) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Pan sufficiently to hit overscroll behaviour. Keep the finger down so
  // the pan does not end.
  int touchStart = 500;
  int touchEnd = 10;
  Pan(apzc, touchStart, touchEnd, PanOptions::KeepFingerDown);
  EXPECT_TRUE(apzc->IsOverscrolled());

  // Check that calling CancelAnimation() while the user is still panning
  // (and thus no fling or snap-back animation has had a chance to start)
  // clears the overscroll.
  apzc->CancelAnimation();
  EXPECT_FALSE(apzc->IsOverscrolled());
  apzc->AssertStateIsReset();
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Maybe fails on Android
TEST_F(APZCOverscrollTester, OverscrollByVerticalPanGestures) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  EXPECT_TRUE(apzc->IsOverscrolled());

  // Check that we recover from overscroll via an animation.
  ParentLayerPoint expectedScrollOffset(0, 0);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, StuckInOverscroll_Bug1767337) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());

  // Send two PANGESTURE_END in a row, to see if the second one gets us
  // stuck in overscroll.
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time(), MODIFIER_NONE, true);
  SampleAnimationOnce();
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time(), MODIFIER_NONE, true);

  EXPECT_TRUE(apzc->IsOverscrolled());

  // Check that we recover from overscroll via an animation.
  ParentLayerPoint expectedScrollOffset(0, 0);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, OverscrollByVerticalAndHorizontalPanGestures) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-10, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, 0), mcc->Time());

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  EXPECT_TRUE(apzc->IsOverscrolled());

  // Check that we recover from overscroll via an animation.
  ParentLayerPoint expectedScrollOffset(0, 0);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, OverscrollByPanMomentumGestures) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  // Make sure we are not yet in overscrolled region.
  EXPECT_TRUE(!apzc->IsOverscrolled());

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 200), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 100), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());

  EXPECT_TRUE(apzc->IsOverscrolled());

  // Check that we recover from overscroll via an animation.
  ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, IgnoreMomemtumDuringOverscroll) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  float yMost = GetScrollRange().YMost();
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, yMost / 10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, yMost), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, yMost / 10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  // Make sure we've started an overscroll animation.
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());

  // And check the overscrolled transform value before/after calling PanGesture
  // to make sure the overscroll amount isn't affected by momentum events.
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  AsyncTransformComponentMatrix overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_EQ(
      overscrolledTransform,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling));

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 200), mcc->Time());
  EXPECT_EQ(
      overscrolledTransform,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling));

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 100), mcc->Time());
  EXPECT_EQ(
      overscrolledTransform,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling));

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 2), mcc->Time());
  EXPECT_EQ(
      overscrolledTransform,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling));

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_EQ(
      overscrolledTransform,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling));

  // Check that we've recovered from overscroll via an animation.
  ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, VerticalOnlyOverscroll) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Make the content scrollable only vertically.
  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
  apzc->SetFrameMetrics(metrics);

  // Scroll up into overscroll a bit.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-10, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());
  // Now it's overscrolled.
  EXPECT_TRUE(apzc->IsOverscrolled());
  AsyncTransformComponentMatrix overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  // The overscroll shouldn't happen horizontally.
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  // Happens only vertically.
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  // Send pan momentum events including horizontal bits.
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(-10, -100), mcc->Time());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  // The overscroll shouldn't happen horizontally.
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(-5, -50), mcc->Time());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, -2), mcc->Time());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  // Check that we recover from overscroll via an animation.
  ParentLayerPoint expectedScrollOffset(0, 0);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, VerticalOnlyOverscrollByPanMomentum) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Make the content scrollable only vertically.
  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
  // Scrolls the content down a bit.
  metrics.SetVisualScrollOffset(CSSPoint(0, 50));
  apzc->SetFrameMetrics(metrics);

  // Scroll up a bit where overscroll will not happen.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  // Make sure it's not yet overscrolled.
  EXPECT_TRUE(!apzc->IsOverscrolled());

  // Send pan momentum events including horizontal bits.
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(-10, -100), mcc->Time());
  // Now it's overscrolled.
  EXPECT_TRUE(apzc->IsOverscrolled());

  AsyncTransformComponentMatrix overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  // But the overscroll shouldn't happen horizontally.
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  // Happens only vertically.
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(-5, -50), mcc->Time());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, -2), mcc->Time());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  overscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_TRUE(overscrolledTransform._41 == 0);
  EXPECT_TRUE(overscrolledTransform._42 != 0);

  // Check that we recover from overscroll via an animation.
  ParentLayerPoint expectedScrollOffset(0, 0);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, DisallowOverscrollInSingleLineTextControl) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Create a horizontal scrollable frame with `vertical disregarded direction`.
  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 10));
  metrics.SetScrollableRect(CSSRect(0, 0, 1000, 10));
  apzc->SetFrameMetrics(metrics);
  metadata.SetDisregardedDirection(Some(ScrollDirection::eVertical));
  apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
                            /*aThisLayerTreeUpdated=*/true);

  // Try to overscroll up and left with pan gestures.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 5),
             ScreenPoint(-2, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 5),
             ScreenPoint(-10, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 5),
             ScreenPoint(-2, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 5),
             ScreenPoint(0, 0), mcc->Time());

  // No overscrolling should happen.
  EXPECT_TRUE(!apzc->IsOverscrolled());

  // Send pan momentum events too.
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 5), ScreenPoint(0, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 5), ScreenPoint(-100, -100), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 5), ScreenPoint(-50, -50), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 5), ScreenPoint(-2, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 5), ScreenPoint(0, 0), mcc->Time());
  // No overscrolling should happen either.
  EXPECT_TRUE(!apzc->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Maybe fails on Android
// Tests that horizontal overscroll animation keeps running with vertical
// pan momentum scrolling.
TEST_F(APZCOverscrollTester,
       HorizontalOverscrollAnimationWithVerticalPanMomentumScrolling) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 1000, 5000));
  apzc->SetFrameMetrics(metrics);

  // Try to overscroll left with pan gestures.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-10, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  // Make sure we've started an overscroll animation.
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  AsyncTransformComponentMatrix initialOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);

  // Send lengthy downward momentums to make sure the overscroll animation
  // doesn't clobber the momentums scrolling.
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll amount on X axis has started being managed by the overscroll
  // animation.
  AsyncTransformComponentMatrix currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_NE(initialOverscrolledTransform._41, currentOverscrolledTransform._41);
  // There is no overscroll on Y axis.
  EXPECT_EQ(currentOverscrolledTransform._42, 0);
  ParentLayerPoint scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  // The scroll offset shouldn't be changed by the overscroll animation.
  EXPECT_EQ(scrollOffset.y, 0);

  // Simple gesture on the Y axis to ensure that we can send a vertical
  // momentum scroll
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  ParentLayerPoint offsetAfterPan = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);

  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on both axes shouldn't be changed by this pan
  // momentum start event since the displacement is zero.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  EXPECT_EQ(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll amount should be managed by the overscroll animation.
  EXPECT_NE(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  // Not yet started scrolling.
  EXPECT_EQ(scrollOffset.y, offsetAfterPan.y);
  EXPECT_EQ(scrollOffset.x, 0);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);

  // Send a long pan momentum.
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 200), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on X axis shouldn't be changed by this momentum pan.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // Now it started scrolling vertically.
  scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  EXPECT_GT(scrollOffset.y, 0);
  EXPECT_EQ(scrollOffset.x, 0);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll on X axis keeps being managed by the overscroll animation.
  EXPECT_NE(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // The scroll offset on Y axis shouldn't be changed by the overscroll
  // animation.
  EXPECT_EQ(scrollOffset.y, apzc->GetCurrentAsyncScrollOffset(
                                    AsyncPanZoomController::eForEventHandling)
                                .y);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 100), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on X axis shouldn't be changed by this momentum pan.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // Scrolling keeps going by momentum.
  EXPECT_GT(apzc->GetCurrentAsyncScrollOffset(
                    AsyncPanZoomController::eForEventHandling)
                .y,
            scrollOffset.y);

  scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 10), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // Scrolling keeps going by momentum.
  EXPECT_GT(apzc->GetCurrentAsyncScrollOffset(
                    AsyncPanZoomController::eForEventHandling)
                .y,
            scrollOffset.y);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // This momentum event doesn't change the scroll offset since its
  // displacement is zero.
  EXPECT_EQ(apzc->GetCurrentAsyncScrollOffset(
                    AsyncPanZoomController::eForEventHandling)
                .y,
            scrollOffset.y);

  // Check that we recover from the horizontal overscroll via the animation.
  ParentLayerPoint expectedScrollOffset(0, scrollOffset.y);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Maybe fails on Android
// Similar to above
// HorizontalOverscrollAnimationWithVerticalPanMomentumScrolling,
// but having OverscrollAnimation on both axes initially.
TEST_F(APZCOverscrollTester,
       BothAxesOverscrollAnimationWithPanMomentumScrolling) {
  // TODO: This test currently requires gestures that cause movement on both
  // axis, which excludes DOMINANT_AXIS locking mode. The gestures should be
  // broken up into multiple gestures to cause the overscroll.
  SCOPED_GFX_PREF_INT("apz.axis_lock.mode", 2);
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 1000, 5000));
  apzc->SetFrameMetrics(metrics);

  // Try to overscroll up and left with pan gestures.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-10, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  // Make sure we've started an overscroll animation.
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  AsyncTransformComponentMatrix initialOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);

  // Send lengthy downward momentums to make sure the overscroll animation
  // doesn't clobber the momentums scrolling.
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll amount has started being managed by the overscroll
  // animation.
  AsyncTransformComponentMatrix currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_NE(initialOverscrolledTransform._41, currentOverscrolledTransform._41);
  EXPECT_NE(initialOverscrolledTransform._42, currentOverscrolledTransform._42);

  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on both axes shouldn't be changed by this pan
  // momentum start event since the displacement is zero.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  EXPECT_EQ(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // Still being managed by the overscroll animation.
  EXPECT_NE(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  EXPECT_NE(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  // Send a long pan momentum.
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 200), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on X axis shouldn't be changed by this momentum pan.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // But now the overscroll amount on Y axis should be changed by this momentum
  // pan.
  EXPECT_NE(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);
  // Actually it's no longer overscrolled.
  EXPECT_EQ(
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42,
      0);

  ParentLayerPoint currentScrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  // Now it started scrolling.
  EXPECT_GT(currentScrollOffset.y, 0);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll on X axis keeps being managed by the overscroll animation.
  EXPECT_NE(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // But the overscroll on Y axis is no longer affected by the overscroll
  // animation.
  EXPECT_EQ(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);
  // The scroll offset on Y axis shouldn't be changed by the overscroll
  // animation.
  EXPECT_EQ(currentScrollOffset.y,
            apzc->GetCurrentAsyncScrollOffset(
                    AsyncPanZoomController::eForEventHandling)
                .y);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  currentScrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 100), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on X axis shouldn't be changed by this momentum pan.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // Keeping no overscrolling on Y axis.
  EXPECT_EQ(
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42,
      0);
  // Scrolling keeps going by momentum.
  EXPECT_GT(apzc->GetCurrentAsyncScrollOffset(
                    AsyncPanZoomController::eForEventHandling)
                .y,
            currentScrollOffset.y);

  currentScrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 10), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // Keeping no overscrolling on Y axis.
  EXPECT_EQ(
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42,
      0);
  // Scrolling keeps going by momentum.
  EXPECT_GT(apzc->GetCurrentAsyncScrollOffset(
                    AsyncPanZoomController::eForEventHandling)
                .y,
            currentScrollOffset.y);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  currentScrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // Keeping no overscrolling on Y axis.
  EXPECT_EQ(
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42,
      0);
  // This momentum event doesn't change the scroll offset since its
  // displacement is zero.
  EXPECT_EQ(apzc->GetCurrentAsyncScrollOffset(
                    AsyncPanZoomController::eForEventHandling)
                .y,
            currentScrollOffset.y);

  // Check that we recover from the horizontal overscroll via the animation.
  ParentLayerPoint expectedScrollOffset(0, currentScrollOffset.y);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Maybe fails on Android
// This is another variant of
// HorizontalOverscrollAnimationWithVerticalPanMomentumScrolling. In this test,
// after a horizontal overscroll animation started, upwards pan moments happen,
// thus there should be a new vertical overscroll animation in addition to
// the horizontal one.
TEST_F(
    APZCOverscrollTester,
    VerticalOverscrollAnimationInAdditionToExistingHorizontalOverscrollAnimation) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 1000, 5000));
  // Scrolls the content 50px down.
  metrics.SetVisualScrollOffset(CSSPoint(0, 50));
  apzc->SetFrameMetrics(metrics);

  // Try to overscroll left with pan gestures.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-10, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(-2, 0), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  // Make sure we've started an overscroll animation.
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  AsyncTransformComponentMatrix initialOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);

  // Send lengthy __upward__ momentums to make sure the overscroll animation
  // doesn't clobber the momentums scrolling.
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll amount on X axis has started being managed by the overscroll
  // animation.
  AsyncTransformComponentMatrix currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  EXPECT_NE(initialOverscrolledTransform._41, currentOverscrolledTransform._41);
  // There is no overscroll on Y axis.
  EXPECT_EQ(
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42,
      0);
  ParentLayerPoint scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  // The scroll offset shouldn't be changed by the overscroll animation.
  EXPECT_EQ(scrollOffset.y, 50);

  // Simple gesture on the Y axis to ensure that we can send a vertical
  // momentum scroll
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 0), mcc->Time());

  ParentLayerPoint offsetAfterPan = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);

  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on both axes shouldn't be changed by this pan
  // momentum start event since the displacement is zero.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  EXPECT_EQ(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll amount should be managed by the overscroll animation.
  EXPECT_NE(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  // Not yet started scrolling.
  EXPECT_EQ(scrollOffset.y, offsetAfterPan.y);
  EXPECT_EQ(scrollOffset.x, 0);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);

  // Send a long pan momentum.
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, -200), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on X axis shouldn't be changed by this momentum pan.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // Now it started scrolling vertically.
  scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  EXPECT_EQ(scrollOffset.y, 0);
  EXPECT_EQ(scrollOffset.x, 0);
  // Actually it's also vertically overscrolled.
  EXPECT_GT(
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42,
      0);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // The overscroll on X axis keeps being managed by the overscroll animation.
  EXPECT_NE(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // The overscroll on Y Axis hasn't been changed by the overscroll animation at
  // this moment, sine the last displacement was consumed in the last pan
  // momentum.
  EXPECT_EQ(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, -100), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on X axis shouldn't be changed by this momentum pan.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // Now the overscroll amount on Y axis shouldn't be changed by this momentum
  // pan either.
  EXPECT_EQ(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  EXPECT_NE(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  // And now the overscroll on Y Axis should be also managed by the overscroll
  // animation.
  EXPECT_NE(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, -10), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  // The overscroll amount on both axes shouldn't be changed by momentum event.
  EXPECT_EQ(
      currentOverscrolledTransform._41,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._41);
  EXPECT_EQ(
      currentOverscrolledTransform._42,
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling)
          ._42);

  currentOverscrolledTransform =
      apzc->GetOverscrollTransform(AsyncPanZoomController::eForEventHandling);
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_MOMENTUMEND, apzc,
             ScreenIntPoint(50, 80), ScreenPoint(0, 0), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());

  // Check that we recover from the horizontal overscroll via the animation.
  ParentLayerPoint expectedScrollOffset(0, 0);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTester, OverscrollByPanGesturesInterruptedByReflowZoom) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);
  SCOPED_GFX_PREF_INT("mousewheel.with_control.action", 3);  // reflow zoom.

  // A sanity check that pan gestures with ctrl modifier will not be handled by
  // APZ.
  PanGestureInput panInput(PanGestureInput::PANGESTURE_START, mcc->Time(),
                           ScreenIntPoint(5, 5), ScreenPoint(0, -2),
                           MODIFIER_CONTROL);
  WidgetWheelEvent wheelEvent = panInput.ToWidgetEvent(nullptr);
  EXPECT_FALSE(APZInputBridge::ActionForWheelEvent(&wheelEvent).isSome());

  ScrollableLayerGuid rootGuid = CreateSimpleRootScrollableForWebRender();
  RefPtr<AsyncPanZoomController> apzc =
      tm->GetTargetAPZC(rootGuid.mLayersId, rootGuid.mScrollId);

  PanGesture(PanGestureInput::PANGESTURE_START, tm, ScreenIntPoint(50, 80),
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, tm, ScreenIntPoint(50, 80),
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());

  // Make sure overscrolling has started.
  EXPECT_TRUE(apzc->IsOverscrolled());

  // Press ctrl until PANGESTURE_END.
  PanGestureWithModifiers(PanGestureInput::PANGESTURE_PAN, MODIFIER_CONTROL, tm,
                          ScreenIntPoint(50, 80), ScreenPoint(0, -2),
                          mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  // At this moment (i.e. PANGESTURE_PAN), still in overscrolling state.
  EXPECT_TRUE(apzc->IsOverscrolled());

  PanGestureWithModifiers(PanGestureInput::PANGESTURE_END, MODIFIER_CONTROL, tm,
                          ScreenIntPoint(50, 80), ScreenPoint(0, 0),
                          mcc->Time());
  // The overscrolling state should have been restored.
  EXPECT_TRUE(!apzc->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTester, SmoothTransitionFromPanToAnimation) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
  // Start scrolled down to y=500px.
  metrics.SetVisualScrollOffset(CSSPoint(0, 500));
  apzc->SetFrameMetrics(metrics);

  int frameLength = 10;    // milliseconds; 10 to keep the math simple
  float panVelocity = 10;  // pixels per millisecond
  int panPixelsPerFrame = frameLength * panVelocity;  // 100 pixels per frame

  ScreenIntPoint panPoint(50, 50);
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, -1), mcc->Time());
  // Pan up for 6 frames at 100 pixels per frame. This should reduce
  // the vertical scroll offset from 500 to 0, and get us into overscroll.
  for (int i = 0; i < 6; ++i) {
    mcc->AdvanceByMillis(frameLength);
    PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
               ScreenPoint(0, -panPixelsPerFrame), mcc->Time());
  }
  EXPECT_TRUE(apzc->IsOverscrolled());

  // Pan further into overscroll at the same input velocity, enough
  // for the frames while we are in overscroll to dominate the computation
  // in the velocity tracker.
  // Importantly, while the input velocity is still 100 pixels per frame,
  // in the overscrolled state the page only visual moves by at most 8 pixels
  // per frame.
  int frames = StaticPrefs::apz_velocity_relevance_time_ms() / frameLength;
  for (int i = 0; i < frames; ++i) {
    mcc->AdvanceByMillis(frameLength);
    PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
               ScreenPoint(0, -panPixelsPerFrame), mcc->Time());
  }
  EXPECT_TRUE(apzc->IsOverscrolled());

  // End the pan, allowing an overscroll animation to start.
  mcc->AdvanceByMillis(frameLength);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());

  // Check that the velocity reflects the actual movement (no more than 8
  // pixels/frame ==> 0.8 pixels per millisecond), not the input velocity
  // (100 pixels/frame ==> 10 pixels per millisecond). This ensures that
  // the transition from the pan to the animation appears smooth.
  // (Note: velocities are negative since they are upwards.)
  EXPECT_LT(apzc->GetVelocityVector().y, 0);
  EXPECT_GT(apzc->GetVelocityVector().y, -0.8);
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTester, NoOverscrollForMousewheel) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
  // Start scrolled down just a few pixels from the top.
  metrics.SetVisualScrollOffset(CSSPoint(0, 3));
  // Set line and page scroll amounts. Otherwise, even though Wheel() uses
  // SCROLLDELTA_PIXEL, the wheel handling code will get confused by things
  // like the "don't scroll more than one page" check.
  metadata.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
  metadata.SetLineScrollAmount(LayoutDeviceIntSize(5, 10));
  apzc->SetScrollMetadata(metadata);

  // Send a wheel with enough delta to scrollto y=0 *and* overscroll.
  Wheel(apzc, ScreenIntPoint(10, 10), ScreenPoint(0, -10), mcc->Time());

  // Check that we did not actually go into overscroll.
  EXPECT_FALSE(apzc->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTester, ClickWhileOverscrolled) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
  metrics.SetVisualScrollOffset(CSSPoint(0, 0));
  apzc->SetFrameMetrics(metrics);

  // Pan into overscroll at the top.
  ScreenIntPoint panPoint(50, 50);
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, -1), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
             ScreenPoint(0, -100), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->GetOverscrollAmount().y < 0);  // overscrolled at top

  // End the pan. This should start an overscroll animation.
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());
  EXPECT_TRUE(apzc->GetOverscrollAmount().y < 0);  // overscrolled at top
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());

  // Send a mouse-down. This should interrupt the animation but not relieve
  // overscroll yet.
  ParentLayerPoint overscrollBefore = apzc->GetOverscrollAmount();
  MouseDown(apzc, panPoint, mcc->Time());
  EXPECT_FALSE(apzc->IsOverscrollAnimationRunning());
  EXPECT_EQ(overscrollBefore, apzc->GetOverscrollAmount());

  // Send a mouse-up. This should start an overscroll animation again.
  MouseUp(apzc, panPoint, mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());

  SampleAnimationUntilRecoveredFromOverscroll(ParentLayerPoint(0, 0));
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTester, DynamicallyLoadingContent) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
  metrics.SetVisualScrollOffset(CSSPoint(0, 0));
  apzc->SetFrameMetrics(metrics);

  // Pan to the bottom of the page, and further, into overscroll.
  ScreenIntPoint panPoint(50, 50);
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, 1), mcc->Time());
  for (int i = 0; i < 12; ++i) {
    mcc->AdvanceByMillis(10);
    PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
               ScreenPoint(0, 100), mcc->Time());
  }
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->GetOverscrollAmount().y > 0);  // overscrolled at bottom

  // Grow the scrollable rect at the bottom, simulating the page loading content
  // dynamically.
  CSSRect scrollableRect = metrics.GetScrollableRect();
  scrollableRect.height += 500;
  metrics.SetScrollableRect(scrollableRect);
  apzc->NotifyLayersUpdated(metadata, falsetrue);

  // Check that the modified scrollable rect cleared the overscroll.
  EXPECT_FALSE(apzc->IsOverscrolled());

  // Pan back up to the top, and further, into overscroll.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, -1), mcc->Time());
  for (int i = 0; i < 12; ++i) {
    mcc->AdvanceByMillis(10);
    PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
               ScreenPoint(0, -100), mcc->Time());
  }
  EXPECT_TRUE(apzc->IsOverscrolled());
  ParentLayerPoint overscrollAmount = apzc->GetOverscrollAmount();
  EXPECT_TRUE(overscrollAmount.y < 0);  // overscrolled at top

  // Grow the scrollable rect at the bottom again.
  scrollableRect = metrics.GetScrollableRect();
  scrollableRect.height += 500;
  metrics.SetScrollableRect(scrollableRect);
  apzc->NotifyLayersUpdated(metadata, falsetrue);

  // Check that the modified scrollable rect did NOT clear overscroll at the
  // top.
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_EQ(overscrollAmount,
            apzc->GetOverscrollAmount());  // overscroll did not change at all
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTester, SmallAmountOfOverscroll) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));

  // Do vertical overscroll first.
  ScreenIntPoint panPoint(50, 50);
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());
  mcc->AdvanceByMillis(10);

  // Then do small horizontal overscroll which will be considered as "finished"
  // by our overscroll animation physics model.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(-0.1, 0), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
             ScreenPoint(-0.2, 0), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());
  mcc->AdvanceByMillis(10);

  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->GetOverscrollAmount().y < 0);  // overscrolled at top
  EXPECT_TRUE(apzc->GetOverscrollAmount().x < 0);  // and overscrolled at left

  // Then do vertical scroll.
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, 10), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
             ScreenPoint(0, 100), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());

  ParentLayerPoint scrollOffset = apzc->GetCurrentAsyncScrollOffset(
      AsyncPanZoomController::eForEventHandling);
  EXPECT_GT(scrollOffset.y, 0);  // Make sure the vertical scroll offset is
                                 // greater than zero.

  // The small horizontal overscroll amount should be restored to zero.
  ParentLayerPoint expectedScrollOffset(0, scrollOffset.y);
  SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
#endif

#ifdef MOZ_WIDGET_ANDROID  // Only applies to WidgetOverscrollEffect
TEST_F(APZCOverscrollTester, StuckInOverscroll_Bug1786452) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));

  // Over the course of the test, expect one or more calls to
  // UpdateOverscrollOffset(), followed by a call to UpdateOverscrollVelocity().
  // The latter ensures the widget has a chance to end its overscroll effect.
  InSequence s;
  EXPECT_CALL(*mcc, UpdateOverscrollOffset(_, _, _, _)).Times(AtLeast(1));
  EXPECT_CALL(*mcc, UpdateOverscrollVelocity(_, _, _, _)).Times(1);

  // Pan into overscroll, keeping the finger down
  ScreenIntPoint startPoint(10, 500);
  ScreenIntPoint endPoint(10, 10);
  Pan(apzc, startPoint, endPoint, PanOptions::KeepFingerDown);
  EXPECT_TRUE(apzc->IsOverscrolled());

  // Linger a while to cause the velocity to drop to very low or zero
  mcc->AdvanceByMillis(100);
  TouchMove(apzc, endPoint, mcc->Time());
  EXPECT_LT(apzc->GetVelocityVector().Length(),
            StaticPrefs::apz_fling_min_velocity_threshold());
  EXPECT_TRUE(apzc->IsOverscrolled());

  // Lift the finger
  mcc->AdvanceByMillis(20);
  TouchUp(apzc, endPoint, mcc->Time());
  EXPECT_FALSE(apzc->IsOverscrolled());
}
#endif

class APZCOverscrollTesterMock : public APZCTreeManagerTester {
 public:
  APZCOverscrollTesterMock() { CreateMockHitTester(); }

  UniquePtr<ScopedLayerTreeRegistration> registration;
  TestAsyncPanZoomController* rootApzc;
};

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTesterMock, OverscrollHandoff) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  const char* treeShape = "x(x)";
  LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100),
                                     LayerIntRect(0, 0, 100, 50)};
  CreateScrollData(treeShape, layerVisibleRect);
  SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
                            CSSRect(0, 0, 200, 200));
  SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1,
                            // same size as the visible region so that
                            // the container is not scrollable in any directions
                            // actually. This is simulating overflow: hidden
                            // iframe document in Fission, though we don't set
                            // a different layers id.
                            CSSRect(0, 0, 100, 50));

  SetScrollHandoff(layers[1], root);

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);
  rootApzc->GetFrameMetrics().SetIsRootContent(true);

  // A pan gesture on the child scroller (which is not scrollable though).
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, ScreenIntPoint(50, 20),
             ScreenPoint(0, -2), mcc->Time());
  EXPECT_TRUE(rootApzc->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTesterMock, VerticalOverscrollHandoffToScrollableRoot) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Create a layer tree having two vertical scrollable layers.
  const char* treeShape = "x(x)";
  LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100),
                                     LayerIntRect(0, 0, 100, 50)};
  CreateScrollData(treeShape, layerVisibleRect);
  SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
                            CSSRect(0, 0, 100, 200));
  SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1,
                            CSSRect(0, 0, 100, 200));

  SetScrollHandoff(layers[1], root);

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);
  rootApzc->GetFrameMetrics().SetIsRootContent(true);

  // A vertical pan gesture on the child scroller which will be handed off to
  // the root APZC.
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, ScreenIntPoint(50, 20),
             ScreenPoint(0, -2), mcc->Time());
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_FALSE(ApzcOf(layers[1])->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTesterMock, NoOverscrollHandoffToNonScrollableRoot) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Create a layer tree having non-scrollable root and a vertical scrollable
  // child.
  const char* treeShape = "x(x)";
  LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100),
                                     LayerIntRect(0, 0, 100, 50)};
  CreateScrollData(treeShape, layerVisibleRect);
  SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
                            CSSRect(0, 0, 100, 100));
  SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1,
                            CSSRect(0, 0, 100, 200));

  SetScrollHandoff(layers[1], root);

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);
  rootApzc->GetFrameMetrics().SetIsRootContent(true);

  // A vertical pan gesture on the child scroller which should not be handed
  // off the root APZC.
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, ScreenIntPoint(50, 20),
             ScreenPoint(0, -2), mcc->Time());
  EXPECT_FALSE(rootApzc->IsOverscrolled());
  EXPECT_TRUE(ApzcOf(layers[1])->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Currently fails on Android
TEST_F(APZCOverscrollTesterMock, NoOverscrollHandoffOrthogonalPanGesture) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Create a layer tree having horizontal scrollable root and a vertical
  // scrollable child.
  const char* treeShape = "x(x)";
  LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100),
                                     LayerIntRect(0, 0, 100, 50)};
  CreateScrollData(treeShape, layerVisibleRect);
  SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
                            CSSRect(0, 0, 200, 100));
  SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1,
                            CSSRect(0, 0, 100, 200));

  SetScrollHandoff(layers[1], root);

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);
  rootApzc->GetFrameMetrics().SetIsRootContent(true);

  // A vertical pan gesture on the child scroller which should not be handed
  // off the root APZC because the root APZC is not scrollable vertically.
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, ScreenIntPoint(50, 20),
             ScreenPoint(0, -2), mcc->Time());
  EXPECT_FALSE(rootApzc->IsOverscrolled());
  EXPECT_TRUE(ApzcOf(layers[1])->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTesterMock,
       RetriggerCancelledOverscrollAnimationByNewPanGesture) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Create a layer tree having vertical scrollable root and a horizontal
  // scrollable child.
  const char* treeShape = "x(x)";
  LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100),
                                     LayerIntRect(0, 0, 100, 50)};
  CreateScrollData(treeShape, layerVisibleRect);
  SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
                            CSSRect(0, 0, 100, 200));
  SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1,
                            CSSRect(0, 0, 200, 50));

  SetScrollHandoff(layers[1], root);

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);
  rootApzc->GetFrameMetrics().SetIsRootContent(true);

  ScreenIntPoint panPoint(50, 20);
  // A vertical pan gesture on the child scroller which should be handed off the
  // root APZC.
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, panPoint,
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint,
             ScreenPoint(0, 0), mcc->Time());

  // The root APZC should be overscrolled and the child APZC should not be.
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_FALSE(ApzcOf(layers[1])->IsOverscrolled());

  mcc->AdvanceByMillis(10);

  // Make sure the root APZC is still overscrolled.
  EXPECT_TRUE(rootApzc->IsOverscrolled());

  // Start a new horizontal pan gesture on the child scroller which should be
  // handled by the child APZC now.
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  APZEventResult result = PanGesture(PanGestureInput::PANGESTURE_START, manager,
                                     panPoint, ScreenPoint(-2, 0), mcc->Time());
  // The above horizontal pan start event was flagged as "this event may trigger
  // swipe" and either the root scrollable frame or the horizontal child
  // scrollable frame is not scrollable in the pan start direction, thus the pan
  // start event run into the short circuit path for swipe-to-navigation in
  // InputQueue::ReceivePanGestureInput, which means it's waiting for the
  // content response, so we need to respond explicitly here.
  manager->ContentReceivedInputBlock(result.mInputBlockId, false);
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(-10, 0), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint,
             ScreenPoint(0, 0), mcc->Time());

  // Now both APZCs should be overscrolled.
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_TRUE(ApzcOf(layers[1])->IsOverscrolled());

  // Sample all animations until all of them have been finished.
  while (SampleAnimationsOnce());

  // After the animations finished, all overscrolled states should have been
  // restored.
  EXPECT_FALSE(rootApzc->IsOverscrolled());
  EXPECT_FALSE(ApzcOf(layers[1])->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTesterMock, RetriggeredOverscrollAnimationVelocity) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Setup two nested vertical scrollable frames.
  const char* treeShape = "x(x)";
  LayerIntRect layerVisibleRect[] = {LayerIntRect(0, 0, 100, 100),
                                     LayerIntRect(0, 0, 100, 50)};
  CreateScrollData(treeShape, layerVisibleRect);
  SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
                            CSSRect(0, 0, 100, 200));
  SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1,
                            CSSRect(0, 0, 100, 200));

  SetScrollHandoff(layers[1], root);

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);
  rootApzc->GetFrameMetrics().SetIsRootContent(true);

  ScreenIntPoint panPoint(50, 20);
  // A vertical upward pan gesture on the child scroller which should be handed
  // off the root APZC.
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, panPoint,
             ScreenPoint(0, -2), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint,
             ScreenPoint(0, 0), mcc->Time());

  // The root APZC should be overscrolled and the child APZC should not be.
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_FALSE(ApzcOf(layers[1])->IsOverscrolled());

  mcc->AdvanceByMillis(10);

  // Make sure the root APZC is still overscrolled and there's an overscroll
  // animation.
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_TRUE(rootApzc->IsOverscrollAnimationRunning());

  // And make sure the overscroll animation's velocity is a certain amount in
  // the upward direction.
  EXPECT_LT(rootApzc->GetVelocityVector().y, 0);

  // Start a new downward pan gesture on the child scroller which
  // should be handled by the child APZC now.
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, panPoint,
             ScreenPoint(0, 2), mcc->Time());
  mcc->AdvanceByMillis(10);
  // The new pan-start gesture stops the overscroll animation at this moment.
  EXPECT_TRUE(!rootApzc->IsOverscrollAnimationRunning());

  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(0, 10), mcc->Time());
  mcc->AdvanceByMillis(10);
  // There's no overscroll animation yet even if the root APZC is still
  // overscrolled.
  EXPECT_TRUE(!rootApzc->IsOverscrollAnimationRunning());
  EXPECT_TRUE(rootApzc->IsOverscrolled());

  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
  PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint,
             ScreenPoint(0, 10), mcc->Time());

  // Now an overscroll animation should have been triggered by the pan-end
  // gesture.
  EXPECT_TRUE(rootApzc->IsOverscrollAnimationRunning());
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  // And the newly created overscroll animation's positions should never exceed
  // 0.
  while (SampleAnimationsOnce()) {
    EXPECT_LE(rootApzc->GetOverscrollAmount().y, 0);
  }
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTesterMock, OverscrollIntoPreventDefault) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  const char* treeShape = "x";
  LayerIntRect layerVisibleRects[] = {LayerIntRect(0, 0, 100, 100)};
  CreateScrollData(treeShape, layerVisibleRects);
  SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
                            CSSRect(0, 0, 100, 200));

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);

  // Start a pan gesture a few pixels below the 20px DTC region.
  ScreenIntPoint cursorLocation(10, 25);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
  APZEventResult result =
      PanGesture(PanGestureInput::PANGESTURE_START, manager, cursorLocation,
                 ScreenPoint(0, -2), mcc->Time());

  // At this point, we should be overscrolled.
  EXPECT_TRUE(rootApzc->IsOverscrolled());

  // Pan further, until the DTC region is under the cursor.
  // Note that, due to ApplyResistance(), we need a large input delta to cause a
  // visual transform enough to bridge the 5px to the DTC region.
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, cursorLocation,
             ScreenPoint(0, -100), mcc->Time());

  // At this point, we are still overscrolled. Record the overscroll amount.
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  float overscrollY = rootApzc->GetOverscrollAmount().y;

  // Send a content response with preventDefault = true.
  manager->SetAllowedTouchBehavior(result.mInputBlockId,
                                   {AllowedTouchBehavior::VERTICAL_PAN});
  manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
  manager->ContentReceivedInputBlock(result.mInputBlockId,
                                     /*aPreventDefault=*/true);

  // The content response has the effect of interrupting the input block
  // but no processing happens yet (as there are no events in the block).
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_EQ(overscrollY, rootApzc->GetOverscrollAmount().y);

  // Send one more pan event. This starts a new, *unconfirmed* input block
  // (via the "transmogrify" codepath).
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID,
                     {CompositorHitTestFlags::eVisibleToHitTest,
                      CompositorHitTestFlags::eIrregularArea});
  result = PanGesture(PanGestureInput::PANGESTURE_PAN, manager, cursorLocation,
                      ScreenPoint(0, -10), mcc->Time());

  // No overscroll occurs (the event is waiting in the queue for confirmation).
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_EQ(overscrollY, rootApzc->GetOverscrollAmount().y);

  // preventDefault the new event as well
  manager->SetAllowedTouchBehavior(result.mInputBlockId,
                                   {AllowedTouchBehavior::VERTICAL_PAN});
  manager->SetTargetAPZC(result.mInputBlockId, {result.mTargetGuid});
  manager->ContentReceivedInputBlock(result.mInputBlockId,
                                     /*aPreventDefault=*/true);

  // This should trigger clearing the overscrolling and resetting the state.
  EXPECT_FALSE(rootApzc->IsOverscrolled());
  rootApzc->AssertStateIsReset();

  // If there are momentum events after this point, they should not cause
  // further scrolling or overscorll.
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
  result = PanGesture(PanGestureInput::PANGESTURE_MOMENTUMSTART, manager,
                      cursorLocation, ScreenPoint(0, -100), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
  result = PanGesture(PanGestureInput::PANGESTURE_MOMENTUMPAN, manager,
                      cursorLocation, ScreenPoint(0, -100), mcc->Time());
  EXPECT_FALSE(rootApzc->IsOverscrolled());
  EXPECT_EQ(rootApzc->GetFrameMetrics().GetVisualScrollOffset(),
            CSSPoint(0, 0));
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Only applies to GenericOverscrollEffect
TEST_F(APZCOverscrollTesterMock, StuckInOverscroll_Bug1810935) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  using ViewID = ScrollableLayerGuid::ViewID;
  ViewID rootScrollId = ScrollableLayerGuid::START_SCROLL_ID;
  ViewID subframeScrollId = ScrollableLayerGuid::START_SCROLL_ID + 1;

  const char* treeShape = "x(x)";
  LayerIntRect layerVisibleRects[] = {LayerIntRect(0, 0, 100, 100),
                                      LayerIntRect(50, 0, 50, 100)};
  CreateScrollData(treeShape, layerVisibleRects);
  SetScrollableFrameMetrics(root, rootScrollId, CSSRect(0, 0, 100, 200));
  SetScrollableFrameMetrics(layers[1], subframeScrollId,
                            CSSRect(0, 0, 50, 200));
  SetScrollHandoff(layers[1], root);

  registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
  UpdateHitTestingTree();
  rootApzc = ApzcOf(root);
  auto* subframeApzc = ApzcOf(layers[1]);
  rootApzc->GetFrameMetrics().SetIsRootContent(true);

  // Try to scroll upwards over the subframe.
  ScreenIntPoint panPoint(75, 50);
  QueueMockHitResult(subframeScrollId);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, panPoint,
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(subframeScrollId);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(0, -50), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(subframeScrollId);
  PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint,
             ScreenPoint(0, 0), mcc->Time());

  // The root APZC should be overscrolled. (The subframe APZC should be be.)
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_FALSE(subframeApzc->IsOverscrolled());

  // Give the overscroll animation on the root a chance to start.
  mcc->AdvanceByMillis(10);
  EXPECT_TRUE(rootApzc->IsOverscrollAnimationRunning());

  // Scroll the subframe downwards, with a large delta.
  QueueMockHitResult(subframeScrollId);
  PanGesture(PanGestureInput::PANGESTURE_START, manager, panPoint,
             ScreenPoint(0, 50), mcc->Time());

  // Already after the first event, the overscroll animation should be
  // interrupted.
  EXPECT_FALSE(rootApzc->IsOverscrollAnimationRunning());

  // Cotninue the downward scroll gesture.
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(subframeScrollId);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(0, 100), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(subframeScrollId);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(0, 100), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(subframeScrollId);
  PanGesture(PanGestureInput::PANGESTURE_PAN, manager, panPoint,
             ScreenPoint(0, 100), mcc->Time());
  mcc->AdvanceByMillis(10);
  QueueMockHitResult(subframeScrollId);
  // Important: pass aSimulateMomentum=true for the pan-end to exercise the bug.
  PanGesture(PanGestureInput::PANGESTURE_END, manager, panPoint,
             ScreenPoint(0, 0), mcc->Time(), MODIFIER_NONE,
             /*aSimulateMomentum=*/true);

  // The root and the subframe should both be overscrolled.
  EXPECT_TRUE(rootApzc->IsOverscrolled());
  EXPECT_TRUE(subframeApzc->IsOverscrolled());

  // Sample animations until all of them have been finished.
  while (SampleAnimationsOnce());

  // All overscrolled APZCs should have snapped back.
  EXPECT_FALSE(rootApzc->IsOverscrolled());
  EXPECT_FALSE(subframeApzc->IsOverscrolled());
}
#endif

#ifndef MOZ_WIDGET_ANDROID  // Not valid on Android
// Tests that the scroll offset is shifted with the overscroll amount when the
// content scroll range got expaned.
TEST_F(APZCOverscrollTester, FillOutGutterWhilePanning) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Scroll to the bottom edge.
  ScrollMetadata metadata = apzc->GetScrollMetadata();
  metadata.GetMetrics().SetLayoutScrollOffset(
      CSSPoint(0, GetScrollRange().YMost()));
  nsTArray<ScrollPositionUpdate> scrollUpdates;
  scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll(
      ScrollOrigin::Other,
      CSSPoint::ToAppUnits(CSSPoint(0, GetScrollRange().YMost()))));
  metadata.SetScrollUpdates(scrollUpdates);
  metadata.GetMetrics().SetScrollGeneration(
      scrollUpdates.LastElement().GetGeneration());
  apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
                            /*aThisLayerTreeUpdated=*/true);

  CSSPoint scrollOffset = metadata.GetMetrics().GetLayoutScrollOffset();

  // Start panning to overscroll the content.
  Pan(apzc, 20, 10, PanOptions::KeepFingerDown);
  EXPECT_TRUE(apzc->IsOverscrolled());
  float overscrollY = apzc->GetOverscrollAmount().y;
  EXPECT_GT(overscrollY, 0);

  // Expand the content scroll range.
  metadata = apzc->GetScrollMetadata();
  FrameMetrics& metrics = metadata.GetMetrics();
  const CSSRect& scrollableRect = metrics.GetScrollableRect();
  metrics.SetScrollableRect(scrollableRect +
                            CSSSize(0, scrollableRect.height + 10));
  apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
                            /*aThisLayerTreeUpdated=*/true);

  // Now that the scroll position was shifted with the overscroll amount.
  EXPECT_EQ(apzc->GetScrollMetadata().GetMetrics().GetVisualScrollOffset().y,
            scrollOffset.y + overscrollY);
  EXPECT_FALSE(apzc->IsOverscrolled());
}

// Similar to FillOutGutterWhilePanning but expanding the content while an
// overscroll animation is running.
TEST_F(APZCOverscrollTester, FillOutGutterWhileAnimating) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Scroll to the bottom edge.
  ScrollMetadata metadata = apzc->GetScrollMetadata();
  metadata.GetMetrics().SetLayoutScrollOffset(
      CSSPoint(0, GetScrollRange().YMost()));
  nsTArray<ScrollPositionUpdate> scrollUpdates;
  scrollUpdates.AppendElement(ScrollPositionUpdate::NewScroll(
      ScrollOrigin::Other,
      CSSPoint::ToAppUnits(CSSPoint(0, GetScrollRange().YMost()))));
  metadata.SetScrollUpdates(scrollUpdates);
  metadata.GetMetrics().SetScrollGeneration(
      scrollUpdates.LastElement().GetGeneration());
  apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
                            /*aThisLayerTreeUpdated=*/true);

  CSSPoint scrollOffset = metadata.GetMetrics().GetLayoutScrollOffset();

  PanGesture(PanGestureInput::PANGESTURE_START, apzc, ScreenIntPoint(50, 80),
             ScreenPoint(0, 20), mcc->Time());
  mcc->AdvanceByMillis(5);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 60),
             ScreenPoint(0, 10), mcc->Time());
  mcc->AdvanceByMillis(5);
  apzc->AdvanceAnimations(mcc->GetSampleTime());
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, ScreenIntPoint(50, 50),
             ScreenPoint(0, 10), mcc->Time());
  mcc->AdvanceByMillis(5);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, ScreenIntPoint(50, 50),
             ScreenPoint(0, 0), mcc->Time());
  mcc->AdvanceByMillis(5);

  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());
  float overscrollY = apzc->GetOverscrollAmount().y;
  EXPECT_GT(overscrollY, 0);

  // Expand the content scroll range.
  metadata = apzc->GetScrollMetadata();
  FrameMetrics& metrics = metadata.GetMetrics();
  const CSSRect& scrollableRect = metrics.GetScrollableRect();
  metrics.SetScrollableRect(scrollableRect +
                            CSSSize(0, scrollableRect.height + 10));
  apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
                            /*aThisLayerTreeUpdated=*/true);

  // Now that the scroll position was shifted with the overscroll amount.
  EXPECT_EQ(apzc->GetScrollMetadata().GetMetrics().GetVisualScrollOffset().y,
            scrollOffset.y + overscrollY);
  EXPECT_FALSE(apzc->IsOverscrolled());
}
#endif

// Test that a programmatic scroll animation does NOT trigger overscroll.
TEST_F(APZCOverscrollTester, ProgrammaticScroll) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  // Send a SmoothMsd scroll update to a destination far outside of the
  // scroll range (here, y=100000). This probably shouldn't happen in the
  // first place, but even if it does for whatever reason, the smooth scroll
  // should not trigger overscroll.
  ScrollMetadata metadata = apzc->GetScrollMetadata();
  nsTArray<ScrollPositionUpdate> scrollUpdates;
  scrollUpdates.AppendElement(ScrollPositionUpdate::NewSmoothScroll(
      ScrollMode::SmoothMsd, ScrollOrigin::Other,
      CSSPoint::ToAppUnits(CSSPoint(0, 100000)), ScrollTriggeredByScript::Yes,
      nullptr));
  metadata.SetScrollUpdates(scrollUpdates);
  metadata.GetMetrics().SetScrollGeneration(
      scrollUpdates.LastElement().GetGeneration());
  apzc->NotifyLayersUpdated(metadata, /*aIsFirstPaint=*/false,
                            /*aThisLayerTreeUpdated=*/true);

  apzc->AssertStateIsSmoothMsdScroll();

  while (SampleAnimationOneFrame()) {
    EXPECT_FALSE(apzc->IsOverscrolled());
  }
}

// A touchpad hold gesture should pause any ongoing overscroll animation (so
// that the page is not moving while the fingers are down on the touchpad),
// but should not cancel it. The animation should continue if the finger is
// lifted.
#ifndef MOZ_WIDGET_ANDROID  // Requires GenericOverscrollEffect
TEST_F(APZCOverscrollTester, HoldGestureDuringOverscroll) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  ScrollMetadata metadata;
  FrameMetrics& metrics = metadata.GetMetrics();
  metrics.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
  metrics.SetScrollableRect(CSSRect(0, 0, 100, 1000));
  metrics.SetVisualScrollOffset(CSSPoint(0, 0));
  apzc->SetFrameMetrics(metrics);

  // Pan into overscroll at the top.
  ScreenIntPoint panPoint(50, 50);
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, -1), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
             ScreenPoint(0, -100), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrolled());
  EXPECT_TRUE(apzc->GetOverscrollAmount().y < 0);  // overscrolled at top

  // End the pan. This should start an overscroll animation.
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());
  EXPECT_TRUE(apzc->GetOverscrollAmount().y < 0);  // overscrolled at top
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());

  // Start a hold gesture (represented using PANGESTURE_MAYSTART).
  // This should interrupt the animation but not relieve overscroll yet.
  ParentLayerPoint overscrollBefore = apzc->GetOverscrollAmount();
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_MAYSTART, apzc, panPoint,
             ScreenPoint(0, 0), mcc->Time());
  EXPECT_FALSE(apzc->IsOverscrollAnimationRunning());
  EXPECT_EQ(overscrollBefore, apzc->GetOverscrollAmount());

  // End the hold gesture (represented using PANGESTURE_CANCELLED).
  // This should start an overscroll animation again.
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_CANCELLED, apzc, panPoint,
             ScreenPoint(0, 0), mcc->Time());
  EXPECT_TRUE(apzc->IsOverscrollAnimationRunning());

  SampleAnimationUntilRecoveredFromOverscroll(ParentLayerPoint(0, 0));
}
#endif

#ifdef MOZ_WIDGET_ANDROID  // Only testable with WidgetOverscrollEffect
TEST_F(APZCOverscrollTester, NoResetTouchInputStateCalled) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  MakeApzcWaitForMainThread();

  InSequence s;
  // The UpdateOverscrollVelocity should never be called since there's no
  // overscrolling state.
  EXPECT_CALL(*mcc, UpdateOverscrollVelocity(_, _, _, _)).Times(0);

  ScreenIntPoint touchPoint(5, 5);
  APZEventResult result = TouchDown(apzc, touchPoint, mcc->Time());
  SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId);
  apzc->ContentReceivedInputBlock(result.mInputBlockId,
                                  /*aPreventDefault=*/true);

  for (int i = 0; i < 5; ++i) {
    touchPoint.y -= 1;
    mcc->AdvanceByMillis(10);
    TouchMove(apzc, touchPoint, mcc->Time());
  }

  mcc->AdvanceByMillis(10);
  TouchUp(apzc, touchPoint, mcc->Time());
}
#endif

#ifdef MOZ_WIDGET_ANDROID  // Only testable with WidgetOverscrollEffect
TEST_F(APZCOverscrollTester, ResetTouchInputStateJustOnce) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);
  SCOPED_GFX_PREF_FLOAT("apz.touch_start_tolerance", 0.1f);
  SCOPED_GFX_PREF_FLOAT("apz.touch_move_tolerance", 0.0f);
  // To avoid falling into a fast fling state so that the second touch-start
  // event can be preventDefault-ed.
  SCOPED_GFX_PREF_FLOAT("apz.fling_stop_on_tap_threshold", 1000.0f);

  InSequence s;
  // The UpdateOverscrollVelocity should be called just once for the second
  // touchdown event which will be preventDefault-ed.
  EXPECT_CALL(*mcc, UpdateOverscrollVelocity(_, _, _, _)).Times(1);

  // Overscroll once.
  PanIntoOverscroll();

  // Now the touch-start event will be preventDefault-ed.
  MakeApzcWaitForMainThread();

  ScreenIntPoint touchPoint(5, 5);
  APZEventResult result = TouchDown(apzc, touchPoint, mcc->Time());
  SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId);
  apzc->ContentReceivedInputBlock(result.mInputBlockId,
                                  /*aPreventDefault=*/true);

  for (int i = 0; i < 5; ++i) {
    touchPoint.y -= 1;
    mcc->AdvanceByMillis(10);
    TouchMove(apzc, touchPoint, mcc->Time());
  }

  mcc->AdvanceByMillis(10);
  TouchUp(apzc, touchPoint, mcc->Time());
}
#endif

#ifdef MOZ_WIDGET_ANDROID  // Only testable with WidgetOverscrollEffect
TEST_F(APZCOverscrollTester, NoResetPanGestureInputStateCalled) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  MakeApzcWaitForMainThread();

  InSequence s;
  // The UpdateOverscrollVelocity should never be called since there's no
  // overscrolling state.
  EXPECT_CALL(*mcc, UpdateOverscrollVelocity(_, _, _, _)).Times(0);

  ScreenIntPoint panPoint(5, 5);
  APZEventResult result = PanGesture(PanGestureInput::PANGESTURE_START, apzc,
                                     panPoint, ScreenPoint(0, 1), mcc->Time());
  apzc->ContentReceivedInputBlock(result.mInputBlockId,
                                  /*aPreventDefault=*/true);
  for (int i = 0; i < 5; ++i) {
    mcc->AdvanceByMillis(10);
    PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
               ScreenPoint(0, 1), mcc->Time());
  }

  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());
}
#endif

#ifdef MOZ_WIDGET_ANDROID  // Only testable with WidgetOverscrollEffect
TEST_F(APZCOverscrollTester, ResetPanGestureInputStateJustOnce) {
  SCOPED_GFX_PREF_BOOL("apz.overscroll.enabled"true);

  InSequence s;
  // The UpdateOverscrollVelocity should be called just once because
  // the second pan-start event (wheel event) during overscrolling
  // which will be preventDefault-ed.
  EXPECT_CALL(*mcc, UpdateOverscrollVelocity(_, _, _, _)).Times(1);

  // NOTE: PanIntoOverscroll uses touch events. We do overscroll with pan
  // gestures.
  ScreenIntPoint panPoint(5, 5);
  PanGesture(PanGestureInput::PANGESTURE_START, apzc, panPoint,
             ScreenPoint(0, -10), mcc->Time());
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
             ScreenPoint(0, -100), mcc->Time());
  // Make sure it's overscrolling.
  EXPECT_TRUE(apzc->IsOverscrolled());
  // Finish the pan gesture.
  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());

  // Now the next pan gesture event will be preventDefault-ed.
  MakeApzcWaitForMainThread();

  APZEventResult result = PanGesture(PanGestureInput::PANGESTURE_START, apzc,
                                     panPoint, ScreenPoint(0, 10), mcc->Time());
  apzc->ContentReceivedInputBlock(result.mInputBlockId,
                                  /*aPreventDefault=*/true);
  for (int i = 0; i < 5; ++i) {
    mcc->AdvanceByMillis(10);
    PanGesture(PanGestureInput::PANGESTURE_PAN, apzc, panPoint,
               ScreenPoint(0, 10), mcc->Time());
  }

  mcc->AdvanceByMillis(10);
  PanGesture(PanGestureInput::PANGESTURE_END, apzc, panPoint, ScreenPoint(0, 0),
             mcc->Time());
}
#endif

Messung V0.5 in Prozent
C=87 H=95 G=90

¤ Dauer der Verarbeitung: 0.41 Sekunden  (vorverarbeitet am  2026-04-25) ¤

*© 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