Don't async scroll overflowed single-line text boxes in APZ. (bug 1126090 part 6, r=botond)

This commit is contained in:
dvander@alliedmods.net 2015-03-06 14:26:59 -08:00
parent e2a7179153
commit 3564b8945d
12 changed files with 126 additions and 30 deletions

View File

@ -2021,6 +2021,21 @@ GetParentFrameToScroll(nsIFrame* aFrame)
return aFrame->GetParent();
}
/*static*/ bool
EventStateManager::CanVerticallyScrollFrameWithWheel(nsIFrame* aFrame)
{
nsIContent* c = aFrame->GetContent();
if (!c) {
return true;
}
nsCOMPtr<nsITextControlElement> ctrl =
do_QueryInterface(c->IsInAnonymousSubtree() ? c->GetBindingParent() : c);
if (ctrl && ctrl->IsSingleLineTextControl()) {
return false;
}
return true;
}
void
EventStateManager::DispatchLegacyMouseScrollEvents(nsIFrame* aTargetFrame,
WidgetWheelEvent* aEvent,
@ -2289,10 +2304,7 @@ EventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
// Don't scroll vertically by mouse-wheel on a single-line text control.
if (checkIfScrollableY) {
nsIContent* c = scrollFrame->GetContent();
nsCOMPtr<nsITextControlElement> ctrl =
do_QueryInterface(c->IsInAnonymousSubtree() ? c->GetBindingParent() : c);
if (ctrl && ctrl->IsSingleLineTextControl()) {
if (!CanVerticallyScrollFrameWithWheel(scrollFrame)) {
continue;
}
}

View File

@ -225,6 +225,10 @@ public:
// Returns true if the given WidgetWheelEvent will resolve to a scroll action.
static bool WheelEventIsScrollAction(WidgetWheelEvent* aEvent);
// Returns whether or not a frame can be vertically scrolled with a mouse
// wheel (as opposed to, say, a selection or touch scroll).
static bool CanVerticallyScrollFrameWithWheel(nsIFrame* aFrame);
// Holds the point in screen coords that a mouse event was dispatched to,
// before we went into pointer lock mode. This is constantly updated while
// the pointer is not locked, but we don't update it while the pointer is

View File

@ -736,6 +736,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
WriteParam(aMsg, aParam.mDoSmoothScroll);
WriteParam(aMsg, aParam.mSmoothScrollOffset);
WriteParam(aMsg, aParam.GetLineScrollAmount());
WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel());
WriteParam(aMsg, aParam.GetContentDescription());
}
@ -778,6 +779,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) &&
ReadContentDescription(aMsg, aIter, aResult));
}
};

View File

@ -66,6 +66,7 @@ public:
, mExtraResolution(1)
, mBackgroundColor(0, 0, 0, 0)
, mLineScrollAmount(0, 0)
, mAllowVerticalScrollWithWheel(false)
{
}
@ -98,7 +99,8 @@ public:
mExtraResolution == aOther.mExtraResolution &&
mBackgroundColor == aOther.mBackgroundColor &&
mDoSmoothScroll == aOther.mDoSmoothScroll &&
mLineScrollAmount == aOther.mLineScrollAmount;
mLineScrollAmount == aOther.mLineScrollAmount &&
mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel;
}
bool operator!=(const FrameMetrics& aOther) const
{
@ -520,6 +522,16 @@ public:
mScrollableRect = aScrollableRect;
}
bool AllowVerticalScrollWithWheel() const
{
return mAllowVerticalScrollWithWheel;
}
void SetAllowVerticalScrollWithWheel()
{
mAllowVerticalScrollWithWheel = true;
}
private:
// The pres-shell resolution that has been induced on the document containing
@ -670,6 +682,9 @@ private:
// The value of GetLineScrollAmount(), for scroll frames.
LayoutDeviceIntSize mLineScrollAmount;
// Whether or not the frame can be vertically scrolled with a mouse wheel.
bool mAllowVerticalScrollWithWheel;
};
/**

View File

@ -23,6 +23,17 @@ enum CancelAnimationFlags : uint32_t {
ExcludeOverscroll = 1 /* Don't clear overscroll */
};
enum class ScrollSource {
// scrollTo() or something similar.
DOM,
// Touch-screen or trackpad with gesture support.
Touch,
// Mouse wheel.
Wheel
};
typedef uint32_t TouchBehaviorFlags;
}

View File

@ -669,6 +669,7 @@ private:
class SmoothScrollAnimation : public AsyncPanZoomAnimation {
public:
SmoothScrollAnimation(AsyncPanZoomController& aApzc,
ScrollSource aSource,
const nsPoint &aInitialPosition,
const nsPoint &aInitialVelocity,
const nsPoint& aDestination, double aSpringConstant,
@ -680,6 +681,7 @@ public:
aSpringConstant, aDampingRatio)
, mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
aSpringConstant, aDampingRatio)
, mSource(aSource)
{
}
@ -725,7 +727,11 @@ public:
ParentLayerPoint overscroll;
ParentLayerPoint adjustedOffset;
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
bool forceVerticalOverscroll = mSource == ScrollSource::Wheel &&
!aFrameMetrics.AllowVerticalScrollWithWheel();
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
forceVerticalOverscroll);
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
@ -771,6 +777,7 @@ public:
private:
AsyncPanZoomController& mApzc;
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
ScrollSource mSource;
};
void
@ -983,10 +990,10 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent,
case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, true); break;
case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, ScrollSource::Touch, true); break;
case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, false); break;
case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, ScrollSource::Touch, false); break;
case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
default: NS_WARNING("Unhandled pan gesture"); break;
}
@ -1441,7 +1448,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
aEvent.modifiers);
move.mLocalPanStartPoint = aEvent.mLocalOrigin;
move.mLocalPanDisplacement = delta;
OnPan(move, false);
OnPan(move, ScrollSource::Wheel, false);
PanGestureInput end(PanGestureInput::PANGESTURE_END, aEvent.mTime, aEvent.mTimeStamp,
aEvent.mOrigin, ScreenPoint(0, 0), aEvent.modifiers);
@ -1461,7 +1468,7 @@ nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEve
} else {
mFrameMetrics.SetSmoothScrollOffset(mFrameMetrics.GetSmoothScrollOffset() + delta);
}
StartSmoothScroll();
StartSmoothScroll(ScrollSource::Wheel);
break;
}
}
@ -1520,7 +1527,7 @@ nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent)
return nsEventStatus_eConsumeNoDefault;
}
nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, ScrollSource aSource, bool aFingersOnTouchpad) {
APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
if (mState == SMOOTH_SCROLL) {
@ -1550,7 +1557,9 @@ nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool
if (mPanGestureState) {
ScreenPoint panDistance(fabs(aEvent.mPanDisplacement.x), fabs(aEvent.mPanDisplacement.y));
OverscrollHandoffState handoffState(
*mPanGestureState->GetOverscrollHandoffChain(), panDistance);
*mPanGestureState->GetOverscrollHandoffChain(),
panDistance,
aSource);
CallDispatchScroll(aEvent.mLocalPanStartPoint,
aEvent.mLocalPanStartPoint + aEvent.mLocalPanDisplacement,
handoffState);
@ -1884,8 +1893,13 @@ bool AsyncPanZoomController::AttemptScroll(const ParentLayerPoint& aStartPoint,
ReentrantMonitorAutoEnter lock(mMonitor);
ParentLayerPoint adjustedDisplacement;
bool forceVerticalOverscroll =
(aOverscrollHandoffState.mScrollSource == ScrollSource::Wheel &&
!mFrameMetrics.AllowVerticalScrollWithWheel());
bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y,
forceVerticalOverscroll);
bool xChanged = mX.AdjustDisplacement(displacement.x, adjustedDisplacement.x, overscroll.x);
bool yChanged = mY.AdjustDisplacement(displacement.y, adjustedDisplacement.y, overscroll.y);
if (xChanged || yChanged) {
ScheduleComposite();
}
@ -2050,7 +2064,7 @@ void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint
HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain());
}
void AsyncPanZoomController::StartSmoothScroll() {
void AsyncPanZoomController::StartSmoothScroll(ScrollSource aSource) {
SetState(SMOOTH_SCROLL);
nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
// Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to
@ -2060,6 +2074,7 @@ void AsyncPanZoomController::StartSmoothScroll() {
nsPoint destination = CSSPoint::ToAppUnits(mFrameMetrics.GetSmoothScrollOffset());
StartAnimation(new SmoothScrollAnimation(*this,
aSource,
initialPosition, initialVelocity,
destination,
gfxPrefs::ScrollBehaviorSpringConstant(),
@ -2078,8 +2093,11 @@ bool AsyncPanZoomController::CallDispatchScroll(const ParentLayerPoint& aStartPo
// null before calling DispatchScroll(). This is necessary because
// Destroy(), which nulls out mTreeManager, could be called concurrently.
APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
return treeManagerLocal
&& treeManagerLocal->DispatchScroll(this, aStartPoint, aEndPoint,
if (!treeManagerLocal) {
return false;
}
return treeManagerLocal->DispatchScroll(this,
aStartPoint, aEndPoint,
aOverscrollHandoffState);
}
@ -2097,7 +2115,9 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
if (prevTouchPoint != touchPoint) {
OverscrollHandoffState handoffState(
*CurrentTouchBlock()->GetOverscrollHandoffChain(), panDistance);
*CurrentTouchBlock()->GetOverscrollHandoffChain(),
panDistance,
ScrollSource::Touch);
CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
}
}
@ -2780,7 +2800,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
CancelAnimation();
mLastDispatchedPaintMetrics = aLayerMetrics;
StartSmoothScroll();
StartSmoothScroll(ScrollSource::DOM);
scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
}

View File

@ -407,7 +407,7 @@ protected:
nsEventStatus OnPanMayBegin(const PanGestureInput& aEvent);
nsEventStatus OnPanCancelled(const PanGestureInput& aEvent);
nsEventStatus OnPanBegin(const PanGestureInput& aEvent);
nsEventStatus OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad);
nsEventStatus OnPan(const PanGestureInput& aEvent, ScrollSource aSource, bool aFingersOnTouchpad);
nsEventStatus OnPanEnd(const PanGestureInput& aEvent);
nsEventStatus OnPanMomentumStart(const PanGestureInput& aEvent);
nsEventStatus OnPanMomentumEnd(const PanGestureInput& aEvent);
@ -847,7 +847,7 @@ private:
// Start an overscroll animation with the given initial velocity.
void StartOverscrollAnimation(const ParentLayerPoint& aVelocity);
void StartSmoothScroll();
void StartSmoothScroll(ScrollSource aSource);
/* ===================================================================
* The functions and members in this section are used to make ancestor chains
@ -910,7 +910,8 @@ public:
* state). If this returns false, the caller APZC knows that it should enter
* an overscrolled state itself if it can.
*/
bool AttemptScroll(const ParentLayerPoint& aStartPoint, const ParentLayerPoint& aEndPoint,
bool AttemptScroll(const ParentLayerPoint& aStartPoint,
const ParentLayerPoint& aEndPoint,
OverscrollHandoffState& aOverscrollHandoffState);
void FlushRepaintForOverscrollHandoff();

View File

@ -119,13 +119,20 @@ void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) {
bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement,
/* ParentLayerCoord */ float& aDisplacementOut,
/* ParentLayerCoord */ float& aOverscrollAmountOut)
/* ParentLayerCoord */ float&
aOverscrollAmountOut,
bool forceOverscroll /* = false */)
{
if (mAxisLocked) {
aOverscrollAmountOut = 0;
aDisplacementOut = 0;
return false;
}
if (forceOverscroll) {
aOverscrollAmountOut = aDisplacement;
aDisplacementOut = 0;
return false;
}
StopSamplingOverscrollAnimation();

View File

@ -75,7 +75,8 @@ public:
*/
bool AdjustDisplacement(ParentLayerCoord aDisplacement,
/* ParentLayerCoord */ float& aDisplacementOut,
/* ParentLayerCoord */ float& aOverscrollAmountOut);
/* ParentLayerCoord */ float& aOverscrollAmountOut,
bool forceOverscroll = false);
/**
* Overscrolls this axis by the requested amount in the requested direction.

View File

@ -127,8 +127,13 @@ private:
*/
struct OverscrollHandoffState {
OverscrollHandoffState(const OverscrollHandoffChain& aChain,
const ScreenPoint& aPanDistance)
: mChain(aChain), mChainIndex(0), mPanDistance(aPanDistance) {}
const ScreenPoint& aPanDistance,
ScrollSource aScrollSource)
: mChain(aChain),
mChainIndex(0),
mPanDistance(aPanDistance),
mScrollSource(aScrollSource)
{}
// The chain of APZCs along which we hand off scroll.
// This is const to indicate that the chain does not change over the
@ -144,6 +149,8 @@ struct OverscrollHandoffState {
// course of handoff.
// The x/y components of this are non-negative.
const ScreenPoint mPanDistance;
ScrollSource mScrollSource;
};
// Don't pollute other files with this macro for now.
#undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING

View File

@ -63,6 +63,7 @@
#include "UnitTransforms.h"
#include "LayersLogging.h"
#include "FrameLayerBuilder.h"
#include "mozilla/EventStateManager.h"
#include "RestyleManager.h"
#include "nsCaret.h"
#include "nsISelection.h"
@ -784,6 +785,12 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
LayoutDeviceIntSize lineScrollAmountInDevPixels =
LayoutDeviceIntSize::FromAppUnitsRounded(lineScrollAmount, presContext->AppUnitsPerDevPixel());
metrics.SetLineScrollAmount(lineScrollAmountInDevPixels);
if (!aScrollFrame->GetParent() ||
EventStateManager::CanVerticallyScrollFrameWithWheel(aScrollFrame->GetParent()))
{
metrics.SetAllowVerticalScrollWithWheel();
}
}
metrics.SetScrollId(scrollId);

View File

@ -1071,6 +1071,7 @@ ScrollFrameHelper::HandleScrollbarStyleSwitching()
}
}
#if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
static bool IsFocused(nsIContent* aContent)
{
// Some content elements, like the GetContent() of a scroll frame
@ -1083,22 +1084,30 @@ static bool IsFocused(nsIContent* aContent)
return aContent ? nsContentUtils::IsFocusedContent(aContent) : false;
}
#endif
bool
ScrollFrameHelper::WantAsyncScroll() const
{
nsRect scrollRange = GetScrollRange();
ScrollbarStyles styles = GetScrollbarStylesFromFrame();
bool isFocused = IsFocused(mOuter->GetContent());
bool isVScrollable = (scrollRange.height > 0)
&& (styles.mVertical != NS_STYLE_OVERFLOW_HIDDEN);
bool isHScrollable = (scrollRange.width > 0)
&& (styles.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN);
#if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
// Mobile platforms need focus to scroll.
bool canScrollWithoutScrollbars = IsFocused(mOuter->GetContent());
#else
bool canScrollWithoutScrollbars = true;
#endif
// The check for scroll bars was added in bug 825692 to prevent layerization
// of text inputs for performance reasons. However, if a text input is
// focused we want to layerize it so we can async scroll it (bug 946408).
bool isVAsyncScrollable = isVScrollable && (mVScrollbarBox || isFocused);
bool isHAsyncScrollable = isHScrollable && (mHScrollbarBox || isFocused);
// of text inputs for performance reasons.
bool isVAsyncScrollable = isVScrollable && (mVScrollbarBox || canScrollWithoutScrollbars);
bool isHAsyncScrollable = isHScrollable && (mHScrollbarBox || canScrollWithoutScrollbars);
return isVAsyncScrollable || isHAsyncScrollable;
}