diff options
Diffstat (limited to 'gfx/layers/apz/src/Axis.cpp')
-rw-r--r-- | gfx/layers/apz/src/Axis.cpp | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/Axis.cpp b/gfx/layers/apz/src/Axis.cpp new file mode 100644 index 000000000..ddd660e0b --- /dev/null +++ b/gfx/layers/apz/src/Axis.cpp @@ -0,0 +1,681 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Axis.h" +#include <math.h> // for fabsf, pow, powf +#include <algorithm> // for max +#include "AsyncPanZoomController.h" // for AsyncPanZoomController +#include "mozilla/layers/APZCTreeManager.h" // for APZCTreeManager +#include "mozilla/layers/APZThreadUtils.h" // for AssertOnControllerThread +#include "FrameMetrics.h" // for FrameMetrics +#include "mozilla/Attributes.h" // for final +#include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction +#include "mozilla/Preferences.h" // for Preferences +#include "mozilla/gfx/Rect.h" // for RoundedIn +#include "mozilla/mozalloc.h" // for operator new +#include "mozilla/FloatingPoint.h" // for FuzzyEqualsAdditive +#include "mozilla/StaticPtr.h" // for StaticAutoPtr +#include "nsMathUtils.h" // for NS_lround +#include "nsPrintfCString.h" // for nsPrintfCString +#include "nsThreadUtils.h" // for NS_DispatchToMainThread, etc +#include "nscore.h" // for NS_IMETHOD +#include "gfxPrefs.h" // for the preferences + +#define AXIS_LOG(...) +// #define AXIS_LOG(...) printf_stderr("AXIS: " __VA_ARGS__) + +namespace mozilla { +namespace layers { + +// When we compute the velocity we do so by taking two input events and +// dividing the distance delta over the time delta. In some cases the time +// delta can be really small, which can make the velocity computation very +// volatile. To avoid this we impose a minimum time delta below which we do +// not recompute the velocity. +const uint32_t MIN_VELOCITY_SAMPLE_TIME_MS = 5; + +bool FuzzyEqualsCoordinate(float aValue1, float aValue2) +{ + return FuzzyEqualsAdditive(aValue1, aValue2, COORDINATE_EPSILON) + || FuzzyEqualsMultiplicative(aValue1, aValue2); +} + +extern StaticAutoPtr<ComputedTimingFunction> gVelocityCurveFunction; + +Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController) + : mPos(0), + mVelocitySampleTimeMs(0), + mVelocitySamplePos(0), + mVelocity(0.0f), + mAxisLocked(false), + mAsyncPanZoomController(aAsyncPanZoomController), + mOverscroll(0), + mFirstOverscrollAnimationSample(0), + mLastOverscrollPeak(0), + mOverscrollScale(1.0f) +{ +} + +float Axis::ToLocalVelocity(float aVelocityInchesPerMs) const { + ScreenPoint velocity = MakePoint(aVelocityInchesPerMs * APZCTreeManager::GetDPI()); + // Use ToScreenCoordinates() to convert a point rather than a vector by + // treating the point as a vector, and using (0, 0) as the anchor. + ScreenPoint panStart = mAsyncPanZoomController->ToScreenCoordinates( + mAsyncPanZoomController->PanStart(), + ParentLayerPoint()); + ParentLayerPoint localVelocity = + mAsyncPanZoomController->ToParentLayerCoordinates(velocity, panStart); + return localVelocity.Length(); +} + +void Axis::UpdateWithTouchAtDevicePoint(ParentLayerCoord aPos, ParentLayerCoord aAdditionalDelta, uint32_t aTimestampMs) { + // mVelocityQueue is controller-thread only + APZThreadUtils::AssertOnControllerThread(); + + if (aTimestampMs <= mVelocitySampleTimeMs + MIN_VELOCITY_SAMPLE_TIME_MS) { + // See also the comment on MIN_VELOCITY_SAMPLE_TIME_MS. + // We still update mPos so that the positioning is correct (and we don't run + // into problems like bug 1042734) but the velocity will remain where it was. + // In particular we don't update either mVelocitySampleTimeMs or + // mVelocitySamplePos so that eventually when we do get an event with the + // required time delta we use the corresponding distance delta as well. + AXIS_LOG("%p|%s skipping velocity computation for small time delta %dms\n", + mAsyncPanZoomController, Name(), (aTimestampMs - mVelocitySampleTimeMs)); + mPos = aPos; + return; + } + + float newVelocity = mAxisLocked ? 0.0f : (float)(mVelocitySamplePos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mVelocitySampleTimeMs); + + newVelocity = ApplyFlingCurveToVelocity(newVelocity); + + AXIS_LOG("%p|%s updating velocity to %f with touch\n", + mAsyncPanZoomController, Name(), newVelocity); + mVelocity = newVelocity; + mPos = aPos; + mVelocitySampleTimeMs = aTimestampMs; + mVelocitySamplePos = aPos; + + AddVelocityToQueue(aTimestampMs, mVelocity); +} + +float Axis::ApplyFlingCurveToVelocity(float aVelocity) const { + float newVelocity = aVelocity; + if (gfxPrefs::APZMaxVelocity() > 0.0f) { + bool velocityIsNegative = (newVelocity < 0); + newVelocity = fabs(newVelocity); + + float maxVelocity = ToLocalVelocity(gfxPrefs::APZMaxVelocity()); + newVelocity = std::min(newVelocity, maxVelocity); + + if (gfxPrefs::APZCurveThreshold() > 0.0f && gfxPrefs::APZCurveThreshold() < gfxPrefs::APZMaxVelocity()) { + float curveThreshold = ToLocalVelocity(gfxPrefs::APZCurveThreshold()); + if (newVelocity > curveThreshold) { + // here, 0 < curveThreshold < newVelocity <= maxVelocity, so we apply the curve + float scale = maxVelocity - curveThreshold; + float funcInput = (newVelocity - curveThreshold) / scale; + float funcOutput = + gVelocityCurveFunction->GetValue(funcInput, + ComputedTimingFunction::BeforeFlag::Unset); + float curvedVelocity = (funcOutput * scale) + curveThreshold; + AXIS_LOG("%p|%s curving up velocity from %f to %f\n", + mAsyncPanZoomController, Name(), newVelocity, curvedVelocity); + newVelocity = curvedVelocity; + } + } + + if (velocityIsNegative) { + newVelocity = -newVelocity; + } + } + + return newVelocity; +} + +void Axis::AddVelocityToQueue(uint32_t aTimestampMs, float aVelocity) { + mVelocityQueue.AppendElement(std::make_pair(aTimestampMs, aVelocity)); + if (mVelocityQueue.Length() > gfxPrefs::APZMaxVelocityQueueSize()) { + mVelocityQueue.RemoveElementAt(0); + } +} + +void Axis::HandleTouchVelocity(uint32_t aTimestampMs, float aSpeed) { + // mVelocityQueue is controller-thread only + APZThreadUtils::AssertOnControllerThread(); + + mVelocity = ApplyFlingCurveToVelocity(aSpeed); + mVelocitySampleTimeMs = aTimestampMs; + + AddVelocityToQueue(aTimestampMs, mVelocity); +} + +void Axis::StartTouch(ParentLayerCoord aPos, uint32_t aTimestampMs) { + mStartPos = aPos; + mPos = aPos; + mVelocitySampleTimeMs = aTimestampMs; + mVelocitySamplePos = aPos; + mAxisLocked = false; +} + +bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement, + /* ParentLayerCoord */ float& aDisplacementOut, + /* ParentLayerCoord */ float& aOverscrollAmountOut, + bool aForceOverscroll /* = false */) +{ + if (mAxisLocked) { + aOverscrollAmountOut = 0; + aDisplacementOut = 0; + return false; + } + if (aForceOverscroll) { + aOverscrollAmountOut = aDisplacement; + aDisplacementOut = 0; + return false; + } + + EndOverscrollAnimation(); + + ParentLayerCoord displacement = aDisplacement; + + // First consume any overscroll in the opposite direction along this axis. + ParentLayerCoord consumedOverscroll = 0; + if (mOverscroll > 0 && aDisplacement < 0) { + consumedOverscroll = std::min(mOverscroll, -aDisplacement); + } else if (mOverscroll < 0 && aDisplacement > 0) { + consumedOverscroll = 0.f - std::min(-mOverscroll, aDisplacement); + } + mOverscroll -= consumedOverscroll; + displacement += consumedOverscroll; + + // Split the requested displacement into an allowed displacement that does + // not overscroll, and an overscroll amount. + aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement); + if (aOverscrollAmountOut != 0.0f) { + // No need to have a velocity along this axis anymore; it won't take us + // anywhere, so we're just spinning needlessly. + AXIS_LOG("%p|%s has overscrolled, clearing velocity\n", + mAsyncPanZoomController, Name()); + mVelocity = 0.0f; + displacement -= aOverscrollAmountOut; + } + aDisplacementOut = displacement; + return fabsf(consumedOverscroll) > EPSILON; +} + +ParentLayerCoord Axis::ApplyResistance(ParentLayerCoord aRequestedOverscroll) const { + // 'resistanceFactor' is a value between 0 and 1, which: + // - tends to 1 as the existing overscroll tends to 0 + // - tends to 0 as the existing overscroll tends to the composition length + // The actual overscroll is the requested overscroll multiplied by this + // factor; this should prevent overscrolling by more than the composition + // length. + float resistanceFactor = 1 - fabsf(GetOverscroll()) / GetCompositionLength(); + return resistanceFactor < 0 ? ParentLayerCoord(0) : aRequestedOverscroll * resistanceFactor; +} + +void Axis::OverscrollBy(ParentLayerCoord aOverscroll) { + MOZ_ASSERT(CanScroll()); + // We can get some spurious calls to OverscrollBy() with near-zero values + // due to rounding error. Ignore those (they might trip the asserts below.) + if (FuzzyEqualsAdditive(aOverscroll.value, 0.0f, COORDINATE_EPSILON)) { + return; + } + EndOverscrollAnimation(); + aOverscroll = ApplyResistance(aOverscroll); + if (aOverscroll > 0) { +#ifdef DEBUG + if (!FuzzyEqualsCoordinate(GetCompositionEnd().value, GetPageEnd().value)) { + nsPrintfCString message("composition end (%f) is not equal (within error) to page end (%f)\n", + GetCompositionEnd().value, GetPageEnd().value); + NS_ASSERTION(false, message.get()); + MOZ_CRASH("GFX: Overscroll issue > 0"); + } +#endif + MOZ_ASSERT(mOverscroll >= 0); + } else if (aOverscroll < 0) { +#ifdef DEBUG + if (!FuzzyEqualsCoordinate(GetOrigin().value, GetPageStart().value)) { + nsPrintfCString message("composition origin (%f) is not equal (within error) to page origin (%f)\n", + GetOrigin().value, GetPageStart().value); + NS_ASSERTION(false, message.get()); + MOZ_CRASH("GFX: Overscroll issue < 0"); + } +#endif + MOZ_ASSERT(mOverscroll <= 0); + } + mOverscroll += aOverscroll; +} + +ParentLayerCoord Axis::GetOverscroll() const { + ParentLayerCoord result = (mOverscroll - mLastOverscrollPeak) / mOverscrollScale; + + // Assert that we return overscroll in the correct direction +#ifdef DEBUG + if ((result.value * mFirstOverscrollAnimationSample.value) < 0.0f) { + nsPrintfCString message("GetOverscroll() (%f) and first overscroll animation sample (%f) have different signs\n", + result.value, mFirstOverscrollAnimationSample.value); + NS_ASSERTION(false, message.get()); + MOZ_CRASH("GFX: Overscroll issue"); + } +#endif + + return result; +} + +void Axis::StartOverscrollAnimation(float aVelocity) { + // Make sure any state from a previous animation has been cleared. + MOZ_ASSERT(mFirstOverscrollAnimationSample == 0 && + mLastOverscrollPeak == 0 && + mOverscrollScale == 1); + + SetVelocity(aVelocity); +} + +void Axis::EndOverscrollAnimation() { + ParentLayerCoord overscroll = GetOverscroll(); + mFirstOverscrollAnimationSample = 0; + mLastOverscrollPeak = 0; + mOverscrollScale = 1.0f; + mOverscroll = overscroll; +} + +void Axis::StepOverscrollAnimation(double aStepDurationMilliseconds) { + // Apply spring physics to the overscroll as time goes on. + // Note: this method of sampling isn't perfectly smooth, as it assumes + // a constant velocity over 'aDelta', instead of an accelerating velocity. + // (The way we applying friction to flings has the same issue.) + // Hooke's law with damping: + // F = -kx - bv + // where + // k is a constant related to the stiffness of the spring + // The larger the constant, the stiffer the spring. + // x is the displacement of the end of the spring from its equilibrium + // In our scenario, it's the amount of overscroll on the axis. + // b is a constant that provides damping (friction) + // v is the velocity of the point at the end of the spring + // See http://gafferongames.com/game-physics/spring-physics/ + const float kSpringStiffness = gfxPrefs::APZOverscrollSpringStiffness(); + const float kSpringFriction = gfxPrefs::APZOverscrollSpringFriction(); + + // Apply spring force. + float springForce = -1 * kSpringStiffness * mOverscroll; + // Assume unit mass, so force = acceleration. + float oldVelocity = mVelocity; + mVelocity += springForce * aStepDurationMilliseconds; + + // Apply dampening. + mVelocity *= pow(double(1 - kSpringFriction), aStepDurationMilliseconds); + AXIS_LOG("%p|%s sampled overscroll animation, leaving velocity at %f\n", + mAsyncPanZoomController, Name(), mVelocity); + + // At the peak of each oscillation, record new offset and scaling factors for + // overscroll, to ensure that GetOverscroll always returns a value of the + // same sign, and that this value is correctly adjusted as the spring is + // dampened. + // To handle the case where one of the velocity samples is exaclty zero, + // consider a sign change to have occurred when the outgoing velocity is zero. + bool velocitySignChange = (oldVelocity * mVelocity) < 0 || mVelocity == 0; + if (mFirstOverscrollAnimationSample == 0.0f) { + mFirstOverscrollAnimationSample = mOverscroll; + + // It's possible to start sampling overscroll with velocity == 0, or + // velocity in the opposite direction of overscroll, so make sure we + // correctly record the peak in this case. + if (mOverscroll != 0 && ((mOverscroll > 0 ? oldVelocity : -oldVelocity) <= 0.0f)) { + velocitySignChange = true; + } + } + if (velocitySignChange) { + bool oddOscillation = (mOverscroll.value * mFirstOverscrollAnimationSample.value) < 0.0f; + mLastOverscrollPeak = oddOscillation ? mOverscroll : -mOverscroll; + mOverscrollScale = 2.0f; + } + + // Adjust the amount of overscroll based on the velocity. + // Note that we allow for oscillations. + mOverscroll += (mVelocity * aStepDurationMilliseconds); + + // Our mechanism for translating a set of mOverscroll values that oscillate + // around zero to a set of GetOverscroll() values that have the same sign + // (so content is always stretched, never compressed) assumes that + // mOverscroll does not exceed mLastOverscrollPeak in magnitude. If our + // calculations were exact, this would be the case, as a dampened spring + // should never attain a displacement greater in magnitude than a previous + // peak. In our approximation calculations, however, this may not hold + // exactly. To ensure the assumption is not violated, we clamp the magnitude + // of mOverscroll. + if (mLastOverscrollPeak != 0 && fabs(mOverscroll) > fabs(mLastOverscrollPeak)) { + mOverscroll = (mOverscroll >= 0) ? fabs(mLastOverscrollPeak) : -fabs(mLastOverscrollPeak); + } +} + +bool Axis::SampleOverscrollAnimation(const TimeDuration& aDelta) { + // Short-circuit early rather than running through all the sampling code. + if (mVelocity == 0.0f && mOverscroll == 0.0f) { + return false; + } + + // We approximate the curve traced out by the velocity of the spring + // over time by breaking up the curve into small segments over which we + // consider the velocity to be constant. If the animation is sampled + // sufficiently often, then treating |aDelta| as a single segment of this + // sort would be fine, but the frequency at which the animation is sampled + // can be affected by external factors, and as the segment size grows larger, + // the approximation gets worse and the approximated curve can even diverge + // (i.e. oscillate forever, with displacements of increasing absolute value)! + // To avoid this, we break up |aDelta| into smaller segments of length 1 ms + // each, and a segment of any remaining fractional milliseconds. + double milliseconds = aDelta.ToMilliseconds(); + int wholeMilliseconds = (int) aDelta.ToMilliseconds(); + double fractionalMilliseconds = milliseconds - wholeMilliseconds; + for (int i = 0; i < wholeMilliseconds; ++i) { + StepOverscrollAnimation(1); + } + StepOverscrollAnimation(fractionalMilliseconds); + + // If both the velocity and the displacement fall below a threshold, stop + // the animation so we don't continue doing tiny oscillations that aren't + // noticeable. + if (fabs(mOverscroll) < gfxPrefs::APZOverscrollStopDistanceThreshold() && + fabs(mVelocity) < gfxPrefs::APZOverscrollStopVelocityThreshold()) { + // "Jump" to the at-rest state. The jump shouldn't be noticeable as the + // velocity and overscroll are already low. + AXIS_LOG("%p|%s oscillation dropped below threshold, going to rest\n", + mAsyncPanZoomController, Name()); + ClearOverscroll(); + mVelocity = 0; + return false; + } + + // Otherwise, continue the animation. + return true; +} + +bool Axis::IsOverscrolled() const { + return mOverscroll != 0.f; +} + +void Axis::ClearOverscroll() { + EndOverscrollAnimation(); + mOverscroll = 0; +} + +ParentLayerCoord Axis::PanStart() const { + return mStartPos; +} + +ParentLayerCoord Axis::PanDistance() const { + return fabs(mPos - mStartPos); +} + +ParentLayerCoord Axis::PanDistance(ParentLayerCoord aPos) const { + return fabs(aPos - mStartPos); +} + +void Axis::EndTouch(uint32_t aTimestampMs) { + // mVelocityQueue is controller-thread only + APZThreadUtils::AssertOnControllerThread(); + + mAxisLocked = false; + mVelocity = 0; + int count = 0; + while (!mVelocityQueue.IsEmpty()) { + uint32_t timeDelta = (aTimestampMs - mVelocityQueue[0].first); + if (timeDelta < gfxPrefs::APZVelocityRelevanceTime()) { + count++; + mVelocity += mVelocityQueue[0].second; + } + mVelocityQueue.RemoveElementAt(0); + } + if (count > 1) { + mVelocity /= count; + } + AXIS_LOG("%p|%s ending touch, computed velocity %f\n", + mAsyncPanZoomController, Name(), mVelocity); +} + +void Axis::CancelGesture() { + // mVelocityQueue is controller-thread only + APZThreadUtils::AssertOnControllerThread(); + + AXIS_LOG("%p|%s cancelling touch, clearing velocity queue\n", + mAsyncPanZoomController, Name()); + mVelocity = 0.0f; + while (!mVelocityQueue.IsEmpty()) { + mVelocityQueue.RemoveElementAt(0); + } +} + +bool Axis::CanScroll() const { + return GetPageLength() - GetCompositionLength() > COORDINATE_EPSILON; +} + +bool Axis::CanScroll(ParentLayerCoord aDelta) const +{ + if (!CanScroll() || mAxisLocked) { + return false; + } + + return fabs(DisplacementWillOverscrollAmount(aDelta) - aDelta) > COORDINATE_EPSILON; +} + +CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin) const +{ + CSSToParentLayerScale zoom = GetScaleForAxis(GetFrameMetrics().GetZoom()); + ParentLayerCoord origin = aOrigin * zoom; + + ParentLayerCoord result; + if (origin < GetPageStart()) { + result = GetPageStart(); + } else if (origin + GetCompositionLength() > GetPageEnd()) { + result = GetPageEnd() - GetCompositionLength(); + } else { + return aOrigin; + } + + return result / zoom; +} + +bool Axis::CanScrollNow() const { + return !mAxisLocked && CanScroll(); +} + +bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta, + float aFriction, + float aThreshold) { + if (fabsf(mVelocity) <= aThreshold) { + // If the velocity is very low, just set it to 0 and stop the fling, + // otherwise we'll just asymptotically approach 0 and the user won't + // actually see any changes. + mVelocity = 0.0f; + return false; + } else { + mVelocity *= pow(1.0f - aFriction, float(aDelta.ToMilliseconds())); + } + AXIS_LOG("%p|%s reduced velocity to %f due to friction\n", + mAsyncPanZoomController, Name(), mVelocity); + return true; +} + +ParentLayerCoord Axis::DisplacementWillOverscrollAmount(ParentLayerCoord aDisplacement) const { + ParentLayerCoord newOrigin = GetOrigin() + aDisplacement; + ParentLayerCoord newCompositionEnd = GetCompositionEnd() + aDisplacement; + // If the current pan plus a displacement takes the window to the left of or + // above the current page rect. + bool minus = newOrigin < GetPageStart(); + // If the current pan plus a displacement takes the window to the right of or + // below the current page rect. + bool plus = newCompositionEnd > GetPageEnd(); + if (minus && plus) { + // Don't handle overscrolled in both directions; a displacement can't cause + // this, it must have already been zoomed out too far. + return 0; + } + if (minus) { + return newOrigin - GetPageStart(); + } + if (plus) { + return newCompositionEnd - GetPageEnd(); + } + return 0; +} + +CSSCoord Axis::ScaleWillOverscrollAmount(float aScale, CSSCoord aFocus) const { + // Internally, do computations in ParentLayer coordinates *before* the scale + // is applied. + CSSToParentLayerScale zoom = GetFrameMetrics().GetZoom().ToScaleFactor(); + ParentLayerCoord focus = aFocus * zoom; + ParentLayerCoord originAfterScale = (GetOrigin() + focus) - (focus / aScale); + + bool both = ScaleWillOverscrollBothSides(aScale); + bool minus = GetPageStart() - originAfterScale > COORDINATE_EPSILON; + bool plus = (originAfterScale + (GetCompositionLength() / aScale)) - GetPageEnd() > COORDINATE_EPSILON; + + if ((minus && plus) || both) { + // If we ever reach here it's a bug in the client code. + MOZ_ASSERT(false, "In an OVERSCROLL_BOTH condition in ScaleWillOverscrollAmount"); + return 0; + } + if (minus) { + return (originAfterScale - GetPageStart()) / zoom; + } + if (plus) { + return (originAfterScale + (GetCompositionLength() / aScale) - GetPageEnd()) / zoom; + } + return 0; +} + +bool Axis::IsAxisLocked() const { + return mAxisLocked; +} + +float Axis::GetVelocity() const { + return mAxisLocked ? 0 : mVelocity; +} + +void Axis::SetVelocity(float aVelocity) { + AXIS_LOG("%p|%s direct-setting velocity to %f\n", + mAsyncPanZoomController, Name(), aVelocity); + mVelocity = aVelocity; +} + +ParentLayerCoord Axis::GetCompositionEnd() const { + return GetOrigin() + GetCompositionLength(); +} + +ParentLayerCoord Axis::GetPageEnd() const { + return GetPageStart() + GetPageLength(); +} + +ParentLayerCoord Axis::GetScrollRangeEnd() const { + return GetPageEnd() - GetCompositionLength(); +} + +ParentLayerCoord Axis::GetOrigin() const { + ParentLayerPoint origin = GetFrameMetrics().GetScrollOffset() * GetFrameMetrics().GetZoom(); + return GetPointOffset(origin); +} + +ParentLayerCoord Axis::GetCompositionLength() const { + return GetRectLength(GetFrameMetrics().GetCompositionBounds()); +} + +ParentLayerCoord Axis::GetPageStart() const { + ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() * GetFrameMetrics().GetZoom(); + return GetRectOffset(pageRect); +} + +ParentLayerCoord Axis::GetPageLength() const { + ParentLayerRect pageRect = GetFrameMetrics().GetExpandedScrollableRect() * GetFrameMetrics().GetZoom(); + return GetRectLength(pageRect); +} + +bool Axis::ScaleWillOverscrollBothSides(float aScale) const { + const FrameMetrics& metrics = GetFrameMetrics(); + ParentLayerRect screenCompositionBounds = metrics.GetCompositionBounds() + / ParentLayerToParentLayerScale(aScale); + return GetRectLength(screenCompositionBounds) - GetPageLength() > COORDINATE_EPSILON; +} + +const FrameMetrics& Axis::GetFrameMetrics() const { + return mAsyncPanZoomController->GetFrameMetrics(); +} + + +AxisX::AxisX(AsyncPanZoomController* aAsyncPanZoomController) + : Axis(aAsyncPanZoomController) +{ + +} + +ParentLayerCoord AxisX::GetPointOffset(const ParentLayerPoint& aPoint) const +{ + return aPoint.x; +} + +ParentLayerCoord AxisX::GetRectLength(const ParentLayerRect& aRect) const +{ + return aRect.width; +} + +ParentLayerCoord AxisX::GetRectOffset(const ParentLayerRect& aRect) const +{ + return aRect.x; +} + +CSSToParentLayerScale AxisX::GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const +{ + return CSSToParentLayerScale(aScale.xScale); +} + +ScreenPoint AxisX::MakePoint(ScreenCoord aCoord) const +{ + return ScreenPoint(aCoord, 0); +} + +const char* AxisX::Name() const +{ + return "X"; +} + +AxisY::AxisY(AsyncPanZoomController* aAsyncPanZoomController) + : Axis(aAsyncPanZoomController) +{ + +} + +ParentLayerCoord AxisY::GetPointOffset(const ParentLayerPoint& aPoint) const +{ + return aPoint.y; +} + +ParentLayerCoord AxisY::GetRectLength(const ParentLayerRect& aRect) const +{ + return aRect.height; +} + +ParentLayerCoord AxisY::GetRectOffset(const ParentLayerRect& aRect) const +{ + return aRect.y; +} + +CSSToParentLayerScale AxisY::GetScaleForAxis(const CSSToParentLayerScale2D& aScale) const +{ + return CSSToParentLayerScale(aScale.yScale); +} + +ScreenPoint AxisY::MakePoint(ScreenCoord aCoord) const +{ + return ScreenPoint(0, aCoord); +} + +const char* AxisY::Name() const +{ + return "Y"; +} + +} // namespace layers +} // namespace mozilla |