From 36369e0fef4642068bbe842b6dfea3e3789d1582 Mon Sep 17 00:00:00 2001 From: win7-7 Date: Fri, 8 May 2020 00:52:23 +0300 Subject: Issue #1355 - Avoid unnecessary work in nsIFrame::BuildDisplayListForStackingContext() and nsIFrame::BuildDisplayListForChild() and Cleanup DescendIntoChild Bug 1441796 - Part 1: Optimize the (pseudo)-stacking context conditions Bug 1441796 - Part 3: Reuse the results in nsIFrame::BuildDisplayListForStackingContext() for ChildrenHavePerspective(), IsTransformed(), and Combines3DTransformWithAncestors() Bug 1512244 - Part 1: Cleanup DescendIntoChild --- layout/base/nsDisplayList.cpp | 23 ++++++---- layout/base/nsDisplayList.h | 14 +++++- layout/generic/nsFrame.cpp | 100 ++++++++++++++++++++++-------------------- 3 files changed, 80 insertions(+), 57 deletions(-) (limited to 'layout') diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index b08fe4219..a869564a4 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1009,8 +1009,22 @@ nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame, } state->mInsidePointerEventsNoneDoc = pointerEventsNone; - if (!buildCaret) + state->mPresShellIgnoreScrollFrame = + state->mPresShell->IgnoringViewportScrolling() + ? state->mPresShell->GetRootScrollFrame() + : nullptr; + + nsPresContext* pc = aReferenceFrame->PresContext(); + nsCOMPtr docShell = pc->GetDocShell(); + if (docShell) { + docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + } + + mIsInChromePresContext = pc->IsChrome(); + + if (!buildCaret) { return; + } RefPtr caret = state->mPresShell->GetCaret(); state->mCaretFrame = caret->GetPaintGeometry(&state->mCaretRect); @@ -1018,13 +1032,6 @@ nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame, mFramesMarkedForDisplay.AppendElement(state->mCaretFrame); MarkFrameForDisplay(state->mCaretFrame, nullptr); } - - nsPresContext* pc = aReferenceFrame->PresContext(); - nsCOMPtr docShell = pc->GetDocShell(); - if (docShell) { - docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed); - } - mIsInChromePresContext = pc->IsChrome(); } // A non-blank paint is a paint that does not just contain the canvas background. diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 1b2010d33..3aeb20d19 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -520,6 +520,14 @@ public: * Get the caret associated with the current presshell. */ nsCaret* GetCaret(); + + /** + * Returns the root scroll frame for the current PresShell, if the PresShell + * is ignoring viewport scrolling. + */ + nsIFrame* GetPresShellIgnoreScrollFrame() { + return CurrentPresShellState()->mPresShellIgnoreScrollFrame; + } /** * Notify the display list builder that we're entering a presshell. * aReferenceFrame should be a frame in the new presshell. @@ -805,10 +813,11 @@ public: friend class AutoSaveRestorePerspectiveIndex; class AutoSaveRestorePerspectiveIndex { public: - AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) + AutoSaveRestorePerspectiveIndex(nsDisplayListBuilder* aBuilder, + const bool aChildrenHavePerspective) : mBuilder(nullptr) { - if (aFrame->ChildrenHavePerspective()) { + if (aChildrenHavePerspective) { mBuilder = aBuilder; mCachedItemIndex = aBuilder->mPerspectiveItemIndex; aBuilder->mPerspectiveItemIndex = 0; @@ -1175,6 +1184,7 @@ private: nsRect mCaretRect; uint32_t mFirstFrameMarkedForDisplay; bool mIsBackgroundOnly; + nsIFrame* mPresShellIgnoreScrollFrame; // This is a per-document flag turning off event handling for all content // in the document, and is set when we enter a subdocument for a pointer- // events:none frame. diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 0fb16e4ce..1c0b49895 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2122,9 +2122,14 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, aBuilder->AddToWillChangeBudget(this, GetSize()); } - bool extend3DContext = Extend3DContext(); + const bool isTransformed = IsTransformed(); + const bool hasPerspective = isTransformed && HasPerspective(); + const bool extend3DContext = Extend3DContext(); + const bool combines3DTransformWithAncestors = + (extend3DContext || isTransformed) && Combines3DTransformWithAncestors(); + const bool childrenHavePerspective = ChildrenHavePerspective(); Maybe autoPreserves3DContext; - if (extend3DContext && !Combines3DTransformWithAncestors()) { + if (extend3DContext && !combines3DTransformWithAncestors) { // Start a new preserves3d context to keep informations on // nsDisplayListBuilder. autoPreserves3DContext.emplace(aBuilder); @@ -2138,9 +2143,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, // the chain. nsRect dirtyRect = aBuilder->GetDirtyRect(); - bool inTransform = aBuilder->IsInTransform(); - bool isTransformed = IsTransformed(); - bool hasPerspective = HasPerspective(); // reset blend mode so we can keep track if this stacking context needs have // a nsDisplayBlendContainer. Set the blend mode back when the routine exits // so we keep track if the parent stacking context needs a container too. @@ -2148,6 +2150,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, aBuilder->SetContainsBlendMode(false); nsRect dirtyRectOutsideTransform = dirtyRect; + bool inTransform = aBuilder->IsInTransform(); if (isTransformed) { const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); if (nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, @@ -2160,7 +2163,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, // If we're in preserve-3d then grab the dirty rect that was given to the root // and transform using the combined transform. - if (Combines3DTransformWithAncestors()) { + if (combines3DTransformWithAncestors) { dirtyRect = aBuilder->GetPreserves3DRects(); } @@ -2275,7 +2278,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder, inTransform); nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex - perspectiveIndex(aBuilder, this); + perspectiveIndex(aBuilder, childrenHavePerspective); CheckForApzAwareEventHandlers(aBuilder, this); @@ -2599,6 +2602,35 @@ WrapInWrapList(nsDisplayListBuilder* aBuilder, return item; } +static bool DescendIntoChild(nsDisplayListBuilder* aBuilder, + const nsIFrame* aChild, const nsRect& aDirty) { + if (aChild->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) { + return true; + } + + // If the child is a scrollframe that we want to ignore, then we need + // to descend into it because its scrolled child may intersect the dirty + // area even if the scrollframe itself doesn't. + if (aChild == aBuilder->GetIgnoreScrollFrame()) { + return true; + } + + // There are cases where the "ignore scroll frame" on the builder is not set + // correctly, and so we additionally want to catch cases where the child is + // a root scrollframe and we are ignoring scrolling on the viewport. + if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) { + return true; + } + + const nsRect overflow = aChild->GetVisualOverflowRect(); + + if (aDirty.Intersects(overflow)) { + return true; + } + + return false; + } + void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsIFrame* aChild, @@ -2620,12 +2652,13 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return; - bool isSVG = (child->GetStateBits() & NS_FRAME_SVG_LAYOUT); + const bool isSVG = child->GetStateBits() & NS_FRAME_SVG_LAYOUT; // true if this is a real or pseudo stacking context bool pseudoStackingContext = (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; - if (!isSVG && + if (!pseudoStackingContext && + !isSVG && (aFlags & DISPLAY_CHILD_INLINE) && !child->IsFrameOfType(eLineParticipant)) { // child is a non-inline frame in an inline context, i.e., @@ -2689,36 +2722,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, !aChild->IsSelected()) { return; } - - if (aBuilder->GetIncludeAllOutOfFlows() && - (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { + if (aBuilder->GetIncludeAllOutOfFlows() && isPlaceholder) { dirty = child->GetVisualOverflowRect(); - } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { - // No need to descend into child to catch placeholders for visible - // positioned stuff. So see if we can short-circuit frame traversal here. - - // We can stop if child's frame subtree's intersection with the - // dirty area is empty. - // If the child is a scrollframe that we want to ignore, then we need - // to descend into it because its scrolled child may intersect the dirty - // area even if the scrollframe itself doesn't. - // There are cases where the "ignore scroll frame" on the builder is not set - // correctly, and so we additionally want to catch cases where the child is - // a root scrollframe and we are ignoring scrolling on the viewport. - nsIPresShell* shell = PresContext()->PresShell(); - bool keepDescending = child == aBuilder->GetIgnoreScrollFrame() || - (shell->IgnoringViewportScrolling() && child == shell->GetRootScrollFrame()); - if (!keepDescending) { - nsRect childDirty; - if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect())) { - return; - } - // Usually we could set dirty to childDirty now but there's no - // benefit, and it can be confusing. It can especially confuse - // situations where we're going to ignore a scrollframe's clipping; - // we wouldn't want to clip the dirty area to the scrollframe's - // bounds in that case. - } + } else if (!DescendIntoChild(aBuilder, child, dirty)) { + return; } // XXX need to have inline-block and inline-table set pseudoStackingContext @@ -2742,7 +2749,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, const nsStyleDisplay* disp = child->StyleDisplay(); const nsStyleEffects* effects = child->StyleEffects(); const nsStylePosition* pos = child->StylePosition(); - bool isVisuallyAtomic = child->HasOpacity() + const bool isVisuallyAtomic = child->HasOpacity() || child->IsTransformed() // strictly speaking, 'perspective' doesn't require visual atomicity, // but the spec says it acts like the rest of these @@ -2750,23 +2757,22 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, || effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL || nsSVGIntegrationUtils::UsingEffectsForFrame(child); - bool isPositioned = disp->IsAbsPosContainingBlock(child); - bool isStackingContext = + const bool isPositioned = disp->IsAbsPosContainingBlock(child); + const bool isStackingContext = (isPositioned && (disp->IsPositionForcingStackingContext() || pos->mZIndex.GetUnit() == eStyleUnit_Integer)) || (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || disp->mIsolation != NS_STYLE_ISOLATION_AUTO || isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT); - if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) || - ((effects->mClipFlags & NS_STYLE_CLIP_RECT) && - IsSVGContentWithCSSClip(child)) || - disp->mIsolation != NS_STYLE_ISOLATION_AUTO || - (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) || - (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { + if (pseudoStackingContext || isStackingContext || isPositioned || + (!isSVG && disp->IsFloating(child)) || + (isSVG && (effects->mClipFlags & NS_STYLE_CLIP_RECT) && + IsSVGContentWithCSSClip(child))) { // If you change this, also change IsPseudoStackingContextFromStyle() pseudoStackingContext = true; } + NS_ASSERTION(!isStackingContext || pseudoStackingContext, "Stacking contexts must also be pseudo-stacking-contexts"); -- cgit v1.2.3