Bug 951793 - Obey overscroll-behavior for wheel and pan gesture events. r=kats

MozReview-Commit-ID: EmbsMu9Esww

--HG--
extra : rebase_source : f681f55f274757be4965e6c912e514e81e8b1c8f
This commit is contained in:
Botond Ballo 2017-11-17 18:52:58 -05:00
Родитель 15d10c1b0d
Коммит bfb1cd4ddd
8 изменённых файлов: 94 добавлений и 13 удалений

Просмотреть файл

@ -11,6 +11,7 @@
#include "LayersTypes.h" #include "LayersTypes.h"
#include "UnitTransforms.h" #include "UnitTransforms.h"
#include "mozilla/gfx/Point.h" #include "mozilla/gfx/Point.h"
#include "mozilla/EnumSet.h"
#include "mozilla/FloatingPoint.h" #include "mozilla/FloatingPoint.h"
namespace mozilla { namespace mozilla {
@ -42,6 +43,8 @@ operator|(CancelAnimationFlags a, CancelAnimationFlags b)
| static_cast<int>(b)); | static_cast<int>(b));
} }
typedef EnumSet<ScrollDirection> ScrollDirections;
enum class ScrollSource { enum class ScrollSource {
// scrollTo() or something similar. // scrollTo() or something similar.
DOM, DOM,

Просмотреть файл

@ -1914,9 +1914,8 @@ AsyncPanZoomController::GetKeyboardDestination(const KeyboardScrollAction& aActi
return scrollDestination; return scrollDestination;
} }
// Return whether or not the underlying layer can be scrolled on either axis. ParentLayerPoint
bool AsyncPanZoomController::GetDeltaForEvent(const InputData& aEvent) const
AsyncPanZoomController::CanScroll(const InputData& aEvent) const
{ {
ParentLayerPoint delta; ParentLayerPoint delta;
if (aEvent.mInputType == SCROLLWHEEL_INPUT) { if (aEvent.mInputType == SCROLLWHEEL_INPUT) {
@ -1925,6 +1924,14 @@ AsyncPanZoomController::CanScroll(const InputData& aEvent) const
const PanGestureInput& panInput = aEvent.AsPanGestureInput(); const PanGestureInput& panInput = aEvent.AsPanGestureInput();
delta = ToParentLayerCoordinates(panInput.UserMultipliedPanDisplacement(), panInput.mPanStartPoint); 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) { if (!delta.x && !delta.y) {
return false; return false;
} }
@ -1932,6 +1939,20 @@ AsyncPanZoomController::CanScroll(const InputData& aEvent) const
return CanScrollWithWheel(delta); 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 bool
AsyncPanZoomController::CanScrollWithWheel(const ParentLayerPoint& aDelta) const AsyncPanZoomController::CanScrollWithWheel(const ParentLayerPoint& aDelta) const
{ {
@ -1999,6 +2020,19 @@ ScrollInputMethodForWheelDeltaType(ScrollWheelInput::ScrollDeltaType aDeltaType)
return ScrollInputMethod::ApzWheelLine; 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) nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
{ {
ParentLayerPoint delta = GetScrollWheelDelta(aEvent); ParentLayerPoint delta = GetScrollWheelDelta(aEvent);
@ -2017,6 +2051,10 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
return nsEventStatus_eConsumeNoDefault; return nsEventStatus_eConsumeNoDefault;
} }
MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock());
AdjustDeltaForAllowedScrollDirections(delta,
mInputQueue->GetCurrentWheelBlock()->GetAllowedScrollDirections());
if (delta.x == 0 && delta.y == 0) { if (delta.x == 0 && delta.y == 0) {
// Avoid spurious state changes and unnecessary work // Avoid spurious state changes and unnecessary work
return nsEventStatus_eIgnore; return nsEventStatus_eIgnore;
@ -2040,7 +2078,6 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
CancelAnimation(); CancelAnimation();
MOZ_ASSERT(mInputQueue->GetCurrentWheelBlock());
OverscrollHandoffState handoffState( OverscrollHandoffState handoffState(
*mInputQueue->GetCurrentWheelBlock()->GetOverscrollHandoffChain(), *mInputQueue->GetCurrentWheelBlock()->GetOverscrollHandoffChain(),
distance, distance,
@ -2217,6 +2254,10 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool
ScreenPoint physicalPanDisplacement = aEvent.mPanDisplacement; ScreenPoint physicalPanDisplacement = aEvent.mPanDisplacement;
ParentLayerPoint logicalPanDisplacement = aEvent.UserMultipliedLocalPanDisplacement(); 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 // 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. // 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 // 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); (uint32_t) ScrollInputMethod::ApzPanGesture);
ScreenPoint panDistance(fabs(physicalPanDisplacement.x), fabs(physicalPanDisplacement.y)); ScreenPoint panDistance(fabs(physicalPanDisplacement.x), fabs(physicalPanDisplacement.y));
MOZ_ASSERT(GetCurrentPanGestureBlock());
OverscrollHandoffState handoffState( OverscrollHandoffState handoffState(
*GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(), *GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(),
panDistance, panDistance,

Просмотреть файл

@ -420,6 +420,10 @@ public:
// direction. // direction.
bool CanScroll(const InputData& aEvent) const; 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 // Return whether or not a scroll delta will be able to scroll in either
// direction. // direction.
bool CanScrollWithWheel(const ParentLayerPoint& aDelta) const; bool CanScrollWithWheel(const ParentLayerPoint& aDelta) const;
@ -1182,6 +1186,8 @@ private:
*/ */
void OverscrollBy(ParentLayerPoint& aOverscroll); 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 * The functions and members in this section are used to maintain the

Просмотреть файл

@ -322,7 +322,8 @@ WheelBlockState::WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetAp
// content should have found a scrollable apzc, so we don't need to handle // content should have found a scrollable apzc, so we don't need to handle
// that case. // that case.
RefPtr<AsyncPanZoomController> apzc = RefPtr<AsyncPanZoomController> apzc =
mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent); mOverscrollHandoffChain->FindFirstScrollable(
aInitialEvent, &mAllowedScrollDirections);
// If nothing is scrollable, we don't consider this block as starting a // If nothing is scrollable, we don't consider this block as starting a
// transaction. // transaction.
@ -356,7 +357,8 @@ WheelBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController>& aT
// one by looking for the first apzc the next pending event can scroll. // one by looking for the first apzc the next pending event can scroll.
RefPtr<AsyncPanZoomController> apzc = aTargetApzc; RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
if (apzc && aFirstInput) { if (apzc && aFirstInput) {
apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput); apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(
*aFirstInput, &mAllowedScrollDirections);
} }
InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput); InputBlockState::SetConfirmedTargetApzc(apzc, aState, aFirstInput);
@ -553,7 +555,8 @@ PanGestureBlockState::PanGestureBlockState(const RefPtr<AsyncPanZoomController>&
// content should have found a scrollable apzc, so we don't need to handle // content should have found a scrollable apzc, so we don't need to handle
// that case. // that case.
RefPtr<AsyncPanZoomController> apzc = RefPtr<AsyncPanZoomController> apzc =
mOverscrollHandoffChain->FindFirstScrollable(aInitialEvent); mOverscrollHandoffChain->FindFirstScrollable(
aInitialEvent, &mAllowedScrollDirections);
if (apzc && apzc != GetTargetApzc()) { if (apzc && apzc != GetTargetApzc()) {
UpdateTargetApzc(apzc); UpdateTargetApzc(apzc);
@ -572,7 +575,8 @@ PanGestureBlockState::SetConfirmedTargetApzc(const RefPtr<AsyncPanZoomController
RefPtr<AsyncPanZoomController> apzc = aTargetApzc; RefPtr<AsyncPanZoomController> apzc = aTargetApzc;
if (apzc && aFirstInput) { if (apzc && aFirstInput) {
RefPtr<AsyncPanZoomController> scrollableApzc = RefPtr<AsyncPanZoomController> scrollableApzc =
apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(*aFirstInput); apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(
*aFirstInput, &mAllowedScrollDirections);
if (scrollableApzc) { if (scrollableApzc) {
apzc = scrollableApzc; apzc = scrollableApzc;
} }

Просмотреть файл

@ -282,6 +282,8 @@ public:
*/ */
void Update(ScrollWheelInput& aEvent); void Update(ScrollWheelInput& aEvent);
ScrollDirections GetAllowedScrollDirections() const { return mAllowedScrollDirections; }
protected: protected:
void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override; void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
@ -290,6 +292,7 @@ private:
TimeStamp mLastMouseMove; TimeStamp mLastMouseMove;
uint32_t mScrollSeriesCounter; uint32_t mScrollSeriesCounter;
bool mTransactionEnded; bool mTransactionEnded;
ScrollDirections mAllowedScrollDirections;
}; };
/** /**
@ -354,9 +357,12 @@ public:
void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse); void SetNeedsToWaitForContentResponse(bool aWaitForContentResponse);
ScrollDirections GetAllowedScrollDirections() const { return mAllowedScrollDirections; }
private: private:
bool mInterrupted; bool mInterrupted;
bool mWaitingForContentResponse; bool mWaitingForContentResponse;
ScrollDirections mAllowedScrollDirections;
}; };
/** /**

Просмотреть файл

@ -318,9 +318,13 @@ CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
{ {
PanGestureInput horizontalComponent = aInitialEvent; PanGestureInput horizontalComponent = aInitialEvent;
horizontalComponent.mPanDisplacement.y = 0; horizontalComponent.mPanDisplacement.y = 0;
ScrollDirections allowedScrollDirections;
RefPtr<AsyncPanZoomController> horizontallyScrollableAPZC = RefPtr<AsyncPanZoomController> horizontallyScrollableAPZC =
aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(horizontalComponent); aBlock->GetOverscrollHandoffChain()->FindFirstScrollable(
return horizontallyScrollableAPZC && horizontallyScrollableAPZC == aBlock->GetTargetApzc(); horizontalComponent, &allowedScrollDirections);
return horizontallyScrollableAPZC &&
horizontallyScrollableAPZC == aBlock->GetTargetApzc() &&
allowedScrollDirections.contains(ScrollDirection::eHorizontal);
} }
nsEventStatus nsEventStatus

Просмотреть файл

@ -161,12 +161,24 @@ OverscrollHandoffChain::HasFastFlungApzc() const
} }
RefPtr<AsyncPanZoomController> RefPtr<AsyncPanZoomController>
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++) { for (size_t i = 0; i < Length(); i++) {
if (mChain[i]->CanScroll(aInput)) { if (mChain[i]->CanScroll(aInput)) {
return mChain[i]; return mChain[i];
} }
*aOutAllowedScrollDirections &= mChain[i]->GetAllowedHandoffDirections();
if (aOutAllowedScrollDirections->isEmpty()) {
return nullptr;
}
} }
return nullptr; return nullptr;
} }

Просмотреть файл

@ -89,7 +89,13 @@ public:
// Determine whether any APZC along this handoff chain has been flung fast. // Determine whether any APZC along this handoff chain has been flung fast.
bool HasFastFlungApzc() const; bool HasFastFlungApzc() const;
RefPtr<AsyncPanZoomController> 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<AsyncPanZoomController> FindFirstScrollable(
const InputData& aInput,
ScrollDirections* aOutAllowedScrollDirections) const;
private: private:
std::vector<RefPtr<AsyncPanZoomController>> mChain; std::vector<RefPtr<AsyncPanZoomController>> mChain;