commit 7e09ce156f3fc4f357176d2d52758d88e82ce0a7
parent 24a6bb46e8d7b3562b70d5e440b8a29ccdf41e3e
Author: Dan Robertson <drobertson@mozilla.com>
Date: Thu, 13 Nov 2025 04:59:06 +0000
Bug 1730749 - Handle sticky ASRs in ClipManager. r=mstange
This patch generalizes ClipManager::DefineScrollLayers into
DefineSpatialNodes, and introduces ClipManager::DefineStickyNode to
handle creating a spatial node for a sticky ASR.
For ease of review, this patch temporarily assumes that we can get hold of
the sticky display item in ClipManager, and contains a stub function for
doing so. That allows the initial implementation of DefineStickyNode to
mostly be cut-and-paste from nsDisplayStickyPosition::CreateWebRenderCommands.
The next patch will revise the implementation of DefineStickyNode to
avoid the dependency on the sticky display item.
Differential Revision: https://phabricator.services.mozilla.com/D254152
Diffstat:
4 files changed, 277 insertions(+), 207 deletions(-)
diff --git a/gfx/layers/wr/ClipManager.cpp b/gfx/layers/wr/ClipManager.cpp
@@ -251,14 +251,15 @@ wr::WrSpaceAndClipChain ClipManager::SwitchItem(nsDisplayListBuilder* aBuilder,
// ASR chain pointed to by |asr|. The other is the
// ASR chain pointed to by clip->mASR. We pick the leafmost
// of these two chains because that one will include the other. Calling
- // DefineScrollLayers with this leafmost ASR will recursively define all the
+ // DefineSpatialNodes with this leafmost ASR will recursively define all the
// ASRs that we care about for this item, but will not actually push
// anything onto the WR stack.
const ActiveScrolledRoot* leafmostASR = asr;
if (clip) {
leafmostASR = ActiveScrolledRoot::PickDescendant(leafmostASR, clip->mASR);
}
- Maybe<wr::WrSpatialId> leafmostId = DefineScrollLayers(leafmostASR, aItem);
+ Maybe<wr::WrSpatialId> leafmostId =
+ DefineSpatialNodes(aBuilder, leafmostASR, aItem);
(void)leafmostId;
// Define all the clips in the item's clip chain, and obtain a clip chain id
@@ -308,8 +309,268 @@ wr::WrSpatialId ClipManager::GetSpatialId(const ActiveScrolledRoot* aASR) {
return *space;
}
-Maybe<wr::WrSpatialId> ClipManager::DefineScrollLayers(
+StickyScrollContainer* ClipManager::GetStickyScrollContainer(
+ const ActiveScrolledRoot* aASR) {
+ MOZ_ASSERT(aASR->mKind == ActiveScrolledRoot::ASRKind::Sticky);
+ StickyScrollContainer* stickyScrollContainer =
+ StickyScrollContainer::GetOrCreateForFrame(aASR->mFrame);
+ if (stickyScrollContainer) {
+ // If there's no ASR for the scrollframe that this sticky item is attached
+ // to, then don't create a WR sticky item for it either. Trying to do so
+ // will end in sadness because WR will interpret some coordinates as
+ // relative to the nearest enclosing scrollframe, which will correspond
+ // to the nearest ancestor ASR on the gecko side. That ASR will not be the
+ // same as the scrollframe this sticky item is actually supposed to be
+ // attached to, thus the sadness.
+ // Not sending WR the sticky item is ok, because the enclosing scrollframe
+ // will never be asynchronously scrolled. Instead we will always position
+ // the sticky items correctly on the gecko side and WR will never need to
+ // adjust their position itself.
+ if (!stickyScrollContainer->ScrollContainer()
+ ->IsMaybeAsynchronouslyScrolled()) {
+ stickyScrollContainer = nullptr;
+ }
+ }
+ return stickyScrollContainer;
+}
+
+static nsDisplayStickyPosition* FindStickyPositionItem(
const ActiveScrolledRoot* aASR, nsDisplayItem* aItem) {
+ // TODO(bug 1730749): Implement somehow.
+ return nullptr;
+}
+
+// Returns the smallest distance from "0" to the range [min, max] where
+// min <= max. Despite the name, the return value is actually a 1-D vector,
+// and so may be negative if max < 0.
+static nscoord DistanceToRange(nscoord min, nscoord max) {
+ MOZ_ASSERT(min <= max);
+ if (max < 0) {
+ return max;
+ }
+ if (min > 0) {
+ return min;
+ }
+ MOZ_ASSERT(min <= 0 && max >= 0);
+ return 0;
+}
+
+// Returns the magnitude of the part of the range [min, max] that is greater
+// than zero. The return value is always non-negative.
+static nscoord PositivePart(nscoord min, nscoord max) {
+ MOZ_ASSERT(min <= max);
+ if (min >= 0) {
+ return max - min;
+ }
+ if (max > 0) {
+ return max;
+ }
+ return 0;
+}
+
+// Returns the magnitude of the part of the range [min, max] that is less
+// than zero. The return value is always non-negative.
+static nscoord NegativePart(nscoord min, nscoord max) {
+ MOZ_ASSERT(min <= max);
+ if (max <= 0) {
+ return max - min;
+ }
+ if (min < 0) {
+ return 0 - min;
+ }
+ return 0;
+}
+
+Maybe<wr::WrSpatialId> ClipManager::DefineStickyNode(
+ nsDisplayListBuilder* aBuilder, Maybe<wr::WrSpatialId> aParentSpatialId,
+ const ActiveScrolledRoot* aASR, nsDisplayItem* aItem) {
+ nsIFrame* stickyFrame = aASR->mFrame;
+ nsDisplayStickyPosition* stickyItem = FindStickyPositionItem(aASR, aItem);
+ MOZ_ASSERT(stickyItem->Frame() == stickyFrame);
+
+ // Do not create a spatial node for sticky items with mShouldFlatten=true.
+ // These are inside inactive scroll frames and so cannot move asynchronously.
+ if (stickyItem->ShouldFlattenAway(aBuilder)) {
+ return Nothing();
+ }
+
+ if (Maybe<wr::WrSpatialId> space =
+ mBuilder->GetSpatialIdForDefinedStickyLayer(aASR)) {
+ return space;
+ }
+
+ StickyScrollContainer* stickyScrollContainer = GetStickyScrollContainer(aASR);
+ if (!stickyScrollContainer) {
+ // This may indicated a sticky item that does not need a webrender spatial
+ // node. See the comment in GetStickyScrollContainer for details.
+ return Nothing();
+ }
+
+ float auPerDevPixel = stickyFrame->PresContext()->AppUnitsPerDevPixel();
+
+ bool snap;
+ nsRect itemBounds = stickyItem->GetBounds(aBuilder, &snap);
+
+ Maybe<float> topMargin;
+ Maybe<float> rightMargin;
+ Maybe<float> bottomMargin;
+ Maybe<float> leftMargin;
+ wr::StickyOffsetBounds vBounds = {0.0, 0.0};
+ wr::StickyOffsetBounds hBounds = {0.0, 0.0};
+ nsPoint appliedOffset;
+
+ nsRectAbsolute outer;
+ nsRectAbsolute inner;
+ stickyScrollContainer->GetScrollRanges(stickyFrame, &outer, &inner);
+
+ nsPoint offset =
+ stickyScrollContainer->ScrollContainer()->GetOffsetToCrossDoc(
+ stickyFrame) +
+ stickyItem->ToReferenceFrame();
+
+ // Adjust the scrollPort coordinates to be relative to the reference frame,
+ // so that it is in the same space as everything else.
+ nsRect scrollPort =
+ stickyScrollContainer->ScrollContainer()->GetScrollPortRect();
+ scrollPort += offset;
+
+ // The following computations make more sense upon understanding the
+ // semantics of "inner" and "outer", which is explained in the comment on
+ // SetStickyPositionData in Layers.h.
+
+ if (outer.YMost() != inner.YMost()) {
+ // Question: How far will itemBounds.y be from the top of the scrollport
+ // when we have scrolled from the current scroll position of "0" to
+ // reach the range [inner.YMost(), outer.YMost()] where the item gets
+ // stuck?
+ // Answer: the current distance is "itemBounds.y - scrollPort.y". That
+ // needs to be adjusted by the distance to the range, less any other
+ // sticky ranges that fall between 0 and the range. If the distance is
+ // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
+ // scrolling upwards (decreasing scroll offset) to reach that range,
+ // which would increase itemBounds.y and make it farther away from the
+ // top of the scrollport. So in that case the adjustment is -distance.
+ // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
+ // we would be scrolling downwards, itemBounds.y would decrease, and we
+ // again need to adjust by -distance. If we are already in the range
+ // then no adjustment is needed and distance is 0 so again using
+ // -distance works. If the distance is positive, and the item has both
+ // top and bottom sticky ranges, then the bottom sticky range may fall
+ // (entirely[1] or partly[2]) between the current scroll position.
+ // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
+ // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
+ // In these cases, the item doesn't actually move for that part of the
+ // distance, so we need to subtract out that bit, which can be computed
+ // as the positive portion of the range [outer.Y(), inner.Y()].
+ nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
+ if (distance > 0) {
+ distance -= PositivePart(outer.Y(), inner.Y());
+ }
+ topMargin = Some(NSAppUnitsToFloatPixels(
+ itemBounds.y - scrollPort.y - distance, auPerDevPixel));
+ // Question: What is the maximum positive ("downward") offset that WR
+ // will have to apply to this item in order to prevent the item from
+ // visually moving?
+ // Answer: Since the item is "sticky" in the range [inner.YMost(),
+ // outer.YMost()], the maximum offset will be the size of the range, which
+ // is outer.YMost() - inner.YMost().
+ vBounds.max =
+ NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
+ // Question: how much of an offset has layout already applied to the item?
+ // Answer: if we are
+ // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
+ // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
+ // then layout has already applied some offset to the position of the
+ // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
+ // and |outer.YMost() - inner.YMost()| in case (b).
+ if (inner.YMost() < 0) {
+ appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
+ MOZ_ASSERT(appliedOffset.y > 0);
+ }
+ }
+ if (outer.Y() != inner.Y()) {
+ // Similar logic as in the previous section, but this time we care about
+ // the distance from itemBounds.YMost() to scrollPort.YMost().
+ nscoord distance = DistanceToRange(outer.Y(), inner.Y());
+ if (distance < 0) {
+ distance += NegativePart(inner.YMost(), outer.YMost());
+ }
+ bottomMargin = Some(NSAppUnitsToFloatPixels(
+ scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
+ // And here WR will be moving the item upwards rather than downwards so
+ // again things are inverted from the previous block.
+ vBounds.min = NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
+ // We can't have appliedOffset be both positive and negative, and the top
+ // adjustment takes priority. So here we only update appliedOffset.y if
+ // it wasn't set by the top-sticky case above.
+ if (appliedOffset.y == 0 && inner.Y() > 0) {
+ appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
+ MOZ_ASSERT(appliedOffset.y < 0);
+ }
+ }
+ // Same as above, but for the x-axis
+ if (outer.XMost() != inner.XMost()) {
+ nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
+ if (distance > 0) {
+ distance -= PositivePart(outer.X(), inner.X());
+ }
+ leftMargin = Some(NSAppUnitsToFloatPixels(
+ itemBounds.x - scrollPort.x - distance, auPerDevPixel));
+ hBounds.max =
+ NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
+ if (inner.XMost() < 0) {
+ appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
+ MOZ_ASSERT(appliedOffset.x > 0);
+ }
+ }
+ if (outer.X() != inner.X()) {
+ nscoord distance = DistanceToRange(outer.X(), inner.X());
+ if (distance < 0) {
+ distance += NegativePart(inner.XMost(), outer.XMost());
+ }
+ rightMargin = Some(NSAppUnitsToFloatPixels(
+ scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
+ hBounds.min = NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
+ if (appliedOffset.x == 0 && inner.X() > 0) {
+ appliedOffset.x = std::max(0, outer.X()) - inner.X();
+ MOZ_ASSERT(appliedOffset.x < 0);
+ }
+ }
+
+ LayoutDeviceRect bounds =
+ LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
+ wr::LayoutVector2D applied = {
+ NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
+ NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
+ bool needsProp = stickyItem->ShouldGetStickyAnimationId();
+ Maybe<wr::WrAnimationProperty> prop;
+ auto spatialKey =
+ wr::SpatialKey(uint64_t(stickyFrame), stickyItem->GetPerFrameKey(),
+ wr::SpatialKeyKind::Sticky);
+ if (needsProp) {
+ RefPtr<WebRenderAPZAnimationData> animationData =
+ mManager->CommandBuilder()
+ .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(
+ stickyItem);
+ uint64_t animationId = animationData->GetAnimationId();
+
+ prop.emplace();
+ prop->id = animationId;
+ prop->key = spatialKey;
+ prop->effect_type = wr::WrAnimationType::Transform;
+ }
+ wr::WrSpatialId spatialId = mBuilder->DefineStickyFrame(
+ aASR, aParentSpatialId, wr::ToLayoutRect(bounds),
+ topMargin.ptrOr(nullptr), rightMargin.ptrOr(nullptr),
+ bottomMargin.ptrOr(nullptr), leftMargin.ptrOr(nullptr), vBounds, hBounds,
+ applied, spatialKey, prop.ptrOr(nullptr));
+
+ return Some(spatialId);
+}
+
+Maybe<wr::WrSpatialId> ClipManager::DefineSpatialNodes(
+ nsDisplayListBuilder* aBuilder, const ActiveScrolledRoot* aASR,
+ nsDisplayItem* aItem) {
if (!aASR) {
// Recursion base case
return Nothing();
@@ -328,11 +589,12 @@ Maybe<wr::WrSpatialId> ClipManager::DefineScrollLayers(
// Recurse to define the ancestors
Maybe<wr::WrSpatialId> ancestorSpace =
- DefineScrollLayers(aASR->mParent, aItem);
+ DefineSpatialNodes(aBuilder, aASR->mParent, aItem);
if (aASR->mKind == ActiveScrolledRoot::ASRKind::Sticky) {
- // TODO: Handle.
- return ancestorSpace;
+ Maybe<wr::WrSpatialId> parent = ancestorSpace.map(
+ [this](wr::WrSpatialId& aId) { return SpatialIdAfterOverride(aId); });
+ return ClipManager::DefineStickyNode(aBuilder, parent, aASR, aItem);
}
MOZ_ASSERT(viewId != ScrollableLayerGuid::NULL_SCROLL_ID);
diff --git a/gfx/layers/wr/ClipManager.h b/gfx/layers/wr/ClipManager.h
@@ -12,6 +12,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/webrender/WebRenderAPI.h"
+#include "mozilla/layout/StickyScrollContainer.h"
namespace mozilla {
@@ -71,8 +72,14 @@ class ClipManager {
wr::WrSpatialId SpatialIdAfterOverride(const wr::WrSpatialId& aSpatialId);
wr::WrSpatialId GetSpatialId(const ActiveScrolledRoot* aASR);
- Maybe<wr::WrSpatialId> DefineScrollLayers(const ActiveScrolledRoot* aASR,
+ static StickyScrollContainer* GetStickyScrollContainer(
+ const ActiveScrolledRoot* aASR);
+ Maybe<wr::WrSpatialId> DefineSpatialNodes(nsDisplayListBuilder* aBuilder,
+ const ActiveScrolledRoot* aASR,
nsDisplayItem* aItem);
+ Maybe<wr::WrSpatialId> DefineStickyNode(
+ nsDisplayListBuilder* aBuilder, Maybe<wr::WrSpatialId> aParentSpatialId,
+ const ActiveScrolledRoot* aASR, nsDisplayItem* aItem);
Maybe<wr::WrClipChainId> DefineClipChain(const DisplayItemClipChain* aChain,
int32_t aAppUnitsPerDevPixel);
diff --git a/layout/generic/moz.build b/layout/generic/moz.build
@@ -163,6 +163,7 @@ EXPORTS.mozilla += [
EXPORTS.mozilla.layout += [
"ScrollAnchorContainer.h",
+ "StickyScrollContainer.h",
]
UNIFIED_SOURCES += [
diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp
@@ -5817,47 +5817,6 @@ nsDisplayStickyPosition::nsDisplayStickyPosition(
MOZ_COUNT_CTOR(nsDisplayStickyPosition);
}
-// Returns the smallest distance from "0" to the range [min, max] where
-// min <= max. Despite the name, the return value is actually a 1-D vector,
-// and so may be negative if max < 0.
-static nscoord DistanceToRange(nscoord min, nscoord max) {
- MOZ_ASSERT(min <= max);
- if (max < 0) {
- return max;
- }
- if (min > 0) {
- return min;
- }
- MOZ_ASSERT(min <= 0 && max >= 0);
- return 0;
-}
-
-// Returns the magnitude of the part of the range [min, max] that is greater
-// than zero. The return value is always non-negative.
-static nscoord PositivePart(nscoord min, nscoord max) {
- MOZ_ASSERT(min <= max);
- if (min >= 0) {
- return max - min;
- }
- if (max > 0) {
- return max;
- }
- return 0;
-}
-
-// Returns the magnitude of the part of the range [min, max] that is less
-// than zero. The return value is always non-negative.
-static nscoord NegativePart(nscoord min, nscoord max) {
- MOZ_ASSERT(min <= max);
- if (max <= 0) {
- return max - min;
- }
- if (min < 0) {
- return 0 - min;
- }
- return 0;
-}
-
StickyScrollContainer* nsDisplayStickyPosition::GetStickyScrollContainer() {
auto* ssc = StickyScrollContainer::GetOrCreateForFrame(mFrame);
if (!ssc) {
@@ -5890,165 +5849,6 @@ bool nsDisplayStickyPosition::CreateWebRenderCommands(
Maybe<wr::SpaceAndClipChainHelper> saccHelper;
if (stickyScrollContainer && !stickyScrollContainer->ShouldFlattenAway()) {
- float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
-
- bool snap;
- nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
-
- Maybe<float> topMargin;
- Maybe<float> rightMargin;
- Maybe<float> bottomMargin;
- Maybe<float> leftMargin;
- wr::StickyOffsetBounds vBounds = {0.0, 0.0};
- wr::StickyOffsetBounds hBounds = {0.0, 0.0};
- nsPoint appliedOffset;
-
- nsRectAbsolute outer;
- nsRectAbsolute inner;
- stickyScrollContainer->GetScrollRanges(mFrame, &outer, &inner);
-
- nsPoint offset =
- stickyScrollContainer->ScrollContainer()->GetOffsetToCrossDoc(Frame()) +
- ToReferenceFrame();
-
- // Adjust the scrollPort coordinates to be relative to the reference frame,
- // so that it is in the same space as everything else.
- nsRect scrollPort =
- stickyScrollContainer->ScrollContainer()->GetScrollPortRect();
- scrollPort += offset;
-
- // The following computations make more sense upon understanding the
- // semantics of "inner" and "outer", which is explained in the comment on
- // SetStickyPositionData in Layers.h.
-
- if (outer.YMost() != inner.YMost()) {
- // Question: How far will itemBounds.y be from the top of the scrollport
- // when we have scrolled from the current scroll position of "0" to
- // reach the range [inner.YMost(), outer.YMost()] where the item gets
- // stuck?
- // Answer: the current distance is "itemBounds.y - scrollPort.y". That
- // needs to be adjusted by the distance to the range, less any other
- // sticky ranges that fall between 0 and the range. If the distance is
- // negative (i.e. inner.YMost() <= outer.YMost() < 0) then we would be
- // scrolling upwards (decreasing scroll offset) to reach that range,
- // which would increase itemBounds.y and make it farther away from the
- // top of the scrollport. So in that case the adjustment is -distance.
- // If the distance is positive (0 < inner.YMost() <= outer.YMost()) then
- // we would be scrolling downwards, itemBounds.y would decrease, and we
- // again need to adjust by -distance. If we are already in the range
- // then no adjustment is needed and distance is 0 so again using
- // -distance works. If the distance is positive, and the item has both
- // top and bottom sticky ranges, then the bottom sticky range may fall
- // (entirely[1] or partly[2]) between the current scroll position.
- // [1]: 0 <= outer.Y() <= inner.Y() < inner.YMost() <= outer.YMost()
- // [2]: outer.Y() < 0 <= inner.Y() < inner.YMost() <= outer.YMost()
- // In these cases, the item doesn't actually move for that part of the
- // distance, so we need to subtract out that bit, which can be computed
- // as the positive portion of the range [outer.Y(), inner.Y()].
- nscoord distance = DistanceToRange(inner.YMost(), outer.YMost());
- if (distance > 0) {
- distance -= PositivePart(outer.Y(), inner.Y());
- }
- topMargin = Some(NSAppUnitsToFloatPixels(
- itemBounds.y - scrollPort.y - distance, auPerDevPixel));
- // Question: What is the maximum positive ("downward") offset that WR
- // will have to apply to this item in order to prevent the item from
- // visually moving?
- // Answer: Since the item is "sticky" in the range [inner.YMost(),
- // outer.YMost()], the maximum offset will be the size of the range, which
- // is outer.YMost() - inner.YMost().
- vBounds.max =
- NSAppUnitsToFloatPixels(outer.YMost() - inner.YMost(), auPerDevPixel);
- // Question: how much of an offset has layout already applied to the item?
- // Answer: if we are
- // (a) inside the sticky range (inner.YMost() < 0 <= outer.YMost()), or
- // (b) past the sticky range (inner.YMost() < outer.YMost() < 0)
- // then layout has already applied some offset to the position of the
- // item. The amount of the adjustment is |0 - inner.YMost()| in case (a)
- // and |outer.YMost() - inner.YMost()| in case (b).
- if (inner.YMost() < 0) {
- appliedOffset.y = std::min(0, outer.YMost()) - inner.YMost();
- MOZ_ASSERT(appliedOffset.y > 0);
- }
- }
- if (outer.Y() != inner.Y()) {
- // Similar logic as in the previous section, but this time we care about
- // the distance from itemBounds.YMost() to scrollPort.YMost().
- nscoord distance = DistanceToRange(outer.Y(), inner.Y());
- if (distance < 0) {
- distance += NegativePart(inner.YMost(), outer.YMost());
- }
- bottomMargin = Some(NSAppUnitsToFloatPixels(
- scrollPort.YMost() - itemBounds.YMost() + distance, auPerDevPixel));
- // And here WR will be moving the item upwards rather than downwards so
- // again things are inverted from the previous block.
- vBounds.min =
- NSAppUnitsToFloatPixels(outer.Y() - inner.Y(), auPerDevPixel);
- // We can't have appliedOffset be both positive and negative, and the top
- // adjustment takes priority. So here we only update appliedOffset.y if
- // it wasn't set by the top-sticky case above.
- if (appliedOffset.y == 0 && inner.Y() > 0) {
- appliedOffset.y = std::max(0, outer.Y()) - inner.Y();
- MOZ_ASSERT(appliedOffset.y < 0);
- }
- }
- // Same as above, but for the x-axis
- if (outer.XMost() != inner.XMost()) {
- nscoord distance = DistanceToRange(inner.XMost(), outer.XMost());
- if (distance > 0) {
- distance -= PositivePart(outer.X(), inner.X());
- }
- leftMargin = Some(NSAppUnitsToFloatPixels(
- itemBounds.x - scrollPort.x - distance, auPerDevPixel));
- hBounds.max =
- NSAppUnitsToFloatPixels(outer.XMost() - inner.XMost(), auPerDevPixel);
- if (inner.XMost() < 0) {
- appliedOffset.x = std::min(0, outer.XMost()) - inner.XMost();
- MOZ_ASSERT(appliedOffset.x > 0);
- }
- }
- if (outer.X() != inner.X()) {
- nscoord distance = DistanceToRange(outer.X(), inner.X());
- if (distance < 0) {
- distance += NegativePart(inner.XMost(), outer.XMost());
- }
- rightMargin = Some(NSAppUnitsToFloatPixels(
- scrollPort.XMost() - itemBounds.XMost() + distance, auPerDevPixel));
- hBounds.min =
- NSAppUnitsToFloatPixels(outer.X() - inner.X(), auPerDevPixel);
- if (appliedOffset.x == 0 && inner.X() > 0) {
- appliedOffset.x = std::max(0, outer.X()) - inner.X();
- MOZ_ASSERT(appliedOffset.x < 0);
- }
- }
-
- LayoutDeviceRect bounds =
- LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
- wr::LayoutVector2D applied = {
- NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
- NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
- bool needsProp = ShouldGetStickyAnimationId();
- Maybe<wr::WrAnimationProperty> prop;
- auto spatialKey = wr::SpatialKey(uint64_t(mFrame), GetPerFrameKey(),
- wr::SpatialKeyKind::Sticky);
- if (needsProp) {
- RefPtr<WebRenderAPZAnimationData> animationData =
- aManager->CommandBuilder()
- .CreateOrRecycleWebRenderUserData<WebRenderAPZAnimationData>(
- this);
- mWrStickyAnimationId = animationData->GetAnimationId();
-
- prop.emplace();
- prop->id = mWrStickyAnimationId;
- prop->key = spatialKey;
- prop->effect_type = wr::WrAnimationType::Transform;
- }
- aBuilder.DefineStickyFrame(
- mStickyASR, Nothing(), wr::ToLayoutRect(bounds),
- topMargin.ptrOr(nullptr), rightMargin.ptrOr(nullptr),
- bottomMargin.ptrOr(nullptr), leftMargin.ptrOr(nullptr), vBounds,
- hBounds, applied, spatialKey, prop.ptrOr(nullptr));
-
const ActiveScrolledRoot* stickyAsr =
ActiveScrolledRoot::GetStickyASRFromFrame(mFrame);
MOZ_ASSERT(stickyAsr);