Bug 907396 - Frame constructor changes for display:contents. r=bzbarsky
authorMats Palmgren <mats@mozilla.com>
Thu, 20 Nov 2014 18:24:09 +0000
changeset 216690 af9ca68d20ed255765df49142608d4345689a901
parent 216689 fbedf09e4ec38d08f956d8383ade5e06b8da6e19
child 216691 e0a73ea28c25bd217e079cfcef9ac678c69d7594
push id27858
push userkwierso@gmail.com
push dateFri, 21 Nov 2014 01:35:46 +0000
treeherdermozilla-central@6309710dd71d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs907396
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 907396 - Frame constructor changes for display:contents. r=bzbarsky
layout/base/RestyleManager.cpp
layout/base/RestyleManager.h
layout/base/RestyleTracker.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/xul/nsMenuBarFrame.cpp
layout/xul/nsMenuPopupFrame.cpp
layout/xul/nsXULPopupManager.cpp
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -3762,16 +3762,54 @@ RestyleManager::ComputeStyleChangeFor(ns
         NS_ASSERTION(!cont->GetPrevContinuation(),
                      "continuing frame had more severe impact than first-in-flow");
         return;
       }
     }
   }
 }
 
+AutoDisplayContentsAncestorPusher::AutoDisplayContentsAncestorPusher(
+  TreeMatchContext& aTreeMatchContext, nsPresContext* aPresContext,
+  nsIContent* aParent)
+  : mTreeMatchContext(aTreeMatchContext)
+  , mPresContext(aPresContext)
+{
+  if (aParent) {
+    nsFrameManager* fm = mPresContext->FrameManager();
+    // Push display:contents mAncestors onto mTreeMatchContext.
+    for (nsIContent* p = aParent; p && fm->GetDisplayContentsStyleFor(p);
+         p = p->GetParent()) {
+      mAncestors.AppendElement(p->AsElement());
+    }
+    bool hasFilter = mTreeMatchContext.mAncestorFilter.HasFilter();
+    nsTArray<mozilla::dom::Element*>::size_type i = mAncestors.Length();
+    while (i--) {
+      if (hasFilter) {
+        mTreeMatchContext.mAncestorFilter.PushAncestor(mAncestors[i]);
+      }
+      mTreeMatchContext.PushStyleScope(mAncestors[i]);
+    }
+  }
+}
+
+AutoDisplayContentsAncestorPusher::~AutoDisplayContentsAncestorPusher()
+{
+  // Pop the ancestors we pushed in the CTOR, if any.
+  typedef nsTArray<mozilla::dom::Element*>::size_type sz;
+  sz len = mAncestors.Length();
+  bool hasFilter = mTreeMatchContext.mAncestorFilter.HasFilter();
+  for (sz i = 0; i < len; ++i) {
+    if (hasFilter) {
+      mTreeMatchContext.mAncestorFilter.PopAncestor();
+    }
+    mTreeMatchContext.PopStyleScope(mAncestors[i]);
+  }
+}
+
 #ifdef RESTYLE_LOGGING
 uint32_t
 RestyleManager::StructsToLog()
 {
   static bool initialized = false;
   static uint32_t structs;
   if (!initialized) {
     structs = 0;
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -26,17 +26,18 @@ struct TreeMatchContext;
 
 namespace mozilla {
   class EventStates;
 
 namespace dom {
   class Element;
 } // namespace dom
 
-class RestyleManager MOZ_FINAL {
+class RestyleManager MOZ_FINAL
+{
 public:
   friend class ::nsRefreshDriver;
   friend class RestyleTracker;
 
   typedef mozilla::dom::Element Element;
 
   explicit RestyleManager(nsPresContext* aPresContext);
 
@@ -511,17 +512,18 @@ private:
   int32_t mLoggingDepth;
 #endif
 };
 
 /**
  * An ElementRestyler is created for *each* element in a subtree that we
  * recompute styles for.
  */
-class ElementRestyler MOZ_FINAL {
+class ElementRestyler MOZ_FINAL
+{
 public:
   typedef mozilla::dom::Element Element;
 
   // Construct for the root of the subtree that we're restyling.
   ElementRestyler(nsPresContext* aPresContext,
                   nsIFrame* aFrame,
                   nsStyleChangeList* aChangeList,
                   nsChangeHint aHintsHandledByAncestors,
@@ -683,11 +685,32 @@ private:
   bool mWasFrameVisible;
 #endif
 
 #ifdef RESTYLE_LOGGING
   int32_t mLoggingDepth;
 #endif
 };
 
+/**
+ * This pushes any display:contents nodes onto a TreeMatchContext.
+ * Use it before resolving style for kids of aParent where aParent
+ * (and further ancestors) may be display:contents nodes which have
+ * not yet been pushed onto TreeMatchContext.
+ */
+class MOZ_STACK_CLASS AutoDisplayContentsAncestorPusher MOZ_FINAL
+{
+ public:
+  typedef mozilla::dom::Element Element;
+  AutoDisplayContentsAncestorPusher(TreeMatchContext& aTreeMatchContext,
+                                    nsPresContext*    aPresContext,
+                                    nsIContent*       aParent);
+  ~AutoDisplayContentsAncestorPusher();
+  bool IsEmpty() const { return mAncestors.Length() == 0; }
+private:
+  TreeMatchContext& mTreeMatchContext;
+  nsPresContext* const mPresContext;
+  nsAutoTArray<mozilla::dom::Element*, 4> mAncestors;
+};
+
 } // namespace mozilla
 
 #endif /* mozilla_RestyleManager_h */
--- a/layout/base/RestyleTracker.cpp
+++ b/layout/base/RestyleTracker.cpp
@@ -4,20 +4,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * A class which manages pending restyles.  This handles keeping track
  * of what nodes restyles need to happen on and so forth.
  */
 
 #include "RestyleTracker.h"
+
+#include "GeckoProfiler.h"
+#include "nsFrameManager.h"
+#include "nsIDocument.h"
 #include "nsStyleChangeList.h"
 #include "RestyleManager.h"
-#include "GeckoProfiler.h"
-#include "nsIDocument.h"
 #include "RestyleTrackerInlines.h"
 
 namespace mozilla {
 
 #ifdef RESTYLE_LOGGING
 static nsCString
 GetDocumentURI(nsIDocument* aDocument)
 {
@@ -109,18 +111,20 @@ CollectRestyles(nsISupports* aElement,
                    "skipping pending restyle %s, already restyled or no longer "
                    "in the document", FrameTagToString(element).get());
     return PL_DHASH_NEXT;
   }
 
   NS_ASSERTION(!element->HasFlag(collector->tracker->RootBit()) ||
                // Maybe we're just not reachable via the frame tree?
                (element->GetFlattenedTreeParent() &&
-                (!element->GetFlattenedTreeParent()->GetPrimaryFrame()||
-                 element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf())) ||
+                (!element->GetFlattenedTreeParent()->GetPrimaryFrame() ||
+                 element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf() ||
+                 element->GetCurrentDoc()->GetShell()->FrameManager()
+                   ->GetDisplayContentsStyleFor(element))) ||
                // Or not reachable due to an async reinsert we have
                // pending?  If so, we'll have a reframe hint around.
                // That incidentally makes it safe that we still have
                // the bit, since any descendants that didn't get added
                // to the roots list because we had the bits will be
                // completely restyled in a moment.
                (aData->mChangeHint & nsChangeHint_ReconstructFrame),
                "Why did this not get handled while processing mRestyleRoots?");
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -606,40 +606,16 @@ FindFirstNonBlock(const nsFrameList& aLi
 inline void
 SetInitialSingleChild(nsContainerFrame* aParent, nsIFrame* aFrame)
 {
   NS_PRECONDITION(!aFrame->GetNextSibling(), "Should be using a frame list");
   nsFrameList temp(aFrame, aFrame);
   aParent->SetInitialChildList(kPrincipalList, temp);
 }
 
-static nsContainerFrame*
-GetContentInsertionFrameFor(nsIContent* aContent)
-{
-  // Get the primary frame associated with the content
-  nsIFrame* frame = aContent->GetPrimaryFrame();
-
-  if (!frame)
-    return nullptr;
-
-  // If the content of the frame is not the desired content then this is not
-  // really a frame for the desired content.
-  // XXX This check is needed due to bug 135040. Remove it once that's fixed.
-  if (frame->GetContent() != aContent) {
-    return nullptr;
-  }
-
-  nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame();
-
-  NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(),
-    "The insertion frame is the primary frame or the primary frame isn't a leaf");
-
-  return insertionFrame;
-}
-
 // -----------------------------------------------------------
 
 // Structure used when constructing formatting object trees.
 struct nsFrameItems : public nsFrameList
 {
   // Appends the frame to the end of the list
   void AddChild(nsIFrame* aChild);
 };
@@ -2283,18 +2259,19 @@ NeedFrameFor(const nsFrameConstructorSta
   // whitespace.
 
   // We could handle all this in CreateNeededPseudos or some other place
   // after we build our frame construction items, but that would involve
   // creating frame construction items for whitespace kids of
   // eExcludesIgnorableWhitespace frames, where we know we'll be dropping them
   // all anyway, and involve an extra walk down the frame construction item
   // list.
-  if (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
-      aParentFrame->IsGeneratedContentFrame() ||
+  if ((aParentFrame &&
+       (!aParentFrame->IsFrameOfType(nsIFrame::eExcludesIgnorableWhitespace) ||
+        aParentFrame->IsGeneratedContentFrame())) ||
       !aChildContent->IsNodeOfType(nsINode::eTEXT)) {
     return true;
   }
 
   aChildContent->SetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
                           NS_REFRAME_IF_WHITESPACE);
   return !aChildContent->TextIsOnlyWhitespace();
 }
@@ -2592,17 +2569,18 @@ nsCSSFrameConstructor::ConstructDocEleme
     // if the document is a table then just populate it.
     contentFrame = static_cast<nsContainerFrame*>(
       ConstructTable(state, item, mDocElementContainingBlock,
                      styleContext->StyleDisplay(),
                      frameItems));
     newFrame = frameItems.FirstChild();
     NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames");
   } else {
-    MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK,
+    MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK ||
+               display->mDisplay == NS_STYLE_DISPLAY_CONTENTS,
                "Unhandled display type for root element");
     contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext);
     nsFrameItems frameItems;
     // Use a null PendingBinding, since our binding is not in fact pending.
     ConstructBlock(state, display, aDocElement,
                    state.GetGeometricParent(display,
                                             mDocElementContainingBlock),
                    mDocElementContainingBlock, styleContext,
@@ -3699,29 +3677,32 @@ nsCSSFrameConstructor::ConstructFrameFro
 
   // Don't create a subdocument frame for iframes if we're creating extra frames
   if (aState.mCreatingExtraFrames && aItem.mContent->IsHTML() &&
       aItem.mContent->Tag() == nsGkAtoms::iframe)
   {
     return;
   }
 
-  nsStyleContext* const styleContext = aItem.mStyleContext;
-  const nsStyleDisplay* display = styleContext->StyleDisplay();
   nsIContent* const content = aItem.mContent;
+  nsIContent* parent = content->GetParent();
+
+  // Push display:contents ancestors.
+  AutoDisplayContentsAncestorPusher adcp(aState.mTreeMatchContext,
+                                         aState.mPresContext, parent);
 
   // Get the parent of the content and check if it is a XBL children element.
   // Push the children element as an ancestor here because it does
   // not have a frame and would not otherwise be pushed as an ancestor. It is
   // necessary to do so in order to correctly handle style resolution on
-  // descendants.
-  nsIContent* parent = content->GetParent();
+  // descendants.  (If !adcp.IsEmpty() then it was already pushed by
+  // AutoDisplayContentsAncestorPusher above.)
   TreeMatchContext::AutoAncestorPusher
     insertionPointPusher(aState.mTreeMatchContext);
-  if (parent && nsContentUtils::IsContentInsertionPoint(parent)) {
+  if (adcp.IsEmpty() && parent && nsContentUtils::IsContentInsertionPoint(parent)) {
     if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
       insertionPointPusher.PushAncestorAndStyleScope(parent);
     } else {
       insertionPointPusher.PushStyleScope(parent);
     }
   }
 
   // Push the content as a style ancestor now, so we don't have to do
@@ -3737,16 +3718,18 @@ nsCSSFrameConstructor::ConstructFrameFro
   if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
     ancestorPusher.PushAncestorAndStyleScope(content);
   } else {
     ancestorPusher.PushStyleScope(content);
   }
 
   nsIFrame* newFrame;
   nsIFrame* primaryFrame;
+  nsStyleContext* const styleContext = aItem.mStyleContext;
+  const nsStyleDisplay* display = styleContext->StyleDisplay();
   if (bits & FCDATA_FUNC_IS_FULL_CTOR) {
     newFrame =
       (this->*(data->mFullConstructor))(aState, aItem, aParentFrame,
                                         display, aFrameItems);
     MOZ_ASSERT(newFrame, "Full constructor failed");
     primaryFrame = newFrame;
   } else {
     newFrame =
@@ -3987,16 +3970,17 @@ nsCSSFrameConstructor::CreateAnonymousFr
   } else {
     ancestorPusher.PushStyleScope(aParent->AsElement());
   }
 
   nsIAnonymousContentCreator* creator = do_QueryFrame(aParentFrame);
   NS_ASSERTION(creator,
                "How can that happen if we have nodes to construct frames for?");
 
+  InsertionPoint insertion(aParentFrame, aParent);
   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");
@@ -4010,17 +3994,17 @@ nsCSSFrameConstructor::CreateAnonymousFr
     } else {
       FrameConstructionItemList items;
       {
         // Skip parent display based style-fixup during our 
         // AddFrameConstructionItems() call:
         TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
           parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext);
 
-        AddFrameConstructionItems(aState, content, true, aParentFrame, items);
+        AddFrameConstructionItems(aState, content, true, insertion, items);
       }
       ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems);
     }
   }
 
   return NS_OK;
 }
 
@@ -4503,17 +4487,18 @@ nsCSSFrameConstructor::FindDisplayData(c
                                        nsStyleContext* aStyleContext)
 {
   PR_STATIC_ASSERT(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)));
 
   // The style system ensures that floated and positioned frames are
   // block-level.
   NS_ASSERTION(!(aDisplay->IsFloatingStyle() ||
                  aDisplay->IsAbsolutelyPositionedStyle()) ||
-               aDisplay->IsBlockOutsideStyle(),
+               aDisplay->IsBlockOutsideStyle() ||
+               aDisplay->mDisplay == NS_STYLE_DISPLAY_CONTENTS,
                "Style system did not apply CSS2.1 section 9.7 fixups");
 
   // If this is "body", try propagating its scroll style to the viewport
   // Note that we need to do this even if the body is NOT scrollable;
   // it might have dynamically changed from scrollable to not scrollable,
   // and that might need to be propagated.
   // XXXbz is this the right place to do this?  If this code moves,
   // make this function static.
@@ -4644,19 +4629,26 @@ nsCSSFrameConstructor::FindDisplayData(c
                        &nsCSSFrameConstructor::ConstructTableCol) },
     { NS_STYLE_DISPLAY_TABLE_ROW,
       FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
                        &nsCSSFrameConstructor::ConstructTableRowOrRowGroup) },
     { NS_STYLE_DISPLAY_TABLE_CELL,
       FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
-                       &nsCSSFrameConstructor::ConstructTableCell) }
+                       &nsCSSFrameConstructor::ConstructTableCell) },
+    { NS_STYLE_DISPLAY_CONTENTS,
+      FULL_CTOR_FCDATA(FCDATA_IS_CONTENTS, nullptr/*never called*/) }
   };
 
+  // See the mDisplay fixup code in nsRuleNode::ComputeDisplayData.
+  MOZ_ASSERT(aDisplay->mDisplay != NS_STYLE_DISPLAY_CONTENTS ||
+             !aElement->IsRootOfNativeAnonymousSubtree(),
+             "display:contents on anonymous content is unsupported");
+
   return FindDataByInt(aDisplay->mDisplay,
                        aElement, aStyleContext, sDisplayData,
                        ArrayLength(sDisplayData));
 }
 
 nsIFrame*
 nsCSSFrameConstructor::ConstructScrollableBlock(nsFrameConstructorState& aState,
                                                 FrameConstructionItem&   aItem,
@@ -4760,37 +4752,58 @@ nsCSSFrameConstructor::InitAndRestoreFra
   if (aAllowCounters &&
       mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
     CountersDirty();
   }
 }
 
 already_AddRefed<nsStyleContext>
 nsCSSFrameConstructor::ResolveStyleContext(nsIFrame*         aParentFrame,
-                                           nsIContent*       aContent,
+                                           nsIContent*       aContainer,
+                                           nsIContent*       aChild,
                                            nsFrameConstructorState* aState)
 {
-  nsStyleContext* parentStyleContext = nullptr;
-  NS_ASSERTION(aContent->GetParent(), "Must have parent here");
-
-  aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr);
-
-  if (aParentFrame) {
-    // Resolve the style context based on the content object and the parent
-    // style context
-    parentStyleContext = aParentFrame->StyleContext();
-  } else {
-    // Perhaps aParentFrame is a canvasFrame and we're replicating
-    // fixed-pos frames.
-    // XXX should we create a way to tell ConstructFrame which style
-    // context to use, and pass it the style context for the
-    // previous page's fixed-pos frame?
-  }
-
-  return ResolveStyleContext(parentStyleContext, aContent, aState);
+  MOZ_ASSERT(aContainer, "Must have parent here");
+  // XXX uncomment when bug 1089223 is fixed:
+  // MOZ_ASSERT(aContainer == aChild->GetFlattenedTreeParent());
+  nsStyleContext* parentStyleContext = GetDisplayContentsStyleFor(aContainer);
+  if (MOZ_LIKELY(!parentStyleContext)) {
+    aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr);
+    if (aParentFrame) {
+      MOZ_ASSERT(aParentFrame->GetContent() == aContainer);
+      // Resolve the style context based on the content object and the parent
+      // style context
+      parentStyleContext = aParentFrame->StyleContext();
+    } else {
+      // Perhaps aParentFrame is a canvasFrame and we're replicating
+      // fixed-pos frames.
+      // XXX should we create a way to tell ConstructFrame which style
+      // context to use, and pass it the style context for the
+      // previous page's fixed-pos frame?
+    }
+  }
+
+  return ResolveStyleContext(parentStyleContext, aChild, aState);
+}
+
+already_AddRefed<nsStyleContext>
+nsCSSFrameConstructor::ResolveStyleContext(nsIFrame*          aParentFrame,
+                                           nsIContent*              aChild,
+                                           nsFrameConstructorState* aState)
+{
+  return ResolveStyleContext(aParentFrame, aChild->GetFlattenedTreeParent(), aChild, aState);
+}
+
+already_AddRefed<nsStyleContext>
+nsCSSFrameConstructor::ResolveStyleContext(const InsertionPoint&    aInsertion,
+                                           nsIContent*              aChild,
+                                           nsFrameConstructorState* aState)
+{
+  return ResolveStyleContext(aInsertion.mParentFrame, aInsertion.mContainer,
+                             aChild, aState);
 }
 
 already_AddRefed<nsStyleContext>
 nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
                                            nsIContent* aContent,
                                            nsFrameConstructorState* aState)
 {
   nsStyleSet *styleSet = mPresShell->StyleSet();
@@ -5298,22 +5311,20 @@ nsCSSFrameConstructor::AddPageBreakItem(
 
   // 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, nullptr);
 }
 
-void
-nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState,
+bool
+nsCSSFrameConstructor::ShouldCreateItemsForChild(nsFrameConstructorState& aState,
                                                  nsIContent* aContent,
-                                                 bool aSuppressWhiteSpaceOptimizations,
-                                                 nsContainerFrame* aParentFrame,
-                                                 FrameConstructionItemList& aItems)
+                                                 nsContainerFrame* aParentFrame)
 {
   aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
   if (aContent->IsElement()) {
     // We can't just remove our pending restyle flags, since we may
     // have restyle-later-siblings set on us.  But we _can_ remove the
     // "is possible restyle root" flags, and need to.  Otherwise we can
     // end up with stale such flags (e.g. if we used to have a
     // display:none parent when our last restyle was posted and
@@ -5324,50 +5335,80 @@ nsCSSFrameConstructor::AddFrameConstruct
 
   // XXX the GetContent() != aContent check is needed due to bug 135040.
   // Remove it once that's fixed.
   if (aContent->GetPrimaryFrame() &&
       aContent->GetPrimaryFrame()->GetContent() == aContent &&
       !aState.mCreatingExtraFrames) {
     NS_ERROR("asked to create frame construction item for a node that already "
              "has a frame");
-    return;
+    return false;
   }
 
   // don't create a whitespace frame if aParent doesn't want it
   if (!NeedFrameFor(aState, aParentFrame, aContent)) {
-    return;
+    return false;
   }
 
   // never create frames for comments or PIs
   if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
-      aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION))
-    return;
-
-  nsRefPtr<nsStyleContext> styleContext;
-  styleContext = ResolveStyleContext(aParentFrame, aContent, &aState);
-
+      aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+nsCSSFrameConstructor::DoAddFrameConstructionItems(nsFrameConstructorState& aState,
+                                                   nsIContent* aContent,
+                                                   nsStyleContext* aStyleContext,
+                                                   bool aSuppressWhiteSpaceOptimizations,
+                                                   nsContainerFrame* aParentFrame,
+                                                   nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
+                                                   FrameConstructionItemList& aItems)
+{
   uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
-  if (aParentFrame->IsSVGText()) {
-    flags |= ITEM_IS_WITHIN_SVG_TEXT;
-  }
-  if (aParentFrame->GetType() == nsGkAtoms::blockFrame &&
-      aParentFrame->GetParent() &&
-      aParentFrame->GetParent()->GetType() == nsGkAtoms::svgTextFrame) {
-    flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
+  if (aParentFrame) {
+    if (aParentFrame->IsSVGText()) {
+      flags |= ITEM_IS_WITHIN_SVG_TEXT;
+    }
+    if (aParentFrame->GetType() == nsGkAtoms::blockFrame &&
+        aParentFrame->GetParent() &&
+        aParentFrame->GetParent()->GetType() == nsGkAtoms::svgTextFrame) {
+      flags |= ITEM_ALLOWS_TEXT_PATH_CHILD;
+    }
   }
   AddFrameConstructionItemsInternal(aState, aContent, aParentFrame,
                                     aContent->Tag(), aContent->GetNameSpaceID(),
                                     aSuppressWhiteSpaceOptimizations,
-                                    styleContext,
-                                    flags, nullptr,
+                                    aStyleContext,
+                                    flags, aAnonChildren,
                                     aItems);
 }
 
 void
+nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState,
+                                                 nsIContent* aContent,
+                                                 bool aSuppressWhiteSpaceOptimizations,
+                                                 const InsertionPoint& aInsertion,
+                                                 FrameConstructionItemList& aItems)
+{
+  nsContainerFrame* parentFrame = aInsertion.mParentFrame;
+  if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) {
+    return;
+  }
+  nsRefPtr<nsStyleContext> styleContext =
+    ResolveStyleContext(aInsertion, aContent, &aState);
+  DoAddFrameConstructionItems(aState, aContent, styleContext,
+                              aSuppressWhiteSpaceOptimizations, parentFrame,
+                              nullptr, aItems);
+}
+
+void
 nsCSSFrameConstructor::SetAsUndisplayedContent(nsFrameConstructorState& aState,
                                                FrameConstructionItemList& aList,
                                                nsIContent* aContent,
                                                nsStyleContext* aStyleContext,
                                                bool aIsGeneratedContent)
 {
   if (aStyleContext->GetPseudo()) {
     if (aIsGeneratedContent) {
@@ -5568,16 +5609,76 @@ nsCSSFrameConstructor::AddFrameConstruct
     !display->IsAbsolutelyPositionedStyle() &&
     !(bits & FCDATA_IS_TABLE_PART) &&
     !(bits & FCDATA_IS_SVG_TEXT);
 
   if (canHavePageBreak && display->mBreakBefore) {
     AddPageBreakItem(aContent, aStyleContext, aItems);
   }
 
+  if (MOZ_UNLIKELY(bits & FCDATA_IS_CONTENTS)) {
+    if (!GetDisplayContentsStyleFor(aContent)) {
+      MOZ_ASSERT(styleContext->GetPseudo() || !isGeneratedContent,
+                 "Should have had pseudo type");
+      aState.mFrameManager->SetDisplayContents(aContent, styleContext);
+    } else {
+      aState.mFrameManager->ChangeDisplayContents(aContent, styleContext);
+    }
+
+    TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
+    if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
+      ancestorPusher.PushAncestorAndStyleScope(aContent->AsElement());
+    } else {
+      ancestorPusher.PushStyleScope(aContent->AsElement());
+    }
+
+    if (aParentFrame) {
+      aParentFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
+    }
+    CreateGeneratedContentItem(aState, aParentFrame, aContent, styleContext,
+                               nsCSSPseudoElements::ePseudo_before, aItems);
+
+    FlattenedChildIterator iter(aContent);
+    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
+      if (!ShouldCreateItemsForChild(aState, child, aParentFrame)) {
+        continue;
+      }
+
+      // Get the parent of the content and check if it is a XBL children element
+      // (if the content is a children element then parent != aContent 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* parent = child->GetParent();
+      MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
+      TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
+      if (parent != aContent && parent->IsElement()) {
+        if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
+          ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
+        } else {
+          ancestorPusher.PushStyleScope(parent->AsElement());
+        }
+      }
+
+      nsRefPtr<nsStyleContext> childContext =
+        ResolveStyleContext(styleContext, child, &aState);
+      DoAddFrameConstructionItems(aState, child, childContext,
+                                  aSuppressWhiteSpaceOptimizations,
+                                  aParentFrame, aAnonChildren, aItems);
+    }
+    aItems.SetParentHasNoXBLChildren(!iter.XBLInvolved());
+
+    CreateGeneratedContentItem(aState, aParentFrame, aContent, styleContext,
+                               nsCSSPseudoElements::ePseudo_after, aItems);
+    if (canHavePageBreak && display->mBreakAfter) {
+      AddPageBreakItem(aContent, aStyleContext, aItems);
+    }
+    return;
+  }
+
   FrameConstructionItem* item =
     aItems.AppendItem(data, aContent, aTag, aNameSpaceID,
                       pendingBinding, styleContext.forget(),
                       aSuppressWhiteSpaceOptimizations, aAnonChildren);
   if (!item) {
     if (isGeneratedContent) {
       aContent->UnbindFromTree();
     }
@@ -5674,34 +5775,27 @@ nsCSSFrameConstructor::AddFrameConstruct
       ((bits & FCDATA_DISALLOW_OUT_OF_FLOW) ||
        !aState.GetGeometricParent(display, nullptr))) {
     item->mIsLineParticipant = true;
     aItems.LineParticipantItemAdded();
   }
 }
 
 static void
-DestroyContent(void* aPropertyValue)
-{
-  nsIContent* content = static_cast<nsIContent*>(aPropertyValue);
-  content->UnbindFromTree();
-  NS_RELEASE(content);
-}
-
-NS_DECLARE_FRAME_PROPERTY(BeforeProperty, DestroyContent)
-NS_DECLARE_FRAME_PROPERTY(AfterProperty, DestroyContent)
-
-static const FramePropertyDescriptor*
-GenConPseudoToProperty(nsIAtom* aPseudo)
-{
-  NS_ASSERTION(aPseudo == nsCSSPseudoElements::before ||
-               aPseudo == nsCSSPseudoElements::after,
-               "Bad gen-con pseudo");
-  return aPseudo == nsCSSPseudoElements::before ? BeforeProperty()
-      : AfterProperty();
+AddGenConPseudoToFrame(nsIFrame* aOwnerFrame, nsIContent* aContent)
+{
+  typedef nsAutoTArray<nsIContent*, 2> T;
+  const FramePropertyDescriptor* prop = nsIFrame::GenConProperty();
+  FrameProperties props = aOwnerFrame->Properties();
+  T* value = static_cast<T*>(props.Get(prop));
+  if (!value) {
+    value = new T;
+    props.Set(prop, value);
+  }
+  value->AppendElement(aContent);
 }
 
 /**
  * Return true if the frame construction item pointed to by aIter will
  * create a frame adjacent to a line boundary in the frame tree, and that
  * line boundary is induced by a content node adjacent to the frame's
  * content node in the content tree. The latter condition is necessary so
  * that ContentAppended/ContentInserted/ContentRemoved can easily find any
@@ -5795,18 +5889,17 @@ nsCSSFrameConstructor::ConstructFramesFr
     // NS_FRAME_GENERATED_CONTENT.
     aState.mAdditionalStateBits |= NS_FRAME_GENERATED_CONTENT;
 
     // Note that we're not necessarily setting this property on the primary
     // frame for the content for which this is generated content.  We might be
     // setting it on a table pseudo-frame inserted under that instead.  That's
     // OK, though; we just need to do the property set so that the content will
     // get cleaned up when the frame is destroyed.
-    aParentFrame->Properties().Set(GenConPseudoToProperty(styleContext->GetPseudo()),
-                                   item.mContent);
+    ::AddGenConPseudoToFrame(aParentFrame, item.mContent);
 
     // Now that we've passed ownership of item.mContent to the frame, unset
     // our generated content flag so we don't release or unbind it ourselves.
     item.mIsGeneratedContent = false;
   }
 
   // XXXbz maybe just inline ConstructFrameFromItemInternal here or something?
   ConstructFrameFromItemInternal(item, aState, adjParentFrame, aFrameItems);
@@ -5931,31 +6024,80 @@ nsCSSFrameConstructor::GetFloatContainin
 
 /**
  * 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(nsPresContext* aPresContext,
+AdjustAppendParentForAfterContent(nsFrameManager* aFrameManager,
                                   nsIContent* aContainer,
                                   nsContainerFrame* aParentFrame,
+                                  nsIContent* aChild,
                                   nsIFrame** aAfterFrame)
 {
-  // See if the parent has an :after pseudo-element.  Check for the presence
-  // of style first, since nsLayoutUtils::GetAfterFrame is sorta expensive.
-  nsStyleContext* parentStyle = aParentFrame->StyleContext();
-  if (nsLayoutUtils::HasPseudoStyle(aContainer, parentStyle,
+  // If the parent frame has any pseudo-elements or aContainer is a
+  // display:contents node 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 (aParentFrame->GetGenConPseudos() ||
+      nsLayoutUtils::HasPseudoStyle(aContainer, aParentFrame->StyleContext(),
                                     nsCSSPseudoElements::ePseudo_after,
-                                    aPresContext)) {
-    // Ensure that the :after frame is on the principal child list.
-    aParentFrame->DrainSelfOverflowList();
-
-    nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(aParentFrame);
+                                    aParentFrame->PresContext()) ||
+      aFrameManager->GetDisplayContentsStyleFor(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->GetLastChild(nsIFrame::kPrincipalList);
+      if (child && child->IsPseudoFrame(aContainer) &&
+          !child->IsGeneratedContentFrame()) {
+        // Drill down into non-generated pseudo frames of aContainer.
+        parent = nsLayoutUtils::LastContinuationWithChild(do_QueryFrame(child));
+        continue;
+      }
+
+      for (; child; child = child->GetPrevSibling()) {
+        nsIContent* c = child->GetContent();
+        if (child->IsGeneratedContentFrame()) {
+          nsIContent* p = c->GetParent();
+          if (c->Tag() == 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;
 
@@ -6208,20 +6350,43 @@ nsCSSFrameConstructor::IsValidSibling(ns
 
   return true;
 }
 
 nsIFrame*
 nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent,
                                                   nsIContent* aTargetContent,
                                                   uint8_t& aTargetContentDisplay,
+                                                  nsContainerFrame* aParentFrame,
                                                   bool aPrevSibling)
 {
   nsIFrame* sibling = aContent->GetPrimaryFrame();
-  if (!sibling || sibling->GetContent() != aContent) {
+  if (!sibling && GetDisplayContentsStyleFor(aContent)) {
+    // A display:contents node - check if it has a ::before / ::after frame...
+    sibling = aPrevSibling ?
+      nsLayoutUtils::GetAfterFrame(aParentFrame) :
+      nsLayoutUtils::GetBeforeFrame(aParentFrame);
+    if (!sibling) {
+      // ... then recurse into children ...
+      const bool forward = !aPrevSibling;
+      FlattenedChildIterator iter(aContent, forward);
+      sibling = aPrevSibling ?
+        FindPreviousSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame) :
+        FindNextSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame);
+    }
+    if (!sibling) {
+      // ... then ::after / ::before on the opposite end.
+      sibling = aPrevSibling ?
+        nsLayoutUtils::GetBeforeFrame(aParentFrame) :
+        nsLayoutUtils::GetAfterFrame(aParentFrame);
+    }
+    if (!sibling) {
+      return nullptr;
+    }
+  } else if (!sibling || sibling->GetContent() != aContent) {
     // XXX the GetContent() != aContent check is needed due to bug 135040.
     // Remove it once that's fixed.
     return nullptr;
   }
 
   // If the frame is out-of-flow, GetPrimaryFrame() will have returned the
   // out-of-flow frame; we want the placeholder.
   if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
@@ -6250,44 +6415,47 @@ nsCSSFrameConstructor::FindFrameForConte
     sibling = nullptr;
   }
 
   return sibling;
 }
 
 nsIFrame*
 nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter,
-                                           uint8_t& aTargetContentDisplay)
-{
-  nsIContent* child = aIter.Get();
-
+                                           nsIContent* aTargetContent,
+                                           uint8_t& aTargetContentDisplay,
+                                           nsContainerFrame* aParentFrame)
+{
   // Note: not all content objects are associated with a frame (e.g., if it's
-  // `display: none') so keep looking until we find a previous frame
+  // `display: none') so keep looking until we find a previous frame.
   while (nsIContent* sibling = aIter.GetPreviousChild()) {
+    MOZ_ASSERT(sibling != aTargetContent);
     nsIFrame* prevSibling =
-      FindFrameForContentSibling(sibling, child, aTargetContentDisplay, true);
-
+      FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay,
+                                 aParentFrame, true);
     if (prevSibling) {
       // Found a previous sibling, we're done!
       return prevSibling;
     }
   }
 
   return nullptr;
 }
 
 nsIFrame*
 nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter,
-                                       uint8_t& aTargetContentDisplay)
-{
-  nsIContent* child = aIter.Get();
-
+                                       nsIContent* aTargetContent,
+                                       uint8_t& aTargetContentDisplay,
+                                       nsContainerFrame* aParentFrame)
+{
   while (nsIContent* sibling = aIter.GetNextChild()) {
+    MOZ_ASSERT(sibling != aTargetContent);
     nsIFrame* nextSibling =
-      FindFrameForContentSibling(sibling, child, aTargetContentDisplay, false);
+      FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay,
+                                 aParentFrame, false);
 
     if (nextSibling) {
       // We found a next sibling, we're done!
       return nextSibling;
     }
   }
 
   return nullptr;
@@ -6310,36 +6478,34 @@ GetAdjustedParentFrame(nsContainerFrame*
     if (!aChildContent->IsHTML(nsGkAtoms::legend)) {
       newParent = GetFieldSetBlockFrame(aParentFrame);
     }
   }
   return newParent ? newParent : aParentFrame;
 }
 
 nsIFrame*
-nsCSSFrameConstructor::GetInsertionPrevSibling(nsContainerFrame*& aParentFrame,
-                                               nsIContent* aContainer,
+nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion,
                                                nsIContent* aChild,
-                                               bool* aIsAppend,
-                                               bool* aIsRangeInsertSafe,
+                                               bool*       aIsAppend,
+                                               bool*       aIsRangeInsertSafe,
                                                nsIContent* aStartSkipChild,
                                                nsIContent* aEndSkipChild)
 {
+  NS_PRECONDITION(aInsertion->mParentFrame, "Must have parent frame to start with");
+
   *aIsAppend = false;
 
   // Find the frame that precedes the insertion point. Walk backwards
   // from the parent frame to get the parent content, because if an
   // XBL insertion point is involved, we'll need to use _that_ to find
   // the preceding frame.
-
-  NS_PRECONDITION(aParentFrame, "Must have parent frame to start with");
-  nsIContent* container = aParentFrame->GetContent();
-
-  FlattenedChildIterator iter(container);
-  bool xblCase = iter.XBLInvolved() || container != aContainer;
+  FlattenedChildIterator iter(aInsertion->mContainer);
+  bool xblCase = iter.XBLInvolved() ||
+         aInsertion->mParentFrame->GetContent() != aInsertion->mContainer;
   if (xblCase || !aChild->IsRootOfAnonymousSubtree()) {
     // The check for IsRootOfAnonymousSubtree() is because editor is
     // severely broken and calls us directly for native anonymous
     // nodes that it creates.
     if (aStartSkipChild) {
       iter.Seek(aStartSkipChild);
     } else {
       iter.Seek(aChild);
@@ -6349,64 +6515,117 @@ nsCSSFrameConstructor::GetInsertionPrevS
     iter.GetNextChild();
     NS_WARNING("Someone passed native anonymous content directly into frame "
                "construction.  Stop doing that!");
   }
 
   // Note that FindPreviousSibling is passed the iterator by value, so that
   // the later usage of the iterator starts from the same place.
   uint8_t childDisplay = UNSET_DISPLAY;
-  nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
+  nsIFrame* prevSibling =
+    FindPreviousSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame);
 
   // Now, find the geometric parent so that we can handle
   // continuations properly. Use the prev sibling if we have it;
   // otherwise use the next sibling.
   if (prevSibling) {
-    aParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
-  }
-  else {
+    aInsertion->mParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
+  } else {
     // If there is no previous sibling, then find the frame that follows
     if (aEndSkipChild) {
       iter.Seek(aEndSkipChild);
       iter.GetPreviousChild();
     }
-    nsIFrame* nextSibling = FindNextSibling(iter, childDisplay);
+    nsIFrame* nextSibling =
+      FindNextSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame);
+    if (GetDisplayContentsStyleFor(aInsertion->mContainer)) {
+      if (!nextSibling) {
+        // Our siblings (if any) does not have a frame to guide us.
+        // The frame for aChild should be inserted whereever a frame for
+        // the container would be inserted.  This is needed when inserting
+        // into nested display:contents nodes.
+        nsIContent* child = aInsertion->mContainer;
+        InsertionPoint fakeInsertion(aInsertion->mParentFrame, child->GetParent());
+        nsIFrame* result = GetInsertionPrevSibling(&fakeInsertion, child, aIsAppend,
+                                                   aIsRangeInsertSafe, nullptr, nullptr);
+        MOZ_ASSERT(aInsertion->mParentFrame == fakeInsertion.mParentFrame);
+        return result;
+      }
+
+      prevSibling = nextSibling->GetPrevSibling();
+    }
 
     if (nextSibling) {
-      aParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
-    }
-    else {
+      aInsertion->mParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
+    } else {
       // No previous or next sibling, so treat this like an appended frame.
       *aIsAppend = true;
-      if (IsFramePartOfIBSplit(aParentFrame)) {
+      if (IsFramePartOfIBSplit(aInsertion->mParentFrame)) {
         // Since we're appending, we'll walk to the last anonymous frame
         // that was created for the broken inline frame.  But don't walk
         // to the trailing inline if it's empty; stop at the block.
-        aParentFrame = GetLastIBSplitSibling(aParentFrame, false);
+        aInsertion->mParentFrame =
+          GetLastIBSplitSibling(aInsertion->mParentFrame, false);
       }
       // Get continuation that parents the last child.  This MUST be done
       // before the AdjustAppendParentForAfterContent call.
-      aParentFrame = nsLayoutUtils::LastContinuationWithChild(aParentFrame);
+      aInsertion->mParentFrame =
+        nsLayoutUtils::LastContinuationWithChild(aInsertion->mParentFrame);
       // Deal with fieldsets
-      aParentFrame = ::GetAdjustedParentFrame(aParentFrame,
-                                              aParentFrame->GetType(),
-                                              aChild);
+      aInsertion->mParentFrame =
+        ::GetAdjustedParentFrame(aInsertion->mParentFrame,
+                                 aInsertion->mParentFrame->GetType(),
+                                 aChild);
       nsIFrame* appendAfterFrame;
-      aParentFrame =
-        ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
-                                            container, aParentFrame,
-                                            &appendAfterFrame);
-      prevSibling = ::FindAppendPrevSibling(aParentFrame, appendAfterFrame);
+      aInsertion->mParentFrame =
+        ::AdjustAppendParentForAfterContent(this, aInsertion->mContainer,
+                                            aInsertion->mParentFrame,
+                                            aChild, &appendAfterFrame);
+      prevSibling = ::FindAppendPrevSibling(aInsertion->mParentFrame, appendAfterFrame);
     }
   }
 
   *aIsRangeInsertSafe = (childDisplay == UNSET_DISPLAY);
   return prevSibling;
 }
 
+nsContainerFrame*
+nsCSSFrameConstructor::GetContentInsertionFrameFor(nsIContent* aContent)
+{
+  // Get the primary frame associated with the content
+  nsIFrame* frame = aContent->GetPrimaryFrame();
+
+  if (!frame) {
+    if (GetDisplayContentsStyleFor(aContent)) {
+      nsIContent* parent = aContent->GetParent();
+      if (parent && parent == aContent->GetContainingShadow()) {
+        parent = parent->GetBindingParent();
+      }
+      frame = parent ? GetContentInsertionFrameFor(parent) : nullptr;
+    }
+    if (!frame) {
+      return nullptr;
+    }
+  } else {
+    // If the content of the frame is not the desired content then this is not
+    // really a frame for the desired content.
+    // XXX This check is needed due to bug 135040. Remove it once that's fixed.
+    if (frame->GetContent() != aContent) {
+      return nullptr;
+    }
+  }
+
+  nsContainerFrame* insertionFrame = frame->GetContentInsertionFrame();
+
+  NS_ASSERTION(!insertionFrame || insertionFrame == frame || !frame->IsLeaf(),
+    "The insertion frame is the primary frame or the primary frame isn't a leaf");
+
+  return insertionFrame;
+}
+
 static bool
 IsSpecialFramesetChild(nsIContent* aContent)
 {
   // IMPORTANT: This must match the conditions in nsHTMLFramesetFrame::Init.
   return aContent->IsHTML() &&
     (aContent->Tag() == nsGkAtoms::frameset ||
      aContent->Tag() == nsGkAtoms::frame);
 }
@@ -6442,32 +6661,32 @@ MaybeGetListBoxBodyFrame(nsIContent* aCo
   }
 
   return nullptr;
 }
 #endif
 
 void
 nsCSSFrameConstructor::AddTextItemIfNeeded(nsFrameConstructorState& aState,
-                                           nsContainerFrame* aParentFrame,
+                                           const InsertionPoint& aInsertion,
                                            nsIContent* aPossibleTextContent,
                                            FrameConstructionItemList& aItems)
 {
   NS_PRECONDITION(aPossibleTextContent, "Must have node");
   if (!aPossibleTextContent->IsNodeOfType(nsINode::eTEXT) ||
       !aPossibleTextContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
     // Not text, or not suppressed due to being all-whitespace (if it
     // were being suppressed, it would have the
     // NS_CREATE_FRAME_IF_NON_WHITESPACE flag)
     return;
   }
   NS_ASSERTION(!aPossibleTextContent->GetPrimaryFrame(),
                "Text node has a frame and NS_CREATE_FRAME_IF_NON_WHITESPACE");
   AddFrameConstructionItems(aState, aPossibleTextContent, false,
-                            aParentFrame, aItems);
+                            aInsertion, aItems);
 }
 
 void
 nsCSSFrameConstructor::ReframeTextIfNeeded(nsIContent* aParentContent,
                                            nsIContent* aContent)
 {
   if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
       !aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE)) {
@@ -6534,16 +6753,20 @@ nsCSSFrameConstructor::MaybeConstructLaz
     }
     if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
       noPrimaryFrame = true;
     }
     if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
       needsFrameBitSet = true;
     }
 #endif
+    // XXXmats no lazy frames for display:contents descendants yet (bug 979782).
+    if (GetDisplayContentsStyleFor(content)) {
+      return false;
+    }
     content->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
     content = content->GetFlattenedTreeParent();
   }
 #ifdef DEBUG
   if (content && content->GetPrimaryFrame() &&
       content->GetPrimaryFrame()->IsLeaf()) {
     noPrimaryFrame = needsFrameBitSet = false;
   }
@@ -6656,18 +6879,18 @@ void
 nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
                                                     nsIContent* aStartChild,
                                                     nsIContent* aEndChild,
                                                     bool aAllowLazyConstruction)
 {
   for (nsIContent* child = aStartChild;
        child != aEndChild;
        child = child->GetNextSibling()) {
-    if ((child->GetPrimaryFrame() ||
-         GetUndisplayedContent(child))
+    if ((child->GetPrimaryFrame() || GetUndisplayedContent(child) ||
+         GetDisplayContentsStyleFor(child))
 #ifdef MOZ_XUL
         //  Except listboxes suck, so do NOT skip anything here if
         //  we plan to notify a listbox.
         && !MaybeGetListBoxBodyFrame(aContainer, child)
 #endif
         ) {
       // Already have a frame or undisplayed entry for this content; a
       // previous ContentInserted in this loop must have reconstructed
@@ -6675,71 +6898,71 @@ nsCSSFrameConstructor::IssueSingleInsert
       continue;
     }
     // Call ContentInserted with this node.
     ContentInserted(aContainer, child, mTempFrameTreeState,
                     aAllowLazyConstruction);
   }
 }
 
-nsContainerFrame*
+nsCSSFrameConstructor::InsertionPoint
 nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
                                               nsIContent* aStartChild,
                                               nsIContent* aEndChild,
                                               bool aAllowLazyConstruction)
 {
   // See if we have an XBL insertion point. If so, then that's our
   // real parent frame; if not, then the frame hasn't been built yet
   // and we just bail.
-  bool multiple = false;
-  nsContainerFrame* insertionPoint = GetInsertionPoint(aContainer, nullptr, &multiple);
-  if (!insertionPoint && !multiple)
-    return nullptr; // Don't build the frames.
+  InsertionPoint insertionPoint = GetInsertionPoint(aContainer, nullptr);
+  if (!insertionPoint.mParentFrame && !insertionPoint.mMultiple) {
+    return insertionPoint; // Don't build the frames.
+  }
 
   bool hasInsertion = false;
-  if (!multiple) {
+  if (!insertionPoint.mMultiple) {
     // XXXbz XBL2/sXBL issue
     nsIDocument* document = aStartChild->GetComposedDoc();
     // XXXbz how would |document| be null here?
     if (document && aStartChild->GetXBLInsertionParent()) {
       hasInsertion = true;
     }
   }
 
-  if (multiple || hasInsertion) {
+  if (insertionPoint.mMultiple || hasInsertion) {
     // We have an insertion point.  There are some additional tests we need to do
     // in order to ensure that an append is a safe operation.
     uint32_t childCount = 0;
 
-    if (!multiple) {
+    if (!insertionPoint.mMultiple) {
       // We may need to make multiple ContentInserted calls instead.  A
       // reasonable heuristic to employ (in order to maintain good performance)
       // is to find out if the insertion point's content node contains any
       // explicit children.  If it does not, then it is highly likely that
       // an append is occurring.  (Note it is not definite, and there are insane
       // cases we will not deal with by employing this heuristic, but it beats
       // always falling back to multiple ContentInserted calls).
       //
       // In the multiple insertion point case, we know we're going to need to do
       // multiple ContentInserted calls anyway.
       // XXXndeakin This test doesn't work in the new world. Or rather, it works, but
       // it's slow
-      childCount = insertionPoint->GetContent()->GetChildCount();
+      childCount = insertionPoint.mParentFrame->GetContent()->GetChildCount();
     }
 
     // If we have multiple insertion points or if we have an insertion point
     // and the operation is not a true append or if the insertion point already
     // has explicit children, then we must fall back.
-    if (multiple || aEndChild != nullptr || childCount > 0) {
+    if (insertionPoint.mMultiple || aEndChild != nullptr || childCount > 0) {
       // Now comes the fun part.  For each inserted child, make a
       // ContentInserted call as if it had just gotten inserted and
       // let ContentInserted handle the mess.
       IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
                                    aAllowLazyConstruction);
-      return nullptr;
+      insertionPoint.mParentFrame = nullptr;
     }
   }
 
   return insertionPoint;
 }
 
 bool
 nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
@@ -6820,33 +7043,32 @@ nsCSSFrameConstructor::ContentAppended(n
     nsIContent* bindingParent = aContainer->GetBindingParent();
     LAYOUT_PHASE_TEMP_EXIT();
     nsresult rv = RecreateFramesForContent(bindingParent, false,
                                            REMOVE_FOR_RECONSTRUCTION, nullptr);
     LAYOUT_PHASE_TEMP_REENTER();
     return rv;
   }
 
-  // Get the frame associated with the content
-  nsContainerFrame* parentFrame = ::GetContentInsertionFrameFor(aContainer);
-
   // See comment in ContentRangeInserted for why this is necessary.
-  if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
+  if (!GetContentInsertionFrameFor(aContainer) &&
+      !aContainer->IsActiveChildrenElement()) {
     return NS_OK;
   }
 
   if (aAllowLazyConstruction &&
       MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
     return NS_OK;
   }
 
   LAYOUT_PHASE_TEMP_EXIT();
-  parentFrame = GetRangeInsertionPoint(aContainer,
-                                       aFirstNewContent, nullptr,
-                                       aAllowLazyConstruction);
+  InsertionPoint insertion =
+    GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr,
+                           aAllowLazyConstruction);
+  nsContainerFrame*& parentFrame = insertion.mParentFrame;
   LAYOUT_PHASE_TEMP_REENTER();
   if (!parentFrame) {
     return NS_OK;
   }
 
   LAYOUT_PHASE_TEMP_EXIT();
   if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
     LAYOUT_PHASE_TEMP_REENTER();
@@ -6895,19 +7117,18 @@ nsCSSFrameConstructor::ContentAppended(n
   // We should never get here with fieldsets, since they have multiple
   // insertion points.
   NS_ASSERTION(parentFrame->GetType() != nsGkAtoms::fieldSetFrame,
                "Unexpected parent");
 
   // Deal with possible :after generated content on the parent
   nsIFrame* parentAfterFrame;
   parentFrame =
-    ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
-                                        aContainer, parentFrame,
-                                        &parentAfterFrame);
+    ::AdjustAppendParentForAfterContent(this, insertion.mContainer, parentFrame,
+                                        aFirstNewContent, &parentAfterFrame);
 
   // Create some new frames
   nsFrameConstructorState state(mPresShell,
                                 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
                                 GetFloatContainingBlock(parentFrame));
   state.mTreeMatchContext.InitAncestors(aContainer->AsElement());
 
@@ -6941,23 +7162,23 @@ nsCSSFrameConstructor::ContentAppended(n
     // one.  No need to do all this if our parent type is not block, though,
     // since WipeContainingBlock already handles that situation.
     //
     // Because we're appending, we don't need to worry about any text
     // after the appended content; there can only be XBL anonymous content
     // (text in an XBL binding is not suppressed) or generated content
     // (and bare text nodes are not generated). Native anonymous content
     // generated by frames never participates in inline layout.
-    AddTextItemIfNeeded(state, parentFrame,
+    AddTextItemIfNeeded(state, insertion,
                         aFirstNewContent->GetPreviousSibling(), items);
   }
   for (nsIContent* child = aFirstNewContent;
        child;
        child = child->GetNextSibling()) {
-    AddFrameConstructionItems(state, child, false, parentFrame, items);
+    AddFrameConstructionItems(state, child, false, insertion, items);
   }
 
   nsIFrame* prevSibling = ::FindAppendPrevSibling(parentFrame, parentAfterFrame);
 
   // Perform special check for diddling around with the frames in
   // a ib-split inline frame.
   // If we're appending before :after content, then we're not really
   // appending, so let WipeContainingBlock know that.
@@ -7260,113 +7481,118 @@ nsCSSFrameConstructor::ContentRangeInser
     nsIContent* bindingParent = aContainer->GetBindingParent();
     LAYOUT_PHASE_TEMP_EXIT();
     nsresult rv = RecreateFramesForContent(bindingParent, false,
                                            REMOVE_FOR_RECONSTRUCTION, nullptr);
     LAYOUT_PHASE_TEMP_REENTER();
     return rv;
   }
 
-  nsContainerFrame* parentFrame = ::GetContentInsertionFrameFor(aContainer);
-  // The xbl:children element won't have a frame, but default content can have the children as
-  // a parent. While its uncommon to change the structure of the default content itself, a label,
-  // for example, can be reframed by having its value attribute set or removed.
-  if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
-    return NS_OK;
-  }
-
-  // Otherwise, we've got parent content. Find its frame.
-  NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer, "New XBL code is possibly wrong!");
-
-  if (aAllowLazyConstruction &&
-      MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
-    return NS_OK;
-  }
-
+  // Put 'parentFrame' inside a scope so we don't confuse it with
+  // 'insertion.mParentFrame' later.
+  {
+    nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
+    // The xbl:children element won't have a frame, but default content can have the children as
+    // a parent. While its uncommon to change the structure of the default content itself, a label,
+    // for example, can be reframed by having its value attribute set or removed.
+    if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
+      return NS_OK;
+    }
+
+    // Otherwise, we've got parent content. Find its frame.
+    NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
+                 GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
+
+    if (aAllowLazyConstruction &&
+        MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
+      return NS_OK;
+    }
+  }
+
+  InsertionPoint insertion;
   if (isSingleInsert) {
     // See if we have an XBL insertion point. If so, then that's our
     // real parent frame; if not, then the frame hasn't been built yet
     // and we just bail.
-    parentFrame = GetInsertionPoint(aContainer, aStartChild);
+    insertion = GetInsertionPoint(aContainer, aStartChild);
   } else {
     // Get our insertion point. If we need to issue single ContentInserted's
     // GetRangeInsertionPoint will take care of that for us.
     LAYOUT_PHASE_TEMP_EXIT();
-    parentFrame = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
-                                         aAllowLazyConstruction);
+    insertion = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
+                                       aAllowLazyConstruction);
     LAYOUT_PHASE_TEMP_REENTER();
   }
 
-  if (!parentFrame) {
+  if (!insertion.mParentFrame) {
     return NS_OK;
   }
 
   bool isAppend, isRangeInsertSafe;
-  nsIFrame* prevSibling =
-    GetInsertionPrevSibling(parentFrame, aContainer, aStartChild,
-                            &isAppend, &isRangeInsertSafe);
+  nsIFrame* prevSibling = GetInsertionPrevSibling(&insertion, aStartChild,
+                                                  &isAppend, &isRangeInsertSafe);
 
   // check if range insert is safe
   if (!isSingleInsert && !isRangeInsertSafe) {
     // must fall back to a single ContertInserted for each child in the range
     LAYOUT_PHASE_TEMP_EXIT();
     IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
                                  aAllowLazyConstruction);
     LAYOUT_PHASE_TEMP_REENTER();
     return NS_OK;
   }
 
-  nsIContent* container = parentFrame->GetContent();
-
-  nsIAtom* frameType = parentFrame->GetType();
+  nsIContent* container = insertion.mParentFrame->GetContent();
+
+  nsIAtom* frameType = insertion.mParentFrame->GetType();
   LAYOUT_PHASE_TEMP_EXIT();
-  if (MaybeRecreateForFrameset(parentFrame, aStartChild, aEndChild)) {
+  if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild, aEndChild)) {
     LAYOUT_PHASE_TEMP_REENTER();
     return NS_OK;
   }
   LAYOUT_PHASE_TEMP_REENTER();
 
   // We should only get here with fieldsets when doing a single insert, because
   // fieldsets have multiple insertion points.
   NS_ASSERTION(isSingleInsert || frameType != nsGkAtoms::fieldSetFrame,
                "Unexpected parent");
-  if (IsFrameForFieldSet(parentFrame, frameType) &&
+  if (IsFrameForFieldSet(insertion.mParentFrame, frameType) &&
       aStartChild->Tag() == nsGkAtoms::legend) {
     // Just reframe the parent, since figuring out whether this
     // should be the new legend and then handling it is too complex.
     // We could do a little better here --- check if the fieldset already
     // has a legend which occurs earlier in its child list than this node,
     // and if so, proceed. But we'd have to extend nsFieldSetFrame
     // to locate this legend in the inserted frames and extract it.
     LAYOUT_PHASE_TEMP_EXIT();
-    nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false,
+    nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
                                            REMOVE_FOR_RECONSTRUCTION, nullptr);
     LAYOUT_PHASE_TEMP_REENTER();
     return rv;
   }
 
   // Don't construct kids of leaves
-  if (parentFrame->IsLeaf()) {
+  if (insertion.mParentFrame->IsLeaf()) {
     // Clear lazy bits so we don't try to construct again.
     ClearLazyBits(aStartChild, aEndChild);
     return NS_OK;
   }
 
-  if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
+  if (insertion.mParentFrame->IsFrameOfType(nsIFrame::eMathML)) {
     LAYOUT_PHASE_TEMP_EXIT();
-    nsresult rv = RecreateFramesForContent(parentFrame->GetContent(), false,
+    nsresult rv = RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
                                            REMOVE_FOR_RECONSTRUCTION, nullptr);
     LAYOUT_PHASE_TEMP_REENTER();
     return rv;
   }
 
   nsFrameConstructorState state(mPresShell,
-                                GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
-                                GetAbsoluteContainingBlock(parentFrame, ABS_POS),
-                                GetFloatContainingBlock(parentFrame),
+                                GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
+                                GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
+                                GetFloatContainingBlock(insertion.mParentFrame),
                                 aFrameState);
   state.mTreeMatchContext.InitAncestors(aContainer ?
                                           aContainer->AsElement() :
                                           nullptr);
 
   // Recover state for the containing block - we need to know if
   // it has :first-letter or :first-line style applied to it. The
   // reason we care is that the internal structure in these cases
@@ -7375,148 +7601,147 @@ nsCSSFrameConstructor::ContentRangeInser
   nsContainerFrame* containingBlock = state.mFloatedItems.containingBlock;
   bool haveFirstLetterStyle = false;
   bool haveFirstLineStyle = false;
 
   // In order to shave off some cycles, we only dig up the
   // containing block haveFirst* flags if the parent frame where
   // the insertion/append is occurring is an inline or block
   // container. For other types of containers this isn't relevant.
-  uint8_t parentDisplay = parentFrame->GetDisplay();
-
-  // Examine the parentFrame where the insertion is taking
+  uint8_t parentDisplay = insertion.mParentFrame->GetDisplay();
+
+  // Examine the insertion.mParentFrame where the insertion is taking
   // place. If it's a certain kind of container then some special
   // processing is done.
   if ((NS_STYLE_DISPLAY_BLOCK == parentDisplay) ||
       (NS_STYLE_DISPLAY_LIST_ITEM == parentDisplay) ||
       (NS_STYLE_DISPLAY_INLINE == parentDisplay) ||
       (NS_STYLE_DISPLAY_INLINE_BLOCK == parentDisplay)) {
     // Recover the special style flags for the containing block
     if (containingBlock) {
       haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
       haveFirstLineStyle =
         ShouldHaveFirstLineStyle(containingBlock->GetContent(),
                                  containingBlock->StyleContext());
     }
 
     if (haveFirstLetterStyle) {
-      // If our current parentFrame is a Letter frame, use its parent as our
+      // If our current insertion.mParentFrame is a Letter frame, use its parent as our
       // new parent hint
-      if (parentFrame->GetType() == nsGkAtoms::letterFrame) {
-        // If parentFrame is out of flow, then we actually want the parent of
+      if (insertion.mParentFrame->GetType() == nsGkAtoms::letterFrame) {
+        // If insertion.mParentFrame is out of flow, then we actually want the parent of
         // the placeholder frame.
-        if (parentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
+        if (insertion.mParentFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
           nsPlaceholderFrame* placeholderFrame =
-            GetPlaceholderFrameFor(parentFrame);
+            GetPlaceholderFrameFor(insertion.mParentFrame);
           NS_ASSERTION(placeholderFrame, "No placeholder for out-of-flow?");
-          parentFrame = placeholderFrame->GetParent();
+          insertion.mParentFrame = placeholderFrame->GetParent();
         } else {
-          parentFrame = parentFrame->GetParent();
+          insertion.mParentFrame = insertion.mParentFrame->GetParent();
         }
       }
 
       // Remove the old letter frames before doing the insertion
       RemoveLetterFrames(state.mPresContext, mPresShell,
                          state.mFloatedItems.containingBlock);
 
       // Removing the letterframes messes around with the frame tree, removing
       // and creating frames.  We need to reget our prevsibling, parent frame,
       // etc.
-      prevSibling = GetInsertionPrevSibling(parentFrame, aContainer,
-                                            aStartChild, &isAppend,
+      prevSibling = GetInsertionPrevSibling(&insertion, aStartChild, &isAppend,
                                             &isRangeInsertSafe);
 
       // Need check whether a range insert is still safe.
       if (!isSingleInsert && !isRangeInsertSafe) {
         // Need to recover the letter frames first.
         RecoverLetterFrames(state.mFloatedItems.containingBlock);
 
         // must fall back to a single ContertInserted for each child in the range
         LAYOUT_PHASE_TEMP_EXIT();
         IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
                                      aAllowLazyConstruction);
         LAYOUT_PHASE_TEMP_REENTER();
         return NS_OK;
       }
 
-      container = parentFrame->GetContent();
-      frameType = parentFrame->GetType();
+      container = insertion.mParentFrame->GetContent();
+      frameType = insertion.mParentFrame->GetType();
     }
   }
 
   if (!prevSibling) {
     // We're inserting the new frames as the first child. See if the
     // parent has a :before pseudo-element
-    nsIFrame* firstChild = parentFrame->GetFirstPrincipalChild();
+    nsIFrame* firstChild = insertion.mParentFrame->GetFirstPrincipalChild();
 
     if (firstChild &&
         nsLayoutUtils::IsGeneratedContentFor(container, firstChild,
                                              nsCSSPseudoElements::before)) {
       // Insert the new frames after the last continuation of the :before
       prevSibling = firstChild->GetTailContinuation();
-      parentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
+      insertion.mParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
       // Don't change isAppend here; we'll can call AppendFrames as needed, and
       // the change to our prevSibling doesn't affect that.
     }
   }
 
   FrameConstructionItemList items;
   ParentType parentType = GetParentType(frameType);
   FlattenedChildIterator iter(aContainer);
   bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
   if (aStartChild->GetPreviousSibling() &&
       parentType == eTypeBlock && haveNoXBLChildren) {
     // If there's a text node in the normal content list just before the
     // new nodes, and it has no frame, make a frame construction item for
     // it, because it might need a frame now.  No need to do this if our
     // parent type is not block, though, since WipeContainingBlock
     // already handles that sitation.
-    AddTextItemIfNeeded(state, parentFrame, aStartChild->GetPreviousSibling(),
+    AddTextItemIfNeeded(state, insertion, aStartChild->GetPreviousSibling(),
                         items);
   }
 
   if (isSingleInsert) {
     AddFrameConstructionItems(state, aStartChild,
                               aStartChild->IsRootOfAnonymousSubtree(),
-                              parentFrame, items);
+                              insertion, items);
   } else {
     for (nsIContent* child = aStartChild;
          child != aEndChild;
          child = child->GetNextSibling()){
-      AddFrameConstructionItems(state, child, false, parentFrame, items);
+      AddFrameConstructionItems(state, child, false, insertion, items);
     }
   }
 
   if (aEndChild && parentType == eTypeBlock && haveNoXBLChildren) {
     // If there's a text node in the normal content list just after the
     // new nodes, and it has no frame, make a frame construction item for
     // it, because it might need a frame now.  No need to do this if our
     // parent type is not block, though, since WipeContainingBlock
     // already handles that sitation.
-    AddTextItemIfNeeded(state, parentFrame, aEndChild, items);
+    AddTextItemIfNeeded(state, insertion, aEndChild, items);
   }
 
   // Perform special check for diddling around with the frames in
   // a special inline frame.
   // If we're appending before :after content, then we're not really
   // appending, so let WipeContainingBlock know that.
   LAYOUT_PHASE_TEMP_EXIT();
-  if (WipeContainingBlock(state, containingBlock, parentFrame, items,
+  if (WipeContainingBlock(state, containingBlock, insertion.mParentFrame, items,
                           isAppend, prevSibling)) {
     LAYOUT_PHASE_TEMP_REENTER();
     return NS_OK;
   }
   LAYOUT_PHASE_TEMP_REENTER();
 
   // If the container is a table and a caption will be appended, it needs to be
   // put in the outer table 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, parentFrame, frameItems);
+  ConstructFramesFromItemList(state, items, insertion.mParentFrame, frameItems);
 
   if (frameItems.NotEmpty()) {
     for (nsIContent* child = aStartChild;
          child != aEndChild;
          child = child->GetNextSibling()){
       InvalidateCanvasIfNeeded(mPresShell, child);
     }
 
@@ -7557,74 +7782,74 @@ nsCSSFrameConstructor::ContentRangeInser
                   frame2->GetType() == nsGkAtoms::tableFrame) ||
                  frame1->GetType() == nsGkAtoms::fieldSetFrame ||
                  (frame1->GetParent() &&
                   frame1->GetParent()->GetType() == nsGkAtoms::fieldSetFrame),
                  "Unexpected frame types");
 #endif
     isAppend = true;
     nsIFrame* appendAfterFrame;
-    parentFrame =
-      ::AdjustAppendParentForAfterContent(mPresShell->GetPresContext(),
-                                          container,
+    insertion.mParentFrame =
+      ::AdjustAppendParentForAfterContent(this, container,
                                           frameItems.FirstChild()->GetParent(),
-                                          &appendAfterFrame);
-    prevSibling = ::FindAppendPrevSibling(parentFrame, appendAfterFrame);
-  }
-
-  if (haveFirstLineStyle && parentFrame == containingBlock) {
+                                          aStartChild, &appendAfterFrame);
+    prevSibling = ::FindAppendPrevSibling(insertion.mParentFrame, appendAfterFrame);
+  }
+
+  if (haveFirstLineStyle && insertion.mParentFrame == containingBlock) {
     // It's possible that the new frame goes into a first-line
     // frame. Look at it and see...
     if (isAppend) {
       // Use append logic when appending
       AppendFirstLineFrames(state, containingBlock->GetContent(),
                             containingBlock, frameItems);
     }
     else {
       // Use more complicated insert logic when inserting
       // XXXbz this method is a no-op, so it's easy for the args being passed
       // here to make no sense without anyone noticing...  If it ever stops
       // being a no-op, vet them carefully!
-      InsertFirstLineFrames(state, container, containingBlock, &parentFrame,
+      InsertFirstLineFrames(state, container, containingBlock, &insertion.mParentFrame,
                             prevSibling, frameItems);
     }
   }
 
   // We might have captions; put them into the caption list of the
   // outer table frame.
   if (captionItems.NotEmpty()) {
     NS_ASSERTION(nsGkAtoms::tableFrame == frameType ||
                  nsGkAtoms::tableOuterFrame == frameType,
                  "parent for caption is not table?");
     // We need to determine where to put the caption items; start with the
     // the parent frame that has already been determined and get the insertion
     // prevsibling of the first caption item.
-    nsContainerFrame* captionParent = parentFrame;
     bool captionIsAppend;
     nsIFrame* captionPrevSibling = nullptr;
 
     // aIsRangeInsertSafe is ignored on purpose because it is irrelevant here.
     bool ignored;
+    InsertionPoint captionInsertion(insertion.mParentFrame, insertion.mContainer);
     if (isSingleInsert) {
       captionPrevSibling =
-        GetInsertionPrevSibling(captionParent, aContainer, aStartChild,
+        GetInsertionPrevSibling(&captionInsertion, aStartChild,
                                 &captionIsAppend, &ignored);
     } else {
       nsIContent* firstCaption = captionItems.FirstChild()->GetContent();
       // It is very important here that we skip the children in
       // [aStartChild,aEndChild) when looking for a
       // prevsibling.
       captionPrevSibling =
-        GetInsertionPrevSibling(captionParent, aContainer, firstCaption,
+        GetInsertionPrevSibling(&captionInsertion, firstCaption,
                                 &captionIsAppend, &ignored,
                                 aStartChild, aEndChild);
     }
 
     nsContainerFrame* outerTable = nullptr;
-    if (GetCaptionAdjustedParent(captionParent, captionItems.FirstChild(),
+    if (GetCaptionAdjustedParent(captionInsertion.mParentFrame,
+                                 captionItems.FirstChild(),
                                  &outerTable)) {
       // If the parent is not an outer table frame we will try to add frames
       // to a named child list that the parent does not honour and the frames
       // will get lost
       NS_ASSERTION(nsGkAtoms::tableOuterFrame == outerTable->GetType(),
                    "Pseudo frame construction failure; "
                    "a caption can be only a child of an outer table frame");
 
@@ -7642,32 +7867,32 @@ nsCSSFrameConstructor::ContentRangeInser
                      captionPrevSibling, captionItems);
       }
     }
   }
 
   if (frameItems.NotEmpty()) {
     // Notify the parent frame
     if (isAppend) {
-      AppendFramesToParent(state, parentFrame, frameItems, prevSibling);
+      AppendFramesToParent(state, insertion.mParentFrame, frameItems, prevSibling);
     } else {
-      InsertFrames(parentFrame, kPrincipalList, prevSibling, frameItems);
+      InsertFrames(insertion.mParentFrame, kPrincipalList, prevSibling, frameItems);
     }
   }
 
   if (haveFirstLetterStyle) {
     // Recover the letter frames for the containing block when
     // it has first-letter style.
     RecoverLetterFrames(state.mFloatedItems.containingBlock);
   }
 
 #ifdef DEBUG
-  if (gReallyNoisyContentUpdates && parentFrame) {
+  if (gReallyNoisyContentUpdates && insertion.mParentFrame) {
     printf("nsCSSFrameConstructor::ContentRangeInserted: resulting frame model:\n");
-    parentFrame->List(stdout, 0);
+    insertion.mParentFrame->List(stdout, 0);
   }
 #endif
 
 #ifdef ACCESSIBILITY
   nsAccessibilityService* accService = nsIPresShell::AccService();
   if (accService) {
     accService->ContentRangeInserted(mPresShell, aContainer,
                                      aStartChild, aEndChild);
@@ -7705,28 +7930,63 @@ nsCSSFrameConstructor::ContentRemoved(ns
            static_cast<void*>(aChild),
            static_cast<void*>(aOldNextSibling));
     if (gReallyNoisyContentUpdates) {
       aContainer->List(stdout, 0);
     }
   }
 #endif
 
-  nsPresContext *presContext = mPresShell->GetPresContext();
-  nsresult                  rv = NS_OK;
-
-  // Find the child frame that maps the content
+  nsresult rv = NS_OK;
   nsIFrame* childFrame = aChild->GetPrimaryFrame();
-
   if (!childFrame || childFrame->GetContent() != aChild) {
     // XXXbz the GetContent() != aChild check is needed due to bug 135040.
     // Remove it once that's fixed.
     ClearUndisplayedContentIn(aChild, aContainer);
   }
-
+  MOZ_ASSERT(!childFrame || !GetDisplayContentsStyleFor(aChild),
+             "display:contents nodes shouldn't have a frame");
+  if (!childFrame && GetDisplayContentsStyleFor(aChild)) {
+    nsIFrame* ancestorFrame = nullptr;
+    nsIContent* ancestor = aContainer;
+    for (; ancestor; ancestor = ancestor->GetParent()) {
+      ancestorFrame = ancestor->GetPrimaryFrame();
+      if (ancestorFrame) {
+        break;
+      }
+    }
+    if (ancestorFrame) {
+      nsTArray<nsIContent*>* generated = ancestorFrame->GetGenConPseudos();
+      if (generated) {
+        *aDidReconstruct = true;
+        LAYOUT_PHASE_TEMP_EXIT();
+        // XXXmats Can we recreate frames only for the ::after/::before content?
+        // XXX Perhaps even only those that belong to the aChild sub-tree?
+        RecreateFramesForContent(ancestor, false, aFlags, aDestroyedFramesFor);
+        LAYOUT_PHASE_TEMP_REENTER();
+        return NS_OK;
+      }
+    }
+
+    FlattenedChildIterator iter(aChild);
+    for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
+      if (c->GetPrimaryFrame() || GetDisplayContentsStyleFor(c)) {
+        LAYOUT_PHASE_TEMP_EXIT();
+        rv = ContentRemoved(aChild, c, nullptr, aFlags, aDidReconstruct, aDestroyedFramesFor);
+        LAYOUT_PHASE_TEMP_REENTER();
+        NS_ENSURE_SUCCESS(rv, rv);
+        if (aFlags != REMOVE_DESTROY_FRAMES && *aDidReconstruct) {
+          return rv;
+        }
+      }
+    }
+    ClearDisplayContentsIn(aChild, aContainer);
+  }
+
+  nsPresContext* presContext = mPresShell->GetPresContext();
 #ifdef MOZ_XUL
   if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
                         mDocument, childFrame, CONTENT_REMOVED)) {
     if (aFlags == REMOVE_DESTROY_FRAMES) {
       CaptureStateForFramesOf(aChild, mTempFrameTreeState);
     }
     return NS_OK;
   }
@@ -8566,77 +8826,72 @@ nsCSSFrameConstructor::ReplicateFixedFra
   // XXXbz this is a little screwed up, since the fixed frames will have
   // broken auto-positioning. Oh, well.
   NS_ASSERTION(!canvasFrame->GetFirstPrincipalChild(),
                "leaking frames; doc root continuation must be empty");
   canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
   return NS_OK;
 }
 
-nsContainerFrame*
-nsCSSFrameConstructor::GetInsertionPoint(nsIContent*   aContainer,
-                                         nsIContent*   aChildContent,
-                                         bool*         aMultiple)
-{
-  nsBindingManager *bindingManager = mDocument->BindingManager();
+nsCSSFrameConstructor::InsertionPoint
+nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer,
+                                         nsIContent* aChild)
+{
+  nsBindingManager* bindingManager = mDocument->BindingManager();
 
   nsIContent* insertionElement;
-  if (aChildContent) {
+  if (aChild) {
     // We've got an explicit insertion child. Check to see if it's
     // anonymous.
-    if (aChildContent->GetBindingParent() == aContainer) {
+    if (aChild->GetBindingParent() == aContainer) {
       // This child content is anonymous. Don't use the insertion
       // point, since that's only for the explicit kids.
-      return ::GetContentInsertionFrameFor(aContainer);
+      return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
     }
 
     if (nsContentUtils::HasDistributedChildren(aContainer)) {
       // The container distributes nodes, use the frame of the flattened tree parent.
       // It may be the case that the node is distributed but not matched to any
       // insertion points, so there is no flattened parent.
-      nsIContent* flattenedParent = aChildContent->GetFlattenedTreeParent();
-      return flattenedParent ? ::GetContentInsertionFrameFor(flattenedParent) : nullptr;
-    }
-
-    insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChildContent);
-  }
-  else {
+      nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
+      if (flattenedParent) {
+        return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
+                              flattenedParent);
+      }
+      return InsertionPoint();
+    }
+
+    insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChild);
+  } else {
     if (nsContentUtils::HasDistributedChildren(aContainer)) {
       // The container distributes nodes to shadow DOM insertion points.
       // Return with aMultiple set to true to induce callers to insert children
       // individually into the node's flattened tree parent.
-      *aMultiple = true;
-      return nullptr;
+      return InsertionPoint(nullptr, nullptr, true);
     }
 
     bool multiple;
     insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple);
-
     if (multiple) {
-      if (aMultiple) {
-        *aMultiple = true;
-      }
-      return nullptr;
+      return InsertionPoint(nullptr, nullptr, true);
     }
   }
 
   if (!insertionElement) {
     insertionElement = aContainer;
   }
-
-  nsContainerFrame* insertionPoint =
-    ::GetContentInsertionFrameFor(insertionElement);
-
-  // fieldsets have multiple insertion points.  Note that we might
-  // have to look at insertionElement here...
-  if (aMultiple && insertionElement->IsHTML(nsGkAtoms::fieldset)) {
-    *aMultiple = true;
-  }
-
-  return insertionPoint;
+  InsertionPoint insertion(GetContentInsertionFrameFor(insertionElement),
+                           insertionElement);
+
+  // Fieldsets have multiple insertion points.
+  if (insertionElement->IsHTML(nsGkAtoms::fieldset)) {
+    insertion.mMultiple = true;
+  }
+
+  return insertion;
 }
 
 // Capture state for the frame tree rooted at the frame associated with the
 // content object, aContent
 void
 nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
                                                nsILayoutHistoryState* aHistoryState)
 {
@@ -8655,46 +8910,56 @@ nsCSSFrameConstructor::CaptureStateForFr
 
 static bool EqualURIs(mozilla::css::URLValue *aURI1,
                       mozilla::css::URLValue *aURI2)
 {
   return aURI1 == aURI2 ||    // handle null==null, and optimize
          (aURI1 && aURI2 && aURI1->URIEquals(*aURI2));
 }
 
-nsresult
+nsStyleContext*
 nsCSSFrameConstructor::MaybeRecreateFramesForElement(Element* aElement)
 {
   nsRefPtr<nsStyleContext> oldContext = GetUndisplayedContent(aElement);
+  uint8_t oldDisplay = NS_STYLE_DISPLAY_NONE;
   if (!oldContext) {
-    return NS_OK;
+    oldContext = GetDisplayContentsStyleFor(aElement);
+    if (!oldContext) {
+      return nullptr;
+    }
+    oldDisplay = NS_STYLE_DISPLAY_CONTENTS;
   }
 
   // The parent has a frame, so try resolving a new context.
   nsRefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->
     ResolveStyleFor(aElement, oldContext->GetParent());
 
-  ChangeUndisplayedContent(aElement, newContext);
+  if (oldDisplay == NS_STYLE_DISPLAY_NONE) {
+    ChangeUndisplayedContent(aElement, newContext);
+  } else {
+    ChangeDisplayContents(aElement, newContext);
+  }
+
   const nsStyleDisplay* disp = newContext->StyleDisplay();
-  if (disp->mDisplay == NS_STYLE_DISPLAY_NONE) {
+  if (oldDisplay == disp->mDisplay) {
     // We can skip trying to recreate frames here, but only if our style
     // context does not have a binding URI that differs from our old one.
     // Otherwise, we should try to recreate, because we may want to apply the
     // new binding
     if (!disp->mBinding) {
-      return NS_OK;
+      return newContext;
     }
     const nsStyleDisplay* oldDisp = oldContext->PeekStyleDisplay();
     if (oldDisp && EqualURIs(disp->mBinding, oldDisp->mBinding)) {
-      return NS_OK;
-    }
-  }
-
-  return RecreateFramesForContent(aElement, false, REMOVE_FOR_RECONSTRUCTION,
-                                  nullptr);
+      return newContext;
+    }
+  }
+
+  RecreateFramesForContent(aElement, false, REMOVE_FOR_RECONSTRUCTION, nullptr);
+  return nullptr;
 }
 
 static nsIFrame*
 FindFirstNonWhitespaceChild(nsIFrame* aParentFrame)
 {
   nsIFrame* f = aParentFrame->GetFirstPrincipalChild();
   while (f && f->GetType() == nsGkAtoms::textFrame &&
          f->GetContent()->TextIsOnlyWhitespace()) {
@@ -9838,41 +10103,45 @@ nsCSSFrameConstructor::ProcessChildren(n
                                  itemsToConstruct);
     }
 
     const bool addChildItems = MOZ_LIKELY(mCurrentDepth < kMaxDepth);
     if (!addChildItems) {
       NS_WARNING("ProcessChildren max depth exceeded");
     }
 
+    InsertionPoint insertion(aFrame, nullptr);
     FlattenedChildIterator iter(aContent);
     for (nsIContent* child = iter.GetNextChild(); child; child = 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 parent != aContent 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.
+      insertion.mContainer = aContent;
       nsIContent* parent = child->GetParent();
       MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
       TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
       if (parent != aContent && parent->IsElement()) {
+        insertion.mContainer = child->GetFlattenedTreeParent();
+        MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(parent, child).mContainer);
         if (aState.mTreeMatchContext.mAncestorFilter.HasFilter()) {
           ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
         } else {
           ancestorPusher.PushStyleScope(parent->AsElement());
         }
       }
 
       // Frame construction item construction should not post
       // restyles, so removing restyle flags here is safe.
       if (child->IsElement()) {
         child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
       }
       if (addChildItems) {
-        AddFrameConstructionItems(aState, child, iter.XBLInvolved(), aFrame,
+        AddFrameConstructionItems(aState, child, iter.XBLInvolved(), insertion,
                                   itemsToConstruct);
       } else {
         ClearLazyBits(child, child->GetNextSibling());
       }
     }
     itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
 
     if (aCanHaveGeneratedContent) {
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -103,25 +103,58 @@ private:
 
   // Issues a single ContentInserted for each child of aContainer in the range
   // [aStartChild, aEndChild).
   void IssueSingleInsertNofications(nsIContent* aContainer,
                                     nsIContent* aStartChild,
                                     nsIContent* aEndChild,
                                     bool aAllowLazyConstruction);
 
-  // Checks if the children of aContainer in the range [aStartChild, aEndChild)
-  // can be inserted/appended to one insertion point together. If so, returns
-  // that insertion point. If not, returns null and issues single
-  // ContentInserted calls for each child.  aEndChild = nullptr indicates that we
-  // are dealing with an append.
-  nsContainerFrame* GetRangeInsertionPoint(nsIContent* aContainer,
-                                           nsIContent* aStartChild,
-                                           nsIContent* aEndChild,
-                                           bool aAllowLazyConstruction);
+  /**
+   * Data that represents an insertion point for some child content.
+   */
+  struct InsertionPoint
+  {
+    InsertionPoint()
+      : mParentFrame(nullptr), mContainer(nullptr), mMultiple(false) {}
+    InsertionPoint(nsContainerFrame* aParentFrame, nsIContent* aContainer,
+                   bool aMultiple = false)
+      : mParentFrame(aParentFrame), mContainer(aContainer),
+        mMultiple(aMultiple) {}
+    /**
+     * The parent frame to use if the inserted children needs to create
+     * frame(s).  May be null, which signals that  we shouldn't try to
+     * create frames for the inserted children; either because there are
+     * no parent frame or because there are multiple insertion points and
+     * we will call IssueSingleInsertNofications for each child instead.
+     * mContainer should not be used when mParentFrame is null.
+     */
+    nsContainerFrame* mParentFrame;
+    /**
+     * The flattened tree parent for the inserted children.
+     * It's undefined if mParentFrame is null.
+     */
+    nsIContent* mContainer;
+    /**
+     * If true then there are multiple insertion points, which means consumers
+     * should insert children individually into the node's flattened tree parent.
+     */
+    bool mMultiple;
+  };
+  /**
+   * Checks if the children of aContainer in the range [aStartChild, aEndChild)
+   * can be inserted/appended to one insertion point together. If so, returns
+   * that insertion point. If not, returns with InsertionPoint.mFrame == nullptr
+   * and issues single ContentInserted calls for each child.
+   * aEndChild = nullptr indicates that we are dealing with an append.
+   */
+  InsertionPoint GetRangeInsertionPoint(nsIContent* aContainer,
+                                        nsIContent* aStartChild,
+                                        nsIContent* aEndChild,
+                                        bool aAllowLazyConstruction);
 
   // Returns true if parent was recreated due to frameset child, false otherwise.
   bool MaybeRecreateForFrameset(nsIFrame* aParentFrame,
                                   nsIContent* aStartChild,
                                   nsIContent* aEndChild);
 
 public:
   /**
@@ -261,20 +294,20 @@ public:
   nsIFrame* CreateContinuingFrame(nsPresContext*    aPresContext,
                                   nsIFrame*         aFrame,
                                   nsContainerFrame* aParentFrame,
                                   bool              aIsFluid = true);
 
   // Copy over fixed frames from aParentFrame's prev-in-flow
   nsresult ReplicateFixedFrames(nsPageContentFrame* aParentFrame);
 
-  // Get the XBL insertion point for a child
-  nsContainerFrame* GetInsertionPoint(nsIContent* aContainer,
-                                      nsIContent* aChildContent,
-                                      bool*       aMultiple = nullptr);
+  /**
+   * Get the XBL insertion point for aChild in aContainer.
+   */
+  InsertionPoint GetInsertionPoint(nsIContent* aContainer, nsIContent* aChild);
 
   nsresult CreateListBoxContent(nsPresContext*    aPresContext,
                                 nsContainerFrame* aParentFrame,
                                 nsIFrame*         aPrevFrame,
                                 nsIContent*       aChild,
                                 nsIFrame**        aResult,
                                 bool              aIsAppend,
                                 bool              aIsScrollbar,
@@ -316,35 +349,61 @@ private:
                             nsIContent*                    aContent,
                             nsContainerFrame*              aParentFrame,
                             nsIFrame*                      aNewFrame,
                             bool                           aAllowCounters = true);
 
   // aState can be null if not available; it's used as an optimization.
   // XXXbz IsValidSibling is the only caller that doesn't pass a state here!
   already_AddRefed<nsStyleContext>
-  ResolveStyleContext(nsIFrame*         aParentFrame,
-                      nsIContent*       aContent,
+  ResolveStyleContext(nsIFrame*                aParentFrame,
+                      nsIContent*              aContainer,
+                      nsIContent*              aChild,
                       nsFrameConstructorState* aState);
   already_AddRefed<nsStyleContext>
-  ResolveStyleContext(nsStyleContext* aParentStyleContext,
-                      nsIContent* aContent,
+  ResolveStyleContext(nsIFrame*                aParentFrame,
+                      nsIContent*              aChild,
+                      nsFrameConstructorState* aState);
+  already_AddRefed<nsStyleContext>
+  ResolveStyleContext(const InsertionPoint&    aInsertion,
+                      nsIContent*              aChild,
+                      nsFrameConstructorState* aState);
+  already_AddRefed<nsStyleContext>
+  ResolveStyleContext(nsStyleContext*          aParentStyleContext,
+                      nsIContent*              aContent,
                       nsFrameConstructorState* aState);
 
   // Add the frame construction items for the given aContent and aParentFrame
   // to the list.  This might add more than one item in some rare cases.
   // If aSuppressWhiteSpaceOptimizations is true, optimizations that
   // may suppress the construction of white-space-only text frames
   // must be skipped for these items and items around them.
   void AddFrameConstructionItems(nsFrameConstructorState& aState,
                                  nsIContent*              aContent,
                                  bool                     aSuppressWhiteSpaceOptimizations,
-                                 nsContainerFrame*        aParentFrame,
+                                 const InsertionPoint&    aInsertion,
                                  FrameConstructionItemList& aItems);
 
+  // Helper method for AddFrameConstructionItems etc.
+  // Unsets the need-frame/restyle bits on aContent.
+  // return true iff we should attempt to create frames for aContent.
+  bool ShouldCreateItemsForChild(nsFrameConstructorState& aState,
+                                 nsIContent* aContent,
+                                 nsContainerFrame* aParentFrame);
+
+  // Helper method for AddFrameConstructionItems etc.
+  // Make sure ShouldCreateItemsForChild() returned true before calling this.
+  void DoAddFrameConstructionItems(nsFrameConstructorState& aState,
+                                   nsIContent* aContent,
+                                   nsStyleContext* aStyleContext,
+                                   bool aSuppressWhiteSpaceOptimizations,
+                                   nsContainerFrame* aParentFrame,
+                                   nsTArray<nsIAnonymousContentCreator::ContentInfo>* aAnonChildren,
+                                   FrameConstructionItemList& aItems);
+
   // Construct the frames for the document element.  This can return null if the
   // document element is display:none, or if the document element has a
   // not-yet-loaded XBL binding, or if it's an SVG element that's not <svg>.
   nsIFrame* ConstructDocElementFrame(Element*                 aDocElement,
                                      nsILayoutHistoryState*   aFrameState);
 
   // Set up our mDocElementContainingBlock correctly for the given root
   // content.
@@ -626,16 +685,20 @@ private:
   /* If FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS is set, then create a
      block formatting context wrapper around the kids of this frame
      using the FrameConstructionData's mPseudoAtom for its anonymous
      box type. */
 #define FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS 0x40000
   /* If FCDATA_IS_SVG_TEXT is set, then this text frame is a descendant of
      an SVG text frame. */
 #define FCDATA_IS_SVG_TEXT 0x80000
+  /**
+   * display:contents
+   */
+#define FCDATA_IS_CONTENTS 0x100000
 
   /* 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
@@ -1158,17 +1221,17 @@ private:
                           nsIContent*              aContent,
                           nsContainerFrame*        aParentFrame,
                           nsStyleContext*          aStyleContext,
                           nsFrameItems&            aFrameItems);
 
   // If aPossibleTextContent is a text node and doesn't have a frame, append a
   // frame construction item for it to aItems.
   void AddTextItemIfNeeded(nsFrameConstructorState& aState,
-                           nsContainerFrame* aParentFrame,
+                           const InsertionPoint& aInsertion,
                            nsIContent* aPossibleTextContent,
                            FrameConstructionItemList& aItems);
 
   // If aParentContent's child aContent is a text node and
   // doesn't have a frame, try to create a frame for it.
   void ReframeTextIfNeeded(nsIContent* aParentContent,
                            nsIContent* aContent);
 
@@ -1497,17 +1560,24 @@ private:
                         nsContainerFrame*        aScrolledFrame,
                         nsIContent*              aContent,
                         nsContainerFrame*        aParentFrame,
                         nsStyleContext*          aStyleContext,
                         bool                     aBuildCombobox,
                         PendingBinding*          aPendingBinding,
                         nsFrameItems&            aFrameItems);
 
-  nsresult MaybeRecreateFramesForElement(Element* aElement);
+  /**
+   * ReResolve style for aElement then recreate frames if required.
+   * Do nothing for other types of style changes, except for undisplayed nodes
+   * (display:none/contents) which will have their style context updated in the
+   * frame manager undisplayed maps.
+   * @return null if frames were recreated, the new style context otherwise
+   */
+  nsStyleContext* MaybeRecreateFramesForElement(Element* aElement);
 
   /**
    * Recreate frames for aContent.
    * @param aContent the content to recreate frames for
    * @param aAsyncInsert if true then a restyle event will be posted to handle
    *   the required ContentInserted call instead of doing it immediately.
    * @param aFlags normally you want to pass REMOVE_FOR_RECONSTRUCTION here
    * @param aDestroyedFramesFor if non-null, it will contain the content that
@@ -1767,61 +1837,101 @@ private:
 
   nsresult InsertFirstLineFrames(nsFrameConstructorState& aState,
                                  nsIContent*              aContent,
                                  nsIFrame*                aBlockFrame,
                                  nsContainerFrame**       aParentFrame,
                                  nsIFrame*                aPrevSibling,
                                  nsFrameItems&            aFrameItems);
 
-  // Find the right frame to use for aContent when looking for sibling
-  // frames for aTargetContent.  If aPrevSibling is true, this
-  // will look for last continuations, etc, as necessary.  This calls
-  // IsValidSibling as needed; if that returns false it returns null.
-  //
-  // @param aTargetContentDisplay the CSS display enum for aTargetContent if
-  // already known, UNSET_DISPLAY otherwise. It will be filled in if needed.
+  /**
+   * Find the right frame to use for aContent when looking for sibling
+   * frames for aTargetContent.  If aPrevSibling is true, this
+   * will look for last continuations, etc, as necessary.  This calls
+   * IsValidSibling as needed; if that returns false it returns null.
+   *
+   * @param aContent the content to search for frames
+   * @param aTargetContent the content we're finding a sibling frame for
+   * @param aTargetContentDisplay the CSS display enum for aTargetContent if
+   *          already known, UNSET_DISPLAY otherwise. It will be filled in
+   *          if needed.
+   * @param aParentFrame the nearest ancestor frame, used internally for
+   *          finding ::after / ::before frames
+   * @param aPrevSibling true if we're searching in reverse DOM order
+   */
   nsIFrame* FindFrameForContentSibling(nsIContent* aContent,
                                        nsIContent* aTargetContent,
                                        uint8_t& aTargetContentDisplay,
+                                       nsContainerFrame* aParentFrame,
                                        bool aPrevSibling);
 
-  // Find the ``rightmost'' frame for the content immediately preceding the one
-  // aIter points to, following continuations if necessary.  aIter is passed by
-  // value on purpose, so as not to modify the caller's iterator.
+  /**
+   * Find the frame for the content immediately preceding the one aIter
+   * points to, following continuations if necessary.  aIter is passed by
+   * value on purpose, so as not to modify the caller's iterator.
+   *
+   * @param aIter should be positioned such that aIter.GetPreviousChild()
+   *          is the first content to search for frames
+   * @param aTargetContent the content we're finding a sibling frame for
+   * @param aTargetContentDisplay the CSS display enum for aTargetContent if
+   *          already known, UNSET_DISPLAY otherwise. It will be filled in
+   *          if needed.
+   * @param aParentFrame the nearest ancestor frame, used inernally for
+   *          finding ::after / ::before frames
+   */
   nsIFrame* FindPreviousSibling(mozilla::dom::FlattenedChildIterator aIter,
-                                uint8_t& aTargetContentDisplay);
+                                nsIContent* aTargetContent,
+                                uint8_t& aTargetContentDisplay,
+                                nsContainerFrame* aParentFrame);
 
-  // Find the frame for the content node immediately following the one aIter
-  // points to, following continuations if necessary.  aIter is passed by value
-  // on purpose, so as not to modify the caller's iterator.
+  /**
+   * Find the frame for the content node immediately following the one aIter
+   * points to, following continuations if necessary.  aIter is passed by value
+   * on purpose, so as not to modify the caller's iterator.
+   *
+   * @param aIter should be positioned such that aIter.GetNextChild()
+   *          is the first content to search for frames
+   * @param aTargetContent the content we're finding a sibling frame for
+   * @param aTargetContentDisplay the CSS display enum for aTargetContent if
+   *          already known, UNSET_DISPLAY otherwise. It will be filled in
+   *          if needed.
+   * @param aParentFrame the nearest ancestor frame, used inernally for
+   *          finding ::after / ::before frames
+   */
   nsIFrame* FindNextSibling(mozilla::dom::FlattenedChildIterator aIter,
-                            uint8_t& aTargetContentDisplay);
+                            nsIContent* aTargetContent,
+                            uint8_t& aTargetContentDisplay,
+                            nsContainerFrame* aParentFrame);
 
   // Find the right previous sibling for an insertion.  This also updates the
   // parent frame to point to the correct continuation of the parent frame to
   // use, and returns whether this insertion is to be treated as an append.
   // aChild is the child being inserted.
   // aIsRangeInsertSafe returns whether it is safe to do a range insert with
   // aChild being the first child in the range. It is the callers'
   // responsibility to check whether a range insert is safe with regards to
   // fieldsets.
   // The skip parameters are used to ignore a range of children when looking
   // for a sibling. All nodes starting from aStartSkipChild and up to but not
   // including aEndSkipChild will be skipped over when looking for sibling
   // frames. Skipping a range can deal with XBL but not when there are multiple
   // insertion points.
-  nsIFrame* GetInsertionPrevSibling(nsContainerFrame*& aParentFrame, /* inout */
-                                    nsIContent* aContainer,
+  nsIFrame* GetInsertionPrevSibling(InsertionPoint* aInsertion, // inout
                                     nsIContent* aChild,
                                     bool* aIsAppend,
                                     bool* aIsRangeInsertSafe,
                                     nsIContent* aStartSkipChild = nullptr,
                                     nsIContent *aEndSkipChild = nullptr);
 
+  /**
+   * Return the insertion frame of the primary frame of aContent, or its nearest
+   * ancestor that isn't display:contents.
+   */
+  nsContainerFrame* GetContentInsertionFrameFor(nsIContent* aContent);
+
   // see if aContent and aSibling are legitimate siblings due to restrictions
   // imposed by table columns
   // XXXbz this code is generally wrong, since the frame for aContent
   // may be constructed based on tag, not based on aDisplay!
   bool IsValidSibling(nsIFrame*              aSibling,
                         nsIContent*            aContent,
                         uint8_t&               aDisplay);
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -8629,16 +8629,29 @@ nsIFrame::DestroyDT(void* aPropertyValue
 }
 
 void
 nsIFrame::DestroyRegion(void* aPropertyValue)
 {
   delete static_cast<nsRegion*>(aPropertyValue);
 }
 
+/*static*/ void
+nsIFrame::DestroyContentArray(void* aPropertyValue)
+{
+  typedef nsTArray<nsIContent*> T;
+  T* arr = static_cast<T*>(aPropertyValue);
+  for (T::size_type i = 0; i < arr->Length(); ++i) {
+    nsIContent* content = (*arr)[i];
+    content->UnbindFromTree();
+    NS_RELEASE(content);
+  }
+  delete arr;
+}
+
 bool
 nsIFrame::IsPseudoStackingContextFromStyle() {
   const nsStyleDisplay* disp = StyleDisplay();
   // If you change this, also change the computation of pseudoStackingContext
   // in BuildDisplayListForChild()
   return disp->mOpacity != 1.0f ||
          disp->IsPositioned(this) ||
          disp->IsFloating(this) ||
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -789,16 +789,17 @@ public:
 
   static void DestroyOverflowAreas(void* aPropertyValue)
   {
     delete static_cast<nsOverflowAreas*>(aPropertyValue);
   }
 
   static void DestroySurface(void* aPropertyValue);
   static void DestroyDT(void* aPropertyValue);
+  static void DestroyContentArray(void* aPropertyValue);
 
 #ifdef _MSC_VER
 // XXX Workaround MSVC issue by making the static FramePropertyDescriptor
 // non-const.  See bug 555727.
 #define NS_PROPERTY_DESCRIPTOR_CONST
 #else
 #define NS_PROPERTY_DESCRIPTOR_CONST const
 #endif
@@ -848,16 +849,23 @@ public:
 
   NS_DECLARE_FRAME_PROPERTY(CachedBackgroundImage, DestroySurface)
   NS_DECLARE_FRAME_PROPERTY(CachedBackgroundImageDT, DestroyDT)
 
   NS_DECLARE_FRAME_PROPERTY(InvalidationRect, DestroyRect)
 
   NS_DECLARE_FRAME_PROPERTY(RefusedAsyncAnimation, nullptr)
 
+  NS_DECLARE_FRAME_PROPERTY(GenConProperty, DestroyContentArray)
+
+  nsTArray<nsIContent*>* GetGenConPseudos() {
+    const FramePropertyDescriptor* prop = GenConProperty();
+    return static_cast<nsTArray<nsIContent*>*>(Properties().Get(prop));
+  }
+
   /**
    * Return the distance between the border edge of the frame and the
    * margin edge of the frame.  Like GetRect(), returns the dimensions
    * as of the most recent reflow.
    *
    * This doesn't include any margin collapsing that may have occurred.
    *
    * It also treats 'auto' margins as zero, and treats any margins that
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -172,18 +172,19 @@ nsMenuBarFrame::FindMenuWithShortcut(nsI
     nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, accessKeys);
   if (accessKeys.IsEmpty() && charCode)
     accessKeys.AppendElement(charCode);
 
   if (accessKeys.IsEmpty())
     return nullptr; // no character was pressed so just return
 
   // Enumerate over our list of frames.
-  nsIFrame* immediateParent = PresContext()->PresShell()->FrameConstructor()->
+  auto insertion = PresContext()->PresShell()->FrameConstructor()->
     GetInsertionPoint(GetContent(), nullptr);
+  nsContainerFrame* immediateParent = insertion.mParentFrame;
   if (!immediateParent)
     immediateParent = this;
 
   // Find a most preferred accesskey which should be returned.
   nsIFrame* foundMenu = nullptr;
   size_t foundIndex = accessKeys.NoIndex;
   nsIFrame* currFrame = immediateParent->GetFirstPrincipalChild();
 
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -1675,18 +1675,19 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
 {
   uint32_t charCode, keyCode;
   aKeyEvent->GetCharCode(&charCode);
   aKeyEvent->GetKeyCode(&keyCode);
 
   doAction = false;
 
   // Enumerate over our list of frames.
-  nsContainerFrame* immediateParent = PresContext()->PresShell()->
+  auto insertion = PresContext()->PresShell()->
     FrameConstructor()->GetInsertionPoint(GetContent(), nullptr);
+  nsContainerFrame* immediateParent = insertion.mParentFrame;
   if (!immediateParent)
     immediateParent = this;
 
   uint32_t matchCount = 0, matchShortcutCount = 0;
   bool foundActive = false;
   bool isShortcut;
   nsMenuFrame* frameBefore = nullptr;
   nsMenuFrame* frameAfter = nullptr;
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -2196,18 +2196,19 @@ nsXULPopupManager::HandleKeyboardEventWi
 }
 
 nsMenuFrame*
 nsXULPopupManager::GetNextMenuItem(nsContainerFrame* aParent,
                                    nsMenuFrame* aStart,
                                    bool aIsPopup)
 {
   nsPresContext* presContext = aParent->PresContext();
-  nsContainerFrame* immediateParent = presContext->PresShell()->
+  auto insertion = presContext->PresShell()->
     FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
+  nsContainerFrame* immediateParent = insertion.mParentFrame;
   if (!immediateParent)
     immediateParent = aParent;
 
   nsIFrame* currFrame = nullptr;
   if (aStart) {
     if (aStart->GetNextSibling())
       currFrame = aStart->GetNextSibling();
     else if (aStart->GetParent()->GetContent()->IsXUL(nsGkAtoms::menugroup))
@@ -2256,18 +2257,19 @@ nsXULPopupManager::GetNextMenuItem(nsCon
 }
 
 nsMenuFrame*
 nsXULPopupManager::GetPreviousMenuItem(nsContainerFrame* aParent,
                                        nsMenuFrame* aStart,
                                        bool aIsPopup)
 {
   nsPresContext* presContext = aParent->PresContext();
-  nsContainerFrame* immediateParent = presContext->PresShell()->
+  auto insertion = presContext->PresShell()->
     FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
+  nsContainerFrame* immediateParent = insertion.mParentFrame;
   if (!immediateParent)
     immediateParent = aParent;
 
   const nsFrameList& frames(immediateParent->PrincipalChildList());
 
   nsIFrame* currFrame = nullptr;
   if (aStart) {
     if (aStart->GetPrevSibling())