diff options
Diffstat (limited to 'layout/generic/nsFrame.cpp')
-rw-r--r-- | layout/generic/nsFrame.cpp | 205 |
1 files changed, 123 insertions, 82 deletions
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index b8b1f312e..07cdbd7e3 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -53,6 +53,7 @@ #include "nsGkAtoms.h" #include "nsHtml5Atoms.h" #include "nsCSSAnonBoxes.h" +#include "nsGenericHTMLElement.h" #include "nsFrameTraversal.h" #include "nsRange.h" @@ -1794,6 +1795,13 @@ void nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { + // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides: + // "All css properties of table-column and table-column-group boxes are + // ignored, except when explicitly specified by this specification." + // CSS outlines fall into this category, so we skip them on these boxes. + + MOZ_ASSERT(GetType() != nsGkAtoms::tableColGroupFrame && GetType() != nsGkAtoms::tableColFrame); + if (StyleOutline()->mOutlineStyle == NS_STYLE_BORDER_STYLE_NONE) { return; } @@ -1814,7 +1822,7 @@ nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder, void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder, - const nsRect& aDirtyRect, nsDisplayList* aList) + nsDisplayList* aList) { if (!IsVisibleForPainting(aBuilder)) return; @@ -1840,7 +1848,9 @@ nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder, if (aBuilder->IsForEventDelivery() || aForceBackground || !StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) { return nsDisplayBackgroundImage::AppendBackgroundItemsToTop( - aBuilder, this, GetRectRelativeToSelf(), aLists.BorderBackground()); + aBuilder, this, + GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this), + aLists.BorderBackground()); } return false; } @@ -1873,7 +1883,9 @@ nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder, // If there's a themed background, we should not create a border item. // It won't be rendered. - if (!bgIsThemed && StyleBorder()->HasBorder()) { + // Don't paint borders for tables here, since they paint them in a different + // order. + if (!bgIsThemed && StyleBorder()->HasBorder() && GetType() != nsGkAtoms::tableFrame) { aLists.BorderBackground()->AppendNewToTop(new (aBuilder) nsDisplayBorder(aBuilder, this)); } @@ -2073,13 +2085,12 @@ ItemParticipatesIn3DContext(nsIFrame* aAncestor, nsDisplayItem* aItem) static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - nsRect& aDirtyRect, nsDisplayList* aSource, nsDisplayList* aTarget, int aIndex) { if (!aSource->IsEmpty()) { nsDisplayTransform *sepIdItem = new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aSource, - aDirtyRect, Matrix4x4(), aIndex); + aBuilder->GetDirtyRect(), Matrix4x4(), aIndex); sepIdItem->SetNoExtendContext(); aTarget->AppendToTop(sepIdItem); } @@ -2087,7 +2098,6 @@ WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, void nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, - const nsRect& aDirtyRect, nsDisplayList* aList) { if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return; @@ -2123,25 +2133,27 @@ 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<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext; - if (extend3DContext && !Combines3DTransformWithAncestors()) { + if (extend3DContext && !combines3DTransformWithAncestors) { // Start a new preserves3d context to keep informations on // nsDisplayListBuilder. autoPreserves3DContext.emplace(aBuilder); // Save dirty rect on the builder to avoid being distorted for // multiple transforms along the chain. - aBuilder->SetPreserves3DDirtyRect(aDirtyRect); + aBuilder->SavePreserves3DRects(); } // For preserves3d, use the dirty rect already installed on the // builder, since aDirtyRect maybe distorted for transforms along // the chain. - nsRect dirtyRect = aDirtyRect; + 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. @@ -2149,6 +2161,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, @@ -2161,8 +2174,8 @@ 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()) { - dirtyRect = aBuilder->GetPreserves3DDirtyRect(this); + if (combines3DTransformWithAncestors) { + dirtyRect = aBuilder->GetPreserves3DRects(); } nsRect untransformedDirtyRect; @@ -2270,19 +2283,19 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, clipState.EnterStackingContextContents(clearClip); - nsDisplayListCollection set; + nsDisplayListCollection set(aBuilder); { DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder); nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder, inTransform); nsDisplayListBuilder::AutoSaveRestorePerspectiveIndex - perspectiveIndex(aBuilder, this); + perspectiveIndex(aBuilder, childrenHavePerspective); CheckForApzAwareEventHandlers(aBuilder, this); Maybe<nsRect> clipPropClip = GetClipPropClipRect(disp, effects, GetSize()); if (clipPropClip) { - dirtyRect.IntersectRect(dirtyRect, *clipPropClip); + aBuilder->IntersectDirtyRect(*clipPropClip); nestedClipState.ClipContentDescendants( *clipPropClip + aBuilder->ToReferenceFrame(this)); } @@ -2295,7 +2308,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, aBuilder->MarkPreserve3DFramesForDisplayList(this); } - MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect); + MarkAbsoluteFramesForDisplayList(aBuilder); nsDisplayLayerEventRegions* eventRegions = nullptr; if (aBuilder->IsBuildingLayerEventRegions()) { @@ -2304,7 +2317,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, aBuilder->SetLayerEventRegions(eventRegions); } aBuilder->AdjustWindowDraggingRegion(this); - BuildDisplayList(aBuilder, dirtyRect, set); + BuildDisplayList(aBuilder, set); if (eventRegions) { // If the event regions item ended up empty, throw it away rather than // adding it to the display list. @@ -2410,7 +2423,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, clipState.ExitStackingContextContents(&containerItemScrollClip); } // Revert to the post-filter dirty rect. - buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects); + aBuilder->SetDirtyRect(dirtyRectOutsideSVGEffects); // Skip all filter effects while generating glyph mask. if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) { @@ -2475,8 +2488,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, while (nsDisplayItem* item = resultList.RemoveBottom()) { if (ItemParticipatesIn3DContext(this, item) && !item->GetClip().HasClip()) { // The frame of this item participates the same 3D context. - WrapSeparatorTransform(aBuilder, this, dirtyRect, - &nonparticipants, &participants, index++); + WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants, index++); participants.AppendToTop(item); } else { // The frame of the item doesn't participate the current @@ -2489,8 +2501,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nonparticipants.AppendToTop(item); } } - WrapSeparatorTransform(aBuilder, this, dirtyRect, - &nonparticipants, &participants, index++); + WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants, index++); resultList.AppendToTop(&participants); } @@ -2501,7 +2512,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, } // Revert to the dirtyrect coming in from the parent, without our transform // taken into account. - buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform); + aBuilder->SetDirtyRect(dirtyRectOutsideTransform); // Revert to the outer reference frame and offset because all display // items we create from now on are outside the transform. nsPoint toOuterReferenceFrame; @@ -2602,10 +2613,63 @@ 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; + } + + if (aChild->IsFrameOfType(nsIFrame::eTablePart)) { + // Relative positioning and transforms can cause table parts to move, but we + // will still paint the backgrounds for their ancestor parts under them at + // their 'normal' position. That means that we must consider the overflow + // rects at both positions. + + // We convert the overflow rect into the nsTableFrame's coordinate + // space, applying the normal position offset at each step. Then we + // compare that against the builder's cached dirty rect in table + // coordinate space. + const nsIFrame* f = aChild; + nsRect normalPositionOverflowRelativeToTable = overflow; + + while (f->IsFrameOfType(nsIFrame::eTablePart)) { + normalPositionOverflowRelativeToTable += f->GetNormalPosition(); + f = f->GetParent(); + } + + nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet(); + if (tableBGs && + tableBGs->GetDirtyRect().Intersects(normalPositionOverflowRelativeToTable)) { + return true; + } + } + + return false; +} + void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsIFrame* aChild, - const nsRect& aDirtyRect, const nsDisplayListSet& aLists, uint32_t aFlags) { // If painting is restricted to just the background of the top level frame, @@ -2624,12 +2688,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., @@ -2638,12 +2703,14 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, pseudoStackingContext = true; } - // dirty rect in child-relative coordinates - nsRect dirty = aDirtyRect - child->GetOffsetTo(this); - nsIAtom* childType = child->GetType(); nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr; bool isPlaceholder = false; + + // dirty rect in child-relative coordinates + NS_ASSERTION(aBuilder->GetCurrentFrame() == this, "Wrong coord space!"); + nsRect dirty = aBuilder->GetDirtyRect() - child->GetOffsetTo(this); + if (childType == nsGkAtoms::placeholderFrame) { isPlaceholder = true; nsPlaceholderFrame* placeholder = static_cast<nsPlaceholderFrame*>(child); @@ -2690,35 +2757,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 +2784,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,28 +2792,28 @@ 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"); nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(aBuilder, child, dirty, pseudoStackingContext); + DisplayListClipState::AutoClipMultiple clipState(aBuilder); CheckForApzAwareEventHandlers(aBuilder, child); @@ -2784,7 +2826,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, savedOutOfFlowData->mContainingBlockScrollClip); } else if (GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO && isPlaceholder) { - NS_ASSERTION(dirty.IsEmpty(), "should have empty dirty rect"); + NS_ASSERTION(dirty.IsEmpty(), "should have empty visible rect"); // Every item we build from now until we descent into an out of flow that // does have saved out of flow data should be invisible. This state gets // restored when AutoBuildingDisplayList gets out of scope. @@ -2820,18 +2862,18 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // True stacking context. // For stacking contexts, BuildDisplayListForStackingContext handles // clipping and MarkAbsoluteFramesForDisplayList. - child->BuildDisplayListForStackingContext(aBuilder, dirty, &list); - aBuilder->DisplayCaret(child, dirty, &list); + child->BuildDisplayListForStackingContext(aBuilder, &list); + aBuilder->DisplayCaret(child, &list); } else { Maybe<nsRect> clipPropClip = child->GetClipPropClipRect(disp, effects, child->GetSize()); if (clipPropClip) { - dirty.IntersectRect(dirty, *clipPropClip); + aBuilder->IntersectDirtyRect(*clipPropClip); clipState.ClipContentDescendants( *clipPropClip + aBuilder->ToReferenceFrame(child)); } - child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); + child->MarkAbsoluteFramesForDisplayList(aBuilder); if (aBuilder->IsBuildingLayerEventRegions()) { // If this frame has a different animated geometry root than its parent, @@ -2863,8 +2905,8 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // return early. aBuilder->AdjustWindowDraggingRegion(child); - child->BuildDisplayList(aBuilder, dirty, aLists); - aBuilder->DisplayCaret(child, dirty, aLists.Content()); + child->BuildDisplayList(aBuilder, aLists); + aBuilder->DisplayCaret(child, aLists.Content()); #ifdef DEBUG DisplayDebugBorders(aBuilder, child, aLists); #endif @@ -2875,10 +2917,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // We allow positioned descendants of the child to escape to our parent // stacking context's positioned descendant list, because they might be // z-index:non-auto - nsDisplayListCollection pseudoStack; + nsDisplayListCollection pseudoStack(aBuilder); aBuilder->AdjustWindowDraggingRegion(child); - child->BuildDisplayList(aBuilder, dirty, pseudoStack); - aBuilder->DisplayCaret(child, dirty, pseudoStack.Content()); + child->BuildDisplayList(aBuilder, pseudoStack); + aBuilder->DisplayCaret(child, pseudoStack.Content()); list.AppendToTop(pseudoStack.BorderBackground()); list.AppendToTop(pseudoStack.BlockBorderBackgrounds()); @@ -2928,11 +2970,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, } void -nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder, - const nsRect& aDirtyRect) +nsIFrame::MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder* aBuilder) { if (IsAbsoluteContainer()) { - aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList(), aDirtyRect); + aBuilder->MarkFramesForDisplayList(this, GetAbsoluteContainingBlock()->GetChildList()); } } |