--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -1607,16 +1607,65 @@ protected:
SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[5], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2);
SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 3);
}
};
+// A version of ApzcPan() that routes the pan through the tree manager,
+// so that the tree manager has the appropriate state for testing.
+static void
+ApzctmPan(APZCTreeManager* aTreeManager,
+ int& aTime,
+ int aTouchStartY,
+ int aTouchEndY,
+ bool aKeepFingerDown = false)
+{
+ // TODO: Reuse some code between this and ApzcPan().
+
+ // Reduce the touch start tolerance to a tiny value.
+ // We can't do what ApzcPan() does to overcome the tolerance (send the
+ // touch-start at (aTouchStartY + some_large_value)) because the tree manager
+ // does hit testing based on the touch-start coordinates, and a different
+ // APZC than the one we intend might be hit.
+ SCOPED_GFX_PREF(APZTouchStartTolerance, float, 1.0f / 1000.0f);
+ const int OVERCOME_TOUCH_TOLERANCE = 1;
+
+ const int TIME_BETWEEN_TOUCH_EVENT = 100;
+
+ // Make sure the move is large enough to not be handled as a tap
+ MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+ if (!aKeepFingerDown) {
+ mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
+ mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
+ aTreeManager->ReceiveInputEvent(mti, nullptr);
+ }
+
+ aTime += TIME_BETWEEN_TOUCH_EVENT;
+}
+
class APZHitTestingTester : public APZCTreeManagerTester {
protected:
Matrix4x4 transformToApzc;
Matrix4x4 transformToGecko;
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
if (hit) {
@@ -2184,16 +2233,53 @@ TEST_F(APZOverscrollHandoffTester, Layer
// Make sure things have scrolled according to the handoff chain in
// place at the time the touch-start of the second pan was queued.
EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
}
+// Test that putting a second finger down on an APZC while a down-chain APZC
+// is overscrolled doesn't result in being stuck in overscroll.
+TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1073250) {
+ // Enable overscrolling.
+ SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+ CreateOverscrollHandoffLayerTree1();
+
+ TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+ // Pan, causing the parent APZC to overscroll.
+ int time = 0;
+ ApzctmPan(manager, time, 10, 40, true /* keep finger down */);
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+ // Put a second finger down.
+ MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+ // Use the same touch identifier for the first touch (0) as ApzctmPan(). (A bit hacky.)
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
+ secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
+ manager->ReceiveInputEvent(secondFingerDown, nullptr);
+
+ // Release the fingers.
+ MultiTouchInput fingersUp = secondFingerDown;
+ fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+ manager->ReceiveInputEvent(fingersUp, nullptr);
+
+ // Allow any animations to run their course.
+ child->AdvanceAnimationsUntilEnd(testStartTime);
+ rootApzc->AdvanceAnimationsUntilEnd(testStartTime);
+
+ // Make sure nothing is overscrolled.
+ EXPECT_FALSE(child->IsOverscrolled());
+ EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
// Here we test that if two flings are happening simultaneously, overscroll
// is handed off correctly for each.
TEST_F(APZOverscrollHandoffTester, SimultaneousFlings) {
// Set up an initial APZC tree.
CreateOverscrollHandoffLayerTree3();
TestAsyncPanZoomController* parent1 = ApzcOf(layers[1]);
TestAsyncPanZoomController* child1 = ApzcOf(layers[2]);