From 81fe352e5f335002d27161d87ef1873e2bac9d7b Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 23 Aug 2016 17:34:33 -0400 Subject: [PATCH] Bug 1298209 - Make clip capturing easier to understand. r=mattwoodrow MozReview-Commit-ID: 6OmdH7WoFma --HG-- extra : rebase_source : 9fc53828b07034b26c6a6075d34bb4c2c4cb1110 --- layout/generic/nsFrame.cpp | 87 +++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index f70c0b8e261d..1b27899d9855 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2271,10 +2271,59 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(aBuilder, this, dirtyRect, true); + // Depending on the effects that are applied to this frame, we can create + // multiple container display items and wrap them around our contents. + // This enum lists all the potential container display items, in the order + // outside to inside. + enum class ContainerItemType : uint8_t { + eNone = 0, + eOwnLayerIfNeeded, + eBlendMode, + eFixedPosition, + eStickyPosition, + eOwnLayerForTransformWithRoundedClip, + ePerspective, + eTransform, + eSeparatorTransforms, + eOpacity, + eSVGEffects, + eBlendContainer + }; + DisplayListClipState::AutoSaveRestore clipState(aBuilder); + // If there is a current clip, then depending on the container items we + // create, different things can happen to it. Some container items simply + // propagate the clip to their children and aren't clipped themselves. + // But other container items, especially those that establish a different + // geometry for their contents (e.g. transforms), capture the clip on + // themselves and unset the clip for their contents. If we create more than + // one of those container items, the clip will be captured on the outermost + // one and the inner container items will be unclipped. + ContainerItemType clipCapturedBy = ContainerItemType::eNone; + if (useFixedPosition) { + clipCapturedBy = ContainerItemType::eFixedPosition; + } else if (useStickyPosition) { + clipCapturedBy = ContainerItemType::eStickyPosition; + } else if (isTransformed) { + if ((hasPerspective || extend3DContext) && clipState.SavedStateHasRoundedCorners()) { + // If we're creating an nsDisplayTransform item that is going to combine + // its transform with its children (preserve-3d or perspective), then we + // can't have an intermediate surface. Mask layers force an intermediate + // surface, so if we're going to need both then create a separate + // wrapping layer for the mask. + clipCapturedBy = ContainerItemType::eOwnLayerForTransformWithRoundedClip; + } else if (hasPerspective) { + clipCapturedBy = ContainerItemType::ePerspective; + } else { + clipCapturedBy = ContainerItemType::eTransform; + } + } else if (usingSVGEffects) { + clipCapturedBy = ContainerItemType::eSVGEffects; + } + bool clearClip = false; - if (isTransformed || usingSVGEffects || useFixedPosition || useStickyPosition) { + if (clipCapturedBy != ContainerItemType::eNone) { // We don't need to pass ancestor clipping down to our children; // everything goes inside a display item's child list, and the display // item itself will be clipped. @@ -2411,18 +2460,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, containerItemScrollClip)); } - if (!isTransformed && !useFixedPosition && !useStickyPosition) { - // Restore saved clip state now so that any display items we create below - // are clipped properly. - clipState.ExitStackingContextContents(&containerItemScrollClip); - } - /* If there are any SVG effects, wrap the list up in an SVG effects item * (which also handles CSS group opacity). Note that we create an SVG effects * item even if resultList is empty, since a filter can produce graphical * output even if the element being filtered wouldn't otherwise do so. */ if (usingSVGEffects) { + if (clipCapturedBy == ContainerItemType::eSVGEffects) { + clipState.ExitStackingContextContents(&containerItemScrollClip); + } // Revert to the post-filter dirty rect. buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects); /* List now emptied, so add the new list to the top. */ @@ -2488,19 +2534,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, resultList.AppendToTop(&participants); } - /* If we're creating an nsDisplayTransform item that is going to combine its transform - * with its children (preserve-3d or perspective), then we can't have an intermediate - * surface. Mask layers force an intermediate surface, so if we're going to need both - * then create a separate wrapping layer for the mask. - */ - bool needsLayerForMask = isTransformed && (extend3DContext || hasPerspective) && - clipState.SavedStateHasRoundedCorners(); - NS_ASSERTION(!Combines3DTransformWithAncestors() || !clipState.SavedStateHasRoundedCorners(), - "Can't support mask layers on intermediate preserve-3d frames"); - if (isTransformed && !resultList.IsEmpty()) { - // Restore clip state now so nsDisplayTransform is clipped properly. - if (!hasPerspective && !useFixedPosition && !useStickyPosition && !needsLayerForMask) { + if (clipCapturedBy == ContainerItemType::eTransform) { + // Restore clip state now so nsDisplayTransform is clipped properly. clipState.ExitStackingContextContents(&containerItemScrollClip); } // Revert to the dirtyrect coming in from the parent, without our transform @@ -2529,7 +2565,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, } if (hasPerspective) { - if (!useFixedPosition && !useStickyPosition && !needsLayerForMask) { + if (clipCapturedBy == ContainerItemType::ePerspective) { clipState.ExitStackingContextContents(&containerItemScrollClip); } resultList.AppendNewToTop( @@ -2539,11 +2575,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, } } - if (useFixedPosition || useStickyPosition || needsLayerForMask) { + if (clipCapturedBy == ContainerItemType::eOwnLayerForTransformWithRoundedClip) { clipState.ExitStackingContextContents(&containerItemScrollClip); - } - - if (needsLayerForMask) { resultList.AppendNewToTop( new (aBuilder) nsDisplayOwnLayer(aBuilder, this, &resultList, 0, mozilla::layers::FrameMetrics::NULL_SCROLL_ID, @@ -2553,9 +2586,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, /* If we have sticky positioning, wrap it in a sticky position item. */ if (useFixedPosition) { + if (clipCapturedBy == ContainerItemType::eFixedPosition) { + clipState.ExitStackingContextContents(&containerItemScrollClip); + } resultList.AppendNewToTop( new (aBuilder) nsDisplayFixedPosition(aBuilder, this, &resultList)); } else if (useStickyPosition) { + if (clipCapturedBy == ContainerItemType::eStickyPosition) { + clipState.ExitStackingContextContents(&containerItemScrollClip); + } resultList.AppendNewToTop( new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList)); }