Bug 1419964: Remove AdjustParentFrameForAfterContent. r=mats

Instead, handle the display: contents case and the ::after case using
FindNextSibling.

The removal of line frames is moved up to not interfere with the insertion point
computation.

Also parentAfterFrame is renamed to nextSibling, which is more relevant to what
it was doing with display: contents.

MozReview-Commit-ID: 1cqclsGQlaw
This commit is contained in:
Emilio Cobos Álvarez 2017-11-22 23:57:17 +01:00
parent c1a9f55817
commit d39e1da3ff

View File

@ -6469,131 +6469,25 @@ nsCSSFrameConstructor::GetFloatContainingBlock(nsIFrame* aFrame)
return nullptr;
}
/**
* This function will check whether aContainer has :after generated content.
* If so, appending to it should actually insert. The return value is the
* parent to use for newly-appended content. *aAfterFrame points to the :after
* frame before which appended content should go, if there is one.
*/
static nsContainerFrame*
AdjustAppendParentForAfterContent(nsFrameManager* aFrameManager,
nsIContent* aContainer,
nsContainerFrame* aParentFrame,
nsIContent* aChild,
nsIFrame** aAfterFrame)
{
// If the parent frame may have an ::after pseudo or aContainer is a
// display:contents node or aContainer have display:contents children
// then we need to walk through the child frames to find the first one
// that is either a ::after frame for an ancestor of aChild or a frame
// that is for a node later in the document than aChild and return that
// in aAfterFrame.
if (nsLayoutUtils::HasPseudoStyle(aContainer, aParentFrame->StyleContext(),
CSSPseudoElementType::after,
aParentFrame->PresContext()) ||
aFrameManager->GetDisplayContentsStyleFor(aContainer) ||
aFrameManager->GetAllRegisteredDisplayContentsStylesIn(aContainer)) {
nsIFrame* afterFrame = nullptr;
nsContainerFrame* parent =
static_cast<nsContainerFrame*>(aParentFrame->LastContinuation());
bool done = false;
while (!done && parent) {
// Ensure that all normal flow children are on the principal child list.
parent->DrainSelfOverflowList();
nsIFrame* child = parent->GetChildList(nsIFrame::kPrincipalList).LastChild();
if (child && child->IsPseudoFrame(aContainer) &&
!child->IsGeneratedContentFrame()) {
// Drill down into non-generated pseudo frames of aContainer.
nsContainerFrame* childAsContainer = do_QueryFrame(child);
if (childAsContainer) {
parent = nsLayoutUtils::LastContinuationWithChild(childAsContainer);
continue;
}
}
for (; child; child = child->GetPrevSibling()) {
nsIContent* c = child->GetContent();
if (child->IsGeneratedContentFrame()) {
nsIContent* p = c->GetParent();
if (c->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter) {
if (!nsContentUtils::ContentIsDescendantOf(aChild, p) &&
p != aContainer &&
nsContentUtils::PositionIsBefore(p, aChild)) {
// ::after generated content for content earlier in the doc and not
// for an ancestor. "p != aContainer" may seem redundant but it
// checks if the ::after belongs to the XBL insertion point we're
// inserting aChild into (in which case ContentIsDescendantOf is
// false even though p == aContainer).
// See layout/reftests/bugs/482592-1a.xhtml for an example of that.
done = true;
break;
}
} else if (nsContentUtils::PositionIsBefore(p, aChild)) {
// Non-::after generated content for content earlier in the doc.
done = true;
break;
}
} else if (nsContentUtils::PositionIsBefore(c, aChild)) {
// Content is before aChild.
done = true;
break;
}
afterFrame = child;
}
parent = static_cast<nsContainerFrame*>(parent->GetPrevContinuation());
}
if (afterFrame) {
*aAfterFrame = afterFrame;
return afterFrame->GetParent();
}
}
*aAfterFrame = nullptr;
if (IsFramePartOfIBSplit(aParentFrame)) {
// We might be in a situation where the last part of the {ib} split was
// empty. Since we have no ::after pseudo-element, we do in fact want to be
// appending to that last part, so advance to it if needed. Note that here
// aParentFrame is the result of a GetLastIBSplitSibling call, so must be
// either the last or next to last ib-split sibling.
nsContainerFrame* trailingInline = GetIBSplitSibling(aParentFrame);
if (trailingInline) {
aParentFrame = trailingInline;
}
// Always make sure to look at the last continuation of the frame
// for the {ib} case, even if that continuation is empty. We
// don't do this for the non-ib-split-frame case, since in the
// other cases appending to the last nonempty continuation is fine
// and in fact not doing that can confuse code that doesn't know
// to pull kids from continuations other than its next one.
aParentFrame =
static_cast<nsContainerFrame*>(aParentFrame->LastContinuation());
}
return aParentFrame;
}
/**
* This function will get the previous sibling to use for an append operation.
* it takes a parent frame (must not be null) and its :after frame (may be
* null).
*
* It takes a parent frame (must not be null) and the next insertion sibling, if
* the parent content is display: contents or has ::after content (may be null).
*/
static nsIFrame*
FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aAfterFrame)
FindAppendPrevSibling(nsIFrame* aParentFrame, nsIFrame* aNextSibling)
{
if (aAfterFrame) {
NS_ASSERTION(aAfterFrame->GetParent() == aParentFrame, "Wrong parent");
NS_ASSERTION(aAfterFrame->GetPrevSibling() ||
aParentFrame->PrincipalChildList().FirstChild() == aAfterFrame,
":after frame must be on the principal child list here");
return aAfterFrame->GetPrevSibling();
}
aParentFrame->DrainSelfOverflowList();
if (aNextSibling) {
MOZ_ASSERT(aNextSibling->GetParent() == aParentFrame, "Wrong parent");
MOZ_ASSERT(aNextSibling->GetPrevSibling() ||
aParentFrame->PrincipalChildList().FirstChild() == aNextSibling,
"next sibling must be on the principal child list here");
return aNextSibling->GetPrevSibling();
}
return aParentFrame->GetChildList(kPrincipalList).LastChild();
}
@ -7637,7 +7531,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
// If the frame we are manipulating is a ib-split frame (that is, one
// that's been created as a result of a block-in-inline situation) then we
// need to append to the last ib-split sibling, not to the frame itself.
bool parentIBSplit = IsFramePartOfIBSplit(parentFrame);
const bool parentIBSplit = IsFramePartOfIBSplit(parentFrame);
if (parentIBSplit) {
#ifdef DEBUG
if (gNoisyContentUpdates) {
@ -7662,36 +7556,66 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
MOZ_ASSERT(!parentFrame->IsFieldSetFrame() && !parentFrame->IsDetailsFrame(),
"Parent frame should not be fieldset or details!");
// Deal with possible :after generated content on the parent
nsIFrame* parentAfterFrame;
nsContainerFrame* preAdjustedParentFrame = parentFrame;
parentFrame =
::AdjustAppendParentForAfterContent(this, insertion.mContainer, parentFrame,
aFirstNewContent, &parentAfterFrame);
// See if the containing block has :first-letter style applied.
bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
if (containingBlock) {
haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
haveFirstLineStyle =
ShouldHaveFirstLineStyle(containingBlock->GetContent(),
containingBlock->StyleContext());
// Deal with possible :after generated content on the parent, or display:
// contents.
nsIFrame* nextSibling = nullptr;
if (GetDisplayContentsStyleFor(insertion.mContainer) ||
nsLayoutUtils::GetAfterFrame(insertion.mContainer)) {
FlattenedChildIterator iter(insertion.mContainer);
iter.Seek(insertion.mContainer->GetLastChild());
StyleDisplay unused = UNSET_DISPLAY;
nextSibling = FindNextSibling(iter, unused);
}
if (nextSibling) {
parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
} else if (parentIBSplit) {
// We might be in a situation where the last part of the {ib} split was
// empty. Since we have no ::after pseudo-element, we do in fact want to be
// appending to that last part, so advance to it if needed. Note that here
// aParentFrame is the result of a GetLastIBSplitSibling call, so must be
// either the last or next to last ib-split sibling.
if (nsContainerFrame* trailingInline = GetIBSplitSibling(parentFrame)) {
parentFrame = trailingInline;
}
// Always make sure to look at the last continuation of the frame
// for the {ib} case, even if that continuation is empty. We
// don't do this for the non-ib-split-frame case, since in the
// other cases appending to the last nonempty continuation is fine
// and in fact not doing that can confuse code that doesn't know
// to pull kids from continuations other than its next one.
parentFrame =
static_cast<nsContainerFrame*>(parentFrame->LastContinuation());
}
nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
// See if the containing block has :first-letter style applied.
const bool haveFirstLetterStyle =
containingBlock && HasFirstLetterStyle(containingBlock);
const bool haveFirstLineStyle =
containingBlock &&
ShouldHaveFirstLineStyle(containingBlock->GetContent(),
containingBlock->StyleContext());
if (haveFirstLetterStyle) {
AutoWeakFrame wf(parentAfterFrame);
AutoWeakFrame wf(nextSibling);
// Before we get going, remove the current letter frames
RemoveLetterFrames(mPresShell, containingBlock);
if (parentAfterFrame && !wf) {
// Ouch, parentAfterFrame was a letter frame and we just deleted it!
// Retry AdjustAppendParentForAfterContent; fortunately this is rare.
parentFrame =
::AdjustAppendParentForAfterContent(this, insertion.mContainer,
preAdjustedParentFrame,
aFirstNewContent, &parentAfterFrame);
if (parentFrame != preAdjustedParentFrame) {
// Reget nextSibling, since we may have killed it.
//
// FIXME(emilio): This kinda sucks! :(
if (nextSibling && !wf) {
FlattenedChildIterator iter(insertion.mContainer);
iter.Seek(insertion.mContainer->GetLastChild());
StyleDisplay unused = UNSET_DISPLAY;
nextSibling = FindNextSibling(iter, unused);
if (nextSibling) {
parentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
containingBlock = GetFloatContainingBlock(parentFrame);
}
}
@ -7717,7 +7641,8 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
LayoutFrameType frameType = parentFrame->Type();
FlattenedChildIterator iter(aContainer);
bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
const bool haveNoXBLChildren = !iter.XBLInvolved() || !iter.GetNextChild();
AutoFrameConstructionItemList items(this);
if (aFirstNewContent->GetPreviousSibling() &&
GetParentType(frameType) == eTypeBlock &&
@ -7742,7 +7667,7 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
AddFrameConstructionItems(state, child, false, insertion, items);
}
nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame);
nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, nextSibling);
// Perform special check for diddling around with the frames in
// a ib-split inline frame.
@ -7765,8 +7690,11 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
!prevSibling->IsInlineOutside() ||
prevSibling->IsBrFrame());
// :after content can't be <br> so no need to check it
items.SetLineBoundaryAtEnd(!parentAfterFrame ||
!parentAfterFrame->IsInlineOutside());
//
// FIXME(emilio): A display: contents sibling could! Write a test-case and
// fix.
items.SetLineBoundaryAtEnd(
!nextSibling || !nextSibling->IsInlineOutside());
}
// To suppress whitespace-only text frames, we have to verify that
// our container's DOM child list matches its flattened tree child list.