diff --git a/gfx/layers/apz/src/APZUtils.h b/gfx/layers/apz/src/APZUtils.h index 6bc4b7ec11b0..f2814ffb118c 100644 --- a/gfx/layers/apz/src/APZUtils.h +++ b/gfx/layers/apz/src/APZUtils.h @@ -11,6 +11,7 @@ #include "LayersTypes.h" #include "UnitTransforms.h" #include "mozilla/gfx/Point.h" +#include "mozilla/EnumSet.h" #include "mozilla/FloatingPoint.h" namespace mozilla { @@ -42,6 +43,8 @@ operator|(CancelAnimationFlags a, CancelAnimationFlags b) | static_cast(b)); } +typedef EnumSet ScrollDirections; + enum class ScrollSource { // scrollTo() or something similar. DOM, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index d86f0099718a..a07335ee762e 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1914,9 +1914,8 @@ AsyncPanZoomController::GetKeyboardDestination(const KeyboardScrollAction& aActi return scrollDestination; } -// Return whether or not the underlying layer can be scrolled on either axis. -bool -AsyncPanZoomController::CanScroll(const InputData& aEvent) const +ParentLayerPoint +AsyncPanZoomController::GetDeltaForEvent(const InputData& aEvent) const { ParentLayerPoint delta; if (aEvent.mInputType == SCROLLWHEEL_INPUT) { @@ -1925,6 +1924,14 @@ AsyncPanZoomController::CanScroll(const InputData& aEvent) const const PanGestureInput& panInput = aEvent.AsPanGestureInput(); delta = ToParentLayerCoordinates(panInput.UserMultipliedPanDisplacement(), panInput.mPanStartPoint); } + return delta; +} + +// Return whether or not the underlying layer can be scrolled on either axis. +bool +AsyncPanZoomController::CanScroll(const InputData& aEvent) const +{ + ParentLayerPoint delta = GetDeltaForEvent(aEvent); if (!delta.x && !delta.y) { return false; } @@ -1932,6 +1939,20 @@ AsyncPanZoomController::CanScroll(const InputData& aEvent) const return CanScrollWithWheel(delta); } +ScrollDirections +AsyncPanZoomController::GetAllowedHandoffDirections() const +{ + ScrollDirections result; + RecursiveMutexAutoLock lock(mRecursiveMutex); + if (mX.OverscrollBehaviorAllowsHandoff()) { + result += ScrollDirection::eHorizontal; + } + if (mY.OverscrollBehaviorAllowsHandoff()) { + result += ScrollDirection::eVertical; + } + return result; +} + bool AsyncPanZoomController::CanScrollWithWheel(const ParentLayerPoint& aDelta) const { @@ -1999,6 +2020,19 @@ ScrollInputMethodForWheelDeltaType(ScrollWheelInput::ScrollDeltaType aDeltaType) return ScrollInputMethod::ApzWheelLine; } +static void +AdjustDeltaForAllowedScrollDirections( + ParentLayerPoint& aDelta, + const ScrollDirections& aAllowedScrollDirections) +{ + if (!aAllowedScrollDirections.contains(ScrollDirection::eHorizontal)) { + aDelta.x = 0; + } + if (!aAllowedScrollDirections.contains(ScrollDirection::eVertical)) { + aDelta.y = 0; + } +} + nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent) { ParentLayerPoint delta = GetScrollWheelDelta(aEvent); @@ -2017,6 +2051,10 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve return nsEventStatus_eConsumeNoDefault; } + MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock()); + AdjustDeltaForAllowedScrollDirections(delta, + mInputQueue->GetCurrentWheelBlock()->GetAllowedScrollDirections()); + if (delta.x == 0 && delta.y == 0) { // Avoid spurious state changes and unnecessary work return nsEventStatus_eIgnore; @@ -2040,7 +2078,6 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve CancelAnimation(); - MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock()); OverscrollHandoffState handoffState( *mInputQueue->GetCurrentWheelBlock()->GetOverscrollHandoffChain(), distance, @@ -2217,6 +2254,10 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool ScreenPoint physicalPanDisplacement = aEvent.mPanDisplacement; ParentLayerPoint logicalPanDisplacement = aEvent.UserMultipliedLocalPanDisplacement(); + MOZ_ASSERT(GetCurrentPanGestureBlock()); + AdjustDeltaForAllowedScrollDirections(logicalPanDisplacement, + GetCurrentPanGestureBlock()->GetAllowedScrollDirections()); + // We need to update the axis velocity in order to get a useful display port // size and position. We need to do so even if this is a momentum pan (i.e. // aFingersOnTouchpad == false); in that case the "with touch" part is not @@ -2230,7 +2271,6 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool (uint32_t) ScrollInputMethod::ApzPanGesture); ScreenPoint panDistance(fabs(physicalPanDisplacement.x), fabs(physicalPanDisplacement.y)); - MOZ_ASSERT(GetCurrentPanGestureBlock()); OverscrollHandoffState handoffState( *GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(), panDistance, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 95e308e85b2d..d603875a7380 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -420,6 +420,10 @@ public: // direction. bool CanScroll(const InputData& aEvent) const; + // Return the directions in which this APZC allows handoff (as governed by + // overscroll-behavior). + ScrollDirections GetAllowedHandoffDirections() const; + // Return whether or not a scroll delta will be able to scroll in either // direction. bool CanScrollWithWheel(const ParentLayerPoint& aDelta) const; @@ -1182,6 +1186,8 @@ private: */ void OverscrollBy(ParentLayerPoint& aOverscroll); + // Helper function for CanScroll(). + ParentLayerPoint GetDeltaForEvent(const InputData& aEvent) const; /* =================================================================== * The functions and members in this section are used to maintain the diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp index f4aac806a968..9b001dbc5f06 100644 --- a/gfx/layers/apz/src/InputBlockState.cpp +++ b/gfx/layers/apz/src/InputBlockState.cpp @@ -322,7 +322,8 @@ WheelBlockState::WheelBlockState(const RefPtr& aTargetAp // content should have found a scrollable apzc, so we don't need to handle // that case. RefPtr apzc = - mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent); + mOverscrollHandoffChain->FindFirstScrollable( + aInitialEvent, &mAllowedScrollDirections); // If nothing is scrollable, we don't consider this block as starting a // transaction. @@ -356,7 +357,8 @@ WheelBlockState::SetConfirmedTargetApzc(const RefPtr& aT // one by looking for the first apzc the next pending event can scroll. RefPtr apzc = aTargetApzc; if (apzc && aFirstInput) { - apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput); + apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable( + *aFirstInput, &mAllowedScrollDirections); } InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput); @@ -553,7 +555,8 @@ PanGestureBlockState::PanGestureBlockState(const RefPtr& // content should have found a scrollable apzc, so we don't need to handle // that case. RefPtr apzc = - mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent); + mOverscrollHandoffChain->FindFirstScrollable( + aInitialEvent, &mAllowedScrollDirections); if (apzc && apzc != GetTargetApzc()) { UpdateTargetApzc(apzc); @@ -572,7 +575,8 @@ PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr apzc = aTargetApzc; if (apzc && aFirstInput) { RefPtr scrollableApzc = - apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput); + apzc->BuildOverscrollHandoffChain()->FindFirstScrollable( + *aFirstInput, &mAllowedScrollDirections); if (scrollableApzc) { apzc = scrollableApzc; } diff --git a/gfx/layers/apz/src/InputBlockState.h b/gfx/layers/apz/src/InputBlockState.h index ac48605b96f2..d100902e9ace 100644 --- a/gfx/layers/apz/src/InputBlockState.h +++ b/gfx/layers/apz/src/InputBlockState.h @@ -282,6 +282,8 @@ public: */ void Update(ScrollWheelInput& aEvent); + ScrollDirections GetAllowedScrollDirections() const { return mAllowedScrollDirections; } + protected: void UpdateTargetApzc(const RefPtr& aTargetApzc) override; @@ -290,6 +292,7 @@ private: TimeStamp mLastMouseMove; uint32_t mScrollSeriesCounter; bool mTransactionEnded; + ScrollDirections mAllowedScrollDirections; }; /** @@ -354,9 +357,12 @@ public: void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse); + ScrollDirections GetAllowedScrollDirections() const { return mAllowedScrollDirections; } + private: bool mInterrupted; bool mWaitingForContentResponse; + ScrollDirections mAllowedScrollDirections; }; /** diff --git a/gfx/layers/apz/src/InputQueue.cpp b/gfx/layers/apz/src/InputQueue.cpp index bec3b5d2b223..83fc8aa98058 100644 --- a/gfx/layers/apz/src/InputQueue.cpp +++ b/gfx/layers/apz/src/InputQueue.cpp @@ -318,9 +318,13 @@ CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent, { PanGestureInput horizontalComponent = aInitialEvent; horizontalComponent.mPanDisplacement.y = 0; + ScrollDirections allowedScrollDirections; RefPtr horizontallyScrollableAPZC = - aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent); - return horizontallyScrollableAPZC && horizontallyScrollableAPZC == aBlock->GetTargetApzc(); + aBlock->GetOverscrollHandoffChain()->FindFirstScrollable( + horizontalComponent, &allowedScrollDirections); + return horizontallyScrollableAPZC && + horizontallyScrollableAPZC == aBlock->GetTargetApzc() && + allowedScrollDirections.contains(ScrollDirection::eHorizontal); } nsEventStatus diff --git a/gfx/layers/apz/src/OverscrollHandoffState.cpp b/gfx/layers/apz/src/OverscrollHandoffState.cpp index c4ed201a5c47..19a89d71d926 100644 --- a/gfx/layers/apz/src/OverscrollHandoffState.cpp +++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp @@ -161,12 +161,24 @@ OverscrollHandoffChain::HasFastFlungApzc() const } RefPtr -OverscrollHandoffChain::FindFirstScrollable(const InputData& aInput) const +OverscrollHandoffChain::FindFirstScrollable( + const InputData& aInput, + ScrollDirections* aOutAllowedScrollDirections) const { + // Start by allowing scrolling in both directions. As we do handoff + // overscroll-behavior may restrict one or both of the directions. + *aOutAllowedScrollDirections += ScrollDirection::eVertical; + *aOutAllowedScrollDirections += ScrollDirection::eHorizontal; + for (size_t i = 0; i < Length(); i++) { if (mChain[i]->CanScroll(aInput)) { return mChain[i]; } + + *aOutAllowedScrollDirections &= mChain[i]->GetAllowedHandoffDirections(); + if (aOutAllowedScrollDirections->isEmpty()) { + return nullptr; + } } return nullptr; } diff --git a/gfx/layers/apz/src/OverscrollHandoffState.h b/gfx/layers/apz/src/OverscrollHandoffState.h index cf4d43e10665..021b0634cc91 100644 --- a/gfx/layers/apz/src/OverscrollHandoffState.h +++ b/gfx/layers/apz/src/OverscrollHandoffState.h @@ -89,7 +89,13 @@ public: // Determine whether any APZC along this handoff chain has been flung fast. bool HasFastFlungApzc() const; - RefPtr FindFirstScrollable(const InputData& aInput) const; + // Find the first APZC in this handoff chain that can be scrolled by |aInput|. + // Since overscroll-behavior can restrict handoff in some directions, + // |aOutAllowedScrollDirections| is populated with the scroll directions + // in which scrolling of the returned APZC is allowed. + RefPtr FindFirstScrollable( + const InputData& aInput, + ScrollDirections* aOutAllowedScrollDirections) const; private: std::vector> mChain;