Bug 1388625 part 6. Flag the in-flow frames of kids of various wrapper frames during frame construction, so we know to restyle the wrapper frames. r?heycam draft
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 11 Aug 2017 00:10:27 -0400
changeset 644575 c5152ed28cf12228a8b49e0ab827771954ce307b
parent 644574 e846c5a2f4c48e051351736599248a71ba7f50f2
child 644599 98c1f6d277e04533933c7c97a972506726c8ace8
push id73471
push userbzbarsky@mozilla.com
push dateFri, 11 Aug 2017 04:10:55 +0000
reviewersheycam
bugs1388625
milestone57.0a1
Bug 1388625 part 6. Flag the in-flow frames of kids of various wrapper frames during frame construction, so we know to restyle the wrapper frames. r?heycam MozReview-Commit-ID: 8KNug88sGp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/css/CSS2/tables/reference/table-anonymous-border-spacing-ref.xht
testing/web-platform/tests/css/CSS2/tables/reference/table-anonymous-text-indent-ref.xht
testing/web-platform/tests/css/CSS2/tables/table-anonymous-border-spacing.xht
testing/web-platform/tests/css/CSS2/tables/table-anonymous-text-indent.xht
testing/web-platform/tests/cssom-view/elementFromPoint-002.html
testing/web-platform/tests/cssom-view/elementFromPoint-003.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -626,16 +626,29 @@ GetIBContainingBlockFor(nsIFrame* aFrame
   // post-conditions
   NS_ASSERTION(parentFrame, "no normal ancestor found for ib-split frame "
                             "in GetIBContainingBlockFor");
   NS_ASSERTION(parentFrame != aFrame, "parentFrame is actually the child frame - bogus reslt");
 
   return parentFrame;
 }
 
+// This is a bit slow, but sometimes we need it.
+static bool
+ParentIsWrapperAnonBox(nsIFrame* aParent)
+{
+  nsIFrame* maybeAnonBox = aParent;
+  if (maybeAnonBox->StyleContext()->GetPseudo() ==
+        nsCSSAnonBoxes::cellContent) {
+    // The thing that would maybe be a wrapper anon box is the cell.
+    maybeAnonBox = maybeAnonBox->GetParent();
+  }
+  return maybeAnonBox->StyleContext()->IsWrapperAnonBox();
+}
+
 //----------------------------------------------------------------------
 
 // Block/inline frame construction logic. We maintain a few invariants here:
 //
 // 1. Block frames contain block and inline frames.
 //
 // 2. Inline frames only contain inline frames. If an inline parent has a block
 // child then the block child is migrated upward until it lands in a block
@@ -2209,17 +2222,19 @@ nsCSSFrameConstructor::ConstructTable(ns
     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);
+                                innerFrame,
+                                aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX,
+                                childItems);
   } else {
     ProcessChildren(aState, content, styleContext, innerFrame,
                     true, childItems, false, aItem.mPendingBinding);
   }
 
   nsFrameItems captionItems;
   PullOutCaptionFrames(childItems, captionItems);
 
@@ -2292,16 +2307,17 @@ nsCSSFrameConstructor::ConstructTableRow
 
   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,
+                                aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX,
                                 childItems);
   } else {
     ProcessChildren(aState, content, styleContext, newFrame,
                     true, childItems, false, aItem.mPendingBinding);
   }
 
   newFrame->SetInitialChildList(kPrincipalList, childItems);
   aFrameItems.AddChild(newFrame);
@@ -2414,16 +2430,17 @@ nsCSSFrameConstructor::ConstructTableCel
     nsFrameConstructorSaveState floatSaveState;
     if (!isBlock) { /* MathML case */
       aState.PushFloatContainingBlock(nullptr, floatSaveState);
     } else {
       aState.PushFloatContainingBlock(cellInnerFrame, floatSaveState);
     }
 
     ConstructFramesFromItemList(aState, aItem.mChildItems, cellInnerFrame,
+                                aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX,
                                 childItems);
   } else {
     // Process the child content
     ProcessChildren(aState, content, styleContext, cellInnerFrame,
                     true, childItems, isBlock, aItem.mPendingBinding);
   }
 
   cellInnerFrame->SetInitialChildList(kPrincipalList, childItems);
@@ -3053,17 +3070,19 @@ nsCSSFrameConstructor::ConstructAnonymou
     return;
   }
 
   FrameConstructionItemList itemsToConstruct;
   nsContainerFrame* frameAsContainer = do_QueryFrame(aFrame);
   AddFCItemsForAnonymousContent(aState, frameAsContainer, anonymousItems, itemsToConstruct);
 
   nsFrameItems frameItems;
-  ConstructFramesFromItemList(aState, itemsToConstruct, frameAsContainer, frameItems);
+  ConstructFramesFromItemList(aState, itemsToConstruct, frameAsContainer,
+                              /* aParentIsWrapperAnonBox = */ false,
+                              frameItems);
   frameAsContainer->AppendFrames(kPrincipalList, frameItems);
 }
 
 nsContainerFrame*
 nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell*  aPresShell,
                                           nsContainerFrame* aParentFrame,
                                           nsIFrame*      aPrevPageFrame,
                                           nsContainerFrame*& aCanvasFrame)
@@ -3258,17 +3277,19 @@ nsCSSFrameConstructor::ConstructSelectFr
     MOZ_ASSERT(customFrame);
     customFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
     childItems.AddChild(customFrame);
 
     // The other piece of NAC can take the normal path.
     FrameConstructionItemList fcItems;
     AddFCItemsForAnonymousContent(aState, comboboxFrame, newAnonymousItems,
                                   fcItems);
-    ConstructFramesFromItemList(aState, fcItems, comboboxFrame, childItems);
+    ConstructFramesFromItemList(aState, fcItems, comboboxFrame,
+                                /* aParentIsWrapperAnonBox = */ false,
+                                childItems);
 
     comboboxFrame->SetInitialChildList(kPrincipalList, childItems);
 
     // Initialize the additional popup child list which contains the
     // dropdown list frame.
     nsFrameItems popupItems;
     popupItems.AddChild(listFrame);
     comboboxFrame->SetInitialChildList(nsIFrame::kSelectPopupList,
@@ -3925,16 +3946,19 @@ nsCSSFrameConstructor::ConstructFrameFro
   CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
                      FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
 #undef CHECK_ONLY_ONE_BIT
   NS_ASSERTION(!(bits & FCDATA_FORCED_NON_SCROLLABLE_BLOCK) ||
                ((bits & FCDATA_FUNC_IS_FULL_CTOR) &&
                 data->mFullConstructor ==
                   &nsCSSFrameConstructor::ConstructNonScrollableBlock),
                "Unexpected FCDATA_FORCED_NON_SCROLLABLE_BLOCK flag");
+  MOZ_ASSERT(!(bits & FCDATA_IS_WRAPPER_ANON_BOX) ||
+             (bits & FCDATA_USE_CHILD_ITEMS),
+             "Wrapper anon boxes should always have FCDATA_USE_CHILD_ITEMS");
 
   // Don't create a subdocument frame for iframes if we're creating extra frames
   if (aState.mCreatingExtraFrames &&
       aItem.mContent->IsHTMLElement(nsGkAtoms::iframe))
   {
     return;
   }
 
@@ -4161,17 +4185,19 @@ nsCSSFrameConstructor::ConstructFrameFro
       if (bits & FCDATA_USE_CHILD_ITEMS) {
         nsFrameConstructorSaveState floatSaveState;
 
         if (ShouldSuppressFloatingOfDescendants(newFrame)) {
           aState.PushFloatContainingBlock(nullptr, floatSaveState);
         } else if (newFrame->IsFloatContainingBlock()) {
           aState.PushFloatContainingBlock(newFrameAsContainer, floatSaveState);
         }
-        ConstructFramesFromItemList(aState, aItem.mChildItems, newFrameAsContainer,
+        ConstructFramesFromItemList(aState, aItem.mChildItems,
+                                    newFrameAsContainer,
+                                    bits & FCDATA_IS_WRAPPER_ANON_BOX,
                                     childItems);
       } else {
         // Process the child frames.
         ProcessChildren(aState, content, styleContext, newFrameAsContainer,
                         !(bits & FCDATA_DISALLOW_GENERATED_CONTENT),
                         childItems,
                         (bits & FCDATA_ALLOW_BLOCK_STYLES) != 0,
                         aItem.mPendingBinding, possiblyLeafFrame);
@@ -4698,17 +4724,19 @@ nsCSSFrameConstructor::BeginBuildingScro
     if (aState.HasAncestorFilter()) {
       ancestorPusher.PushAncestorAndStyleScope(aContent->AsElement());
     } else {
       ancestorPusher.PushStyleScope(aContent->AsElement());
     }
 
     FrameConstructionItemList items;
     AddFCItemsForAnonymousContent(aState, gfxScrollFrame, scrollNAC, items);
-    ConstructFramesFromItemList(aState, items, gfxScrollFrame, anonymousItems);
+    ConstructFramesFromItemList(aState, items, gfxScrollFrame,
+                                /* aParentIsWrapperAnonBox = */ false,
+                                anonymousItems);
   }
 
   aNewFrame = gfxScrollFrame;
   gfxScrollFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
 
   // we used the style that was passed in. So resolve another one.
   StyleSetHandle styleSet = mPresShell->StyleSet();
   RefPtr<nsStyleContext> scrolledChildStyle =
@@ -5263,16 +5291,20 @@ nsCSSFrameConstructor::FlushAccumulatedB
   // MathML frame so that Get(Absolute/Float)ContainingBlockFor know that this
   // is not a suitable block.
   nsContainerFrame* blockFrame =
     NS_NewMathMLmathBlockFrame(mPresShell, blockContext);
   blockFrame->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
 
   InitAndRestoreFrame(aState, aContent, aParentFrame, blockFrame);
   ReparentFrames(this, blockFrame, aBlockItems, false);
+  // We have to walk over aBlockItems before we hand it over to blockFrame.
+  for (nsIFrame* f : aBlockItems) {
+    f->SetParentIsWrapperAnonBox();
+  }
   // abs-pos and floats are disabled in MathML children so we don't have to
   // worry about messing up those.
   blockFrame->SetInitialChildList(kPrincipalList, aBlockItems);
   NS_ASSERTION(aBlockItems.IsEmpty(), "What happened?");
   aBlockItems.Clear();
   aNewItems.AddChild(blockFrame);
 }
 
@@ -5405,17 +5437,19 @@ nsCSSFrameConstructor::ConstructFrameWit
 
   // 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);
+                                innerFrame,
+                                aItem.mFCData->mBits & FCDATA_IS_WRAPPER_ANON_BOX,
+                                childItems);
   } else {
     ProcessChildren(aState, content, styleContext, innerFrame,
                     true, childItems, false, aItem.mPendingBinding);
   }
 
   // Set the inner wrapper frame's initial primary list
   innerFrame->SetInitialChildList(kPrincipalList, childItems);
 
@@ -7823,17 +7857,19 @@ nsCSSFrameConstructor::ContentAppended(n
     items.SetLineBoundaryAtEnd(!parentAfterFrame ||
         !parentAfterFrame->IsInlineOutside());
   }
   // To suppress whitespace-only text frames, we have to verify that
   // our container's DOM child list matches its flattened tree child list.
   items.SetParentHasNoXBLChildren(haveNoXBLChildren);
 
   nsFrameItems frameItems;
-  ConstructFramesFromItemList(state, items, parentFrame, frameItems);
+  ConstructFramesFromItemList(state, items, parentFrame,
+                              ParentIsWrapperAnonBox(parentFrame),
+                              frameItems);
 
   for (nsIContent* child = aFirstNewContent;
        child;
        child = child->GetNextSibling()) {
     // Invalidate now instead of before the WipeContainingBlock call, just in
     // case we do wipe; in that case we don't need to do this walk at all.
     // XXXbz does that matter?  Would it make more sense to save some virtual
     // GetChildAt calls instead and do this during construction of our
@@ -8388,17 +8424,19 @@ nsCSSFrameConstructor::ContentRangeInser
   }
   LAYOUT_PHASE_TEMP_REENTER();
 
   // If the container is a table and a caption will be appended, it needs to be
   // put in the table wrapper frame's additional child list.
   // We make no attempt here to set flags to indicate whether the list
   // will be at the start or end of a block. It doesn't seem worthwhile.
   nsFrameItems frameItems, captionItems;
-  ConstructFramesFromItemList(state, items, insertion.mParentFrame, frameItems);
+  ConstructFramesFromItemList(state, items, insertion.mParentFrame,
+                              ParentIsWrapperAnonBox(insertion.mParentFrame),
+                              frameItems);
 
   if (frameItems.NotEmpty()) {
     for (nsIContent* child = aStartChild;
          child != aEndChild;
          child = child->GetNextSibling()){
       InvalidateCanvasIfNeeded(mPresShell, child);
     }
 
@@ -9516,17 +9554,19 @@ nsCSSFrameConstructor::ReplicateFixedFra
       AddFrameConstructionItemsInternal(state, content, canvasFrame,
                                         content->NodeInfo()->NameAtom(),
                                         content->GetNameSpaceID(),
                                         true,
                                         styleContext,
                                         ITEM_ALLOW_XBL_BASE |
                                           ITEM_ALLOW_PAGE_BREAK,
                                         nullptr, items);
-      ConstructFramesFromItemList(state, items, canvasFrame, fixedPlaceholders);
+      ConstructFramesFromItemList(state, items, canvasFrame,
+                                  /* aParentIsWrapperAnonBox = */ false,
+                                  fixedPlaceholders);
     }
   }
 
   // Add the placeholders to our primary child list.
   // XXXbz this is a little screwed up, since the fixed frames will have
   // broken auto-positioning. Oh, well.
   NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
                "leaking frames; doc root continuation must be empty");
@@ -10178,80 +10218,90 @@ nsCSSFrameConstructor::ShouldHaveSpecial
 }
 
 /* static */
 const nsCSSFrameConstructor::PseudoParentData
 nsCSSFrameConstructor::sPseudoParentData[eParentTypeCount] = {
   { // Cell
     FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
                      FCDATA_USE_CHILD_ITEMS |
+                     FCDATA_IS_WRAPPER_ANON_BOX |
                      FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
                      &nsCSSFrameConstructor::ConstructTableCell),
     &nsCSSAnonBoxes::tableCell
   },
   { // Row
     FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
                      FCDATA_USE_CHILD_ITEMS |
+                     FCDATA_IS_WRAPPER_ANON_BOX |
                      FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
                      &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
     &nsCSSAnonBoxes::tableRow
   },
   { // Row group
     FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
                      FCDATA_USE_CHILD_ITEMS |
+                     FCDATA_IS_WRAPPER_ANON_BOX |
                      FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
                      &nsCSSFrameConstructor::ConstructTableRowOrRowGroup),
     &nsCSSAnonBoxes::tableRowGroup
   },
   { // Column group
     FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_SKIP_FRAMESET |
                 FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_USE_CHILD_ITEMS |
                 FCDATA_SKIP_ABSPOS_PUSH |
+                FCDATA_IS_WRAPPER_ANON_BOX |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
                 NS_NewTableColGroupFrame),
     &nsCSSAnonBoxes::tableColGroup
   },
   { // Table
-    FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
+    FULL_CTOR_FCDATA(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS |
+                     FCDATA_IS_WRAPPER_ANON_BOX,
                      &nsCSSFrameConstructor::ConstructTable),
     &nsCSSAnonBoxes::table
   },
   { // Ruby
     FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
                 FCDATA_USE_CHILD_ITEMS |
+                FCDATA_IS_WRAPPER_ANON_BOX |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyFrame),
     &nsCSSAnonBoxes::ruby
   },
   { // Ruby Base
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
                 FCDATA_IS_LINE_PARTICIPANT |
+                FCDATA_IS_WRAPPER_ANON_BOX |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyBaseFrame),
     &nsCSSAnonBoxes::rubyBase
   },
   { // Ruby Base Container
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
                 FCDATA_IS_LINE_PARTICIPANT |
+                FCDATA_IS_WRAPPER_ANON_BOX |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyBaseContainerFrame),
     &nsCSSAnonBoxes::rubyBaseContainer
   },
   { // Ruby Text
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
                 FCDATA_IS_LINE_PARTICIPANT |
+                FCDATA_IS_WRAPPER_ANON_BOX |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyTextFrame),
     &nsCSSAnonBoxes::rubyText
   },
   { // Ruby Text Container
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
+                FCDATA_IS_WRAPPER_ANON_BOX |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyTextContainerFrame),
     &nsCSSAnonBoxes::rubyTextContainer
   }
 };
 
 void
@@ -10332,17 +10382,19 @@ nsCSSFrameConstructor::CreateNeededAnonF
                             : nsCSSAnonBoxes::anonymousGridItem;
     nsStyleContext* parentStyle = aParentFrame->StyleContext();
     nsIContent* parentContent = aParentFrame->GetContent();
     already_AddRefed<nsStyleContext> wrapperStyle =
       mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(pseudoType,
                                                                  parentStyle);
 
     static const FrameConstructionData sBlockFormattingContextFCData =
-      FCDATA_DECL(FCDATA_SKIP_FRAMESET | FCDATA_USE_CHILD_ITEMS,
+      FCDATA_DECL(FCDATA_SKIP_FRAMESET |
+                  FCDATA_USE_CHILD_ITEMS |
+                  FCDATA_IS_WRAPPER_ANON_BOX,
                   NS_NewBlockFormattingContext);
 
     FrameConstructionItem* newItem =
       new FrameConstructionItem(&sBlockFormattingContextFCData,
                                 // Use the content of our parent frame
                                 parentContent,
                                 // Lie about the tag; it doesn't matter anyway
                                 pseudoType,
@@ -10989,33 +11041,45 @@ VerifyGridFlexContainerChildren(nsIFrame
   }
 #endif
 }
 
 inline void
 nsCSSFrameConstructor::ConstructFramesFromItemList(nsFrameConstructorState& aState,
                                                    FrameConstructionItemList& aItems,
                                                    nsContainerFrame* aParentFrame,
+                                                   bool aParentIsWrapperAnonBox,
                                                    nsFrameItems& aFrameItems)
 {
+  // Ensure aParentIsWrapperAnonBox is correct.  We _could_ compute it directly,
+  // but it would be a bit slow, which is why we pass it from callers, who have
+  // that information offhand in many cases.
+  MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox);
+
   CreateNeededPseudoContainers(aState, aItems, aParentFrame);
   CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
   CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
   CreateNeededPseudoSiblings(aState, aItems, aParentFrame);
 
   aItems.SetTriedConstructingFrames();
   for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
     NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
                  "Needed pseudos didn't get created; expect bad things");
     ConstructFramesFromItem(aState, iter, aParentFrame, aFrameItems);
   }
 
   VerifyGridFlexContainerChildren(aParentFrame, aFrameItems);
   NS_ASSERTION(!aState.mHavePendingPopupgroup,
                "Should have proccessed it by now");
+
+  if (aParentIsWrapperAnonBox) {
+    for (nsIFrame* f : aFrameItems) {
+      f->SetParentIsWrapperAnonBox();
+    }
+  }
 }
 
 void
 nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
             nsFrameConstructorState& aState,
             nsContainerFrame* aFrame,
             nsTArray<nsIAnonymousContentCreator::ContentInfo>& aAnonymousItems,
             FrameConstructionItemList& aItemsToConstruct,
@@ -11308,17 +11372,19 @@ nsCSSFrameConstructor::ProcessChildren(n
       CreateGeneratedContentItem(aState, aFrame, aContent, styleContext,
                                  CSSPseudoElementType::after,
                                  itemsToConstruct);
     }
   } else {
     ClearLazyBits(aContent->GetFirstChild(), nullptr);
   }
 
-  ConstructFramesFromItemList(aState, itemsToConstruct, aFrame, aFrameItems);
+  ConstructFramesFromItemList(aState, itemsToConstruct, aFrame,
+                              /* aParentIsWrapperAnonBox = */ false,
+                              aFrameItems);
 
   NS_ASSERTION(!aAllowBlockStyles || !aFrame->IsXULBoxFrame(),
                "can't be both block and box");
 
   if (haveFirstLetterStyle) {
     WrapFramesInFirstLetterFrame(aFrame, aFrameItems);
   }
   if (haveFirstLineStyle) {
@@ -12225,17 +12291,19 @@ nsCSSFrameConstructor::CreateListBoxCont
     BeginUpdate();
 
     FrameConstructionItemList items;
     AddFrameConstructionItemsInternal(state, aChild, aParentFrame,
                                       aChild->NodeInfo()->NameAtom(),
                                       aChild->GetNameSpaceID(),
                                       true, styleContext,
                                       ITEM_ALLOW_XBL_BASE, nullptr, items);
-    ConstructFramesFromItemList(state, items, aParentFrame, frameItems);
+    ConstructFramesFromItemList(state, items, aParentFrame,
+                                /* aParentIsWrapperAnonBox = */ false,
+                                frameItems);
 
     nsIFrame* newFrame = frameItems.FirstChild();
     *aNewFrame = newFrame;
 
     if (newFrame) {
       // Notify the parent frame
       if (aIsAppend)
         ((nsListBoxBodyFrame*)aParentFrame)->ListBoxAppendFrames(frameItems);
@@ -12421,17 +12489,19 @@ nsCSSFrameConstructor::ConstructInline(n
   if (positioned) {
     // Relatively positioned frames becomes a container for child
     // frames that are positioned
     aState.PushAbsoluteContainingBlock(newFrame, newFrame, absoluteSaveState);
   }
 
   // Process the child content
   nsFrameItems childItems;
-  ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame, childItems);
+  ConstructFramesFromItemList(aState, aItem.mChildItems, newFrame,
+                              /* aParentIsWrapperAnonBox = */ false,
+                              childItems);
 
   nsFrameList::FrameLinkEnumerator firstBlockEnumerator(childItems);
   if (!aItem.mIsAllInline) {
     FindFirstBlock(firstBlockEnumerator);
   }
 
   if (aItem.mIsAllInline || firstBlockEnumerator.AtEnd()) {
     // This part is easy.  We either already know we have no non-inline kids,
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -767,16 +767,22 @@ private:
    */
 #define FCDATA_IS_CONTENTS 0x100000
   /**
    * When FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, this bit says
    * if we should create a grid/flex/columnset container instead of
    * a block wrapper when the styles says so.
    */
 #define FCDATA_ALLOW_GRID_FLEX_COLUMNSET 0x200000
+  /**
+   * Whether the kids of this FrameConstructionData should be flagged as having
+   * a wrapper anon box parent.  This should only be set if
+   * FCDATA_USE_CHILD_ITEMS is set.
+   */
+#define FCDATA_IS_WRAPPER_ANON_BOX 0x400000
 
   /* Structure representing information about how a frame should be
      constructed.  */
   struct FrameConstructionData {
     // Flag bits that can modify the way the construction happens
     uint32_t mBits;
     // We have exactly one of three types of functions, so use a union for
     // better cache locality for the ones that aren't pointer-to-member.  That
@@ -1502,16 +1508,17 @@ private:
 
   /**
    * Construct frames for the given item list and parent frame, and put the
    * resulting frames in aFrameItems.
    */
   void ConstructFramesFromItemList(nsFrameConstructorState& aState,
                                    FrameConstructionItemList& aItems,
                                    nsContainerFrame* aParentFrame,
+                                   bool aParentIsWrapperAnonBox,
                                    nsFrameItems& aFrameItems);
   void ConstructFramesFromItem(nsFrameConstructorState& aState,
                                FCItemIterator& aItem,
                                nsContainerFrame* aParentFrame,
                                nsFrameItems& aFrameItems);
   static bool AtLineBoundary(FCItemIterator& aIter);
 
   nsresult GetAnonymousContent(nsIContent* aParent,
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -88320,16 +88320,28 @@
       [
        "/css/CSS2/tables/separated-border-model-007-ref.xht",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/CSS2/tables/table-anonymous-border-spacing.xht": [
+    [
+     "/css/CSS2/tables/table-anonymous-border-spacing.xht",
+     [
+      [
+       "/css/CSS2/tables/reference/table-anonymous-border-spacing-ref.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/CSS2/tables/table-anonymous-objects-009.xht": [
     [
      "/css/CSS2/tables/table-anonymous-objects-009.xht",
      [
       [
        "/css/CSS2/tables/reference/no_red_antialiasing_a_bc_d-ref.xht",
        "=="
       ]
@@ -90248,16 +90260,28 @@
       [
        "/css/CSS2/tables/reference/no_red_antialiasing_a_bc_d-ref.xht",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/CSS2/tables/table-anonymous-text-indent.xht": [
+    [
+     "/css/CSS2/tables/table-anonymous-text-indent.xht",
+     [
+      [
+       "/css/CSS2/tables/reference/table-anonymous-text-indent-ref.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/CSS2/tables/table-backgrounds-bc-cell-001.xht": [
     [
      "/css/CSS2/tables/table-backgrounds-bc-cell-001.xht",
      [
       [
        "/css/CSS2/tables/table-backgrounds-bc-cell-001-ref.xht",
        "=="
       ]
@@ -220714,16 +220738,26 @@
      {}
     ]
    ],
    "css/CSS2/tables/reference/no_red_antialiasing_a_bc_d-ref.xht": [
     [
      {}
     ]
    ],
+   "css/CSS2/tables/reference/table-anonymous-border-spacing-ref.xht": [
+    [
+     {}
+    ]
+   ],
+   "css/CSS2/tables/reference/table-anonymous-text-indent-ref.xht": [
+    [
+     {}
+    ]
+   ],
    "css/CSS2/tables/reference/table-margin-004-ref.xht": [
     [
      {}
     ]
    ],
    "css/CSS2/tables/separated-border-model-003b-ref.xht": [
     [
      {}
@@ -333996,16 +334030,28 @@
     ]
    ],
    "cssom-view/elementFromPoint-001.html": [
     [
      "/cssom-view/elementFromPoint-001.html",
      {}
     ]
    ],
+   "cssom-view/elementFromPoint-002.html": [
+    [
+     "/cssom-view/elementFromPoint-002.html",
+     {}
+    ]
+   ],
+   "cssom-view/elementFromPoint-003.html": [
+    [
+     "/cssom-view/elementFromPoint-003.html",
+     {}
+    ]
+   ],
    "cssom-view/elementFromPoint.html": [
     [
      "/cssom-view/elementFromPoint.html",
      {}
     ]
    ],
    "cssom-view/elementFromPosition.html": [
     [
@@ -482841,16 +482887,24 @@
   "css/CSS2/tables/reference/no_red_3x3_monospace_table-ref.xht": [
    "621aec22409408f4e4db76839daed205b5c91ede",
    "support"
   ],
   "css/CSS2/tables/reference/no_red_antialiasing_a_bc_d-ref.xht": [
    "fb4b7b86ff4b56d001b6ffe72f6d9c8a91046d2c",
    "support"
   ],
+  "css/CSS2/tables/reference/table-anonymous-border-spacing-ref.xht": [
+   "157b21a9f2954094ec5e682c3d131f320b6473a2",
+   "support"
+  ],
+  "css/CSS2/tables/reference/table-anonymous-text-indent-ref.xht": [
+   "d12237ecbbd205dd38d956aacc6c1a50ee5631bb",
+   "support"
+  ],
   "css/CSS2/tables/reference/table-margin-004-ref.xht": [
    "cb0ab88ee6938c670cdb7140781d13173f759b41",
    "support"
   ],
   "css/CSS2/tables/row-visibility-001.xht": [
    "5f8962c6026581ddd659cb96617f79330ccae7ed",
    "visual"
   ],
@@ -483181,16 +483235,20 @@
   "css/CSS2/tables/table-anonymous-block-018.xht": [
    "102c16ba42dcbf9a54526f3655b8ab5c15fb3442",
    "visual"
   ],
   "css/CSS2/tables/table-anonymous-block-019.xht": [
    "cfb48d7f833a6afe528d58e77d8c387a9c69135e",
    "visual"
   ],
+  "css/CSS2/tables/table-anonymous-border-spacing.xht": [
+   "f71fad8c0d476789196971c66abcba4b23320292",
+   "reftest"
+  ],
   "css/CSS2/tables/table-anonymous-objects-000.xht": [
    "edbc3202f1429ab7002b31af4e545c81cd9ddab2",
    "visual"
   ],
   "css/CSS2/tables/table-anonymous-objects-001.xht": [
    "c78fe15281f61fb23fddfbc5261fcce6491a7ca9",
    "visual"
   ],
@@ -484025,16 +484083,20 @@
   "css/CSS2/tables/table-anonymous-objects-209.xht": [
    "3b5ced13b1370794b069c5431252dc370257a242",
    "visual"
   ],
   "css/CSS2/tables/table-anonymous-objects-210.xht": [
    "516b5f1e6cec15bc53aef58f1eb41973c46e7190",
    "visual"
   ],
+  "css/CSS2/tables/table-anonymous-text-indent.xht": [
+   "e6aea4bbc5ce33d6ec4ae570501fb5493dbc49f0",
+   "reftest"
+  ],
   "css/CSS2/tables/table-anonymous-whitespace-001.xht": [
    "50b8874d4b4b1fbcde591344235be17ce7e4138f",
    "visual"
   ],
   "css/CSS2/tables/table-background-edge-and-border-model-001.xht": [
    "58a9cbd54e49d76742a85730d929cb3d43f0eea9",
    "visual"
   ],
@@ -572445,16 +572507,24 @@
   "cssom-view/cssom-view/window-interface.xht": [
    "4d8c4ddb997d85ca2c971602a3096f57565c01eb",
    "testharness"
   ],
   "cssom-view/elementFromPoint-001.html": [
    "bf1c490777f450275a95ecfc6d6d2c0d055aca82",
    "testharness"
   ],
+  "cssom-view/elementFromPoint-002.html": [
+   "36b8a5f50e6489f9e25c3d09dc523007d442e2b3",
+   "testharness"
+  ],
+  "cssom-view/elementFromPoint-003.html": [
+   "36b8a5f50e6489f9e25c3d09dc523007d442e2b3",
+   "testharness"
+  ],
   "cssom-view/elementFromPoint.html": [
    "0f78405640523cf451b19ea0348b8216139b8168",
    "testharness"
   ],
   "cssom-view/elementFromPosition.html": [
    "d90dff8b15ec2977f341a7add9c7d627b62d9d0f",
    "testharness"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/CSS2/tables/reference/table-anonymous-border-spacing-ref.xht
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <style><![CDATA[
+    #target { border-spacing: 20px; }
+    #target > * { display: table-cell; border: 1px solid black; }
+    ]]></style>
+  </head>
+<body>
+  <p>There should be 20px border-spacing in the table below.</p>
+  <div id="target">
+    <div>First cell</div>
+    <div>Second cell</div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/CSS2/tables/reference/table-anonymous-text-indent-ref.xht
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+    <style><![CDATA[
+    #target { text-indent: 20px; display: table; }
+    #target > * { display: table-cell; border: 1px solid black; }
+    ]]></style>
+  </head>
+<body>
+  <p>There should be 20px text-indent in the table below.</p>
+  <div id="target">
+    <div>First cell</div>
+    Second cell (no element on purpose)
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/CSS2/tables/table-anonymous-border-spacing.xht
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <link rel="author" title="Boris Zbarsky" href="mailto:bzbarsky@mit.edu"/>
+  <link rel="help" href="http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes"/>
+  <link rel="match" href="reference/table-anonymous-border-spacing-ref.xht"/>
+  <meta name="flags" content='dom'/>
+    <script type="text/javascript"><![CDATA[
+      function doTest() {
+        var t = document.getElementById("target");
+        t.style.borderSpacing = '20px';
+      }
+      ]]></script>
+    <style><![CDATA[
+    #target { border-spacing: 0; }
+    #target > * { display: table-cell; border: 1px solid black; }
+    ]]></style>
+  </head>
+<body onload="doTest()">
+  <p>There should be 20px border-spacing in the table below.</p>
+  <div id="target">
+    <div>First cell</div>
+    <div>Second cell</div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/CSS2/tables/table-anonymous-text-indent.xht
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <link rel="author" title="Boris Zbarsky" href="mailto:bzbarsky@mit.edu"/>
+  <link rel="help" href="http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes"/>
+  <link rel="match" href="reference/table-anonymous-text-indent-ref.xht"/>
+  <meta name="flags" content='dom'/>
+  <script type="text/javascript"><![CDATA[
+    function doTest() {
+      var t = document.getElementById("target");
+      t.style.textIndent = '20px';
+    }
+    ]]></script>
+  <style><![CDATA[
+    #target { text-indent: 0; display: table; }
+    #target > * { display: table-cell; border: 1px solid black; }
+  ]]></style>
+</head>
+<body onload="doTest()">
+  <p>There should be 20px text-indent in the table below.</p>
+  <div id="target">
+    <div>First cell</div>
+    Second cell (no element on purpose)
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/cssom-view/elementFromPoint-002.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Checking whether dynamic changes to visibility interact correctly with
+  table anonymous boxes</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#overlay {
+  display: table;
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: white;
+  z-index: 999
+}
+
+#wrapper { position: relative; }
+</style>
+<div id=log></div>
+<div id="wrapper">
+  <div id="overlay"><div></div></div>
+  <div id="target">Some text</div>
+</div>
+<script>
+  test(function() {
+    var t = document.querySelector("#target");
+    var rect = t.getBoundingClientRect();
+    var hit = document.elementFromPoint(rect.x + rect.width/2,
+                                        rect.y + rect.height/2);
+    assert_equals(hit, t.previousElementSibling,
+                  "Should hit the overlay first.");
+    t.previousElementSibling.style.visibility = "hidden";
+    hit = document.elementFromPoint(rect.x + rect.width/2,
+                                    rect.y + rect.height/2);
+    assert_equals(hit, t,
+                  "Should hit our target now that the overlay is hidden.");
+  });
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/cssom-view/elementFromPoint-003.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Checking whether dynamic changes to visibility interact correctly with
+  table anonymous boxes</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#overlay {
+  display: table;
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: white;
+  z-index: 999
+}
+
+#wrapper { position: relative; }
+</style>
+<div id=log></div>
+<div id="wrapper">
+  <div id="overlay"><div></div></div>
+  <div id="target">Some text</div>
+</div>
+<script>
+  test(function() {
+    // Make sure we have boxes constructed already.
+    document.body.offsetWidth;
+    var overlay = document.querySelector("#overlay");
+    overlay.insertBefore(document.createElement("div"), overlay.firstChild);
+    overlay.appendChild(document.createElement("div"));
+    // Make sure we have boxes constructed for those inserts/appends
+    document.body.offsetWidth;
+    overlay.firstChild.nextSibling.remove();
+    var t = document.querySelector("#target");
+    var rect = t.getBoundingClientRect();
+    var hit = document.elementFromPoint(rect.x + rect.width/2,
+                                        rect.y + rect.height/2);
+    assert_equals(hit, t.previousElementSibling,
+                  "Should hit the overlay first.");
+    t.previousElementSibling.style.visibility = "hidden";
+    hit = document.elementFromPoint(rect.x + rect.width/2,
+                                    rect.y + rect.height/2);
+    assert_equals(hit, t,
+                  "Should hit our target now that the overlay is hidden.");
+  });
+</script>