diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 491d5ae9f7e..f7f3a686213 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -329,6 +329,16 @@ static PRInt32 FFWC_recursions=0; static PRInt32 FFWC_nextInFlows=0; #endif +#ifdef MOZ_FLEXBOX +// Returns true if aFrame is an anonymous flex item +static inline bool +IsAnonymousFlexItem(const nsIFrame* aFrame) +{ + const nsIAtom* pseudoType = aFrame->GetStyleContext()->GetPseudo(); + return pseudoType == nsCSSAnonBoxes::anonymousFlexItem; +} +#endif // MOZ_FLEXBOX + static inline nsIFrame* GetFieldSetBlockFrame(nsIFrame* aFieldsetFrame) { @@ -8984,6 +8994,55 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame, return true; } +#ifdef MOZ_FLEXBOX + // Might need to reconstruct things if the removed frame's nextSibling is an + // anonymous flex item. The removed frame might've been what divided two + // runs of inline content into two anonymous flex items, which would now + // need to be merged. + // NOTE: It's fine that we've advanced nextSibling past whitespace (up above); + // we're only interested in anonymous flex items here, and those can never + // be adjacent to whitespace, since they absorb contiguous runs of inline + // non-replaced content (including whitespace). + if (nextSibling && IsAnonymousFlexItem(nextSibling)) { + NS_ABORT_IF_FALSE(parent->GetType() == nsGkAtoms::flexContainerFrame, + "anonymous flex items should only exist as children " + "of flex container frames"); +#ifdef DEBUG + if (gNoisyContentUpdates) { + printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: " + "frame="); + nsFrame::ListTag(stdout, aFrame); + printf(" has an anonymous flex item as its next sibling\n"); + } +#endif // DEBUG + // Recreate frames for the flex container (the removed frame's parent) + *aResult = RecreateFramesForContent(parent->GetContent(), true); + return true; + } + + // Might need to reconstruct things if the removed frame's nextSibling is + // null and its parent is an anonymous flex item. (This might be the last + // remaining child of that anonymous flex item, which can then go away.) + if (!nextSibling && IsAnonymousFlexItem(parent)) { + NS_ABORT_IF_FALSE(parent->GetParent() && + parent->GetParent()->GetType() == nsGkAtoms::flexContainerFrame, + "anonymous flex items should only exist as children " + "of flex container frames"); +#ifdef DEBUG + if (gNoisyContentUpdates) { + printf("nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval: " + "frame="); + nsFrame::ListTag(stdout, aFrame); + printf(" has an anonymous flex item as its parent\n"); + } +#endif // DEBUG + // Recreate frames for the flex container (the removed frame's grandparent) + *aResult = RecreateFramesForContent(parent->GetParent()->GetContent(), + true); + return true; + } +#endif // MOZ_FLEXBOX + #ifdef MOZ_XUL if (aFrame->GetType() == nsGkAtoms::popupSetFrame) { nsIRootBox* rootBox = nsIRootBox::GetRootBox(mPresShell); @@ -11132,7 +11191,67 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, nsIFrame* nextSibling = ::GetInsertNextSibling(aFrame, aPrevSibling); - // Situation #2 is a case when table pseudo-frames don't work out right +#ifdef MOZ_FLEXBOX + // Situation #2 is a flex container frame into which we're inserting new + // inline non-replaced children, adjacent to an existing anonymous flex item. + if (aFrame->GetType() == nsGkAtoms::flexContainerFrame) { + FCItemIterator iter(aItems); + + // Check if we're adding to-be-wrapped content right *after* an existing + // anonymous flex item (which would need to absorb this content). + if (aPrevSibling && IsAnonymousFlexItem(aPrevSibling) && + iter.item().NeedsAnonFlexItem(aState)) { + RecreateFramesForContent(aFrame->GetContent(), true); + return true; + } + + // Check if we're adding to-be-wrapped content right *before* an existing + // anonymous flex item (which would need to absorb this content). + if (nextSibling && IsAnonymousFlexItem(nextSibling)) { + // Jump to the last entry in the list + iter.SetToEnd(); + iter.Prev(); + if (iter.item().NeedsAnonFlexItem(aState)) { + RecreateFramesForContent(aFrame->GetContent(), true); + return true; + } + } + } + + // Situation #3 is an anonymous flex item that's getting new children who + // don't want to be wrapped. + if (IsAnonymousFlexItem(aFrame)) { + nsIFrame* flexContainerFrame = aFrame->GetParent(); + NS_ABORT_IF_FALSE(flexContainerFrame && + flexContainerFrame->GetType() == nsGkAtoms::flexContainerFrame, + "anonymous flex items should only exist as children " + "of flex container frames"); + + // We need to push a null float containing block to be sure that + // "NeedsAnonFlexItem" will know we're not honoring floats for this + // inserted content. (In particular, this is necessary in order for + // NeedsAnonFlexItem's "GetGeometricParent" call to return the correct + // result.) We're not honoring floats on this content because it has the + // _flex container_ as its parent in the content tree. + nsFrameConstructorSaveState floatSaveState; + aState.PushFloatContainingBlock(nsnull, floatSaveState); + + FCItemIterator iter(aItems); + // Skip over things that _do_ need an anonymous flex item, because + // they're perfectly happy to go here -- they won't cause a reframe. + if (!iter.SkipItemsThatNeedAnonFlexItem(aState)) { + // We hit something that _doesn't_ need an anonymous flex item! + // Rebuild the flex container to bust it out. + RecreateFramesForContent(flexContainerFrame->GetContent(), true); + return true; + } + + // If we get here, then everything in |aItems| needs to be wrapped in + // an anonymous flex item. That's where it's already going - good! + } +#endif // MOZ_FLEXBOX + + // Situation #4 is a case when table pseudo-frames don't work out right ParentType parentType = GetParentType(aFrame); // If all the kids want a parent of the type that aFrame is, then we're all // set to go. Indeed, there won't be any table pseudo-frames created between