Closed Bug 1152011 Opened 9 years ago Closed 9 years ago

APZ no longer clamps wheel scrolling to the scrollable rect

Categories

(Core :: Panning and Zooming, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla40
Tracking Status
firefox40 --- fixed

People

(Reporter: dvander, Assigned: dvander)

References

Details

Attachments

(2 files, 4 obsolete files)

This is a regression from introducing WheelScrollAnimation. If the animation is in progress, it arbitrarily accumulates new scroll deltas without bothering to clamp the final destination. If you scroll too far, the scrollbar appears stuck and won't budge in the opposite direction until the animation times out.
Attached patch fix (obsolete) — Splinter Review
Attachment #8589254 - Flags: review?(botond)
Summary: APZ no longer clamps scrolling to the scrollable rect → APZ no longer clamps wheel scrolling to the scrollable rect
Comment on attachment 8589254 [details] [diff] [review]
fix

Review of attachment 8589254 [details] [diff] [review]:
-----------------------------------------------------------------

This can be done more simply: just return false in WheelScrollAnimation::DoSample() if adjustedOffset is zero.
Attachment #8589254 - Flags: review?(botond) → review-
Attached patch v2 (obsolete) — Splinter Review
That will end up being slightly different because it stops the animation and starts a new one. But for all practical purposes I doubt it matters and it is indeed simpler.
Attachment #8589254 - Attachment is obsolete: true
Attachment #8589869 - Flags: review?(botond)
(Also, the original patch will work if both axes scroll at the same time, and the current one won't.)
Comment on attachment 8589869 [details] [diff] [review]
v2

Review of attachment 8589869 [details] [diff] [review]:
-----------------------------------------------------------------

Discussed this on IRC. Summary: starting a new animation instead of re-using it doesn't make a noticeable difference, and the scenario where two axes are scrolling separately can be addressed by zeroing out the velocity along an axis if it gets into overscroll (AdjustDisplacement already does this to Axis::mVelocity, but WheelScrollAnimation currently doesn't use Axis::mVelocity). We can address the latter in a follow-up if we want.

::: gfx/layers/apz/src/WheelScrollAnimation.cpp
@@ +43,5 @@
>    mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
>    mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y,
>                                !aFrameMetrics.AllowVerticalScrollWithWheel());
> +  if (FuzzyEqualsAdditive(adjustedOffset.x, 0.0f) &&
> +      FuzzyEqualsAdditive(adjustedOffset.y, 0.0f))

Let's move IsZero() from AsyncPanZoomController.cpp into APZUtils.h and use it.
Attachment #8589869 - Flags: review?(botond) → review+
Attached patch v3 (obsolete) — Splinter Review
Discussed on IRC, but recap: the above approach ends up not working, since mFinalDestination has to be clamped, and has to be clamped without racing with the input thread (i.e., on Update, not Sample).

This combines both approaches to cover both bases. I'll do a separate patch in another bug for syncing the axis velocity.
Attachment #8589869 - Attachment is obsolete: true
Attachment #8590137 - Flags: review?(botond)
Attached patch part 1, lock fixSplinter Review
(Related to the next patch, since it Update() will require the lock being taken.)
Attachment #8590137 - Attachment is obsolete: true
Attachment #8590137 - Flags: review?(botond)
Attachment #8590436 - Flags: review?(botond)
Attached patch part 2, clamp (obsolete) — Splinter Review
Same as the original patch, except integrating the IsZero() suggestion.

I couldn't see an easy way to merge DisplacementWillOverscroll into ClampOriginToScrollRange, or vice versa. For DisplacementWillOverscroll, the origin is implicit, and it wants to return more data than the Clamp operation provides.
Attachment #8590438 - Flags: review?(botond)
Attached patch part 2, clampSplinter Review
Simply clamping the destination turns out to not be great either, because we could end up with a very small fractional difference between the final destination and the actual destination the timing function computed.

The main thread simply forces a final scroll to the destination to make sure it's correct. The latest patch here does that for APZ as well.
Attachment #8590438 - Attachment is obsolete: true
Attachment #8590438 - Flags: review?(botond)
Attachment #8590558 - Flags: review?(botond)
Comment on attachment 8590436 [details] [diff] [review]
part 1, lock fix

Review of attachment 8590436 [details] [diff] [review]:
-----------------------------------------------------------------

::: gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ +1540,5 @@
>      case ScrollWheelInput::SCROLLMODE_SMOOTH: {
> +      // The lock must be held across the entire update operation, so the
> +      // compositor doesn't end the animation before we get a chance to
> +      // update it.
> +      ReentrantMonitorAutoEnter lock(mMonitor);

Based on the "Intentional scoping for mutex" comment inside SetState(), it shouldn't be called while the monitor is held, but I see there are existing places where we don't obey this. 

Let's do this for now, and I'll file a follow-up to clarify whether not holding the mutex during SetState() is important, and if so, clean up the places where we do.
Attachment #8590436 - Flags: review?(botond) → review+
Comment on attachment 8590558 [details] [diff] [review]
part 2, clamp

Review of attachment 8590558 [details] [diff] [review]:
-----------------------------------------------------------------

r+ w/comments. Thanks!

::: gfx/layers/apz/src/APZUtils.h
@@ +7,5 @@
>  #ifndef mozilla_layers_APZUtils_h
>  #define mozilla_layers_APZUtils_h
>  
>  #include <stdint.h>                     // for uint32_t
> +#include "mozilla/gfx/Point.h"

Also include "mozilla/FloatingPoint.h" for FuzzyEqualsAdditive().

::: gfx/layers/apz/src/Axis.cpp
@@ +411,5 @@
>  }
>  
> +CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin)
> +{
> +  CSSToParentLayerScale zoom = GetFrameMetrics().GetZoom().ToScaleFactor();

For non-root scroll frames, the zoom can be different along the x and y axes, so ScaleFactor2D::ToScaleFactor() will assert.

Instead, give Axis a virtual function GetScaleForAxis(ScaleFactor2D), and override it in AxisX to return the x-scale, and in AxisY to return the y-scale.

::: gfx/layers/apz/src/Axis.h
@@ +171,5 @@
> +  /**
> +   * Clamp a point to the page's scrollable bounds. That is, a scroll
> +   * destination to the returned point will not contain any overscroll.
> +   */
> +  CSSCoord ClampOriginToScrollableRect(CSSCoord aOrigin);

Make this method const.

::: gfx/layers/apz/src/WheelScrollAnimation.cpp
@@ +50,5 @@
> +  } else {
> +    nsPoint position = PositionAt(now);
> +    displacement =
> +      (CSSPoint::FromAppUnits(position) - aFrameMetrics.GetScrollOffset()) * zoom;
> +  }

Little neater:

bool finished = IsFinished(now);
nsPoint sampleDestiantion = finished ? mDestination : PositionAt(now);
ParentLayerPoint displacement = (CSSPoint::FromAppUnits(sampleDestination) - aFrameMetrics.GetScrollOffset()) * zoom;
Attachment #8590558 - Flags: review?(botond) → review+
(In reply to Botond Ballo [:botond] from comment #11)
> > +CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin)
> > +{
> > +  CSSToParentLayerScale zoom = GetFrameMetrics().GetZoom().ToScaleFactor();
> 
> For non-root scroll frames, the zoom can be different along the x and y
> axes, so ScaleFactor2D::ToScaleFactor() will assert.
> 
> Instead, give Axis a virtual function GetScaleForAxis(ScaleFactor2D), and
> override it in AxisX to return the x-scale, and in AxisY to return the
> y-scale.

Another option is to take and return ParentLayerCoords, and have the caller adjust by the zoom.
(In reply to Botond Ballo [:botond] from comment #10)
> Based on the "Intentional scoping for mutex" comment inside SetState(), it
> shouldn't be called while the monitor is held, but I see there are existing
> places where we don't obey this. 
> 
> Let's do this for now, and I'll file a follow-up to clarify whether not
> holding the mutex during SetState() is important, and if so, clean up the
> places where we do.

Filed bug 1153979.
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: