tor-browser

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

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:
Mgfx/layers/AnimationInfo.cpp | 8+++++---
Mgfx/layers/wr/ClipManager.cpp | 28++++++++++++++++++++--------
Mgfx/layers/wr/WebRenderCommandBuilder.cpp | 4++--
Mgfx/layers/wr/WebRenderScrollData.cpp | 9++++++---
Mlayout/generic/nsIFrame.cpp | 4+++-
Mlayout/painting/DisplayItemClipChain.cpp | 4++--
Mlayout/painting/HitTestInfo.cpp | 4+++-
Mlayout/painting/RetainedDisplayListBuilder.cpp | 9++++-----
Mlayout/painting/nsDisplayList.cpp | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mlayout/painting/nsDisplayList.h | 27+++++++++++++++++++++++++--
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);