tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mgfx/layers/wr/ClipManager.cpp | 274+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mgfx/layers/wr/ClipManager.h | 9++++++++-
Mlayout/generic/moz.build | 1+
Mlayout/painting/nsDisplayList.cpp | 200-------------------------------------------------------------------------------
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);