Bug 917386 - Make frame construction handle trees of nsIAnonymousContentCreator::ContentInfo objects, including support for associating CSS pseudo-elements with deeply nested anonymous elements. r=bz
authorJonathan Watt <jwatt@jwatt.org>
Tue, 01 Oct 2013 22:51:29 +0100
changeset 149567 7eca799dbebcedda620fa4c0a4351f036f14b85c
parent 149566 55950ee9ef60c87fbfcf7af859221768352c4ef3
child 149568 d1799802f5c13226c62cc8558ffc7d7a8b5fb7c2
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbz
bugs917386
milestone27.0a1
Bug 917386 - Make frame construction handle trees of nsIAnonymousContentCreator::ContentInfo objects, including support for associating CSS pseudo-elements with deeply nested anonymous elements. r=bz
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/nsIAnonymousContentCreator.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1720,17 +1720,18 @@ nsCSSFrameConstructor::CreateGeneratedCo
     if (content) {
       container->AppendChildTo(content, false);
     }
   }
 
   AddFrameConstructionItemsInternal(aState, container, aParentFrame, elemName,
                                     kNameSpaceID_None, true,
                                     pseudoStyleContext,
-                                    ITEM_IS_GENERATED_CONTENT, aItems);
+                                    ITEM_IS_GENERATED_CONTENT, nullptr,
+                                    aItems);
 }
     
 /****************************************************
  **  BEGIN TABLE SECTION
  ****************************************************/
 
 // The term pseudo frame is being used instead of anonymous frame, since anonymous
 // frame has been used elsewhere to refer to frames that have generated content
@@ -1913,16 +1914,20 @@ nsCSSFrameConstructor::ConstructTable(ns
   newFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
   if ((display->IsRelativelyPositionedStyle() ||
        display->IsAbsolutelyPositionedStyle() ||
        (display->HasTransformStyle() &&
         aParentFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms))) &&
       !aParentFrame->IsSVGText()) {
     aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
   }
+  NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
+               "nsIAnonymousContentCreator::CreateAnonymousContent "
+               "implementations for table frames are not currently expected "
+               "to output a list where the items have their own children");
   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
     ConstructFramesFromItemList(aState, aItem.mChildItems,
                                 innerFrame, childItems);
   } else {
     ProcessChildren(aState, content, styleContext, innerFrame,
                     true, childItems, false, aItem.mPendingBinding);
   }
 
@@ -1957,16 +1962,20 @@ nsCSSFrameConstructor::ConstructTableRow
   if (kNameSpaceID_MathML == nameSpaceID)
     newFrame = NS_NewMathMLmtrFrame(mPresShell, styleContext);
   else
     newFrame = NS_NewTableRowFrame(mPresShell, styleContext);
 
   InitAndRestoreFrame(aState, content, aParentFrame, newFrame);
 
   nsFrameItems childItems;
+  NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
+               "nsIAnonymousContentCreator::CreateAnonymousContent "
+               "implementations for table frames are not currently expected "
+               "to output a list where the items have their own children");
   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
     ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
                                 childItems);
   } else {
     ProcessChildren(aState, content, styleContext, newFrame,
                     true, childItems, false, aItem.mPendingBinding);
   }
 
@@ -2055,16 +2064,20 @@ nsCSSFrameConstructor::ConstructTableCel
   } else {
     cellInnerFrame = NS_NewBlockFormattingContext(mPresShell, innerPseudoStyle);
     isBlock = true;
   }
 
   InitAndRestoreFrame(aState, content, newFrame, cellInnerFrame);
 
   nsFrameItems childItems;
+  NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
+               "nsIAnonymousContentCreator::CreateAnonymousContent "
+               "implementations for table frames are not currently expected "
+               "to output a list where the items have their own children");
   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
     // Need to push ourselves as a float containing block.
     // XXXbz it might be nice to work on getting the parent
     // FrameConstructionItem down into ProcessChildren and just making use of
     // the push there, but that's a bit of work.
     nsFrameConstructorSaveState floatSaveState;
     if (!isBlock) { /* MathML case */
       aState.PushFloatContainingBlock(nullptr, floatSaveState);
@@ -2350,17 +2363,17 @@ nsCSSFrameConstructor::ConstructDocEleme
       // FrameConstructionData/Item, then we'd need the right function
       // here... but would probably be able to get away with less code in this
       // function in general.
       // Use a null PendingBinding, since our binding is not in fact pending.
       static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
       nsRefPtr<nsStyleContext> extraRef(styleContext);
       FrameConstructionItem item(&rootSVGData, aDocElement,
                                  aDocElement->Tag(), kNameSpaceID_SVG,
-                                 nullptr, extraRef.forget(), true);
+                                 nullptr, extraRef.forget(), true, nullptr);
 
       nsFrameItems frameItems;
       contentFrame = ConstructOuterSVG(state, item, mDocElementContainingBlock,
                                        styleContext->StyleDisplay(),
                                        frameItems);
       newFrame = frameItems.FirstChild();
       NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
     } else {
@@ -2376,17 +2389,17 @@ nsCSSFrameConstructor::ConstructDocEleme
       // FrameConstructionData/Item, then we'd need the right function
       // here... but would probably be able to get away with less code in this
       // function in general.
       // Use a null PendingBinding, since our binding is not in fact pending.
       static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
       nsRefPtr<nsStyleContext> extraRef(styleContext);
       FrameConstructionItem item(&rootTableData, aDocElement,
                                  aDocElement->Tag(), kNameSpaceID_None,
-                                 nullptr, extraRef.forget(), true);
+                                 nullptr, extraRef.forget(), true, nullptr);
 
       nsFrameItems frameItems;
       // if the document is a table then just populate it.
       contentFrame = ConstructTable(state, item, mDocElementContainingBlock,
                                     styleContext->StyleDisplay(),
                                     frameItems);
       newFrame = frameItems.FirstChild();
       NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
@@ -3590,16 +3603,25 @@ nsCSSFrameConstructor::ConstructFrameFro
            maybeAbsoluteContainingBlockDisplay->IsRelativelyPositionedStyle() ||
            (maybeAbsoluteContainingBlockDisplay->HasTransformStyle() &&
             cb->IsFrameOfType(nsIFrame::eSupportsCSSTransforms))) &&
           !cb->IsSVGText()) {
         aState.PushAbsoluteContainingBlock(cb, primaryFrame, absoluteSaveState);
       }
     }
 
+    if (!aItem.mAnonChildren.IsEmpty()) {
+      NS_ASSERTION(!(bits & FCDATA_USE_CHILD_ITEMS),
+                   "We should not have both anonymous and non-anonymous "
+                   "children in a given FrameConstructorItem");
+      AddFCItemsForAnonymousContent(aState, newFrame, aItem.mAnonChildren,
+                                    aItem.mChildItems);
+      bits |= FCDATA_USE_CHILD_ITEMS;
+    }
+
     if (bits & FCDATA_USE_CHILD_ITEMS) {
       nsFrameConstructorSaveState floatSaveState;
 
       if (ShouldSuppressFloatingOfDescendants(newFrame)) {
         aState.PushFloatContainingBlock(nullptr, floatSaveState);
       } else if (newFrame->IsFloatContainingBlock()) {
         aState.PushFloatContainingBlock(newFrame, floatSaveState);
       }
@@ -3706,16 +3728,20 @@ nsCSSFrameConstructor::CreateAnonymousFr
   nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
   NS_ASSERTION(creator,
                "How can that happen if we have nodes to construct frames for?");
 
   for (uint32_t i=0; i < count; i++) {
     nsIContent* content = newAnonymousItems[i].mContent;
     NS_ASSERTION(content, "null anonymous content?");
     NS_ASSERTION(!newAnonymousItems[i].mStyleContext, "Unexpected style context");
+    NS_ASSERTION(newAnonymousItems[i].mChildren.IsEmpty(),
+                 "This method is not currently used with frames that implement "
+                 "nsIAnonymousContentCreator::CreateAnonymousContent to "
+                 "output a list where the items have their own children");
 
     nsIFrame* newFrame = creator->CreateFrameFor(content);
     if (newFrame) {
       NS_ASSERTION(content->GetPrimaryFrame(),
                    "Content must have a primary frame now");
       aChildItems.AddChild(newFrame);
     }
     else {
@@ -3746,16 +3772,43 @@ SetFlagsOnSubtree(nsIContent *aNode, uin
   uint32_t count;
   nsIContent * const *children = aNode->GetChildArray(&count);
 
   for (uint32_t index = 0; index < count; ++index) {
     SetFlagsOnSubtree(children[index], aFlagsToSet);
   }
 }
 
+/**
+ * This function takes a tree of nsIAnonymousContentCreator::ContentInfo
+ * objects where the nsIContent nodes have just been created, and appends the
+ * nsIContent children in the tree to their parent. The leaf nsIContent objects
+ * are appended first to minimize the number of notifications that are sent
+ * out (i.e. by appending as many descendants as posible while their parent is
+ * not yet in the document tree).
+ *
+ * This function is used simply as a convenience so that implementations of
+ * nsIAnonymousContentCreator::CreateAnonymousContent don't all have to have
+ * their own code to connect the elements that they create.
+ */
+static void
+ConnectAnonymousTreeDescendants(nsIContent* aParent,
+                                nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
+{
+  uint32_t count = aContent.Length();
+  for (uint32_t i=0; i < count; i++) {
+    nsIContent* content = aContent[i].mContent;
+    NS_ASSERTION(content, "null anonymous content?");
+
+    ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
+
+    aParent->AppendChildTo(content, false);
+  }
+}
+
 nsresult
 nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
                                            nsIFrame* aParentFrame,
                                            nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent)
 {
   nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
   if (!creator)
     return NS_OK;
@@ -3772,16 +3825,18 @@ nsCSSFrameConstructor::GetAnonymousConte
     // least-surprise CSS binding until we do the SVG specified
     // cascading rules for <svg:use> - bug 265894
     if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) {
       content->SetFlags(NODE_IS_ANONYMOUS_ROOT);
     } else {
       content->SetIsNativeAnonymousRoot();
     }
 
+    ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
+
     bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE);
     rv = content->BindToTree(mDocument, aParent, aParent, true);
     // If the anonymous content creator requested that the content should be
     // editable, honor its request.
     // We need to set the flag on the whole subtree, because existing
     // children's flags have already been set as part of the BindToTree operation.
     if (anonContentIsEditable) {
       NS_ASSERTION(aParentFrame->GetType() == nsGkAtoms::textInputFrame,
@@ -4617,16 +4672,20 @@ nsCSSFrameConstructor::ConstructFrameWit
     // The frame we're constructing will be the root element frame.
     // Set mRootElementFrame before processing children.
     mRootElementFrame = newFrame;
   }
 
   nsFrameItems childItems;
 
   // Process children
+  NS_ASSERTION(aItem.mAnonChildren.IsEmpty(),
+               "nsIAnonymousContentCreator::CreateAnonymousContent should not "
+               "be implemented for frames for which we explicitly create an "
+               "anonymous child to wrap its child frames");
   if (aItem.mFCData->mBits & FCDATA_USE_CHILD_ITEMS) {
     ConstructFramesFromItemList(aState, aItem.mChildItems,
                                 innerFrame, childItems);
   } else {
     ProcessChildren(aState, content, styleContext, innerFrame,
                     true, childItems, false, aItem.mPendingBinding);
   }
 
@@ -4946,17 +5005,18 @@ nsCSSFrameConstructor::AddPageBreakItem(
                  NS_STYLE_DISPLAY_BLOCK, "Unexpected display");
 
   static const FrameConstructionData sPageBreakData =
     FCDATA_DECL(FCDATA_SKIP_FRAMESET, NS_NewPageBreakFrame);
 
   // Lie about the tag and namespace so we don't trigger anything
   // interesting during frame construction.
   aItems.AppendItem(&sPageBreakData, aContent, nsCSSAnonBoxes::pageBreak,
-                    kNameSpaceID_None, nullptr, pseudoStyle.forget(), true);
+                    kNameSpaceID_None, nullptr, pseudoStyle.forget(), true,
+                    nullptr);
 }
 
 void
 nsCSSFrameConstructor::ConstructFrame(nsFrameConstructorState& aState,
                                       nsIContent*              aContent,
                                       nsIFrame*                aParentFrame,
                                       nsFrameItems&            aFrameItems)
 
@@ -5035,17 +5095,17 @@ nsCSSFrameConstructor::AddFrameConstruct
       aParentFrame->GetParent() &&
       aParentFrame->GetParent()->GetType() == nsGkAtoms::svgTextFrame2) {
     flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
   }
   AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
                                     aContent->Tag(), aContent->GetNameSpaceID(),
                                     aSuppressWhiteSpaceOptimizations,
                                     styleContext,
-                                    flags,
+                                    flags, nullptr,
                                     aItems);
 }
 
 /* static */ void
 nsCSSFrameConstructor::SetAsUndisplayedContent(FrameConstructionItemList& aList,
                                                nsIContent* aContent,
                                                nsStyleContext* aStyleContext,
                                                bool aIsGeneratedContent)
@@ -5065,16 +5125,17 @@ void
 nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
                                                          nsIContent* aContent,
                                                          nsIFrame* aParentFrame,
                                                          nsIAtom* aTag,
                                                          int32_t aNameSpaceID,
                                                          bool aSuppressWhiteSpaceOptimizations,
                                                          nsStyleContext* aStyleContext,
                                                          uint32_t aFlags,
+                                                         nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
                                                          FrameConstructionItemList& aItems)
 {
   NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT) ||
                   aContent->IsElement(),
                   "Shouldn't get anything else here!");
 
   // The following code allows the user to specify the base tag
   // of an element using XBL.  XUL and HTML objects (like boxes, menus, etc.)
@@ -5244,17 +5305,17 @@ nsCSSFrameConstructor::AddFrameConstruct
 
   if (canHavePageBreak && display->mBreakBefore) {
     AddPageBreakItem(aContent, aStyleContext, aItems);
   }
 
   FrameConstructionItem* item =
     aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
                       pendingBinding, styleContext.forget(),
-                      aSuppressWhiteSpaceOptimizations);
+                      aSuppressWhiteSpaceOptimizations, aAnonChildren);
   if (!item) {
     if (isGeneratedContent) {
       aContent->UnbindFromTree();
     }
     return;
   }
 
   item->mIsText = isText;
@@ -8112,17 +8173,17 @@ nsCSSFrameConstructor::ReplicateFixedFra
       FrameConstructionItemList items;
       AddFrameConstructionItemsInternal(state, content, canvasFrame,
                                         content->Tag(),
                                         content->GetNameSpaceID(),
                                         true,
                                         styleContext,
                                         ITEM_ALLOW_XBL_BASE |
                                           ITEM_ALLOW_PAGE_BREAK,
-                                        items);
+                                        nullptr, items);
       items.SetTriedConstructingFrames();
       for (FCItemIterator iter(items); !iter.IsDone(); iter.Next()) {
         NS_ASSERTION(iter.item().DesiredParentType() ==
                        GetParentType(canvasFrame),
                      "This is not going to work");
         ConstructFramesFromItem(state, iter, canvasFrame, fixedPlaceholders);
       }
     }
@@ -8759,17 +8820,17 @@ nsCSSFrameConstructor::CreateNeededAnonF
                                 // Use the content of our parent frame
                                 parentContent,
                                 // Lie about the tag; it doesn't matter anyway
                                 pseudoType,
                                 iter.item().mNameSpaceID,
                                 // no pending binding
                                 nullptr,
                                 wrapperStyle.forget(),
-                                true);
+                                true, nullptr);
 
     newItem->mIsAllInline = newItem->mHasInlineEnds =
       newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
     newItem->mIsBlock = !newItem->mIsAllInline;
 
     NS_ABORT_IF_FALSE(!newItem->mIsAllInline && newItem->mIsBlock,
                       "expecting anonymous flex items to be block-level "
                       "(this will make a difference when we encounter "
@@ -8965,17 +9026,17 @@ nsCSSFrameConstructor::CreateNeededTable
                                 pseudoType,
                                 // The namespace does matter, however; it needs
                                 // to match that of our first child item to
                                 // match the old behavior
                                 iter.item().mNameSpaceID,
                                 // no pending binding
                                 nullptr,
                                 wrapperStyle.forget(),
-                                true);
+                                true, nullptr);
 
     // Here we're cheating a tad... technically, table-internal items should be
     // inline if aParentFrame is inline, but they'll get wrapped in an
     // inline-table in the end, so it'll all work out.  In any case, arguably
     // we don't need to maintain this state at this point... but it's better
     // to, I guess.
     newItem->mIsAllInline = newItem->mHasInlineEnds =
       newItem->mStyleContext->StyleDisplay()->IsInlineOutsideStyle();
@@ -9026,16 +9087,69 @@ nsCSSFrameConstructor::ConstructFramesFr
     ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
   }
 
   NS_ASSERTION(!aState.mHavePendingPopupgroup,
                "Should have proccessed it by now");
 }
 
 void
+nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
+            nsFrameConstructorState& aState,
+            nsIFrame* aFrame,
+            nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
+            FrameConstructionItemList& aItemsToConstruct,
+            uint32_t aExtraFlags)
+{
+  for (uint32_t i = 0; i < aAnonymousItems.Length(); ++i) {
+    nsIContent* content = aAnonymousItems[i].mContent;
+#ifdef DEBUG
+    nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
+    NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
+                 "If you need to use CreateFrameFor, you need to call "
+                 "CreateAnonymousFrames manually and not follow the standard "
+                 "ProcessChildren() codepath for this frame");
+#endif
+    // Assert some things about this content
+    NS_ABORT_IF_FALSE(!(content->GetFlags() &
+                        (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
+                      "Should not be marked as needing frames");
+    NS_ABORT_IF_FALSE(!content->IsElement() ||
+                      !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS),
+                      "Should have no pending restyle flags");
+    NS_ABORT_IF_FALSE(!content->GetPrimaryFrame(),
+                      "Should have no existing frame");
+    NS_ABORT_IF_FALSE(!content->IsNodeOfType(nsINode::eCOMMENT) &&
+                      !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
+                      "Why is someone creating garbage anonymous content");
+
+    nsRefPtr<nsStyleContext> styleContext;
+    TreeMatchContext::AutoFlexItemStyleFixupSkipper
+      flexItemStyleFixupSkipper(aState.mTreeMatchContext);
+    if (aAnonymousItems[i].mStyleContext) {
+      styleContext = aAnonymousItems[i].mStyleContext.forget();
+    } else {
+      styleContext = ResolveStyleContext(aFrame, content, &aState);
+    }
+
+    nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
+    if (!aAnonymousItems[i].mChildren.IsEmpty()) {
+      anonChildren = &aAnonymousItems[i].mChildren;
+    }
+
+    uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK | aExtraFlags;
+
+    AddFrameConstructionItemsInternal(aState, content, aFrame,
+                                      content->Tag(), content->GetNameSpaceID(),
+                                      true, styleContext, flags,
+                                      anonChildren, aItemsToConstruct);
+  }
+}
+
+void
 nsCSSFrameConstructor::ProcessChildren(nsFrameConstructorState& aState,
                                        nsIContent*              aContent,
                                        nsStyleContext*          aStyleContext,
                                        nsIFrame*                aFrame,
                                        const bool               aCanHaveGeneratedContent,
                                        nsFrameItems&            aFrameItems,
                                        const bool               aAllowBlockStyles,
                                        PendingBinding*          aPendingBinding,
@@ -9085,55 +9199,24 @@ nsCSSFrameConstructor::ProcessChildren(n
     itemsToConstruct.SetLineBoundaryAtEnd(true);
   }
 
   // Create any anonymous frames we need here.  This must happen before the
   // non-anonymous children are processed to ensure that popups are never
   // constructed before the popupset.
   nsAutoTArray<nsIAnonymousContentCreator::ContentInfo, 4> anonymousItems;
   GetAnonymousContent(aContent, aPossiblyLeafFrame, anonymousItems);
+#ifdef DEBUG
   for (uint32_t i = 0; i < anonymousItems.Length(); ++i) {
-    nsIContent* content = anonymousItems[i].mContent;
-#ifdef DEBUG
-    nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
-    NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
-                 "If you need to use CreateFrameFor, you need to call "
-                 "CreateAnonymousFrames manually and not follow the standard "
-                 "ProcessChildren() codepath for this frame");
+    NS_ABORT_IF_FALSE(anonymousItems[i].mContent->IsRootOfAnonymousSubtree(),
+                      "Content should know it's an anonymous subtree");
+  }
 #endif
-    // Assert some things about this content
-    NS_ABORT_IF_FALSE(!(content->GetFlags() &
-                        (NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
-                      "Should not be marked as needing frames");
-    NS_ABORT_IF_FALSE(!content->IsElement() ||
-                      !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS),
-                      "Should have no pending restyle flags");
-    NS_ABORT_IF_FALSE(!content->GetPrimaryFrame(),
-                      "Should have no existing frame");
-    NS_ABORT_IF_FALSE(!content->IsNodeOfType(nsINode::eCOMMENT) &&
-                      !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
-                      "Why is someone creating garbage anonymous content");
-    NS_ABORT_IF_FALSE(content->IsRootOfAnonymousSubtree(),
-                      "Content should know it's an anonymous subtree");
-
-    nsRefPtr<nsStyleContext> styleContext;
-    TreeMatchContext::AutoFlexItemStyleFixupSkipper
-      flexItemStyleFixupSkipper(aState.mTreeMatchContext);
-    if (anonymousItems[i].mStyleContext) {
-      styleContext = anonymousItems[i].mStyleContext.forget();
-    } else {
-      styleContext = ResolveStyleContext(aFrame, content, &aState);
-    }
-
-    AddFrameConstructionItemsInternal(aState, content, aFrame,
-                                      content->Tag(), content->GetNameSpaceID(),
-                                      true, styleContext,
-                                      ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK,
-                                      itemsToConstruct);
-  }
+  AddFCItemsForAnonymousContent(aState, aFrame, anonymousItems,
+                                itemsToConstruct);
 
   if (!aPossiblyLeafFrame->IsLeaf()) {
     // :before/:after content should have the same style context parent
     // as normal kids.
     // Note that we don't use this style context for looking up things like
     // special block styles because in some cases involving table pseudo-frames
     // it has nothing to do with the parent frame's desired behavior.
     nsStyleContext* styleContext;
@@ -10089,17 +10172,17 @@ nsCSSFrameConstructor::CreateListBoxCont
     }
 
     BeginUpdate();
 
     FrameConstructionItemList items;
     AddFrameConstructionItemsInternal(state, aChild, aParentFrame,
                                       aChild->Tag(), aChild->GetNameSpaceID(),
                                       true, styleContext,
-                                      ITEM_ALLOW_XBL_BASE, items);
+                                      ITEM_ALLOW_XBL_BASE, nullptr, items);
     ConstructFramesFromItemList(state, items, aParentFrame, frameItems);
 
     nsIFrame* newFrame = frameItems.FirstChild();
     *aNewFrame = newFrame;
 
     if (newFrame) {
       // Notify the parent frame
       if (aIsAppend)
@@ -10423,63 +10506,74 @@ nsCSSFrameConstructor::BuildInlineChildI
   
   if (!aItemIsWithinSVGText) {
     // Probe for generated content before
     CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
                                nsCSSPseudoElements::ePseudo_before,
                                aParentItem.mChildItems);
   }
 
-  FlattenedChildIterator iter(parentContent);
-  for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
-    // Get the parent of the content and check if it is a XBL children element
-    // (if the content is a children element then contentParent != parentContent because the
-    // FlattenedChildIterator will transitively iterate through <xbl:children>
-    // for default content). Push the children element as an ancestor here because
-    // it does not have a frame and would not otherwise be pushed as an ancestor.
-    nsIContent* contentParent = content->GetParent();
-    MOZ_ASSERT(contentParent, "Parent must be non-null because we are iterating children.");
-    MOZ_ASSERT(contentParent->IsElement());
-    bool pushInsertionPoint = contentParent != parentContent &&
-      aState.mTreeMatchContext.mAncestorFilter.HasFilter();
-    TreeMatchContext::AutoAncestorPusher
-      insertionPointPusher(pushInsertionPoint, aState.mTreeMatchContext,
-                           contentParent->AsElement());
-
-    // Manually check for comments/PIs, since we don't have a frame to pass to
-    // AddFrameConstructionItems.  We know our parent is a non-replaced inline,
-    // so there is no need to do the NeedFrameFor check.
-    content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
-    if (content->IsNodeOfType(nsINode::eCOMMENT) ||
-        content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
-      continue;
-    }
-    if (content->IsElement()) {
-      // See comment explaining why we need to remove the "is possible
-      // restyle root" flags in AddFrameConstructionItems.  But note
-      // that we can remove all restyle flags, just like in
-      // ProcessChildren and for the same reason.
-      content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
-    }
-
-    nsRefPtr<nsStyleContext> childContext =
-      ResolveStyleContext(parentStyleContext, content, &aState);
-
-    uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
-    if (aItemIsWithinSVGText) {
-      flags |= ITEM_IS_WITHIN_SVG_TEXT;
-    }
-    if (aItemAllowsTextPathChild && aParentItem.mIsForSVGAElement) {
-      flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
-    }
-    AddFrameConstructionItemsInternal(aState, content, nullptr, content->Tag(),
-                                      content->GetNameSpaceID(),
-                                      iter.XBLInvolved(), childContext,
-                                      flags,
-                                      aParentItem.mChildItems);
+  uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
+  if (aItemIsWithinSVGText) {
+    flags |= ITEM_IS_WITHIN_SVG_TEXT;
+  }
+  if (aItemAllowsTextPathChild && aParentItem.mIsForSVGAElement) {
+    flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
+  }
+
+  if (!aParentItem.mAnonChildren.IsEmpty()) {
+    // Use the anon-children list instead of the content tree child list so
+    // that we use any special style context that should be associated with
+    // the children, and so that we won't try to construct grandchildren frame
+    // constructor items before the frame is available for their parent.
+    AddFCItemsForAnonymousContent(aState, nullptr, aParentItem.mAnonChildren,
+                                  aParentItem.mChildItems, flags);
+  } else {
+    // Use the content tree child list:
+    FlattenedChildIterator iter(parentContent);
+    for (nsIContent* content = iter.GetNextChild(); content; content = iter.GetNextChild()) {
+      // Get the parent of the content and check if it is a XBL children element
+      // (if the content is a children element then contentParent != parentContent because the
+      // FlattenedChildIterator will transitively iterate through <xbl:children>
+      // for default content). Push the children element as an ancestor here because
+      // it does not have a frame and would not otherwise be pushed as an ancestor.
+      nsIContent* contentParent = content->GetParent();
+      MOZ_ASSERT(contentParent, "Parent must be non-null because we are iterating children.");
+      MOZ_ASSERT(contentParent->IsElement());
+      bool pushInsertionPoint = contentParent != parentContent &&
+        aState.mTreeMatchContext.mAncestorFilter.HasFilter();
+      TreeMatchContext::AutoAncestorPusher
+        insertionPointPusher(pushInsertionPoint, aState.mTreeMatchContext,
+                             contentParent->AsElement());
+
+      // Manually check for comments/PIs, since we don't have a frame to pass to
+      // AddFrameConstructionItems.  We know our parent is a non-replaced inline,
+      // so there is no need to do the NeedFrameFor check.
+      content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
+      if (content->IsNodeOfType(nsINode::eCOMMENT) ||
+          content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
+        continue;
+      }
+      if (content->IsElement()) {
+        // See comment explaining why we need to remove the "is possible
+        // restyle root" flags in AddFrameConstructionItems.  But note
+        // that we can remove all restyle flags, just like in
+        // ProcessChildren and for the same reason.
+        content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
+      }
+
+      nsRefPtr<nsStyleContext> childContext =
+        ResolveStyleContext(parentStyleContext, content, &aState);
+
+      AddFrameConstructionItemsInternal(aState, content, nullptr, content->Tag(),
+                                        content->GetNameSpaceID(),
+                                        iter.XBLInvolved(), childContext,
+                                        flags, nullptr,
+                                        aParentItem.mChildItems);
+    }
   }
 
   if (!aItemIsWithinSVGText) {
     // Probe for generated content after
     CreateGeneratedContentItem(aState, nullptr, parentContent, parentStyleContext,
                                nsCSSPseudoElements::ePseudo_after,
                                aParentItem.mChildItems);
   }
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -710,22 +710,24 @@ private:
     // skip constructing whitespace frames for this item or items
     // around it cannot be performed.
     FrameConstructionItem* AppendItem(const FrameConstructionData* aFCData,
                                       nsIContent* aContent,
                                       nsIAtom* aTag,
                                       int32_t aNameSpaceID,
                                       PendingBinding* aPendingBinding,
                                       already_AddRefed<nsStyleContext> aStyleContext,
-                                      bool aSuppressWhiteSpaceOptimizations)
+                                      bool aSuppressWhiteSpaceOptimizations,
+                                      nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren)
     {
       FrameConstructionItem* item =
         new FrameConstructionItem(aFCData, aContent, aTag, aNameSpaceID,
                                   aPendingBinding, aStyleContext,
-                                  aSuppressWhiteSpaceOptimizations);
+                                  aSuppressWhiteSpaceOptimizations,
+                                  aAnonChildren);
       PR_APPEND_LINK(item, &mItems);
       ++mItemCount;
       ++mDesiredParentCounts[item->DesiredParentType()];
       return item;
     }
 
     void AppendUndisplayedItem(nsIContent* aContent,
                                nsStyleContext* aStyleContext) {
@@ -893,26 +895,39 @@ private:
     // No need to PR_INIT_CLIST in the constructor because the only
     // place that creates us immediately appends us.
     FrameConstructionItem(const FrameConstructionData* aFCData,
                           nsIContent* aContent,
                           nsIAtom* aTag,
                           int32_t aNameSpaceID,
                           PendingBinding* aPendingBinding,
                           already_AddRefed<nsStyleContext> aStyleContext,
-                          bool aSuppressWhiteSpaceOptimizations) :
+                          bool aSuppressWhiteSpaceOptimizations,
+                          nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren) :
       mFCData(aFCData), mContent(aContent), mTag(aTag),
       mNameSpaceID(aNameSpaceID),
       mPendingBinding(aPendingBinding), mStyleContext(aStyleContext),
       mSuppressWhiteSpaceOptimizations(aSuppressWhiteSpaceOptimizations),
       mIsText(false), mIsGeneratedContent(false),
       mIsRootPopupgroup(false), mIsAllInline(false), mIsBlock(false),
       mHasInlineEnds(false), mIsPopup(false),
       mIsLineParticipant(false), mIsForSVGAElement(false)
-    {}
+    {
+      if (aAnonChildren) {
+        NS_ASSERTION(!(mFCData->mBits & FCDATA_FUNC_IS_FULL_CTOR) ||
+                     mFCData->mFullConstructor ==
+                       &nsCSSFrameConstructor::ConstructInline,
+                     "This is going to fail");
+        NS_ASSERTION(!(mFCData->mBits & FCDATA_USE_CHILD_ITEMS),
+                     "nsIAnonymousContentCreator::CreateAnonymousContent "
+                     "implementations should not output a list where the "
+                     "items have children in this case");
+        mAnonChildren.SwapElements(*aAnonChildren);
+      }
+    }
     ~FrameConstructionItem() {
       if (mIsGeneratedContent) {
         mContent->UnbindFromTree();
         NS_RELEASE(mContent);
       }
     }
 
     ParentType DesiredParentType() {
@@ -983,16 +998,32 @@ private:
     // Whether this item should be treated as a line participant
     bool mIsLineParticipant;
     // Whether this item is for an SVG <a> element
     bool mIsForSVGAElement;
 
     // Child frame construction items.
     FrameConstructionItemList mChildItems;
 
+    // ContentInfo list for children that have yet to have
+    // FrameConstructionItem objects created for them. This exists because
+    // AddFrameConstructionItemsInternal needs a valid frame, but in the case
+    // that nsIAnonymousContentCreator::CreateAnonymousContent returns items
+    // that have their own children (so we have a tree of ContentInfo objects
+    // rather than a flat list) we don't yet have a frame to provide to
+    // AddFrameConstructionItemsInternal in order to create the items for the
+    // grandchildren. That prevents FrameConstructionItems from being created
+    // for these grandchildren (and any descendants that they may have),
+    // otherwise they could have been added to the mChildItems member of their
+    // parent FrameConstructionItem. As it is, the grandchildren ContentInfo
+    // list has to be stored in this mAnonChildren member in order to delay
+    // construction of the FrameConstructionItems for the grandchildren until
+    // a frame has been created for their parent item.
+    nsTArray<nsIAnonymousContentCreator::ContentInfo> mAnonChildren;
+
   private:
     FrameConstructionItem(const FrameConstructionItem& aOther) MOZ_DELETE; /* not implemented */
   };
 
   /**
    * Function to create the anonymous flex items that we need.
    * aParentFrame _must_ be a nsFlexContainerFrame -- the caller is responsible
    * for checking this.
@@ -1138,16 +1169,17 @@ private:
   void AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
                                          nsIContent*              aContent,
                                          nsIFrame*                aParentFrame,
                                          nsIAtom*                 aTag,
                                          int32_t                  aNameSpaceID,
                                          bool                     aSuppressWhiteSpaceOptimizations,
                                          nsStyleContext*          aStyleContext,
                                          uint32_t                 aFlags,
+                                         nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
                                          FrameConstructionItemList& aItems);
 
   /**
    * Construct frames for the given item list and parent frame, and put the
    * resulting frames in aFrameItems.
    */
   void ConstructFramesFromItemList(nsFrameConstructorState& aState,
                                    FrameConstructionItemList& aItems,
@@ -1287,16 +1319,28 @@ private:
    */
   nsIFrame* ConstructNonScrollableBlock(nsFrameConstructorState& aState,
                                         FrameConstructionItem&   aItem,
                                         nsIFrame*                aParentFrame,
                                         const nsStyleDisplay*    aDisplay,
                                         nsFrameItems&            aFrameItems);
 
   /**
+   * This adds FrameConstructionItem objects to aItemsToConstruct for the
+   * anonymous content returned by an nsIAnonymousContentCreator::
+   * CreateAnonymousContent implementation.
+   */
+  void AddFCItemsForAnonymousContent(
+            nsFrameConstructorState& aState,
+            nsIFrame* aFrame,
+            nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
+            FrameConstructionItemList& aItemsToConstruct,
+            uint32_t aExtraFlags = 0);
+
+  /**
    * Construct the frames for the children of aContent.  "children" is defined
    * as "whatever FlattenedChildIterator returns for aContent".  This means we're
    * basically operating on children in the "flattened tree" per sXBL/XBL2.
    * This method will also handle constructing ::before, ::after,
    * ::first-letter, and ::first-line frames, as needed and if allowed.
    *
    * If the parent is a float containing block, this method will handle pushing
    * it as the float containing block in aState (so there's no need for callers
--- a/layout/generic/nsIAnonymousContentCreator.h
+++ b/layout/generic/nsIAnonymousContentCreator.h
@@ -36,30 +36,35 @@ public:
     {}
 
     ContentInfo(nsIContent* aContent, nsStyleContext* aStyleContext) :
       mContent(aContent), mStyleContext(aStyleContext)
     {}
 
     nsIContent* mContent;
     nsRefPtr<nsStyleContext> mStyleContext;
+    nsTArray<ContentInfo> mChildren;
   };
 
   /**
    * Creates "native" anonymous content and adds the created content to
    * the aElements array. None of the returned elements can be nullptr.
    *
    * If the anonymous content creator sets the editable flag on some
    * of the elements that it creates, the flag will be applied to the node
    * upon being bound to the document.
    *
    * @note The returned elements are owned by this object. This object is
    *       responsible for calling UnbindFromTree on the elements it returned
    *       from CreateAnonymousContent when appropriate (i.e. before releasing
    *       them).
+   *
+   * @note Implementations of this method that add items to mChildren must not
+   *       hook them up to any parent since frame construction takes care of
+   *       that.
    */
   virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements)=0;
 
   /**
    * Appends "native" anonymous children created by CreateAnonymousContent()
    * to the given content list depending on the filter.
    *
    * @see nsIContent::GetChildren for set of values used for filter.