summaryrefslogtreecommitdiffstats
path: root/gfx/layers/FrameMetrics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/FrameMetrics.cpp')
-rw-r--r--gfx/layers/FrameMetrics.cpp342
1 files changed, 342 insertions, 0 deletions
diff --git a/gfx/layers/FrameMetrics.cpp b/gfx/layers/FrameMetrics.cpp
new file mode 100644
index 0000000000..eb84d22db6
--- /dev/null
+++ b/gfx/layers/FrameMetrics.cpp
@@ -0,0 +1,342 @@
+/* -*- 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 "FrameMetrics.h"
+
+#include <ostream>
+
+#include "gfxUtils.h"
+#include "nsStyleConsts.h"
+#include "nsStyleStruct.h"
+#include "mozilla/WritingModes.h"
+#include "mozilla/gfx/Types.h"
+
+namespace mozilla {
+namespace layers {
+
+const ScrollableLayerGuid::ViewID ScrollableLayerGuid::NULL_SCROLL_ID = 0;
+
+std::ostream& operator<<(std::ostream& aStream, const FrameMetrics& aMetrics) {
+ aStream << "{ [cb=" << aMetrics.GetCompositionBounds()
+ << "] [sr=" << aMetrics.GetScrollableRect()
+ << "] [s=" << aMetrics.GetVisualScrollOffset();
+ if (aMetrics.GetVisualScrollUpdateType() != FrameMetrics::eNone) {
+ aStream << "] [vd=" << aMetrics.GetVisualDestination();
+ }
+ if (aMetrics.IsScrollInfoLayer()) {
+ aStream << "] [scrollinfo";
+ }
+ aStream << "] [dp=" << aMetrics.GetDisplayPort()
+ << "] [rcs=" << aMetrics.GetBoundingCompositionSize()
+ << "] [v=" << aMetrics.GetLayoutViewport()
+ << nsPrintfCString("] [z=(ld=%.3f r=%.3f",
+ aMetrics.GetDevPixelsPerCSSPixel().scale,
+ aMetrics.GetPresShellResolution())
+ .get()
+ << " cr=" << aMetrics.GetCumulativeResolution()
+ << " z=" << aMetrics.GetZoom()
+ << " t=" << aMetrics.GetTransformToAncestorScale() << " )] [u=("
+ << (int)aMetrics.GetVisualScrollUpdateType() << " "
+ << aMetrics.GetScrollGeneration()
+ << ")] scrollId=" << aMetrics.GetScrollId();
+ if (aMetrics.IsRootContent()) {
+ aStream << " [rcd]";
+ }
+ aStream << " }";
+ return aStream;
+}
+
+void FrameMetrics::RecalculateLayoutViewportOffset() {
+ // For subframes, the visual and layout viewports coincide, so just
+ // keep the layout viewport offset in sync with the visual one.
+ if (!mIsRootContent) {
+ mLayoutViewport.MoveTo(GetVisualScrollOffset());
+ return;
+ }
+ // For the root, the two viewports can diverge, but the layout
+ // viewport needs to keep enclosing the visual viewport.
+ KeepLayoutViewportEnclosingVisualViewport(GetVisualViewport(),
+ mScrollableRect, mLayoutViewport);
+}
+
+/* static */
+void FrameMetrics::KeepLayoutViewportEnclosingVisualViewport(
+ const CSSRect& aVisualViewport, const CSSRect& aScrollableRect,
+ CSSRect& aLayoutViewport) {
+ // If the visual viewport is contained within the layout viewport, we don't
+ // need to make any adjustments, so we can exit early.
+ //
+ // Additionally, if the composition bounds changes (due to an orientation
+ // change, window resize, etc.), it may take a few frames for aLayoutViewport
+ // to update and during that time, the visual viewport may be larger than the
+ // layout viewport. In such situations, we take an early exit if the visual
+ // viewport contains the layout viewport.
+ if (aLayoutViewport.Contains(aVisualViewport) ||
+ aVisualViewport.Contains(aLayoutViewport)) {
+ return;
+ }
+
+ // If visual viewport size is greater than the layout viewport, move the
+ // layout viewport such that it remains inside the visual viewport. Otherwise,
+ // move the layout viewport such that the visual viewport is contained
+ // inside the layout viewport.
+ if ((aLayoutViewport.Width() < aVisualViewport.Width() &&
+ !FuzzyEqualsMultiplicative(aLayoutViewport.Width(),
+ aVisualViewport.Width())) ||
+ (aLayoutViewport.Height() < aVisualViewport.Height() &&
+ !FuzzyEqualsMultiplicative(aLayoutViewport.Height(),
+ aVisualViewport.Height()))) {
+ if (aLayoutViewport.X() < aVisualViewport.X()) {
+ // layout viewport moves right
+ aLayoutViewport.MoveToX(aVisualViewport.X());
+ } else if (aVisualViewport.XMost() < aLayoutViewport.XMost()) {
+ // layout viewport moves left
+ aLayoutViewport.MoveByX(aVisualViewport.XMost() -
+ aLayoutViewport.XMost());
+ }
+ if (aLayoutViewport.Y() < aVisualViewport.Y()) {
+ // layout viewport moves down
+ aLayoutViewport.MoveToY(aVisualViewport.Y());
+ } else if (aVisualViewport.YMost() < aLayoutViewport.YMost()) {
+ // layout viewport moves up
+ aLayoutViewport.MoveByY(aVisualViewport.YMost() -
+ aLayoutViewport.YMost());
+ }
+ } else {
+ if (aVisualViewport.X() < aLayoutViewport.X()) {
+ aLayoutViewport.MoveToX(aVisualViewport.X());
+ } else if (aLayoutViewport.XMost() < aVisualViewport.XMost()) {
+ aLayoutViewport.MoveByX(aVisualViewport.XMost() -
+ aLayoutViewport.XMost());
+ }
+ if (aVisualViewport.Y() < aLayoutViewport.Y()) {
+ aLayoutViewport.MoveToY(aVisualViewport.Y());
+ } else if (aLayoutViewport.YMost() < aVisualViewport.YMost()) {
+ aLayoutViewport.MoveByY(aVisualViewport.YMost() -
+ aLayoutViewport.YMost());
+ }
+ }
+
+ // Regardless of any adjustment above, the layout viewport is not allowed
+ // to go outside the scrollable rect.
+ aLayoutViewport = aLayoutViewport.MoveInsideAndClamp(aScrollableRect);
+}
+
+/* static */
+CSSRect FrameMetrics::CalculateScrollRange(
+ const CSSRect& aScrollableRect, const ParentLayerRect& aCompositionBounds,
+ const CSSToParentLayerScale& aZoom) {
+ CSSSize scrollPortSize =
+ CalculateCompositedSizeInCssPixels(aCompositionBounds, aZoom);
+ CSSRect scrollRange = aScrollableRect;
+ scrollRange.SetWidth(
+ std::max(scrollRange.Width() - scrollPortSize.width, 0.0f));
+ scrollRange.SetHeight(
+ std::max(scrollRange.Height() - scrollPortSize.height, 0.0f));
+ return scrollRange;
+}
+
+/* static */
+CSSSize FrameMetrics::CalculateCompositedSizeInCssPixels(
+ const ParentLayerRect& aCompositionBounds,
+ const CSSToParentLayerScale& aZoom) {
+ if (aZoom == CSSToParentLayerScale(0)) {
+ return CSSSize(); // avoid division by zero
+ }
+ return aCompositionBounds.Size() / aZoom;
+}
+
+bool FrameMetrics::ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate) {
+ // In applying a main-thread scroll update, try to preserve the relative
+ // offset between the visual and layout viewports.
+ CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset();
+ MOZ_ASSERT(IsRootContent() || relativeOffset == CSSPoint());
+ // We need to set the two offsets together, otherwise a subsequent
+ // RecalculateLayoutViewportOffset() could see divergent layout and
+ // visual offsets.
+ bool offsetChanged = SetLayoutScrollOffset(aUpdate.GetDestination());
+ offsetChanged |=
+ ClampAndSetVisualScrollOffset(aUpdate.GetDestination() + relativeOffset);
+ return offsetChanged;
+}
+
+CSSPoint FrameMetrics::ApplyRelativeScrollUpdateFrom(
+ const ScrollPositionUpdate& aUpdate) {
+ MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::Relative);
+ CSSPoint origin = GetVisualScrollOffset();
+ CSSPoint delta = (aUpdate.GetDestination() - aUpdate.GetSource());
+ ClampAndSetVisualScrollOffset(origin + delta);
+ return GetVisualScrollOffset() - origin;
+}
+
+CSSPoint FrameMetrics::ApplyPureRelativeScrollUpdateFrom(
+ const ScrollPositionUpdate& aUpdate) {
+ MOZ_ASSERT(aUpdate.GetType() == ScrollUpdateType::PureRelative);
+ CSSPoint origin = GetVisualScrollOffset();
+ ClampAndSetVisualScrollOffset(origin + aUpdate.GetDelta());
+ return GetVisualScrollOffset() - origin;
+}
+
+void FrameMetrics::UpdatePendingScrollInfo(const ScrollPositionUpdate& aInfo) {
+ // We only get this "pending scroll info" for paint-skip transactions,
+ // but PureRelative position updates always trigger a full paint, so
+ // we should never enter this code with a PureRelative update type. For
+ // the other types, the destination field on the ScrollPositionUpdate will
+ // tell us the final layout scroll position on the main thread.
+ MOZ_ASSERT(aInfo.GetType() != ScrollUpdateType::PureRelative);
+
+ // In applying a main-thread scroll update, try to preserve the relative
+ // offset between the visual and layout viewports.
+ CSSPoint relativeOffset = GetVisualScrollOffset() - GetLayoutScrollOffset();
+ MOZ_ASSERT(IsRootContent() || relativeOffset == CSSPoint());
+
+ SetLayoutScrollOffset(aInfo.GetDestination());
+ ClampAndSetVisualScrollOffset(aInfo.GetDestination() + relativeOffset);
+ mScrollGeneration = aInfo.GetGeneration();
+}
+
+ScrollSnapInfo::ScrollSnapInfo()
+ : mScrollSnapStrictnessX(StyleScrollSnapStrictness::None),
+ mScrollSnapStrictnessY(StyleScrollSnapStrictness::None) {}
+
+bool ScrollSnapInfo::HasScrollSnapping() const {
+ return mScrollSnapStrictnessY != StyleScrollSnapStrictness::None ||
+ mScrollSnapStrictnessX != StyleScrollSnapStrictness::None;
+}
+
+bool ScrollSnapInfo::HasSnapPositions() const {
+ if (!HasScrollSnapping()) {
+ return false;
+ }
+
+ for (const auto& target : mSnapTargets) {
+ if ((target.mSnapPositionX &&
+ mScrollSnapStrictnessX != StyleScrollSnapStrictness::None) ||
+ (target.mSnapPositionY &&
+ mScrollSnapStrictnessY != StyleScrollSnapStrictness::None)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ScrollSnapInfo::InitializeScrollSnapStrictness(
+ WritingMode aWritingMode, const nsStyleDisplay* aDisplay) {
+ if (aDisplay->mScrollSnapType.strictness == StyleScrollSnapStrictness::None) {
+ return;
+ }
+
+ mScrollSnapStrictnessX = StyleScrollSnapStrictness::None;
+ mScrollSnapStrictnessY = StyleScrollSnapStrictness::None;
+
+ switch (aDisplay->mScrollSnapType.axis) {
+ case StyleScrollSnapAxis::X:
+ mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
+ break;
+ case StyleScrollSnapAxis::Y:
+ mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
+ break;
+ case StyleScrollSnapAxis::Block:
+ if (aWritingMode.IsVertical()) {
+ mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
+ } else {
+ mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
+ }
+ break;
+ case StyleScrollSnapAxis::Inline:
+ if (aWritingMode.IsVertical()) {
+ mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
+ } else {
+ mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
+ }
+ break;
+ case StyleScrollSnapAxis::Both:
+ mScrollSnapStrictnessX = aDisplay->mScrollSnapType.strictness;
+ mScrollSnapStrictnessY = aDisplay->mScrollSnapType.strictness;
+ break;
+ }
+}
+
+std::ostream& operator<<(std::ostream& aStream,
+ const OverscrollBehavior& aBehavior) {
+ switch (aBehavior) {
+ case OverscrollBehavior::Auto: {
+ aStream << "auto";
+ break;
+ }
+ case OverscrollBehavior::Contain: {
+ aStream << "contain";
+ break;
+ }
+ case OverscrollBehavior::None: {
+ aStream << "none";
+ break;
+ }
+ }
+ return aStream;
+}
+
+OverscrollBehaviorInfo::OverscrollBehaviorInfo()
+ : mBehaviorX(OverscrollBehavior::Auto),
+ mBehaviorY(OverscrollBehavior::Auto) {}
+
+static OverscrollBehavior ToOverscrollBehavior(
+ StyleOverscrollBehavior aBehavior) {
+ switch (aBehavior) {
+ case StyleOverscrollBehavior::Auto:
+ return OverscrollBehavior::Auto;
+ case StyleOverscrollBehavior::Contain:
+ return OverscrollBehavior::Contain;
+ case StyleOverscrollBehavior::None:
+ return OverscrollBehavior::None;
+ }
+ MOZ_ASSERT_UNREACHABLE("Invalid overscroll behavior");
+ return OverscrollBehavior::Auto;
+}
+
+OverscrollBehaviorInfo OverscrollBehaviorInfo::FromStyleConstants(
+ StyleOverscrollBehavior aBehaviorX, StyleOverscrollBehavior aBehaviorY) {
+ OverscrollBehaviorInfo result;
+ result.mBehaviorX = ToOverscrollBehavior(aBehaviorX);
+ result.mBehaviorY = ToOverscrollBehavior(aBehaviorY);
+ return result;
+}
+
+bool OverscrollBehaviorInfo::operator==(
+ const OverscrollBehaviorInfo& aOther) const {
+ return mBehaviorX == aOther.mBehaviorX && mBehaviorY == aOther.mBehaviorY;
+}
+
+std::ostream& operator<<(std::ostream& aStream,
+ const OverscrollBehaviorInfo& aInfo) {
+ if (aInfo.mBehaviorX == aInfo.mBehaviorY) {
+ aStream << aInfo.mBehaviorX;
+ } else {
+ aStream << "{ x=" << aInfo.mBehaviorX << ", y=" << aInfo.mBehaviorY << " }";
+ }
+ return aStream;
+}
+
+std::ostream& operator<<(std::ostream& aStream,
+ const ScrollMetadata& aMetadata) {
+ aStream << "{ [description=" << aMetadata.GetContentDescription()
+ << "] [metrics=" << aMetadata.GetMetrics();
+ if (aMetadata.GetScrollParentId() != ScrollableLayerGuid::NULL_SCROLL_ID) {
+ aStream << "] [scrollParent=" << aMetadata.GetScrollParentId();
+ }
+ if (aMetadata.GetHasScrollgrab()) {
+ aStream << "] [scrollgrab";
+ }
+ aStream << "] [overscroll=" << aMetadata.GetOverscrollBehavior() << "] ["
+ << aMetadata.GetScrollUpdates().Length() << " scrollupdates"
+ << "] }";
+ return aStream;
+}
+
+StaticAutoPtr<const ScrollMetadata> ScrollMetadata::sNullMetadata;
+
+} // namespace layers
+} // namespace mozilla