diff options
Diffstat (limited to 'gfx/layers/apz/src/ScrollThumbUtils.cpp')
-rw-r--r-- | gfx/layers/apz/src/ScrollThumbUtils.cpp | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/ScrollThumbUtils.cpp b/gfx/layers/apz/src/ScrollThumbUtils.cpp new file mode 100644 index 0000000000..48b36b9278 --- /dev/null +++ b/gfx/layers/apz/src/ScrollThumbUtils.cpp @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 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 "ScrollThumbUtils.h" +#include "AsyncPanZoomController.h" +#include "FrameMetrics.h" +#include "gfxPlatform.h" +#include "mozilla/gfx/Matrix.h" + +namespace mozilla { +namespace layers { +namespace apz { + +struct AsyncScrollThumbTransformer { + // Inputs + const LayerToParentLayerMatrix4x4& mCurrentTransform; + const gfx::Matrix4x4& mScrollableContentTransform; + AsyncPanZoomController* mApzc; + const FrameMetrics& mMetrics; + const ScrollbarData& mScrollbarData; + bool mScrollbarIsDescendant; + + // Intermediate results + AsyncTransformComponentMatrix mAsyncTransform; + AsyncTransformComponentMatrix mScrollbarTransform; + CSSToParentLayerScale mEffectiveZoom; + float mUnitlessThumbRatio; + + LayerToParentLayerMatrix4x4 ComputeTransform(); + + private: + // Helper functions for ComputeTransform(). + + // If the thumb's orientation is along |aAxis|, add transformations + // of the thumb into |mScrollbarTransform|. + void ApplyTransformForAxis(const Axis& aAxis); + + enum class ScrollThumbExtent { Start, End }; + + // Scale the thumb by |aScale| along |aAxis|, while keeping constant the + // position of the top denoted by |aExtent|. + void ScaleThumbBy(const Axis& aAxis, float aScale, ScrollThumbExtent aExtent); +}; + +void AsyncScrollThumbTransformer::ScaleThumbBy(const Axis& aAxis, float aScale, + ScrollThumbExtent aExtent) { + // To keep the position of the top of the thumb constant, the thumb needs to + // translated to compensate for the scale applied. The origin with respect to + // which the scale is applied is the origin of the layer tree, rather than + // the origin of the scroll thumb. This means that the space between the + // origin and the top of thumb (including the part of the scrollbar track + // above the thumb, plus whatever content is above the scroll frame) is scaled + // too, effectively translating the thumb. We undo that translation here. + // (One can think of the adjustment being done to the translation here as a + // change of basis. We have a method to help with that, + // Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code cleaner + // in this case). + CSSCoord thumbExtentRelativeToCompBounds = + (aAxis.GetPointOffset(mMetrics.GetVisualScrollOffset()) * + mUnitlessThumbRatio); + CSSCoord compBoundsOrigin = aAxis.GetPointOffset( + mMetrics.CalculateCompositionBoundsInCssPixelsOfSurroundingContent() + .TopLeft()); + CSSCoord thumbExtent = compBoundsOrigin + thumbExtentRelativeToCompBounds; + if (aExtent == ScrollThumbExtent::End) { + thumbExtent += mScrollbarData.mThumbLength; + } + const CSSCoord thumbExtentScaled = thumbExtent * aScale; + const CSSCoord thumbExtentDelta = thumbExtentScaled - thumbExtent; + const ParentLayerCoord thumbExtentDeltaPL = thumbExtentDelta * mEffectiveZoom; + + aAxis.PostScale(mScrollbarTransform, aScale); + aAxis.PostTranslate(mScrollbarTransform, -thumbExtentDeltaPL); +} + +void AsyncScrollThumbTransformer::ApplyTransformForAxis(const Axis& aAxis) { + ParentLayerCoord asyncScroll = aAxis.GetTransformTranslation(mAsyncTransform); + const float asyncZoom = aAxis.GetTransformScale(mAsyncTransform); + + // The scroll thumb needs to be scaled in the direction of scrolling by the + // inverse of the async zoom. This is because zooming in decreases the + // fraction of the whole srollable rect that is in view. + const float scale = 1.f / asyncZoom; + + // Note: |metrics.GetZoom()| doesn't yet include the async zoom. + mEffectiveZoom = CSSToParentLayerScale(mMetrics.GetZoom().scale * asyncZoom); + + if (gfxPlatform::UseDesktopZoomingScrollbars()) { + // As computed by GetCurrentAsyncTransform, asyncScrollY is + // asyncScrollY = -(GetEffectiveScrollOffset - + // mLastContentPaintMetrics.GetLayoutScrollOffset()) * + // effectiveZoom + // where GetEffectiveScrollOffset includes the visual viewport offset that + // the main thread knows about plus any async scrolling to the visual + // viewport offset that the main thread does not (yet) know about. We want + // asyncScrollY to be + // asyncScrollY = -(GetEffectiveScrollOffset - + // mLastContentPaintMetrics.GetVisualScrollOffset()) * effectiveZoom + // because the main thread positions the scrollbars at the visual viewport + // offset that it knows about. (aMetrics is mLastContentPaintMetrics) + + asyncScroll -= aAxis.GetPointOffset( + (mMetrics.GetLayoutScrollOffset() - mMetrics.GetVisualScrollOffset()) * + mEffectiveZoom); + } + + // Here we convert the scrollbar thumb ratio into a true unitless ratio by + // dividing out the conversion factor from the scrollframe's parent's space + // to the scrollframe's space. + mUnitlessThumbRatio = mScrollbarData.mThumbRatio / + (mMetrics.GetPresShellResolution() * asyncZoom); + + // The scroll thumb needs to be translated in opposite direction of the + // async scroll. This is because scrolling down, which translates the layer + // content up, should result in moving the scroll thumb down. + ParentLayerCoord translation = -asyncScroll * mUnitlessThumbRatio; + + // The translation we computed is in the scroll frame's ParentLayer space. + // This includes the full cumulative resolution, even if we are a subframe. + // However, the resulting transform is used in a context where the scrollbar + // is already subject to the resolutions of enclosing scroll frames. To avoid + // double application of these enclosing resolutions, divide them out, leaving + // only the local resolution if any. + translation /= (mMetrics.GetCumulativeResolution().scale / + mMetrics.GetPresShellResolution()); + + // When scaling the thumb to account for the async zoom, keep the position + // of the start of the thumb (which corresponds to the scroll offset) + // constant. + ScaleThumbBy(aAxis, scale, ScrollThumbExtent::Start); + + // If the page is overscrolled, additionally squish the thumb in accordance + // with the overscroll amount. + ParentLayerCoord overscroll = + aAxis.GetPointOffset(mApzc->GetOverscrollAmount()); + if (overscroll != 0) { + float overscrollScale = + 1.0f - (std::abs(overscroll.value) / + aAxis.GetRectLength(mMetrics.GetCompositionBounds())); + MOZ_ASSERT(overscrollScale > 0.0f && overscrollScale <= 1.0f); + // If we're overscrolled at the top, keep the top of the thumb in place + // as we squish it. If we're overscrolled at the bottom, keep the bottom of + // the thumb in place. + ScaleThumbBy( + aAxis, overscrollScale, + overscroll < 0 ? ScrollThumbExtent::Start : ScrollThumbExtent::End); + } + + aAxis.PostTranslate(mScrollbarTransform, translation); +} + +LayerToParentLayerMatrix4x4 AsyncScrollThumbTransformer::ComputeTransform() { + // We only apply the transform if the scroll-target layer has non-container + // children (i.e. when it has some possibly-visible content). This is to + // avoid moving scroll-bars in the situation that only a scroll information + // layer has been built for a scroll frame, as this would result in a + // disparity between scrollbars and visible content. + if (mMetrics.IsScrollInfoLayer()) { + return LayerToParentLayerMatrix4x4{}; + } + + MOZ_RELEASE_ASSERT(mApzc); + + mAsyncTransform = + mApzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing); + + // |mAsyncTransform| represents the amount by which we have scrolled and + // zoomed since the last paint. Because the scrollbar was sized and positioned + // based on the painted content, we need to adjust it based on asyncTransform + // so that it reflects what the user is actually seeing now. + if (*mScrollbarData.mDirection == ScrollDirection::eVertical) { + ApplyTransformForAxis(mApzc->mY); + } + if (*mScrollbarData.mDirection == ScrollDirection::eHorizontal) { + ApplyTransformForAxis(mApzc->mX); + } + + LayerToParentLayerMatrix4x4 transform = + mCurrentTransform * mScrollbarTransform; + + AsyncTransformComponentMatrix compensation; + // If the scrollbar layer is a child of the content it is a scrollbar for, + // then we need to adjust for any async transform (including an overscroll + // transform) on the content. This needs to be cancelled out because layout + // positions and sizes the scrollbar on the assumption that there is no async + // transform, and without this adjustment the scrollbar will end up in the + // wrong place. + // + // Note that since the async transform is applied on top of the content's + // regular transform, we need to make sure to unapply the async transform in + // the same coordinate space. This requires applying the content transform + // and then unapplying it after unapplying the async transform. + if (mScrollbarIsDescendant) { + AsyncTransformComponentMatrix overscroll = + mApzc->GetOverscrollTransform(AsyncPanZoomController::eForCompositing); + gfx::Matrix4x4 asyncUntransform = + (mAsyncTransform * overscroll).Inverse().ToUnknownMatrix(); + const gfx::Matrix4x4& contentTransform = mScrollableContentTransform; + gfx::Matrix4x4 contentUntransform = contentTransform.Inverse(); + + compensation *= ViewAs<AsyncTransformComponentMatrix>( + contentTransform * asyncUntransform * contentUntransform); + } + transform = transform * compensation; + + return transform; +} + +LayerToParentLayerMatrix4x4 ComputeTransformForScrollThumb( + const LayerToParentLayerMatrix4x4& aCurrentTransform, + const gfx::Matrix4x4& aScrollableContentTransform, + AsyncPanZoomController* aApzc, const FrameMetrics& aMetrics, + const ScrollbarData& aScrollbarData, bool aScrollbarIsDescendant) { + return AsyncScrollThumbTransformer{ + aCurrentTransform, aScrollableContentTransform, aApzc, aMetrics, + aScrollbarData, aScrollbarIsDescendant} + .ComputeTransform(); +} + +} // namespace apz +} // namespace layers +} // namespace mozilla |