From 1cb3c53e11039b75939c93c2cfde6c7958b5d033 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Tue, 24 May 2016 01:12:48 -0700 Subject: [PATCH] Bug 1263349 (Part 2) - Cache the current scroll parent and information about scrollports and displayports on nsDisplayListBuilder. r=botond,mstange --- layout/base/nsDisplayList.cpp | 203 +++++++++++++++++++++++++- layout/base/nsDisplayList.h | 133 +++++++++++++---- layout/base/nsLayoutUtils.cpp | 28 ---- layout/generic/nsFrame.cpp | 6 + layout/generic/nsGfxScrollFrame.cpp | 8 +- layout/generic/nsSubDocumentFrame.cpp | 9 +- 6 files changed, 319 insertions(+), 68 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index f3400ffdeb91..6cd4494ce8d2 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -670,6 +670,72 @@ nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer, aLayer, data, pending); } +nsDisplayListBuilder::AutoCurrentScrollParentIdSetter:: + AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, + nsIFrame* aScrollParent) + : mBuilder(aBuilder) + , mOldScrollParent(aBuilder->mCurrentScrollParent) + , mOldDisplayPortConsideringAncestors(aBuilder->mDisplayPortConsideringAncestors) + , mOldScrollPortConsideringAncestors(aBuilder->mScrollPortConsideringAncestors) + , mOldScrollParentId(aBuilder->mCurrentScrollParentId) + , mOldForceLayer(aBuilder->mForceLayerForScrollParent) + , mChangedSubtrees(false) +{ + aBuilder->UpdateCurrentScrollParent(aScrollParent); + Init(); +} + +nsDisplayListBuilder::AutoCurrentScrollParentIdSetter:: + AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, + const OutOfFlowDisplayData* aOutOfFlowData) + : mBuilder(aBuilder) + , mOldScrollParent(aBuilder->mCurrentScrollParent) + , mOldDisplayPortConsideringAncestors(aBuilder->mDisplayPortConsideringAncestors) + , mOldScrollPortConsideringAncestors(aBuilder->mScrollPortConsideringAncestors) + , mOldScrollParentId(aBuilder->mCurrentScrollParentId) + , mOldForceLayer(aBuilder->mForceLayerForScrollParent) + , mChangedSubtrees(true) +{ + aBuilder->UpdateCurrentScrollParentForOutOfFlow(aOutOfFlowData); + Init(); +} + +void +nsDisplayListBuilder::AutoCurrentScrollParentIdSetter::Init() +{ + // If this AutoCurrentScrollParentIdSetter has the same scrollId as the + // previous one on the stack, then that means the scrollframe that + // created this isn't actually scrollable and cannot participate in + // scroll handoff. We set mCanBeScrollParent to false to indicate this. + mCanBeScrollParent = mOldScrollParentId != mBuilder->mCurrentScrollParentId; + + mBuilder->mForceLayerForScrollParent = false; +} + +nsDisplayListBuilder::AutoCurrentScrollParentIdSetter:: + ~AutoCurrentScrollParentIdSetter() +{ + mBuilder->mCurrentScrollParent = mOldScrollParent; + mBuilder->mDisplayPortConsideringAncestors = mOldDisplayPortConsideringAncestors; + mBuilder->mScrollPortConsideringAncestors = mOldScrollPortConsideringAncestors; + mBuilder->mCurrentScrollParentId = mOldScrollParentId; + + if (mCanBeScrollParent || mChangedSubtrees) { + // If |mCanBeScrollParent| is set, caller code is responsible for + // having dealt with the current value of + // mBuilder->mForceLayerForScrollParent, so we can just restore the + // old value. If |mChangedSubtrees| is set, we don't propagate the + // flag because it doesn't make sense to propagate it through + // unrelated subtrees. + mBuilder->mForceLayerForScrollParent = mOldForceLayer; + } else { + // Otherwise we need to keep propagating the force-layerization flag + // upwards to the next ancestor scrollframe that does participate in + // scroll handoff. + mBuilder->mForceLayerForScrollParent |= mOldForceLayer; + } +} + nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, nsDisplayListBuilderMode aMode, bool aBuildCaret) : mReferenceFrame(aReferenceFrame), @@ -685,6 +751,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mGlassDisplayItem(nullptr), mScrollInfoItemsForHoisting(nullptr), mMode(aMode), + mCurrentScrollParent(nullptr), mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID), mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID), mCurrentScrollbarFlags(0), @@ -732,6 +799,9 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mFrameToAnimatedGeometryRootMap.Put(aReferenceFrame, &mRootAGR); + UpdateCurrentScrollParent( + nsLayoutUtils::GetAsyncScrollableProperAncestorFrameOrFallback(aReferenceFrame)); + nsCSSRendering::BeginFrameTreesLocked(); PR_STATIC_ASSERT(nsDisplayItem::TYPE_MAX < (1 << nsDisplayItem::TYPE_BITS)); } @@ -854,7 +924,12 @@ void nsDisplayListBuilder::MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, const DisplayItemClip* oldClip = mClipState.GetClipForContainingBlockDescendants(); const DisplayItemScrollClip* sc = mClipState.GetCurrentInnermostScrollClip(); - OutOfFlowDisplayData* data = new OutOfFlowDisplayData(oldClip, sc, dirty); + OutOfFlowDisplayData* data = + new OutOfFlowDisplayData(oldClip, sc, dirty, + mCurrentScrollParent, mCurrentScrollParentId, + mDisplayPortConsideringAncestors, + mScrollPortConsideringAncestors); + aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data); MarkFrameForDisplay(aFrame, aDirtyFrame); @@ -1221,6 +1296,132 @@ nsDisplayListBuilder::FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame) return cursor; } +void +nsDisplayListBuilder::UpdateCurrentScrollParent(nsIFrame* aScrollParent) +{ + // If the new scroll parent is the same as the old scroll parent, we don't + // have to do anything. This can happen because we sometimes instantiate an + // AutoCurrentScrollParentIdSetter unconditionally but only pass it a new + // scroll parent if certain conditions are true. + if (aScrollParent == mCurrentScrollParent) { + return; + } + + // Update the current scroll parent. + nsIFrame* oldScrollParent = mCurrentScrollParent; + mCurrentScrollParent = aScrollParent; + + // If there's no scroll parent now (which may be true because we're + // initializing this nsDisplayListBuilder for the first time, or because + // we're in a strange state where there's no pres shell or no root frame) + // then we just reset everything to its initial values. + if (!aScrollParent) { + mDisplayPortConsideringAncestors = nsRect(); + mScrollPortConsideringAncestors = nsRect(); + mCurrentScrollParentId = FrameMetrics::NULL_SCROLL_ID; + return; + } + + // Compute the new scroll parent ID. + nsIContent* content = aScrollParent->GetContent(); + mCurrentScrollParentId = content ? nsLayoutUtils::FindOrCreateIDFor(content) + : FrameMetrics::NULL_SCROLL_ID; + + nsIScrollableFrame* newScrollParent = do_QueryFrame(aScrollParent); + if (!newScrollParent) { + // There's no scrollable parent frame, so we've been forced to fall back + // to the root frame. (Most likely this is a XUL document.) Just use its + // size for both the displayport and the scrollport. + MOZ_ASSERT(!oldScrollParent, "Falling back to the root frame when an ancestor " + "scrollable frame existed?"); + nsRect frameRect(nsPoint(0, 0), aScrollParent->GetSize()); + mDisplayPortConsideringAncestors = frameRect; + mScrollPortConsideringAncestors = frameRect; + return; + } + + MOZ_ASSERT(mCurrentScrollParentId != FrameMetrics::NULL_SCROLL_ID, + "Couldn't get a scroll ID for a scrollable frame?"); + + // If we didn't have a scroll parent before, but we do now, the displayport + // and scrollport we're tracking are just those of the new scroll parent. + if (!oldScrollParent) { + mDisplayPortConsideringAncestors = + nsLayoutUtils::GetDisplayPortOrFallbackToScrollPort(newScrollParent); + mScrollPortConsideringAncestors = newScrollParent->GetScrollPortRect(); + return; + } + +#ifdef DEBUG + { + nsIScrollableFrame* oldScrollableParent = do_QueryFrame(oldScrollParent); + if (oldScrollableParent && oldScrollableParent->WantAsyncScroll()) { + // Normally, the old scroll parent should be the nearest async + // scrollable ancestor of the new scroll parent. + MOZ_ASSERT(nsLayoutUtils::GetAsyncScrollableProperAncestorFrame(aScrollParent) == + oldScrollableParent); + } else { + // If the old scroll parent isn't async scrollable or isn't scrollable + // at all, (e.g., because we had to fall back to the root frame in a XUL + // document) then at least assert it's an ancestor of the new scroll parent. + MOZ_ASSERT(nsLayoutUtils::IsProperAncestorFrameCrossDoc(oldScrollParent, + aScrollParent)); + } + } +#endif + + // If the effective displayport, considering ancestors, is already empty, we + // can bail early as we know the intersection checks below will fail. + if (mDisplayPortConsideringAncestors.IsEmpty()) { + mDisplayPortConsideringAncestors = nsRect(); + mScrollPortConsideringAncestors = nsRect(); + return; + } + + // The entire displayport for this subtree is the critical displayport of + // this scroll parent if any part of it is visible within the previous scroll + // parent's critical displayport. (What we're trying to do is capture + // everything that APZ could asynchronously scroll into view.) If it isn't + // visible, then the displayport for this subtree is empty and the + // scrollport, trivially, is too. + nsRect displayPort = + nsLayoutUtils::GetDisplayPortOrFallbackToScrollPort(newScrollParent); + nsRect displayPortIntersection = + nsLayoutUtils::TransformAndIntersectRect(oldScrollParent, + mDisplayPortConsideringAncestors, + aScrollParent, + displayPort); + if (displayPortIntersection.IsEmpty()) { + mDisplayPortConsideringAncestors = nsRect(); + mScrollPortConsideringAncestors = nsRect(); + return; + } + + // See above: we take the whole thing, not the intersection with the + // previous scroll parent's critical displayport. + mDisplayPortConsideringAncestors = displayPort; + + // The scrollport for this subtree is the intersection of aScrollParent's + // scrollport with all ancestor scrollports. + mScrollPortConsideringAncestors = + nsLayoutUtils::TransformAndIntersectRect(oldScrollParent, + mScrollPortConsideringAncestors, + aScrollParent, + newScrollParent->GetScrollPortRect()); +} + +void +nsDisplayListBuilder::UpdateCurrentScrollParentForOutOfFlow(const OutOfFlowDisplayData* aOutOfFlowData) +{ + MOZ_ASSERT(aOutOfFlowData); + + // Just set the information from |aOutOfFlowData| unconditionally. + mCurrentScrollParent = aOutOfFlowData->mCurrentScrollParent; + mCurrentScrollParentId = aOutOfFlowData->mCurrentScrollParentId; + mDisplayPortConsideringAncestors = aOutOfFlowData->mDisplayPortConsideringAncestors; + mScrollPortConsideringAncestors = aOutOfFlowData->mScrollPortConsideringAncestors; +} + void nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot() { diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 97d98107822b..218803d6f1fa 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -368,6 +368,30 @@ public: * Get the scrollframe to ignore, if any. */ nsIFrame* GetIgnoreScrollFrame() { return mIgnoreScrollFrame; } + /** + * Get the nearest ancestor scrollframe, or the root frame if there is no + * ancestor scrollframe, or null if neither exists. + */ + nsIFrame* GetCurrentScrollParent() { return mCurrentScrollParent; } + /** + * Get the critical displayport of the nearest ancestor scrollframe, in the + * coordinate system of the frame returned by GetCurrentScrollParent(). If + * the ancestor scrollframe's critical displayport does not intersect the + * critical displayport of the scrollframe that encloses it, and so on + * transitively up the frame tree, returns the empty rect. The idea is that + * this method considers a displayport visible if its nearest ancestor is + * visible and some part of it intersects its nearest ancestor, which means + * that we capture everything that APZ could asynchronously scroll into + * view. Used for visibility tracking during painting. + */ + nsRect GetDisplayPortConsideringAncestors() { return mDisplayPortConsideringAncestors; } + /** + * Get the intersection of all ancestor scrollframes' scrollports. Used for + * visibility tracking during painting. Unlike the displayport case, for + * scrollports we use strict intersection, because we only want to detect + * frames that are actually visible in the viewport. + */ + nsRect GetScrollPortConsideringAncestors() { return mScrollPortConsideringAncestors; } /** * Get the ViewID of the nearest scrolling ancestor frame. */ @@ -826,49 +850,64 @@ public: uint32_t mCachedItemIndex; }; + struct OutOfFlowDisplayData; + /** - * A helper class to temporarily set the value of mCurrentScrollParentId. + * A helper class to temporarily set the value of mCurrentScrollParentId and + * cache information about the effective displayport and scrollport, + * considering all ancestor scrollframes. See + * GetDisplayPortConsideringAncestors() and + * GetScrollPortConsideringAncestors() for the details. */ class AutoCurrentScrollParentIdSetter; friend class AutoCurrentScrollParentIdSetter; class AutoCurrentScrollParentIdSetter { public: - AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, ViewID aScrollId) - : mBuilder(aBuilder) - , mOldValue(aBuilder->mCurrentScrollParentId) - , mOldForceLayer(aBuilder->mForceLayerForScrollParent) { - // If this AutoCurrentScrollParentIdSetter has the same scrollId as the - // previous one on the stack, then that means the scrollframe that - // created this isn't actually scrollable and cannot participate in - // scroll handoff. We set mCanBeScrollParent to false to indicate this. - mCanBeScrollParent = (mOldValue != aScrollId); - aBuilder->mCurrentScrollParentId = aScrollId; - aBuilder->mForceLayerForScrollParent = false; - } + /** + * Temporarily update the cached scroll parent information on @aBuilder + * from frame @aFrame. @aFrame should be a a scrollframe that is a + * descendant of @aBuilder's current scroll parent. (The exception is if + * there's no current scroll parent, in which case @aFrame does not have to + * be a scrollframe; this is fallback behavior for XUL documents, which + * may not have a root scrollframe.) + * + * If @aNewScrollParentFrom is SCROLLFRAMES_ON_PATH_TO_ROOT, @aFrame is + * interpreted as a scrollframe in a different subtree. In this case we + * have to walk up the frame tree all the way to the root to gather + * information about the effective displayport and scrollport. + */ + AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, + nsIFrame* aScrollParent); + + /** + * Temporarily updated the cached scroll parent information on @aBuilder + * from saved information on @aOutOfFlowData. This is necessary when + * traversing out-of-flow frames via their placeholder frames, since their + * scroll parent may not be the same as the scroll parent of the + * placeholder frame. + */ + AutoCurrentScrollParentIdSetter(nsDisplayListBuilder* aBuilder, + const OutOfFlowDisplayData* aOutOfFlowData); + + ~AutoCurrentScrollParentIdSetter(); + bool ShouldForceLayerForScrollParent() const { // Only scrollframes participating in scroll handoff can be forced to // layerize return mCanBeScrollParent && mBuilder->mForceLayerForScrollParent; - }; - ~AutoCurrentScrollParentIdSetter() { - mBuilder->mCurrentScrollParentId = mOldValue; - if (mCanBeScrollParent) { - // If this flag is set, caller code is responsible for having dealt - // with the current value of mBuilder->mForceLayerForScrollParent, so - // we can just restore the old value. - mBuilder->mForceLayerForScrollParent = mOldForceLayer; - } else { - // Otherwise we need to keep propagating the force-layerization flag - // upwards to the next ancestor scrollframe that does participate in - // scroll handoff. - mBuilder->mForceLayerForScrollParent |= mOldForceLayer; - } } + private: + void Init(); + nsDisplayListBuilder* mBuilder; - ViewID mOldValue; - bool mOldForceLayer; - bool mCanBeScrollParent; + nsIFrame* mOldScrollParent; + nsRect mOldDisplayPortConsideringAncestors; + nsRect mOldScrollPortConsideringAncestors; + ViewID mOldScrollParentId; + bool mOldForceLayer : 1; + bool mCanBeScrollParent : 1; + bool mChangedSubtrees : 1; }; /** @@ -989,14 +1028,26 @@ public: struct OutOfFlowDisplayData { OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip, const DisplayItemScrollClip* aContainingBlockScrollClip, - const nsRect &aDirtyRect) + const nsRect& aDirtyRect, + nsIFrame* aCurrentScrollParent, + ViewID aCurrentScrollParentId, + const nsRect& aDisplayPortConsideringAncestors, + const nsRect& aScrollPortConsideringAncestors) : mContainingBlockClip(aContainingBlockClip ? *aContainingBlockClip : DisplayItemClip()) , mContainingBlockScrollClip(aContainingBlockScrollClip) , mDirtyRect(aDirtyRect) + , mCurrentScrollParent(aCurrentScrollParent) + , mCurrentScrollParentId(aCurrentScrollParentId) + , mDisplayPortConsideringAncestors(aDisplayPortConsideringAncestors) + , mScrollPortConsideringAncestors(aScrollPortConsideringAncestors) {} DisplayItemClip mContainingBlockClip; const DisplayItemScrollClip* mContainingBlockScrollClip; nsRect mDirtyRect; + nsIFrame* mCurrentScrollParent; + ViewID mCurrentScrollParentId; + nsRect mDisplayPortConsideringAncestors; + nsRect mScrollPortConsideringAncestors; }; NS_DECLARE_FRAME_PROPERTY_DELETABLE(OutOfFlowDisplayDataProperty, @@ -1153,6 +1204,23 @@ private: */ nsIFrame* FindAnimatedGeometryRootFrameFor(nsIFrame* aFrame); + /** + * Updates cached information about the nearest ancestor scrollable frame. + * @aScrollParent should be a descendant of the previous nearest ancestor + * scrollable frame. @aScrollParent may be null, indicating that there is + * no ancestor scrollable frame. + */ + void UpdateCurrentScrollParent(nsIFrame* aScrollParent); + + /** + * Updates cached information about the nearest ancestor scrollable frame. + * The new information is read from @aOutOfFlowData, and unconditionally + * replaces the cached information without reference to the current scroll + * parent, since we're descending into an out-of-flow frame that does not + * necessarily have the same scroll parent chain. + */ + void UpdateCurrentScrollParentForOutOfFlow(const OutOfFlowDisplayData* aOutOfFlowData); + friend class nsDisplayCanvasBackgroundImage; friend class nsDisplayBackgroundImage; friend class nsDisplayFixedPosition; @@ -1250,7 +1318,10 @@ private: nsTArray mScrollClipsToDestroy; nsTArray mDisplayItemClipsToDestroy; nsDisplayListBuilderMode mMode; + nsIFrame* mCurrentScrollParent; ViewID mCurrentScrollParentId; + nsRect mDisplayPortConsideringAncestors; + nsRect mScrollPortConsideringAncestors; ViewID mCurrentScrollbarTarget; uint32_t mCurrentScrollbarFlags; Preserves3DContext mPreserves3DCtx; diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index c1f34d4aa4ed..afb4bf39d17a 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3534,34 +3534,6 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram builder.EnterPresShell(aFrame); nsRect dirtyRect = visibleRegion.GetBounds(); { - // If a scrollable container layer is created in nsDisplayList::PaintForFrame, - // it will be the scroll parent for display items that are built in the - // BuildDisplayListForStackingContext call below. We need to set the scroll - // parent on the display list builder while we build those items, so that they - // can pick up their scroll parent's id. - ViewID id = FrameMetrics::NULL_SCROLL_ID; - if (ignoreViewportScrolling && presContext->IsRootContentDocument()) { - if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) { - if (nsIContent* content = rootScrollFrame->GetContent()) { - id = nsLayoutUtils::FindOrCreateIDFor(content); - } - } - } -#if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ) - else if (presShell->GetDocument() && presShell->GetDocument()->IsRootDisplayDocument() - && !presShell->GetRootScrollFrame()) { - // In cases where the root document is a XUL document, we want to take - // the ViewID from the root element, as that will be the ViewID of the - // root APZC in the tree. Skip doing this in cases where we know - // nsGfxScrollFrame::BuilDisplayList will do it instead. - if (dom::Element* element = presShell->GetDocument()->GetDocumentElement()) { - id = nsLayoutUtils::FindOrCreateIDFor(element); - } - } -#endif - - nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id); - PROFILER_LABEL("nsLayoutUtils", "PaintFrame::BuildDisplayList", js::ProfileEntry::Category::GRAPHICS); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index de44d92fe4a0..0ca3228cac85 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2782,11 +2782,17 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, DisplayListClipState::AutoClipMultiple clipState(aBuilder); CheckForApzAwareEventHandlers(aBuilder, child); + Maybe idSetter; if (savedOutOfFlowData) { clipState.SetClipForContainingBlockDescendants( &savedOutOfFlowData->mContainingBlockClip); clipState.SetScrollClipForContainingBlockDescendants( savedOutOfFlowData->mContainingBlockScrollClip); + + // For out-of-flow frames, the current scroll parent is wrong, because it's + // the scroll parent for the placeholder frame rather than the actual + // out-of-flow frame, so we need to update the current scroll parent. + idSetter.emplace(aBuilder, savedOutOfFlowData); } else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO && isPlaceholder) { // If we have nested out-of-flow frames and the outer one isn't visible diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index a17349ca5e36..8e5194a3c412 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3299,11 +3299,11 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder, // parent. This is intentional because that is what happens for positioned children // of scroll layers, and we want to maintain consistent behaviour between scroll layers // and scroll info layers. + nsIContent* content = couldBuildLayer ? mScrolledFrame->GetContent() + : nullptr; + nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter( - aBuilder, - couldBuildLayer && mScrolledFrame->GetContent() - ? nsLayoutUtils::FindOrCreateIDFor(mScrolledFrame->GetContent()) - : aBuilder->GetCurrentScrollParentId()); + aBuilder, content ? mOuter : aBuilder->GetCurrentScrollParent()); nsRect clipRect = mScrollPort + aBuilder->ToReferenceFrame(mOuter); // Our override of GetBorderRadii ensures we never have a radius at diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index 8344a7dfd0c4..49d739ee308f 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -483,11 +483,12 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, if (subdocRootFrame) { nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); + nsIContent* content = ignoreViewportScrolling && rootScrollFrame + ? rootScrollFrame->GetContent() + : nullptr; + nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter( - aBuilder, - ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent() - ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent()) - : aBuilder->GetCurrentScrollParentId()); + aBuilder, content ? rootScrollFrame : aBuilder->GetCurrentScrollParent()); aBuilder->SetAncestorHasApzAwareEventHandler(false); subdocRootFrame->