commit b63f6bdd581f7b07696a01d03b0a1d7f9390d944
parent c8a9b2340a5e4a066a18ee11d9773bd47c46d087
Author: Botond Ballo <botond@mozilla.com>
Date: Thu, 13 Nov 2025 04:59:01 +0000
Bug 1730749 - Introduce a new kind of ASR for sticky content. r=mstange
No one creates ASRs of this kind yet (that will happen in a later patch).
Consumer code is adjusted to check for the ASR kind as appropriate.
To make this easier, two helper functions GetNearestScrollASR()
and GetNearestScrollASRViewId() are added.
Differential Revision: https://phabricator.services.mozilla.com/D254141
Diffstat:
10 files changed, 164 insertions(+), 37 deletions(-)
diff --git a/gfx/layers/AnimationInfo.cpp b/gfx/layers/AnimationInfo.cpp
@@ -723,11 +723,13 @@ static PartialPrerenderData GetPartialPrerenderData(
nsLayoutUtils::AsyncPanZoomEnabled(aFrame)) {
const bool isInPositionFixed =
nsLayoutUtils::IsInPositionFixedSubtree(aFrame);
- const ActiveScrolledRoot* asr = aItem->GetActiveScrolledRoot();
+ // We need to find asynchronously scrollable ASRs, therefore we should
+ // ignore ASRs for pos:sticky display items.
+ const ActiveScrolledRoot* asr = aItem->GetNearestScrollASR();
if (!isInPositionFixed && asr &&
- aFrame->PresContext() == asr->mScrollContainerFrame->PresContext()) {
+ aFrame->PresContext() == asr->ScrollFrame()->PresContext()) {
scrollId = asr->GetViewId();
- MOZ_ASSERT(clipFrame == asr->mScrollContainerFrame);
+ MOZ_ASSERT(clipFrame == asr->ScrollFrame());
} else {
// Use the root scroll id in the same document if the target frame is in
// position:fixed subtree or there is no ASR or the ASR is in a different
diff --git a/gfx/layers/wr/ClipManager.cpp b/gfx/layers/wr/ClipManager.cpp
@@ -310,18 +310,30 @@ Maybe<wr::WrSpatialId> ClipManager::DefineScrollLayers(
// Recursion base case
return Nothing();
}
- ScrollableLayerGuid::ViewID viewId = aASR->GetViewId();
- Maybe<wr::WrSpatialId> space =
- mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
- if (space) {
- // If we've already defined this scroll layer before, we can early-exit
- return space;
+
+ ScrollableLayerGuid::ViewID viewId = ScrollableLayerGuid::NULL_SCROLL_ID;
+ if (aASR->mKind == ActiveScrolledRoot::ASRKind::Scroll) {
+ viewId = aASR->GetViewId();
+ Maybe<wr::WrSpatialId> space =
+ mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
+ if (space) {
+ // If we've already defined this scroll layer before, we can early-exit
+ return space;
+ }
}
+
// Recurse to define the ancestors
Maybe<wr::WrSpatialId> ancestorSpace =
DefineScrollLayers(aASR->mParent, aItem);
- ScrollContainerFrame* scrollContainerFrame = aASR->mScrollContainerFrame;
+ if (aASR->mKind == ActiveScrolledRoot::ASRKind::Sticky) {
+ // TODO: Handle.
+ return ancestorSpace;
+ }
+
+ MOZ_ASSERT(viewId != ScrollableLayerGuid::NULL_SCROLL_ID);
+
+ ScrollContainerFrame* scrollContainerFrame = aASR->ScrollFrame();
Maybe<ScrollMetadata> metadata = scrollContainerFrame->ComputeScrollMetadata(
mManager, aItem->Frame(), aItem->ToReferenceFrame());
if (!metadata) {
@@ -402,7 +414,7 @@ Maybe<wr::WrClipChainId> ClipManager::DefineClipChain(
for (const DisplayItemClipChain* chain = aChain; chain;
chain = chain->mParent) {
MOZ_DIAGNOSTIC_ASSERT(chain->mOnStack || !chain->mASR ||
- chain->mASR->mScrollContainerFrame);
+ chain->mASR->mFrame);
if (!chain->mClip.HasClip()) {
// This item in the chain is a no-op, skip over it
diff --git a/gfx/layers/wr/WebRenderCommandBuilder.cpp b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1699,7 +1699,7 @@ void WebRenderCommandBuilder::DoGroupingForDisplayList(
ScrollableLayerGuid::ViewID scrollId = ScrollableLayerGuid::NULL_SCROLL_ID;
if (const ActiveScrolledRoot* asr = aWrappingItem->GetActiveScrolledRoot()) {
- scrollId = asr->GetViewId();
+ scrollId = asr->GetNearestScrollASRViewId();
}
g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
@@ -1916,7 +1916,7 @@ struct NewLayerData {
}
if (mDeferredItem) {
if (const auto* asr = mDeferredItem->GetActiveScrolledRoot()) {
- mDeferredId = asr->GetViewId();
+ mDeferredId = asr->GetNearestScrollASRViewId();
}
if (mDeferredItem->GetActiveScrolledRoot() !=
aItem->GetActiveScrolledRoot()) {
diff --git a/gfx/layers/wr/WebRenderScrollData.cpp b/gfx/layers/wr/WebRenderScrollData.cpp
@@ -65,15 +65,18 @@ void WebRenderLayerScrollData::Initialize(
while (asr && asr != aStopAtAsr) {
MOZ_ASSERT(aOwner.GetManager());
+ if (asr->mKind != ActiveScrolledRoot::ASRKind::Scroll) {
+ asr = asr->mParent;
+ continue;
+ }
ScrollableLayerGuid::ViewID scrollId = asr->GetViewId();
if (Maybe<size_t> index = aOwner.HasMetadataFor(scrollId)) {
mScrollIds.AppendElement(index.ref());
} else {
Maybe<ScrollMetadata> metadata =
- asr->mScrollContainerFrame->ComputeScrollMetadata(
+ asr->ScrollFrame()->ComputeScrollMetadata(
aOwner.GetManager(), aItem->Frame(), aItem->ToReferenceFrame());
- aOwner.GetBuilder()->AddScrollContainerFrameToNotify(
- asr->mScrollContainerFrame);
+ aOwner.GetBuilder()->AddScrollContainerFrameToNotify(asr->ScrollFrame());
if (metadata) {
MOZ_ASSERT(metadata->GetMetrics().GetScrollId() == scrollId);
mScrollIds.AppendElement(aOwner.AddMetadata(metadata.ref()));
diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp
@@ -3917,7 +3917,9 @@ void nsIFrame::BuildDisplayListForStackingContext(
// inhibits paint skipping).
if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyASR) {
aBuilder->GetFilterASR()
- ->mScrollContainerFrame->SetHasOutOfFlowContentInsideFilter();
+ ->GetNearestScrollASR()
+ ->ScrollFrame()
+ ->SetHasOutOfFlowContentInsideFilter();
}
}
diff --git a/layout/painting/DisplayItemClipChain.cpp b/layout/painting/DisplayItemClipChain.cpp
@@ -68,8 +68,8 @@ nsCString DisplayItemClipChain::ToString(
nsAutoCString str;
for (auto* sc = aClipChain; sc; sc = sc->mParent) {
if (sc->mASR) {
- str.AppendPrintf("0x%p <%s> [0x%p]", sc, sc->mClip.ToString().get(),
- sc->mASR->mScrollContainerFrame);
+ str.AppendPrintf("0x%p <%s> %s", sc, sc->mClip.ToString().get(),
+ ActiveScrolledRoot::ToString(sc->mASR).get());
} else {
str.AppendPrintf("0x%p <%s> [root asr]", sc, sc->mClip.ToString().get());
}
diff --git a/layout/painting/HitTestInfo.cpp b/layout/painting/HitTestInfo.cpp
@@ -42,8 +42,10 @@ ViewID HitTestInfo::GetViewId(wr::DisplayListBuilder& aBuilder,
return *fixedTarget;
}
+ // If the input ASR is non-null and we have a parent scroll ASR, return the
+ // view id from that ASR.
if (aASR) {
- return aASR->GetViewId();
+ return aASR->GetNearestScrollASRViewId();
}
return layers::ScrollableLayerGuid::NULL_SCROLL_ID;
diff --git a/layout/painting/RetainedDisplayListBuilder.cpp b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -300,12 +300,11 @@ bool RetainedDisplayListBuilder::PreProcessDisplayList(
// so we have to work around it. Bug 1730749 and bug 1730826 should resolve
// this.
nsIFrame* agrFrame = nullptr;
- if (aAsyncAncestorASR == item->GetActiveScrolledRoot() ||
- !item->GetActiveScrolledRoot()) {
+ const ActiveScrolledRoot* asr = item->GetNearestScrollASR();
+ if (aAsyncAncestorASR == asr || !asr) {
agrFrame = aAsyncAncestor;
} else {
- auto* scrollContainerFrame =
- item->GetActiveScrolledRoot()->mScrollContainerFrame;
+ auto* scrollContainerFrame = asr->ScrollFrame();
if (MOZ_UNLIKELY(!scrollContainerFrame)) {
MOZ_DIAGNOSTIC_ASSERT(false);
gfxCriticalNoteOnce << "Found null mScrollContainerFrame in asr";
@@ -368,7 +367,7 @@ static Maybe<const ActiveScrolledRoot*> SelectContainerASR(
aClipChain ? aClipChain->mASR : nullptr;
MOZ_DIAGNOSTIC_ASSERT(!aClipChain || aClipChain->mOnStack || !itemClipASR ||
- itemClipASR->mScrollContainerFrame);
+ itemClipASR->mFrame);
const ActiveScrolledRoot* finiteBoundsASR =
ActiveScrolledRoot::PickDescendant(itemClipASR, aItemASR);
diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp
@@ -185,7 +185,36 @@ already_AddRefed<ActiveScrolledRoot> ActiveScrolledRoot::CreateASRForFrame(
}
}
asr->mParent = aParent;
- asr->mScrollContainerFrame = aScrollContainerFrame;
+ asr->mFrame = aScrollContainerFrame;
+ asr->mKind = ASRKind::Scroll;
+ asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
+ asr->mRetained = aIsRetained;
+
+ return asr.forget();
+}
+
+/* static */
+already_AddRefed<ActiveScrolledRoot>
+ActiveScrolledRoot::CreateASRForStickyFrame(const ActiveScrolledRoot* aParent,
+ nsIFrame* aStickyFrame,
+ bool aIsRetained) {
+ RefPtr<ActiveScrolledRoot> asr;
+ if (aIsRetained) {
+ asr = aStickyFrame->GetProperty(ActiveScrolledRootCache());
+ }
+
+ if (!asr) {
+ asr = new ActiveScrolledRoot();
+
+ if (aIsRetained) {
+ RefPtr<ActiveScrolledRoot> ref = asr;
+ aStickyFrame->SetProperty(ActiveScrolledRootCache(), ref.forget().take());
+ }
+ }
+
+ asr->mParent = aParent;
+ asr->mFrame = aStickyFrame;
+ asr->mKind = ASRKind::Sticky;
asr->mDepth = aParent ? aParent->mDepth + 1 : 1;
asr->mRetained = aIsRetained;
@@ -219,12 +248,52 @@ bool ActiveScrolledRoot::IsProperAncestor(
return aAncestor != aDescendant && IsAncestor(aAncestor, aDescendant);
}
+ScrollContainerFrame* ActiveScrolledRoot::ScrollFrameOrNull() const {
+ if (mKind == ASRKind::Scroll) {
+ ScrollContainerFrame* scrollFrame =
+ static_cast<ScrollContainerFrame*>(mFrame);
+ MOZ_ASSERT(scrollFrame);
+ return scrollFrame;
+ }
+ return nullptr;
+}
+
+const ActiveScrolledRoot* ActiveScrolledRoot::GetNearestScrollASR() const {
+ const ActiveScrolledRoot* ret = this;
+
+ while (ret && ret->mKind != ASRKind::Scroll) {
+ ret = ret->mParent;
+ }
+
+ if (!ret || ret->mKind != ASRKind::Scroll) {
+ return nullptr;
+ }
+
+ return ret;
+}
+
+layers::ScrollableLayerGuid::ViewID
+ActiveScrolledRoot::GetNearestScrollASRViewId() const {
+ const ActiveScrolledRoot* scrollASR = GetNearestScrollASR();
+ if (scrollASR) {
+ return scrollASR->GetViewId();
+ }
+ return ScrollableLayerGuid::NULL_SCROLL_ID;
+}
+
/* static */
nsCString ActiveScrolledRoot::ToString(
const ActiveScrolledRoot* aActiveScrolledRoot) {
nsAutoCString str;
+ if (!aActiveScrolledRoot) {
+ str.AppendPrintf("null");
+ return str;
+ }
+ if (aActiveScrolledRoot->mKind == ASRKind::Sticky) {
+ str.AppendPrintf("sticky ");
+ }
for (const auto* asr = aActiveScrolledRoot; asr; asr = asr->mParent) {
- str.AppendPrintf("<0x%p>", asr->mScrollContainerFrame);
+ str.AppendPrintf("<0x%p>", asr->mFrame);
if (asr->mParent) {
str.AppendLiteral(", ");
}
@@ -233,13 +302,16 @@ nsCString ActiveScrolledRoot::ToString(
}
ScrollableLayerGuid::ViewID ActiveScrolledRoot::ComputeViewId() const {
- nsIContent* content = mScrollContainerFrame->GetScrolledFrame()->GetContent();
+ const ActiveScrolledRoot* scrollASR = GetNearestScrollASR();
+ MOZ_ASSERT(scrollASR,
+ "ComputeViewId() called on ASR with no enclosing scroll frame");
+ nsIContent* content = scrollASR->ScrollFrame()->GetContent();
return nsLayoutUtils::FindOrCreateIDFor(content);
}
ActiveScrolledRoot::~ActiveScrolledRoot() {
- if (mScrollContainerFrame && mRetained) {
- mScrollContainerFrame->RemoveProperty(ActiveScrolledRootCache());
+ if (mFrame && mRetained) {
+ mFrame->RemoveProperty(ActiveScrolledRootCache());
}
}
@@ -454,7 +526,9 @@ void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::
aActiveScrolledRoot, mBuilder->mFilterASR)) {
for (const ActiveScrolledRoot* asr = mBuilder->mFilterASR;
asr && asr != aActiveScrolledRoot; asr = asr->mParent) {
- asr->mScrollContainerFrame->SetHasOutOfFlowContentInsideFilter();
+ if (ScrollContainerFrame* scrollFrame = asr->ScrollFrameOrNull()) {
+ scrollFrame->SetHasOutOfFlowContentInsideFilter();
+ }
}
}
@@ -2627,6 +2701,14 @@ nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
void nsDisplayItem::SetDeletedFrame() { mItemFlags += ItemFlag::DeletedFrame; }
+const ActiveScrolledRoot* nsDisplayItem::GetNearestScrollASR() const {
+ const ActiveScrolledRoot* asr = GetActiveScrolledRoot();
+ if (asr) {
+ return asr->GetNearestScrollASR();
+ }
+ return nullptr;
+}
+
bool nsDisplayItem::HasDeletedFrame() const {
bool retval = mItemFlags.contains(ItemFlag::DeletedFrame) ||
(GetType() == DisplayItemType::TYPE_REMOTE &&
@@ -7675,10 +7757,12 @@ bool nsDisplayPerspective::CreateWebRenderCommands(
// In OOP documents, the root scrollable frame of the in-process root
// document is always active, so using IsAncestorFrameCrossDocInProcess
// should be fine here.
- if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
- asr->mScrollContainerFrame->GetScrolledFrame(), perspectiveFrame)) {
- scrollingRelativeTo.emplace(asr->GetViewId());
- break;
+ if (ScrollContainerFrame* scrollFrame = asr->ScrollFrameOrNull()) {
+ if (nsLayoutUtils::IsAncestorFrameCrossDocInProcess(
+ scrollFrame->GetScrolledFrame(), perspectiveFrame)) {
+ scrollingRelativeTo.emplace(asr->GetViewId());
+ break;
+ }
}
}
diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h
@@ -184,9 +184,13 @@ LazyLogModule& GetLoggerByProcess();
* is on the stack.
*/
struct ActiveScrolledRoot {
+ // TODO: Just have one function with an extra ASRKind parameter
static already_AddRefed<ActiveScrolledRoot> CreateASRForFrame(
const ActiveScrolledRoot* aParent,
ScrollContainerFrame* aScrollContainerFrame, bool aIsRetained);
+ static already_AddRefed<ActiveScrolledRoot> CreateASRForStickyFrame(
+ const ActiveScrolledRoot* aParent, nsIFrame* aStickyFrame,
+ bool aIsRetained);
static const ActiveScrolledRoot* PickAncestor(
const ActiveScrolledRoot* aOne, const ActiveScrolledRoot* aTwo) {
@@ -215,14 +219,32 @@ struct ActiveScrolledRoot {
* corresponding to the ASR.
*/
layers::ScrollableLayerGuid::ViewID GetViewId() const {
+ MOZ_ASSERT(mKind == ASRKind::Scroll);
if (!mViewId.isSome()) {
mViewId = Some(ComputeViewId());
}
return *mViewId;
}
+ ScrollContainerFrame* ScrollFrame() const {
+ MOZ_ASSERT(mKind == ASRKind::Scroll);
+ return ScrollFrameOrNull();
+ }
+
+ ScrollContainerFrame* ScrollFrameOrNull() const;
+
+ // Return the nearest ASR that is of ASR kind scroll.
+ const ActiveScrolledRoot* GetNearestScrollASR() const;
+
+ // Return the scrollable layer view id of the nearest scroll ASR, otherwise
+ // return the null scroll id.
+ layers::ScrollableLayerGuid::ViewID GetNearestScrollASRViewId() const;
+
+ enum class ASRKind { Root, Scroll, Sticky };
+
RefPtr<const ActiveScrolledRoot> mParent;
- ScrollContainerFrame* mScrollContainerFrame = nullptr;
+ nsIFrame* mFrame = nullptr;
+ ASRKind mKind = ASRKind::Root;
NS_INLINE_DECL_REFCOUNTING(ActiveScrolledRoot)
@@ -233,7 +255,7 @@ struct ActiveScrolledRoot {
static void DetachASR(ActiveScrolledRoot* aASR) {
aASR->mParent = nullptr;
- aASR->mScrollContainerFrame = nullptr;
+ aASR->mFrame = nullptr;
NS_RELEASE(aASR);
}
NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(ActiveScrolledRootCache,
@@ -2806,6 +2828,7 @@ class nsDisplayItem {
const ActiveScrolledRoot* GetActiveScrolledRoot() const {
return mActiveScrolledRoot;
}
+ const ActiveScrolledRoot* GetNearestScrollASR() const;
virtual void SetClipChain(const DisplayItemClipChain* aClipChain,
bool aStore);