Bug 1303605: Remove the undisplayed maps. r?bz,mats draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 29 Mar 2018 03:49:26 +0200
changeset 775948 fd33bfa6bab482db4cd942481f21b7eb84fad6e4
parent 775947 a12a879089ce5722f6af658fbcd8c0a9294fc173
child 775949 c1c50627d70eb97d57be56af984e7a5164a33a65
push id104762
push userbmo:emilio@crisal.io
push dateMon, 02 Apr 2018 03:56:19 +0000
reviewersbz, mats
bugs1303605
milestone61.0a1
Bug 1303605: Remove the undisplayed maps. r?bz,mats This is mostly code removal, changing GetDisplayContentsStyle(..) checks by an FFI call to Servo. The tricky parts are: * MaybeCreateLazily, which I fixed to avoid setting bits under display: none stuff. This was a pre-existing problem, which was wallpapered by the sc->IsInDisplayNoneSubtree() check, which effectively made the whole assertion useless. * ContentRemoved, where we can no longer know for sure whether the element is actually display: contents if we're removing it as a response to a style change. See the comment there. That kinda sucks, but that case is relatively weird, and it's better than adding tons of complexity to handle that. * GetParentComputedStyle, which also has a comment there. Also, this function has only one caller now, so we should maybe try to remove it. The different assertions after DestroyFramesForAndRestyle are changed for a single assertion in the function itself, and the node bit used as an optimization to avoid hashtable lookups is taken back. MozReview-Commit-ID: AZm822QnhF9
dom/base/Element.cpp
dom/base/nsINode.h
layout/base/PresShell.cpp
layout/base/ServoRestyleManager.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsFrameManager.cpp
layout/base/nsFrameManager.h
layout/base/nsStyleChangeList.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrameSelection.cpp
layout/generic/nsPlaceholderFrame.cpp
layout/style/ServoBindingList.h
servo/ports/geckolib/glue.rs
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1245,17 +1245,16 @@ Element::AttachShadowInternal(ShadowRoot
   RefPtr<mozilla::dom::NodeInfo> nodeInfo =
     mNodeInfo->NodeInfoManager()->GetNodeInfo(
       nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None,
       DOCUMENT_FRAGMENT_NODE);
 
   if (nsIDocument* doc = GetComposedDoc()) {
     if (nsIPresShell* shell = doc->GetShell()) {
       shell->DestroyFramesForAndRestyle(this);
-      MOZ_ASSERT(!shell->FrameConstructor()->GetDisplayContentsStyleFor(this));
     }
   }
   MOZ_ASSERT(!GetPrimaryFrame());
 
   /**
    * 4. Let shadow be a new shadow root whose node document is
    *    context object’s node document, host is context object,
    *    and mode is init’s mode.
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -1568,19 +1568,16 @@ private:
     ElementHasWeirdParserInsertionMode,
     // Parser sets this flag if it has notified about the node.
     ParserHasNotified,
     // Sets if the node is apz aware or we have apz aware listeners.
     MayBeApzAware,
     // Set if the element might have any kind of anonymous content children,
     // which would not be found through the element's children list.
     ElementMayHaveAnonymousChildren,
-    // Set if this node has at some point (and may still have)
-    // display:none or display:contents children.
-    NodeMayHaveChildrenWithLayoutBoxesDisabled,
     // Guard value
     BooleanFlagCount
   };
 
   void SetBoolFlag(BooleanFlag name, bool value) {
     static_assert(BooleanFlagCount <= 8*sizeof(mBoolFlags),
                   "Too many boolean flags");
     mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
@@ -1696,29 +1693,16 @@ public:
   bool NodeMayBeApzAware() const
   {
     return GetBoolFlag(MayBeApzAware);
   }
 
   void SetMayHaveAnonymousChildren() { SetBoolFlag(ElementMayHaveAnonymousChildren); }
   bool MayHaveAnonymousChildren() const { return GetBoolFlag(ElementMayHaveAnonymousChildren); }
 
-  void SetMayHaveChildrenWithLayoutBoxesDisabled()
-  {
-    SetBoolFlag(NodeMayHaveChildrenWithLayoutBoxesDisabled);
-  }
-  void UnsetMayHaveChildrenWithLayoutBoxesDisabled()
-  {
-    ClearBoolFlag(NodeMayHaveChildrenWithLayoutBoxesDisabled);
-  }
-  bool MayHaveChildrenWithLayoutBoxesDisabled() const
-  {
-    return GetBoolFlag(NodeMayHaveChildrenWithLayoutBoxesDisabled);
-  }
-
 protected:
   void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
   void SetIsInDocument() { SetBoolFlag(IsInDocument); }
   void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
   void ClearInDocument() { ClearBoolFlag(IsInDocument); }
   void SetIsElement() { SetBoolFlag(NodeIsElement); }
   void SetHasID() { SetBoolFlag(ElementHasID); }
   void ClearHasID() { ClearBoolFlag(ElementHasID); }
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -2986,19 +2986,48 @@ nsIPresShell::SlotAssignmentWillChange(E
       // Now the style dirty bits. Note that we can't just do
       // aElement.NoteDirtyForServo(), because the new slot is not setup yet.
       aNewSlot->SetHasDirtyDescendantsForServo();
       aNewSlot->NoteDirtySubtreeForServo();
     }
   }
 }
 
+#ifdef DEBUG
+static void
+AssertNoFramesInSubtree(nsIContent* aContent)
+{
+  for (nsIContent* c = aContent; c; c = c->GetNextNode(aContent)) {
+    MOZ_ASSERT(!c->GetPrimaryFrame());
+    if (auto* shadowRoot = c->GetShadowRoot()) {
+      AssertNoFramesInSubtree(shadowRoot);
+    }
+    if (auto* binding = c->GetXBLBinding()) {
+      if (auto* bindingWithContent = binding->GetBindingWithContent()) {
+        nsIContent* anonContent = bindingWithContent->GetAnonymousContent();
+        for (nsIContent* child = anonContent->GetFirstChild();
+             child;
+             child = child->GetNextSibling()) {
+          AssertNoFramesInSubtree(child);
+        }
+      }
+    }
+  }
+}
+#endif
+
 void
 nsIPresShell::DestroyFramesForAndRestyle(Element* aElement)
 {
+#ifdef DEBUG
+  auto postCondition = mozilla::MakeScopeExit([&]() {
+    AssertNoFramesInSubtree(aElement);
+  });
+#endif
+
   MOZ_ASSERT(aElement);
   if (MOZ_UNLIKELY(!mDidInitialize)) {
     return;
   }
 
   if (!aElement->GetFlattenedTreeParentNode()) {
     // Nothing to do here, the element already is out of the frame tree.
     return;
@@ -4544,28 +4573,33 @@ PresShell::ContentRemoved(nsIContent* aC
   // Editor calls into here with NAC via HTMLEditor::DeleteRefToAnonymousNode.
   // This could be asserted if that caller is fixed.
   if (MOZ_LIKELY(!aChild->IsRootOfAnonymousSubtree())) {
     oldNextSibling = aPreviousSibling
       ? aPreviousSibling->GetNextSibling()
       : container->GetFirstChild();
   }
 
-  mPresContext->RestyleManager()->ContentRemoved(container, aChild, oldNextSibling);
-
   // After removing aChild from tree we should save information about live ancestor
   if (mPointerEventTarget &&
       nsContentUtils::ContentIsDescendantOf(mPointerEventTarget, aChild)) {
     mPointerEventTarget = aChild->GetParent();
   }
 
   mFrameConstructor->ContentRemoved(
       aChild->GetParent(), aChild, oldNextSibling,
       nsCSSFrameConstructor::REMOVE_CONTENT);
 
+  // NOTE(emilio): It's important that this goes after the frame constructor
+  // stuff, otherwise the frame constructor can't see elements which are
+  // display: contents / display: none, because we'd have cleared all the style
+  // data from there.
+  mPresContext->RestyleManager()->ContentRemoved(container, aChild, oldNextSibling);
+
+
   VERIFY_STYLE_TREE;
 }
 
 void
 PresShell::NotifyCounterStylesAreDirty()
 {
   nsAutoCauseReflowNotifier reflowNotifier(this);
   mFrameConstructor->NotifyCounterStylesAreDirty();
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -846,25 +846,22 @@ ServoRestyleManager::ProcessPostTraversa
   // ComputedStyle case, which uses atomic refcounting.
   //
   // Hold the ComputedStyle alive, because it could become a dangling pointer
   // during the replacement. In practice it's not a huge deal, but better not
   // playing with dangling pointers if not needed.
   RefPtr<ComputedStyle> oldComputedStyle =
     styleFrame ? styleFrame->Style() : nullptr;
 
-  ComputedStyle* displayContentsStyle = nullptr;
-  // FIXME(emilio, bug 1303605): This can be simpler for Servo.
-  // Note that we intentionally don't check for display: none content.
-  if (!oldComputedStyle) {
-    displayContentsStyle =
-      PresContext()->FrameConstructor()->GetDisplayContentsStyleFor(aElement);
-    if (displayContentsStyle) {
-      oldComputedStyle = displayContentsStyle;
-    }
+  const bool isDisplayContents = Servo_Element_IsDisplayContents(aElement);
+  if (isDisplayContents) {
+    // NOTE(emilio): This is kind of a lie, because the old style is lost
+    // already during restyling. But if we're here we know that `display` didn't
+    // change, which is the only thing we really care about.
+    oldComputedStyle = aRestyleState.StyleSet().ResolveServoStyle(aElement);
   }
 
   Maybe<ServoRestyleState> thisFrameRestyleState;
   if (styleFrame) {
     auto type = isOutOfFlow
       ? ServoRestyleState::Type::OutOfFlow
       : ServoRestyleState::Type::InFlow;
 
@@ -881,19 +878,17 @@ ServoRestyleManager::ProcessPostTraversa
       ? aRestyleState.StyleSet().ResolveServoStyle(aElement)
       : oldComputedStyle;
 
   ServoPostTraversalFlags childrenFlags =
     wasRestyled ? ServoPostTraversalFlags::ParentWasRestyled
                 : ServoPostTraversalFlags::Empty;
 
   if (wasRestyled && oldComputedStyle) {
-    MOZ_ASSERT(styleFrame || displayContentsStyle);
-    MOZ_ASSERT(oldComputedStyle->ComputedData() != upToDateContext->ComputedData());
-
+    MOZ_ASSERT(styleFrame || isDisplayContents);
     upToDateContext->ResolveSameStructsAs(oldComputedStyle);
 
     // We want to walk all the continuations here, even the ones with different
     // styles.  In practice, the only reason we get continuations with different
     // styles here is ::first-line (::first-letter never affects element
     // styles).  But in that case, newStyle is the right context for the
     // _later_ continuations anyway (the ones not affected by ::first-line), not
     // the earlier ones, so there is no point stopping right at the point when
@@ -901,22 +896,16 @@ ServoRestyleManager::ProcessPostTraversa
     //
     // This does mean that we may be setting the wrong ComputedStyle on our
     // initial continuations; ::first-line fixes that up after the fact.
     for (nsIFrame* f = styleFrame; f; f = f->GetNextContinuation()) {
       MOZ_ASSERT_IF(f != styleFrame, !f->GetAdditionalComputedStyle(0));
       f->SetComputedStyle(upToDateContext);
     }
 
-    if (MOZ_UNLIKELY(displayContentsStyle)) {
-      MOZ_ASSERT(!styleFrame);
-      PresContext()->FrameConstructor()->
-        ChangeRegisteredDisplayContentsStyleFor(aElement, upToDateContext);
-    }
-
     if (styleFrame) {
       UpdateAdditionalComputedStyles(styleFrame, aRestyleState);
     }
 
     if (!aElement->GetParent()) {
       // This is the root.  Update styles on the viewport as needed.
       ViewportFrame* viewport =
         do_QueryFrame(mPresContext->PresShell()->GetRootFrame());
@@ -933,31 +922,33 @@ ServoRestyleManager::ProcessPostTraversa
     //
     // We can sometimes reach this when the animated style is being removed.
     // Since AddLayerChangesForAnimation checks if |styleFrame| has a transform
     // style or not, we need to call it *after* setting |newStyle| to
     // |styleFrame| to ensure the animated transform has been removed first.
     AddLayerChangesForAnimation(
       styleFrame, aElement, aRestyleState.ChangeList());
 
-    childrenFlags |= SendA11yNotifications(mPresContext, aElement,
+    childrenFlags |= SendA11yNotifications(mPresContext,
+                                           aElement,
                                            oldComputedStyle,
-                                           upToDateContext, aFlags);
+                                           upToDateContext,
+                                           aFlags);
   }
 
   const bool traverseElementChildren =
     aElement->HasAnyOfFlags(Element::kAllServoDescendantBits);
   const bool traverseTextChildren =
     wasRestyled || aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   bool recreatedAnyContext = wasRestyled;
   if (traverseElementChildren || traverseTextChildren) {
     StyleChildrenIterator it(aElement);
     TextPostTraversalState textState(*aElement,
                                      upToDateContext,
-                                     displayContentsStyle && wasRestyled,
+                                     isDisplayContents && wasRestyled,
                                      childrenRestyleState);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {
         recreatedAnyContext |= ProcessPostTraversal(n->AsElement(),
                                                     upToDateContext,
                                                     childrenRestyleState,
                                                     childrenFlags);
       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
@@ -1580,16 +1571,18 @@ ServoRestyleManager::DoReparentComputedS
     nsIFrame* outOfFlow =
       nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame);
     MOZ_ASSERT(outOfFlow, "no out-of-flow frame");
     for (; outOfFlow; outOfFlow = outOfFlow->GetNextContinuation()) {
       DoReparentComputedStyle(outOfFlow, aStyleSet);
     }
   }
 
+  // FIXME(emilio): This is the only caller of GetParentComputedStyle, let's try
+  // to remove it?
   nsIFrame* providerFrame;
   ComputedStyle* newParentStyle =
     aFrame->GetParentComputedStyle(&providerFrame);
   // If our provider is our child, we want to reparent it first, because we
   // inherit style from it.
   bool isChild = providerFrame && providerFrame->GetParent() == aFrame;
   nsIFrame* providerChild = nullptr;
   if (isChild) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -413,16 +413,31 @@ GetFieldSetBlockFrame(nsIFrame* aFieldse
  */
 static bool
 IsInlineFrame(const nsIFrame* aFrame)
 {
   return aFrame->IsFrameOfType(nsIFrame::eLineParticipant);
 }
 
 /**
+ * True for display: contents elements.
+ */
+static inline bool
+IsDisplayContents(const Element* aElement)
+{
+  return aElement->HasServoData() && Servo_Element_IsDisplayContents(aElement);
+}
+
+static inline bool
+IsDisplayContents(const nsIContent* aContent)
+{
+  return aContent->IsElement() && IsDisplayContents(aContent->AsElement());
+}
+
+/**
  * True if aFrame is an instance of an SVG frame class or is an inline/block
  * frame being used for SVG text.
  */
 static bool
 IsFrameForSVG(const nsIFrame* aFrame)
 {
   return aFrame->IsFrameOfType(nsIFrame::eSVG) ||
          nsSVGUtils::IsInSVGTextSubtree(aFrame);
@@ -1628,18 +1643,16 @@ nsCSSFrameConstructor::NotifyDestroyingF
       mCounterManager.DestroyNodesFor(aFrame)) {
     // Technically we don't need to update anything if we destroyed only
     // USE nodes.  However, this is unlikely to happen in the real world
     // since USE nodes generally go along with INCREMENT nodes.
     CountersDirty();
   }
 
   RestyleManager()->NotifyDestroyingFrame(aFrame);
-
-  nsFrameManager::NotifyDestroyingFrame(aFrame);
 }
 
 struct nsGenConInitializer {
   nsAutoPtr<nsGenConNode> mNode;
   nsGenConList*           mList;
   void (nsCSSFrameConstructor::*mDirtyAll)();
 
   nsGenConInitializer(nsGenConNode* aNode, nsGenConList* aList,
@@ -2480,17 +2493,16 @@ nsCSSFrameConstructor::ConstructDocEleme
   // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
 
   NS_ASSERTION(!display->IsScrollableOverflow() ||
                state.mPresContext->IsPaginated() ||
                propagatedScrollFrom == aDocElement,
                "Scrollbars should have been propagated to the viewport");
 
   if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
-    RegisterDisplayNoneStyleFor(aDocElement, computedStyle);
     return nullptr;
   }
 
   // Make sure to start any background image loads for the root element now.
   computedStyle->StartBackgroundImageLoads();
 
   nsFrameConstructorSaveState docElementContainingBlockAbsoluteSaveState;
   if (mHasRootAbsPosContainingBlock) {
@@ -5623,24 +5635,17 @@ nsCSSFrameConstructor::SetAsUndisplayedC
                                                bool aIsGeneratedContent)
 {
   if (aComputedStyle->GetPseudo()) {
     if (aIsGeneratedContent) {
       aContent->UnbindFromTree();
     }
     return;
   }
-  NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type");
-
-  if (aState.mCreatingExtraFrames) {
-    MOZ_ASSERT(GetDisplayNoneStyleFor(aContent),
-               "should have called RegisterDisplayNoneStyleFor earlier");
-    return;
-  }
-  aList.AppendUndisplayedItem(aContent, aComputedStyle);
+  MOZ_ASSERT(!aIsGeneratedContent, "Should have had pseudo type");
 }
 
 void
 nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState,
                                                          nsIContent* aContent,
                                                          nsContainerFrame* aParentFrame,
                                                          nsAtom* aTag,
                                                          int32_t aNameSpaceID,
@@ -5845,26 +5850,16 @@ nsCSSFrameConstructor::AddFrameConstruct
     AddPageBreakItem(aContent, aItems);
   }
 
   // FIXME(emilio, https://github.com/w3c/csswg-drafts/issues/2167):
   //
   // Figure out what should happen for display: contents in MathML.
   if (display->mDisplay == StyleDisplay::Contents &&
       !foundMathMLData) {
-    if (!GetDisplayContentsStyleFor(aContent)) {
-      MOZ_ASSERT(computedStyle->GetPseudo() || !isGeneratedContent,
-                 "Should have had pseudo type");
-      aState.mFrameManager->RegisterDisplayContentsStyleFor(aContent,
-                                                            computedStyle);
-    } else {
-      aState.mFrameManager->ChangeRegisteredDisplayContentsStyleFor(aContent,
-                                                                    computedStyle);
-    }
-
     if (aParentFrame) {
       aParentFrame->AddStateBits(NS_FRAME_MAY_HAVE_GENERATED_CONTENT);
     }
     CreateGeneratedContentItem(aState, aParentFrame, aContent->AsElement(),
                                computedStyle, CSSPseudoElementType::before,
                                aItems);
 
     FlattenedChildIterator iter(aContent);
@@ -6519,17 +6514,17 @@ nsCSSFrameConstructor::FindSiblingIntern
       // Remove it once that's fixed.
       if (primaryFrame->GetContent() == sibling) {
         if (nsIFrame* frame = adjust(primaryFrame)) {
           return frame;
         }
       }
     }
 
-    if (GetDisplayContentsStyleFor(sibling)) {
+    if (IsDisplayContents(sibling)) {
       if (nsIFrame* frame = adjust(getNearPseudo(sibling))) {
         return frame;
       }
 
       const bool startFromBeginning = aDirection == SiblingDirection::Forward;
       FlattenedChildIterator iter(sibling, startFromBeginning);
       nsIFrame* sibling = FindSiblingInternal<aDirection>(
         iter, aTargetContent, aTargetContentDisplay);
@@ -6604,17 +6599,17 @@ nsCSSFrameConstructor::FindSibling(const
   if (sibling) {
     return sibling;
   }
 
   // Our siblings (if any) do not have a frame to guide us. The frame for the
   // target content should be inserted whereever a frame for the container would
   // be inserted. This is needed when inserting into display: contents nodes.
   const nsIContent* current = aIter.Parent();
-  while (GetDisplayContentsStyleFor(current)) {
+  while (IsDisplayContents(current)) {
     const nsIContent* parent = current->GetFlattenedTreeParent();
     MOZ_ASSERT(parent, "No display: contents on the root");
 
     FlattenedChildIterator iter(parent);
     iter.Seek(current);
     sibling = FindSiblingInternal<aDirection>(
         iter, targetContent, aTargetContentDisplay);
     if (sibling) {
@@ -6720,17 +6715,17 @@ nsCSSFrameConstructor::GetInsertionPrevS
   return prevSibling;
 }
 
 nsContainerFrame*
 nsCSSFrameConstructor::GetContentInsertionFrameFor(nsIContent* aContent)
 {
   nsIFrame* frame;
   while (!(frame = aContent->GetPrimaryFrame())) {
-    if (!GetDisplayContentsStyleFor(aContent)) {
+    if (!IsDisplayContents(aContent)) {
       return nullptr;
     }
 
     aContent = aContent->GetFlattenedTreeParent();
     if (!aContent) {
       return nullptr;
     }
   }
@@ -6846,19 +6841,17 @@ nsCSSFrameConstructor::CheckBitsForLazyF
   bool noPrimaryFrame = false;
   bool needsFrameBitSet = false;
   nsIContent* content = aParent;
   while (content && !content->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
     if (content->GetPrimaryFrame() && content->GetPrimaryFrame()->IsLeaf()) {
       noPrimaryFrame = needsFrameBitSet = false;
     }
     if (!noPrimaryFrame && !content->GetPrimaryFrame()) {
-      ComputedStyle* sc = GetDisplayNoneStyleFor(content);
-      noPrimaryFrame = !GetDisplayContentsStyleFor(content) &&
-        (sc && !sc->IsInDisplayNoneSubtree());
+      noPrimaryFrame = !IsDisplayContents(content);
     }
     if (!needsFrameBitSet && content->HasFlag(NODE_NEEDS_FRAME)) {
       needsFrameBitSet = true;
     }
 
     content = content->GetFlattenedTreeParent();
   }
   if (content && content->GetPrimaryFrame() &&
@@ -6899,22 +6892,26 @@ nsCSSFrameConstructor::MaybeConstructLaz
       if (child->IsXULElement()) {
         return false;
       }
     }
   }
 
   // We can construct lazily; just need to set suitable bits in the content
   // tree.
-  nsIContent* parent = aChild->GetFlattenedTreeParent();
+  Element* parent = aChild->GetFlattenedTreeParentElement();
   if (!parent) {
     // Not part of the flat tree, nothing to do.
     return true;
   }
 
+  if (Servo_Element_IsDisplayNone(parent)) {
+    // Nothing to do either.
+    return true;
+  }
 
   // Set NODE_NEEDS_FRAME on the new nodes.
   if (aOperation == CONTENTINSERT) {
     NS_ASSERTION(!aChild->GetPrimaryFrame() ||
                  aChild->GetPrimaryFrame()->GetContent() != aChild,
                  //XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
                  // check is needed due to bug 135040. Remove it once that's
                  // fixed.
@@ -6928,35 +6925,33 @@ nsCSSFrameConstructor::MaybeConstructLaz
                    // check is needed due to bug 135040. Remove it once that's
                    // fixed.
                    "setting NEEDS_FRAME on a node that already has a frame?");
       child->SetFlags(NODE_NEEDS_FRAME);
     }
   }
 
   CheckBitsForLazyFrameConstruction(parent);
-  parent->AsElement()->NoteDescendantsNeedFramesForServo();
+  parent->NoteDescendantsNeedFramesForServo();
 
   return true;
 }
 
 
 void
 nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
                                                     nsIContent* aStartChild,
                                                     nsIContent* aEndChild)
 {
   for (nsIContent* child = aStartChild;
        child != aEndChild;
        child = child->GetNextSibling()) {
     // listboxes suck.
     MOZ_ASSERT(MaybeGetListBoxBodyFrame(aContainer, child) ||
-               (!child->GetPrimaryFrame() &&
-                !GetDisplayNoneStyleFor(child) &&
-                !GetDisplayContentsStyleFor(child)));
+               !child->GetPrimaryFrame());
 
     // Call ContentRangeInserted with this node.
     ContentRangeInserted(aContainer, child, child->GetNextSibling(),
                          mTempFrameTreeState, InsertionKind::Sync);
   }
 }
 
 bool
@@ -7111,19 +7106,19 @@ nsCSSFrameConstructor::ContentAppended(n
 #endif
 
 #ifdef DEBUG
   for (nsIContent* child = aFirstNewContent;
        child;
        child = child->GetNextSibling()) {
     // XXX the GetContent() != child check is needed due to bug 135040.
     // Remove it once that's fixed.
-    NS_ASSERTION(!child->GetPrimaryFrame() ||
-                 child->GetPrimaryFrame()->GetContent() != child,
-                 "asked to construct a frame for a node that already has a frame");
+    MOZ_ASSERT(!child->GetPrimaryFrame() ||
+               child->GetPrimaryFrame()->GetContent() != child,
+               "asked to construct a frame for a node that already has a frame");
   }
 #endif
 
 #ifdef MOZ_XUL
   if (aContainer) {
     int32_t namespaceID;
     nsAtom* tag =
       mDocument->BindingManager()->ResolveTag(aContainer, &namespaceID);
@@ -7203,17 +7198,17 @@ nsCSSFrameConstructor::ContentAppended(n
   // We should never get here with fieldsets or details, since they have
   // multiple insertion points.
   MOZ_ASSERT(!parentFrame->IsFieldSetFrame() && !parentFrame->IsDetailsFrame(),
              "Parent frame should not be fieldset or details!");
 
   // Deal with possible :after generated content on the parent, or display:
   // contents.
   nsIFrame* nextSibling = nullptr;
-  if (GetDisplayContentsStyleFor(insertion.mContainer) ||
+  if (IsDisplayContents(insertion.mContainer) ||
       nsLayoutUtils::GetAfterFrame(insertion.mContainer)) {
     FlattenedChildIterator iter(insertion.mContainer);
     iter.Seek(insertion.mContainer->GetLastChild());
     StyleDisplay unused = UNSET_DISPLAY;
     nextSibling = FindNextSibling(iter, unused);
   }
 
   if (nextSibling) {
@@ -7608,17 +7603,18 @@ nsCSSFrameConstructor::ContentRangeInser
     }
     return;
   }
 
   MOZ_ASSERT_IF(aContainer->IsShadowRoot(), !parentFrame);
 
   // Otherwise, we've got parent content. Find its frame.
   NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
-               GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
+               IsDisplayContents(aContainer),
+               "New XBL code is possibly wrong!");
 
   if (aInsertionKind == InsertionKind::Async &&
       MaybeConstructLazily(CONTENTINSERT, aStartChild)) {
     LazilyStyleNewChildRange(aStartChild, aEndChild);
     return;
   }
 
   // We couldn't construct lazily. Make Servo eagerly traverse the new content
@@ -8028,70 +8024,83 @@ nsCSSFrameConstructor::ContentRemoved(ns
   }
 #endif
 
   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.
     childFrame = nullptr;
-    UnregisterDisplayNoneStyleFor(aChild, aContainer);
-  }
-  MOZ_ASSERT(!childFrame || !GetDisplayContentsStyleFor(aChild),
-             "display:contents nodes shouldn't have a frame");
-  if (!childFrame && GetDisplayContentsStyleFor(aChild)) {
-    // NOTE(emilio): We may iterate through ::before and ::after here and they
-    // may be gone after the respective ContentRemoved call. Right now
-    // StyleChildrenIterator handles that properly, so it's not an issue.
-    StyleChildrenIterator iter(aChild);
-    for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
-      if (c->GetPrimaryFrame() || GetDisplayContentsStyleFor(c)) {
-        LAYOUT_PHASE_TEMP_EXIT();
-        bool didReconstruct =
-          ContentRemoved(aChild, c, nullptr, REMOVE_FOR_RECONSTRUCTION);
-        LAYOUT_PHASE_TEMP_REENTER();
-        if (didReconstruct) {
-          return true;
-        }
-      }
-    }
-    UnregisterDisplayContentsStyleFor(aChild, aContainer);
-    return false;
   }
 
 #ifdef MOZ_XUL
   if (NotifyListBoxBody(presContext, aContainer, aChild, aOldNextSibling,
                         childFrame, CONTENT_REMOVED)) {
     return false;
   }
 #endif // MOZ_XUL
 
   // If we're removing the root, then make sure to remove things starting at
   // the viewport's child instead of the primary frame (which might even be
   // null if the root had an XBL binding or display:none, even though the
-  // frames above it got created).  We do the adjustment after the childFrame
-  // check above, because we do want to clear any undisplayed content we might
-  // have for the root.  Detecting removal of a root is a little exciting; in
-  // particular, having a null aContainer is necessary but NOT sufficient.  Due
-  // to how we process reframes, the content node might not even be in our
-  // document by now.  So explicitly check whether the viewport's first kid's
-  // content node is aChild.
+  // frames above it got created).  Detecting removal of a root is a little
+  // exciting; in particular, having a null aContainer is necessary but NOT
+  // sufficient.  Due to how we process reframes, the content node might not
+  // even be in our document by now.  So explicitly check whether the viewport's
+  // first kid's content node is aChild.
+  //
+  // FIXME(emilio): I think the "might not be in our document" bit is impossible
+  // now.
   bool isRoot = false;
   if (!aContainer) {
-    nsIFrame* viewport = GetRootFrame();
-    if (viewport) {
+    if (nsIFrame* viewport = GetRootFrame()) {
       nsIFrame* firstChild = viewport->PrincipalChildList().FirstChild();
       if (firstChild && firstChild->GetContent() == aChild) {
         isRoot = true;
         childFrame = firstChild;
         NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
       }
     }
   }
 
+  // We need to be conservative about when to determine whether something has
+  // display: contents or not because at this point our actual display may be
+  // different.
+  //
+  // Consider the case of:
+  //
+  //   <div id="A" style="display: contents"><div id="B"></div></div>
+  //
+  // If we reconstruct A because its display changed to "none", we still need to
+  // cleanup the frame on B, but A's display is now "none", so we can't poke at
+  // the style of it.
+  auto CouldHaveBeenDisplayContents = [&](nsIContent* aContent) -> bool {
+    return aFlags == REMOVE_FOR_RECONSTRUCTION || IsDisplayContents(aContent);
+  };
+
+  if (!childFrame && CouldHaveBeenDisplayContents(aChild)) {
+    // NOTE(emilio): We may iterate through ::before and ::after here and they
+    // may be gone after the respective ContentRemoved call. Right now
+    // StyleChildrenIterator handles that properly, so it's not an issue.
+    StyleChildrenIterator iter(aChild);
+    for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
+      if (c->GetPrimaryFrame() || CouldHaveBeenDisplayContents(aChild)) {
+        LAYOUT_PHASE_TEMP_EXIT();
+        bool didReconstruct =
+          ContentRemoved(aChild, c, nullptr, REMOVE_FOR_RECONSTRUCTION);
+        LAYOUT_PHASE_TEMP_REENTER();
+        if (didReconstruct) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+
   if (childFrame) {
     InvalidateCanvasIfNeeded(mPresShell, aChild);
 
     // See whether we need to remove more than just childFrame
     LAYOUT_PHASE_TEMP_EXIT();
     if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
       LAYOUT_PHASE_TEMP_REENTER();
       return true;
@@ -8174,17 +8183,16 @@ nsCSSFrameConstructor::ContentRemoved(ns
       // simpler.
       RemoveLetterFrames(mPresShell, containingBlock);
 
       // Recover childFrame and parentFrame
       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.
-        UnregisterDisplayNoneStyleFor(aChild, aContainer);
         return false;
       }
       parentFrame = childFrame->GetParent();
       parentType = parentFrame->Type();
 
 #ifdef NOISY_FIRST_LETTER
       printf("  ==> revised parentFrame=");
       nsFrame::ListTag(stdout, parentFrame);
@@ -12293,18 +12301,17 @@ nsCSSFrameConstructor::FrameConstruction
 Iterator::AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd,
                             FrameConstructionItemList& aTargetList)
 {
   NS_ASSERTION(&aTargetList != &mList, "Unexpected call");
   NS_PRECONDITION(&mList == &aEnd.mList, "End iterator for some other list?");
 
   // We can't just move our guts to the other list if it already has
   // some information or if we're not moving our entire list.
-  if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty() ||
-      !aTargetList.mUndisplayedItems.IsEmpty()) {
+  if (!AtStart() || !aEnd.IsDone() || !aTargetList.IsEmpty()) {
     do {
       AppendItemToList(aTargetList);
     } while (*this != aEnd);
     return;
   }
 
   // Move our entire list of items into the empty target list.
   aTargetList.mItems = Move(mList.mItems);
@@ -12312,19 +12319,16 @@ Iterator::AppendItemsToList(nsCSSFrameCo
   // Copy over the various counters
   aTargetList.mInlineCount = mList.mInlineCount;
   aTargetList.mBlockCount = mList.mBlockCount;
   aTargetList.mLineParticipantCount = mList.mLineParticipantCount;
   aTargetList.mItemCount = mList.mItemCount;
   memcpy(aTargetList.mDesiredParentCounts, mList.mDesiredParentCounts,
          sizeof(aTargetList.mDesiredParentCounts));
 
-  // Swap out undisplayed item arrays, before we nuke the array on our end
-  aTargetList.mUndisplayedItems.SwapElements(mList.mUndisplayedItems);
-
   // reset mList
   mList.Reset(aFCtor);
 
   // Point ourselves to aEnd, as advertised
   SetToEnd();
   MOZ_ASSERT(*this == aEnd, "How did that happen?");
 }
 
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -889,21 +889,16 @@ private:
                                            aSuppressWhiteSpaceOptimizations,
                                            aAnonChildren);
       mItems.insertFront(item);
       ++mItemCount;
       ++mDesiredParentCounts[item->DesiredParentType()];
       return item;
     }
 
-    void AppendUndisplayedItem(nsIContent* aContent,
-                               ComputedStyle* aComputedStyle) {
-      mUndisplayedItems.AppendElement(UndisplayedItem(aContent, aComputedStyle));
-    }
-
     void InlineItemAdded() { ++mInlineCount; }
     void BlockItemAdded() { ++mBlockCount; }
     void LineParticipantItemAdded() { ++mLineParticipantCount; }
 
     class Iterator {
     public:
       explicit Iterator(FrameConstructionItemList& aList)
         : mCurrent(aList.mItems.getFirst())
@@ -1037,26 +1032,16 @@ private:
       memset(mDesiredParentCounts, 0, sizeof(mDesiredParentCounts));
     }
 
     void Destroy(nsCSSFrameConstructor* aFCtor)
     {
       while (FrameConstructionItem* item = mItems.popFirst()) {
         item->Delete(aFCtor);
       }
-
-      // Create the undisplayed entries for our mUndisplayedItems, if any, but
-      // only if we have tried constructing frames for this item list.  If we
-      // haven't, then we're just throwing it away and will probably try again.
-      if (!mUndisplayedItems.IsEmpty() && mTriedConstructingFrames) {
-        for (uint32_t i = 0; i < mUndisplayedItems.Length(); ++i) {
-          UndisplayedItem& item = mUndisplayedItems[i];
-          aFCtor->RegisterDisplayNoneStyleFor(item.mContent, item.mComputedStyle);
-        }
-      }
     }
 
     // Prevent stack instances (except as AutoFrameConstructionItemList).
     friend struct FrameConstructionItem;
     ~FrameConstructionItemList()
     {
       MOZ_COUNT_DTOR(FrameConstructionItemList);
       MOZ_ASSERT(mItems.isEmpty(), "leaking");
@@ -1082,17 +1067,16 @@ private:
       nsIContent * const mContent;
       RefPtr<ComputedStyle> mComputedStyle;
     };
 
     // Adjust our various counts for aItem being added or removed.  aDelta
     // should be either +1 or -1 depending on which is happening.
     void AdjustCountsForItem(FrameConstructionItem* aItem, int32_t aDelta);
 
-    nsTArray<UndisplayedItem> mUndisplayedItems;
     mozilla::LinkedList<FrameConstructionItem> mItems;
     uint32_t mInlineCount;
     uint32_t mBlockCount;
     uint32_t mLineParticipantCount;
     uint32_t mItemCount;
     uint32_t mDesiredParentCounts[eParentTypeCount];
     // True if there is guaranteed to be a line boundary before the
     // frames created by these items
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -35,69 +35,16 @@
 #include "mozilla/MemoryReporting.h"
 
 // #define DEBUG_UNDISPLAYED_MAP
 // #define DEBUG_DISPLAY_CONTENTS_MAP
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-/**
- * The undisplayed map is a class that maps a parent content node to the
- * undisplayed content children, and their ComputedStyles.
- *
- * The linked list of nodes holds strong references to the ComputedStyle and the
- * content.
- */
-class nsFrameManager::UndisplayedMap :
-  private nsClassHashtable<nsPtrHashKey<nsIContent>,
-                           LinkedList<UndisplayedNode>>
-{
-  typedef nsClassHashtable<nsPtrHashKey<nsIContent>, LinkedList<UndisplayedNode>> base_type;
-
-public:
-  UndisplayedMap();
-  ~UndisplayedMap();
-
-  UndisplayedNode* GetFirstNode(nsIContent* aParentContent);
-
-  void AddNodeFor(nsIContent* aParentContent,
-                  nsIContent* aChild,
-                  ComputedStyle* aStyle);
-
-  void RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode);
-
-  void RemoveNodesFor(nsIContent* aParentContent);
-
-  nsAutoPtr<LinkedList<UndisplayedNode>>
-    UnlinkNodesFor(nsIContent* aParentContent);
-
-  // Removes all entries from the hash table
-  void  Clear();
-
-  /**
-   * Get the applicable parent for the map lookup. This is almost always the
-   * provided argument, except if it's a <xbl:children> element, in which case
-   * it's the parent of the children element.
-   *
-   * All functions that are entry points into code that handles "parent"
-   * objects (used as the hash table keys) must ensure that the parent objects
-   * that they act on (and pass to other code) have been normalized by calling
-   * this method.
-   */
-  static nsIContent* GetApplicableParent(nsIContent* aParent);
-
-  void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const;
-
-protected:
-  LinkedList<UndisplayedNode>* GetListFor(nsIContent* aParentContent);
-  LinkedList<UndisplayedNode>* GetOrCreateListFor(nsIContent* aParentContent);
-  void AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent);
-};
-
 //----------------------------------------------------------------------
 
 nsFrameManager::~nsFrameManager()
 {
   NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called");
 }
 
 void
@@ -108,368 +55,20 @@ nsFrameManager::Destroy()
   // Destroy the frame hierarchy.
   mPresShell->SetIgnoreFrameDestruction(true);
 
   if (mRootFrame) {
     mRootFrame->Destroy();
     mRootFrame = nullptr;
   }
 
-  delete mDisplayNoneMap;
-  mDisplayNoneMap = nullptr;
-  delete mDisplayContentsMap;
-  mDisplayContentsMap = nullptr;
-
   mPresShell = nullptr;
 }
 
 //----------------------------------------------------------------------
-
-/* static */ nsIContent*
-nsFrameManager::ParentForUndisplayedMap(const nsIContent* aContent)
-{
-  MOZ_ASSERT(aContent);
-
-  nsIContent* parent = aContent->GetParentElementCrossingShadowRoot();
-
-  // Normalize the parent:
-  parent = UndisplayedMap::GetApplicableParent(parent);
-
-  return parent;
-}
-
-/* static */ ComputedStyle*
-nsFrameManager::GetComputedStyleInMap(UndisplayedMap* aMap,
-                                     const nsIContent* aContent)
-{
-  UndisplayedNode* node = GetUndisplayedNodeInMapFor(aMap, aContent);
-  return node ? node->mStyle.get() : nullptr;
-}
-
-/* static */ UndisplayedNode*
-nsFrameManager::GetUndisplayedNodeInMapFor(UndisplayedMap* aMap,
-                                           const nsIContent* aContent)
-{
-  if (!aContent) {
-    return nullptr;
-  }
-
-  // This function is an entry point into UndisplayedMap handling code, so the
-  // parent that we act on must be normalized by GetApplicableParent (as per
-  // that function's documentation).  We rely on ParentForUndisplayedMap to
-  // have done that for us.
-  nsIContent* parent = ParentForUndisplayedMap(aContent);
-
-  for (UndisplayedNode* node = aMap->GetFirstNode(parent);
-       node; node = node->getNext()) {
-    if (node->mContent == aContent)
-      return node;
-  }
-
-  return nullptr;
-}
-
-
-/* static */ UndisplayedNode*
-nsFrameManager::GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap,
-                                               nsIContent* aParentContent)
-{
-  return aMap ? aMap->GetFirstNode(aParentContent) : nullptr;
-}
-
-UndisplayedNode*
-nsFrameManager::GetAllRegisteredDisplayNoneStylesIn(nsIContent* aParentContent)
-{
-  return GetAllUndisplayedNodesInMapFor(mDisplayNoneMap, aParentContent);
-}
-
-/* static */ void
-nsFrameManager::SetComputedStyleInMap(UndisplayedMap* aMap,
-                                     nsIContent* aContent,
-                                     ComputedStyle* aComputedStyle)
-{
-  MOZ_ASSERT(!aComputedStyle->GetPseudo(),
-             "Should only have actual elements here");
-
-#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP)
-  static int i = 0;
-  printf("SetComputedStyleInMap(%d): p=%p \n", i++, (void *)aContent);
-#endif
-
-  MOZ_ASSERT(!GetComputedStyleInMap(aMap, aContent),
-             "Already have an entry for aContent");
-
-  // This function is an entry point into UndisplayedMap handling code, so the
-  // parent that we act on must be normalized by GetApplicableParent (as per
-  // that function's documentation).  We rely on ParentForUndisplayedMap to
-  // have done that for us.
-  nsIContent* parent = ParentForUndisplayedMap(aContent);
-  MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements");
-
-#ifdef DEBUG
-  nsIPresShell* shell = aComputedStyle->PresContext()->PresShell();
-  NS_ASSERTION(parent || (shell && shell->GetDocument() &&
-                          shell->GetDocument()->GetRootElement() == aContent),
-               "undisplayed content must have a parent, unless it's the root "
-               "element");
-#endif
-
-  // We set this bit as an optimization so that we can can know when a content
-  // node may have |display:none| or |display:contents| children.  This allows
-  // other parts of the code to avoid checking for such children in
-  // mDisplayNoneMap and mDisplayContentsMap if the bit isn't present on a node
-  // that it's handling.
-  if (parent) {
-    parent->SetMayHaveChildrenWithLayoutBoxesDisabled();
-  }
-
-  aMap->AddNodeFor(parent, aContent, aComputedStyle);
-}
-
-void
-nsFrameManager::RegisterDisplayNoneStyleFor(nsIContent* aContent,
-                                            ComputedStyle* aComputedStyle)
-{
-  if (!mDisplayNoneMap) {
-    mDisplayNoneMap = new UndisplayedMap;
-  }
-  SetComputedStyleInMap(mDisplayNoneMap, aContent, aComputedStyle);
-}
-
-/* static */ void
-nsFrameManager::ChangeComputedStyleInMap(UndisplayedMap* aMap,
-                                        nsIContent* aContent,
-                                        ComputedStyle* aComputedStyle)
-{
-  MOZ_ASSERT(aMap, "expecting a map");
-
-#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP)
-   static int i = 0;
-   printf("ChangeComputedStyleInMap(%d): p=%p \n", i++, (void *)aContent);
-#endif
-
-  // This function is an entry point into UndisplayedMap handling code, so the
-  // parent that we act on must be normalized by GetApplicableParent (as per
-  // that function's documentation).  We rely on ParentForUndisplayedMap to
-  // have done that for us.
-  nsIContent* parent = ParentForUndisplayedMap(aContent);
-  MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements");
-
-  for (UndisplayedNode* node = aMap->GetFirstNode(parent);
-       node; node = node->getNext()) {
-    if (node->mContent == aContent) {
-      node->mStyle = aComputedStyle;
-      return;
-    }
-  }
-
-  MOZ_CRASH("couldn't find the entry to change");
-}
-
-void
-nsFrameManager::UnregisterDisplayNoneStyleFor(nsIContent* aContent,
-                                              nsIContent* aParentContent)
-{
-#ifdef DEBUG_UNDISPLAYED_MAP
-  static int i = 0;
-  printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
-#endif
-
-  if (!mDisplayNoneMap) {
-    return;
-  }
-
-  // This function is an entry point into UndisplayedMap handling code, so we
-  // must call GetApplicableParent so the parent we pass around is correct.
-  aParentContent = UndisplayedMap::GetApplicableParent(aParentContent);
-
-  if (aParentContent &&
-      !aParentContent->MayHaveChildrenWithLayoutBoxesDisabled()) {
-    MOZ_ASSERT(!mDisplayNoneMap->GetFirstNode(aParentContent),
-               "MayHaveChildrenWithLayoutBoxesDisabled bit out of sync - "
-               "may fail to remove node from mDisplayNoneMap");
-    return;
-  }
-
-  UndisplayedNode* node = mDisplayNoneMap->GetFirstNode(aParentContent);
-
-  const bool haveOneDisplayNoneChild = node && !node->getNext();
-
-  for (; node; node = node->getNext()) {
-    if (node->mContent == aContent) {
-      mDisplayNoneMap->RemoveNodeFor(aParentContent, node);
-
-#ifdef DEBUG_UNDISPLAYED_MAP
-      printf( "REMOVED!\n");
-#endif
-      // make sure that there are no more entries for the same content
-      MOZ_ASSERT(!GetDisplayNoneStyleFor(aContent),
-                 "Found more undisplayed content data after removal");
-
-      if (haveOneDisplayNoneChild) {
-        // There are no more children of aParentContent in mDisplayNoneMap.
-        MOZ_ASSERT(!mDisplayNoneMap->GetFirstNode(aParentContent),
-                   "Bad UnsetMayHaveChildrenWithLayoutBoxesDisabled call");
-        // If we also know that none of its children are in mDisplayContentsMap
-        // then we can call UnsetMayHaveChildrenWithLayoutBoxesDisabled.  We
-        // don't want to check mDisplayContentsMap though since that involves a
-        // hash table lookup in relatively hot code.  Still, we know there are
-        // no children in mDisplayContentsMap if the map is empty, so we do
-        // check for that.
-        if (aParentContent && !mDisplayContentsMap) {
-          aParentContent->UnsetMayHaveChildrenWithLayoutBoxesDisabled();
-        }
-      }
-
-      return;
-    }
-  }
-
-#ifdef DEBUG_UNDISPLAYED_MAP
-  printf( "not found.\n");
-#endif
-}
-
-void
-nsFrameManager::ClearAllMapsFor(nsIContent* aParentContent)
-{
-#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_CONTENTS_MAP)
-  static int i = 0;
-  printf("ClearAllMapsFor(%d): parent=%p \n", i++, aParentContent);
-#endif
-
-  if (!aParentContent ||
-      aParentContent->MayHaveChildrenWithLayoutBoxesDisabled()) {
-    if (mDisplayNoneMap) {
-      mDisplayNoneMap->RemoveNodesFor(aParentContent);
-    }
-    if (mDisplayContentsMap) {
-      nsAutoPtr<LinkedList<UndisplayedNode>> list =
-        mDisplayContentsMap->UnlinkNodesFor(aParentContent);
-      if (list) {
-        while (UndisplayedNode* node = list->popFirst()) {
-          ClearAllMapsFor(node->mContent);
-          delete node;
-        }
-      }
-    }
-    if (aParentContent) {
-      aParentContent->UnsetMayHaveChildrenWithLayoutBoxesDisabled();
-    }
-  }
-#ifdef DEBUG
-  else {
-    if (mDisplayNoneMap) {
-      MOZ_ASSERT(!mDisplayNoneMap->GetFirstNode(aParentContent),
-                 "We failed to remove a node from mDisplayNoneMap");
-    }
-    if (mDisplayContentsMap) {
-      MOZ_ASSERT(!mDisplayContentsMap->GetFirstNode(aParentContent),
-                 "We failed to remove a node from mDisplayContentsMap");
-    }
-  }
-#endif
-
-  // Need to look at aParentContent's content list due to XBL insertions.
-  // Nodes in aParentContent's content list do not have aParentContent as a
-  // parent, but are treated as children of aParentContent. We iterate over
-  // the flattened content list and just ignore any nodes we don't care about.
-  FlattenedChildIterator iter(aParentContent);
-  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
-    auto parent = child->GetParent();
-    if (parent != aParentContent) {
-      UnregisterDisplayNoneStyleFor(child, parent);
-      UnregisterDisplayContentsStyleFor(child, parent);
-    }
-  }
-}
-
-//----------------------------------------------------------------------
-
-void
-nsFrameManager::RegisterDisplayContentsStyleFor(nsIContent* aContent,
-                                                ComputedStyle* aComputedStyle)
-{
-  if (!mDisplayContentsMap) {
-    mDisplayContentsMap = new UndisplayedMap;
-  }
-  SetComputedStyleInMap(mDisplayContentsMap, aContent, aComputedStyle);
-}
-
-UndisplayedNode*
-nsFrameManager::GetAllRegisteredDisplayContentsStylesIn(nsIContent* aParentContent)
-{
-  return GetAllUndisplayedNodesInMapFor(mDisplayContentsMap, aParentContent);
-}
-
-void
-nsFrameManager::UnregisterDisplayContentsStyleFor(nsIContent* aContent,
-                                                  nsIContent* aParentContent)
-{
-#ifdef DEBUG_DISPLAY_CONTENTS_MAP
-  static int i = 0;
-  printf("ClearDisplayContents(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
-#endif
-
-  if (!mDisplayContentsMap) {
-    return;
-  }
-
-  // This function is an entry point into UndisplayedMap handling code, so we
-  // must call GetApplicableParent so the parent we pass around is correct.
-  aParentContent = UndisplayedMap::GetApplicableParent(aParentContent);
-
-  if (aParentContent &&
-      !aParentContent->MayHaveChildrenWithLayoutBoxesDisabled()) {
-    MOZ_ASSERT(!mDisplayContentsMap->GetFirstNode(aParentContent),
-               "MayHaveChildrenWithLayoutBoxesDisabled bit out of sync - "
-               "may fail to remove node from mDisplayContentsMap");
-    return;
-  }
-
-  UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent);
-
-  const bool haveOneDisplayContentsChild = node && !node->getNext();
-
-  for (; node; node = node->getNext()) {
-    if (node->mContent == aContent) {
-      mDisplayContentsMap->RemoveNodeFor(aParentContent, node);
-
-#ifdef DEBUG_DISPLAY_CONTENTS_MAP
-      printf( "REMOVED!\n");
-#endif
-      // make sure that there are no more entries for the same content
-      MOZ_ASSERT(!GetDisplayContentsStyleFor(aContent),
-                 "Found more entries for aContent after removal");
-      ClearAllMapsFor(aContent);
-
-      if (haveOneDisplayContentsChild) {
-        // There are no more children of aParentContent in mDisplayContentsMap.
-        MOZ_ASSERT(!mDisplayContentsMap->GetFirstNode(aParentContent),
-                   "Bad UnsetMayHaveChildrenWithLayoutBoxesDisabled call");
-        // If we also know that none of its children are in mDisplayNoneMap
-        // then we can call UnsetMayHaveChildrenWithLayoutBoxesDisabled.  We
-        // don't want to check mDisplayNoneMap though since that involves a
-        // hash table lookup in relatively hot code.  Still, we know there are
-        // no children in mDisplayNoneMap if the map is empty, so we do
-        // check for that.
-        if (aParentContent && !mDisplayNoneMap) {
-          aParentContent->UnsetMayHaveChildrenWithLayoutBoxesDisabled();
-        }
-      }
-
-      return;
-    }
-  }
-#ifdef DEBUG_DISPLAY_CONTENTS_MAP
-  printf( "not found.\n");
-#endif
-}
-
-//----------------------------------------------------------------------
 void
 nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame,
                              ChildListID       aListID,
                              nsFrameList&      aFrameList)
 {
   if (aParentFrame->IsAbsoluteContainer() &&
       aListID == aParentFrame->GetAbsoluteListID()) {
     aParentFrame->GetAbsoluteContainingBlock()->
@@ -530,25 +129,16 @@ nsFrameManager::RemoveFrame(ChildListID 
     parentFrame->RemoveFrame(aListID, aOldFrame);
   }
 
   mIsDestroyingFrames = wasDestroyingFrames;
 }
 
 //----------------------------------------------------------------------
 
-void
-nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame)
-{
-  nsIContent* content = aFrame->GetContent();
-  if (content && content->GetPrimaryFrame() == aFrame) {
-    ClearAllMapsFor(content);
-  }
-}
-
 // Capture state for a given frame.
 // Accept a content id here, in some cases we may not have content (scroll position)
 void
 nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
                                      nsILayoutHistoryState* aState)
 {
   if (!aFrame || !aState) {
     NS_WARNING("null frame, or state");
@@ -673,189 +263,18 @@ nsFrameManager::RestoreFrameState(nsIFra
       RestoreFrameState(childFrames.get(), aState);
     }
   }
 }
 
 void
 nsFrameManager::DestroyAnonymousContent(already_AddRefed<nsIContent> aContent)
 {
-  nsCOMPtr<nsIContent> content = aContent;
-  if (content) {
-    // Invoke ClearAllMapsFor before unbinding from the tree. When we unbind,
-    // we remove the mPrimaryFrame pointer, which is used by the frame
-    // teardown code to determine whether to invoke ClearAllMapsFor or not.
-    // These maps will go away when we drop support for the old style system.
-    ClearAllMapsFor(content);
-
+  if (nsCOMPtr<nsIContent> content = aContent) {
     content->UnbindFromTree();
   }
 }
 
 void
 nsFrameManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
 {
   aSizes.mLayoutPresShellSize += aSizes.mState.mMallocSizeOf(this);
-  if (mDisplayNoneMap) {
-    mDisplayNoneMap->AddSizeOfIncludingThis(aSizes);
-  }
-  if (mDisplayContentsMap) {
-    mDisplayContentsMap->AddSizeOfIncludingThis(aSizes);
-  }
 }
-
-//----------------------------------------------------------------------
-
-nsFrameManager::UndisplayedMap::UndisplayedMap()
-{
-  MOZ_COUNT_CTOR(nsFrameManager::UndisplayedMap);
-}
-
-nsFrameManager::UndisplayedMap::~UndisplayedMap(void)
-{
-  MOZ_COUNT_DTOR(nsFrameManager::UndisplayedMap);
-  Clear();
-}
-
-void
-nsFrameManager::UndisplayedMap::Clear()
-{
-  for (auto iter = Iter(); !iter.Done(); iter.Next()) {
-    auto* list = iter.UserData();
-    while (auto* node = list->popFirst()) {
-      delete node;
-    }
-    iter.Remove();
-  }
-}
-
-
-nsIContent*
-nsFrameManager::UndisplayedMap::GetApplicableParent(nsIContent* aParent)
-{
-  // In the case of XBL default content, <xbl:children> elements do not get a
-  // frame causing a mismatch between the content tree and the frame tree.
-  // |GetEntryFor| is sometimes called with the content tree parent (which may
-  // be a <xbl:children> element) but the parent in the frame tree would be the
-  // insertion parent (parent of the <xbl:children> element). Here the children
-  // elements are normalized to the insertion parent to correct for the mismatch.
-  if (aParent && aParent->IsActiveChildrenElement()) {
-    return aParent->GetParent();
-  }
-
-  return aParent;
-}
-
-LinkedList<UndisplayedNode>*
-nsFrameManager::UndisplayedMap::GetListFor(nsIContent* aParent)
-{
-  MOZ_ASSERT(aParent == GetApplicableParent(aParent),
-             "The parent that we use as the hash key must have been normalized");
-
-  LinkedList<UndisplayedNode>* list;
-  if (Get(aParent, &list)) {
-    return list;
-  }
-
-  return nullptr;
-}
-
-LinkedList<UndisplayedNode>*
-nsFrameManager::UndisplayedMap::GetOrCreateListFor(nsIContent* aParent)
-{
-  MOZ_ASSERT(aParent == GetApplicableParent(aParent),
-             "The parent that we use as the hash key must have been normalized");
-
-  return LookupOrAdd(aParent);
-}
-
-
-UndisplayedNode*
-nsFrameManager::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
-{
-  auto* list = GetListFor(aParentContent);
-  return list ? list->getFirst() : nullptr;
-}
-
-
-void
-nsFrameManager::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode,
-                                              nsIContent* aParentContent)
-{
-  LinkedList<UndisplayedNode>* list = GetOrCreateListFor(aParentContent);
-
-#ifdef DEBUG
-  for (UndisplayedNode* node = list->getFirst(); node; node = node->getNext()) {
-    // NOTE: In the original code there was a work around for this case, I want
-    // to check it still happens before hacking around it the same way.
-    MOZ_ASSERT(node->mContent != aNode->mContent,
-               "Duplicated content in undisplayed list!");
-  }
-#endif
-
-  list->insertBack(aNode);
-}
-
-void
-nsFrameManager::UndisplayedMap::AddNodeFor(nsIContent* aParentContent,
-                                           nsIContent* aChild,
-                                           ComputedStyle* aStyle)
-{
-  UndisplayedNode*  node = new UndisplayedNode(aChild, aStyle);
-  AppendNodeFor(node, aParentContent);
-}
-
-void
-nsFrameManager::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent,
-                                              UndisplayedNode* aNode)
-{
-#ifdef DEBUG
-  auto list = GetListFor(aParentContent);
-  MOZ_ASSERT(list, "content not in map");
-  aNode->removeFrom(*list);
-#else
-  aNode->remove();
-#endif
-  delete aNode;
-}
-
-
-nsAutoPtr<LinkedList<UndisplayedNode>>
-nsFrameManager::UndisplayedMap::UnlinkNodesFor(nsIContent* aParentContent)
-{
-  nsAutoPtr<LinkedList<UndisplayedNode>> list;
-  Remove(GetApplicableParent(aParentContent), &list);
-  return list;
-}
-
-void
-nsFrameManager::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent)
-{
-  nsAutoPtr<LinkedList<UndisplayedNode>> list = UnlinkNodesFor(aParentContent);
-  if (list) {
-    while (auto* node = list->popFirst()) {
-      delete node;
-    }
-  }
-}
-
-void
-nsFrameManager::UndisplayedMap::
-AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
-{
-  MallocSizeOf mallocSizeOf = aSizes.mState.mMallocSizeOf;
-  aSizes.mLayoutPresShellSize += ShallowSizeOfIncludingThis(mallocSizeOf);
-
-  nsWindowSizes staleSizes(aSizes.mState);
-  for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
-    const LinkedList<UndisplayedNode>* list = iter.UserData();
-    aSizes.mLayoutPresShellSize += list->sizeOfExcludingThis(mallocSizeOf);
-    for (const UndisplayedNode* node = list->getFirst();
-          node; node = node->getNext()) {
-      ComputedStyle* computedStyle = node->mStyle;
-      if (!aSizes.mState.HaveSeenPtr(computedStyle)) {
-        computedStyle->AddSizeOfIncludingThis(
-          staleSizes, &aSizes.mLayoutComputedValuesStale);
-      }
-    }
-  }
-  aSizes.mLayoutComputedValuesStale += staleSizes.getTotalSize();
-}
--- a/layout/base/nsFrameManager.h
+++ b/layout/base/nsFrameManager.h
@@ -14,42 +14,34 @@
 #include "nsFrameList.h"
 
 class nsContainerFrame;
 class nsIFrame;
 class nsILayoutHistoryState;
 class nsIPresShell;
 class nsPlaceholderFrame;
 class nsWindowSizes;
-namespace mozilla {
-class ComputedStyle;
-struct UndisplayedNode;
-}
 
 /**
  * Frame manager interface. The frame manager serves one purpose:
  * <li>handles structural modifications to the frame model. If the frame model
  * lock can be acquired, then the changes are processed immediately; otherwise,
  * they're queued and processed later.
  *
  * FIXME(emilio): The comment above doesn't make any sense, there's no "frame
  * model lock" of any sort afaict.
  */
 class nsFrameManager
 {
-  typedef mozilla::ComputedStyle ComputedStyle;
   typedef mozilla::layout::FrameChildListID ChildListID;
-  typedef mozilla::UndisplayedNode UndisplayedNode;
 
 public:
   explicit nsFrameManager(nsIPresShell* aPresShell)
     : mPresShell(aPresShell)
     , mRootFrame(nullptr)
-    , mDisplayNoneMap(nullptr)
-    , mDisplayContentsMap(nullptr)
     , mIsDestroyingFrames(false)
   {
     MOZ_ASSERT(mPresShell, "need a pres shell");
   }
   ~nsFrameManager();
 
   bool IsDestroyingFrames() const { return mIsDestroyingFrames; }
 
@@ -67,127 +59,29 @@ public:
 
   /*
    * After Destroy is called, it is an error to call any FrameManager methods.
    * Destroy should be called when the frame tree managed by the frame
    * manager is no longer being displayed.
    */
   void Destroy();
 
-
-  // display:none and display:contents content does not get an nsIFrame.  To
-  // enable the style for such content to be obtained we store them in a
-  // couple of hash tables.  The following methods provide the API that's used
-  // to set, reset, obtain and clear these styles.
-  //
-  // FIXME(stylo-everywhere): This should go away now.
-
-  /**
-   * Register the style for the display:none content, aContent.
-   */
-  void RegisterDisplayNoneStyleFor(nsIContent* aContent,
-                                   ComputedStyle* aComputedStyle);
-
-  /**
-   * Register the style for the display:contents content, aContent.
-   */
-  void RegisterDisplayContentsStyleFor(nsIContent* aContent,
-                                       ComputedStyle* aComputedStyle);
-
-  /**
-   * Change the style for the display:none content, aContent.
-   */
-  void ChangeRegisteredDisplayNoneStyleFor(nsIContent* aContent,
-                                           ComputedStyle* aComputedStyle)
-  {
-    ChangeComputedStyleInMap(mDisplayNoneMap, aContent, aComputedStyle);
-  }
-
-  /**
-   * Change the style for the display:contents content, aContent.
-   */
-  void ChangeRegisteredDisplayContentsStyleFor(nsIContent* aContent,
-                                               ComputedStyle* aComputedStyle)
-  {
-    ChangeComputedStyleInMap(mDisplayContentsMap, aContent, aComputedStyle);
-  }
-
-  /**
-   * Get the style for the display:none content, aContent, if any.
-   */
-  ComputedStyle* GetDisplayNoneStyleFor(const nsIContent* aContent)
-  {
-    if (!mDisplayNoneMap) {
-      return nullptr;
-    }
-    return GetComputedStyleInMap(mDisplayNoneMap, aContent);
-  }
-
-  /**
-   * Get the style for the display:contents content, aContent, if any.
-   */
-  ComputedStyle* GetDisplayContentsStyleFor(const nsIContent* aContent)
-  {
-    if (!mDisplayContentsMap) {
-      return nullptr;
-    }
-    return GetComputedStyleInMap(mDisplayContentsMap, aContent);
-  }
-
-  /**
-   * Return the linked list of UndisplayedNodes that contain the styles that
-   * been registered for the display:none children of aParentContent.
-   */
-  UndisplayedNode*
-  GetAllRegisteredDisplayNoneStylesIn(nsIContent* aParentContent);
-
-  /**
-   * Return the linked list of UndisplayedNodes that contain the styles
-   * that have been registered for the display:contents children of
-   * aParentContent.
-   */
-  UndisplayedNode*
-  GetAllRegisteredDisplayContentsStylesIn(nsIContent* aParentContent);
-
-  /**
-   * Unregister the style for the display:none content, aContent, if
-   * any.  If found, then this method also unregisters the styles for any
-   * display:contents and display:none descendants of aContent.
-   */
-  void UnregisterDisplayNoneStyleFor(nsIContent* aContent,
-                                     nsIContent* aParentContent);
-
-  /**
-   * Unregister the style for the display:contents content, aContent, if any.
-   * If found, then this method also unregisters the style for any
-   * display:contents and display:none descendants of aContent.
-   */
-  void UnregisterDisplayContentsStyleFor(nsIContent* aContent,
-                                         nsIContent* aParentContent);
-
-
   // Functions for manipulating the frame model
   void AppendFrames(nsContainerFrame* aParentFrame,
                     ChildListID aListID,
                     nsFrameList& aFrameList);
 
   void InsertFrames(nsContainerFrame* aParentFrame,
                     ChildListID aListID,
                     nsIFrame* aPrevFrame,
                     nsFrameList& aFrameList);
 
   void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame);
 
   /*
-   * Notification that a frame is about to be destroyed. This allows any
-   * outstanding references to the frame to be cleaned up.
-   */
-  void NotifyDestroyingFrame(nsIFrame* aFrame);
-
-  /*
    * Capture/restore frame state for the frame subtree rooted at aFrame.
    * aState is the document state storage object onto which each frame
    * stores its state.  Callers of CaptureFrameState are responsible for
    * traversing next continuations of special siblings of aFrame as
    * needed; this method will only work with actual frametree descendants
    * of aFrame.
    */
 
@@ -202,39 +96,15 @@ public:
 
   void RestoreFrameStateFor(nsIFrame* aFrame, nsILayoutHistoryState* aState);
 
   void DestroyAnonymousContent(already_AddRefed<nsIContent> aContent);
 
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes) const;
 
 protected:
-  class UndisplayedMap;
-
-  static nsIContent* ParentForUndisplayedMap(const nsIContent* aContent);
-
-  void ClearAllMapsFor(nsIContent* aParentContent);
-
-  static ComputedStyle* GetComputedStyleInMap(UndisplayedMap* aMap,
-                                              const nsIContent* aContent);
-  static UndisplayedNode* GetUndisplayedNodeInMapFor(UndisplayedMap* aMap,
-                                                     const nsIContent* aContent);
-  static UndisplayedNode* GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap,
-                                                         nsIContent* aParentContent);
-  static void SetComputedStyleInMap(
-      UndisplayedMap* aMap,
-      nsIContent* aContent,
-      ComputedStyle* aComputedStyle);
-
-  static void ChangeComputedStyleInMap(
-      UndisplayedMap* aMap,
-      nsIContent* aContent,
-      ComputedStyle* aComputedStyle);
-
   // weak link, because the pres shell owns us
   nsIPresShell* MOZ_NON_OWNING_REF mPresShell;
   nsIFrame* mRootFrame;
-  UndisplayedMap* mDisplayNoneMap;
-  UndisplayedMap* mDisplayContentsMap;
   bool mIsDestroyingFrames;  // The frame manager is destroying some frame(s).
 };
 
 #endif
--- a/layout/base/nsStyleChangeList.cpp
+++ b/layout/base/nsStyleChangeList.cpp
@@ -6,16 +6,18 @@
 
 /*
  * a list of the recomputation that needs to be done in response to a
  * style change
  */
 
 #include "nsStyleChangeList.h"
 
+#include "mozilla/dom/ElementInlines.h"
+
 #include "nsCSSFrameConstructor.h"
 #include "nsIContent.h"
 #include "nsIFrame.h"
 
 void
 nsStyleChangeList::AppendChange(nsIFrame* aFrame, nsIContent* aContent, nsChangeHint aHint)
 {
   MOZ_ASSERT(aFrame || (aHint & nsChangeHint_ReconstructFrame),
@@ -24,19 +26,19 @@ nsStyleChangeList::AppendChange(nsIFrame
   MOZ_ASSERT(!(aHint & nsChangeHint_NeutralChange),
              "Neutral changes do not need extra processing, "
              "and should be stripped out");
   MOZ_ASSERT(aContent || !(aHint & nsChangeHint_ReconstructFrame),
              "must have content");
   // XXXbz we should make this take Element instead of nsIContent
   MOZ_ASSERT(!aContent || aContent->IsElement() ||
              // display:contents elements posts the changes for their children:
-             (aFrame && aContent->GetParent() &&
-             aFrame->PresContext()->FrameConstructor()->
-               GetDisplayContentsStyleFor(aContent->GetParent())) ||
+             (aFrame && aContent->GetFlattenedTreeParentElementForStyle() &&
+              Servo_Element_IsDisplayContents(
+                aContent->GetFlattenedTreeParentElementForStyle())) ||
              (aContent->IsNodeOfType(nsINode::eTEXT) &&
               aContent->HasFlag(NODE_NEEDS_FRAME) &&
               aHint & nsChangeHint_ReconstructFrame),
              "Shouldn't be trying to restyle non-elements directly, "
              "except if it's a display:contents child or a text node "
              "doing lazy frame construction");
   MOZ_ASSERT(!(aHint & nsChangeHint_AllReflowHints) ||
              (aHint & nsChangeHint_NeedReflow),
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -11,16 +11,17 @@
 #include <stdarg.h>
 #include <algorithm>
 
 #include "gfx2DGlue.h"
 #include "gfxUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/dom/ElementInlines.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/Sprintf.h"
 
 #include "nsCOMPtr.h"
 #include "nsFrameList.h"
 #include "nsPlaceholderFrame.h"
 #include "nsPluginFrame.h"
@@ -101,16 +102,17 @@
 #include "mozilla/EffectSet.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/ServoStyleSet.h"
+#include "mozilla/ServoStyleSetInlines.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/gfx/Tools.h"
 #include "nsPrintfCString.h"
 #include "ActiveLayerTracker.h"
 
 #include "nsITheme.h"
 #include "nsThemeConstants.h"
 
@@ -9860,32 +9862,37 @@ nsFrame::CorrectStyleParentFrame(nsIFram
 ComputedStyle*
 nsFrame::DoGetParentComputedStyle(nsIFrame** aProviderFrame) const
 {
   *aProviderFrame = nullptr;
 
   // Handle display:contents and the root frame, when there's no parent frame
   // to inherit from.
   if (MOZ_LIKELY(mContent)) {
-    nsIContent* parentContent = mContent->GetFlattenedTreeParent();
-    if (MOZ_LIKELY(parentContent)) {
+    Element* parentElement = mContent->GetFlattenedTreeParentElement();
+    if (MOZ_LIKELY(parentElement)) {
       nsAtom* pseudo = Style()->GetPseudo();
       if (!pseudo || !mContent->IsElement() ||
           (!nsCSSAnonBoxes::IsAnonBox(pseudo) &&
            // Ensure that we don't return the display:contents style
            // of the parent content for pseudos that have the same content
            // as their primary frame (like -moz-list-bullets do):
            IsPrimaryFrame()) ||
           /* if next is true then it's really a request for the table frame's
              parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
           pseudo == nsCSSAnonBoxes::tableWrapper) {
-        nsCSSFrameConstructor* fm = PresContext()->FrameConstructor();
-        ComputedStyle* sc = fm->GetDisplayContentsStyleFor(parentContent);
-        if (MOZ_UNLIKELY(sc)) {
-          return sc;
+        if (Servo_Element_IsDisplayContents(parentElement)) {
+          RefPtr<ComputedStyle> style =
+            PresShell()->StyleSet()->ResolveServoStyle(parentElement);
+          // NOTE(emilio): we return a weak reference because the element also
+          // holds the style context alive. This is a bit silly (we could've
+          // returned a weak ref directly), but it's probably not worth
+          // optimizing, given this function has just one caller which is rare,
+          // and this path is rare itself.
+          return style;
         }
       }
     } else {
       if (!Style()->GetPseudo()) {
         // We're a frame for the root.  We have no style parent.
         return nullptr;
       }
     }
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -1583,30 +1583,36 @@ nsFrameSelection::RepaintSelection(Selec
   // and that it's a normal selection.
   if (fm->GetActiveWindow() && aSelectionType == SelectionType::eNormal) {
     UpdateSelectionCacheOnRepaintSelection(mDomSelections[index]);
   }
 #endif
   return mDomSelections[index]->Repaint(mShell->GetPresContext());
 }
 
+static bool
+IsDisplayContents(const nsIContent* aContent)
+{
+  return aContent->IsElement() && aContent->AsElement()->HasServoData() &&
+    Servo_Element_IsDisplayContents(aContent->AsElement());
+}
+
 nsIFrame*
 nsFrameSelection::GetFrameForNodeOffset(nsIContent*        aNode,
                                         int32_t            aOffset,
                                         CaretAssociateHint aHint,
                                         int32_t*           aReturnOffset) const
 {
   if (!aNode || !aReturnOffset || !mShell)
     return nullptr;
 
   if (aOffset < 0)
     return nullptr;
 
-  if (!aNode->GetPrimaryFrame() &&
-      !mShell->FrameConstructor()->GetDisplayContentsStyleFor(aNode)) {
+  if (!aNode->GetPrimaryFrame() && !IsDisplayContents(aNode)) {
     return nullptr;
   }
 
   nsIFrame* returnFrame = nullptr;
   nsCOMPtr<nsIContent> theNode;
 
   while (true) {
     *aReturnOffset = aOffset;
--- a/layout/generic/nsPlaceholderFrame.cpp
+++ b/layout/generic/nsPlaceholderFrame.cpp
@@ -8,17 +8,19 @@
  * rendering object for the point that anchors out-of-flow rendering
  * objects such as floats and absolutely positioned elements
  */
 
 #include "nsPlaceholderFrame.h"
 
 #include "gfxContext.h"
 #include "gfxUtils.h"
+#include "mozilla/dom/ElementInlines.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/ServoStyleSetInlines.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDisplayList.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsIFrameInlines.h"
 #include "nsIContentInlines.h"
 
 using namespace mozilla;
@@ -210,24 +212,25 @@ nsPlaceholderFrame::CanContinueTextRun()
   return mOutOfFlowFrame->CanContinueTextRun();
 }
 
 ComputedStyle*
 nsPlaceholderFrame::GetParentComputedStyleForOutOfFlow(nsIFrame** aProviderFrame) const
 {
   NS_PRECONDITION(GetParent(), "How can we not have a parent here?");
 
-  nsIContent* parentContent = mContent ? mContent->GetFlattenedTreeParent() : nullptr;
-  if (parentContent) {
-    ComputedStyle* sc =
-      PresContext()->FrameConstructor()->GetDisplayContentsStyleFor(parentContent);
-    if (sc) {
-      *aProviderFrame = nullptr;
-      return sc;
-    }
+  Element* parentElement =
+    mContent ? mContent->GetFlattenedTreeParentElement() : nullptr;
+  if (parentElement && Servo_Element_IsDisplayContents(parentElement)) {
+    RefPtr<ComputedStyle> style =
+      PresShell()->StyleSet()->ResolveServoStyle(parentElement);
+    *aProviderFrame = nullptr;
+    // See the comment in GetParentComputedStyle to see why returning this as a
+    // weak ref is fine.
+    return style;
   }
 
   return GetLayoutParentStyleForOutOfFlow(aProviderFrame);
 }
 
 ComputedStyle*
 nsPlaceholderFrame::GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const
 {
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -32,16 +32,19 @@ SERVO_BINDING_FUNC(Servo_Element_GetPrim
 SERVO_BINDING_FUNC(Servo_Element_HasPseudoComputedValues, bool,
                    RawGeckoElementBorrowed node, size_t index)
 SERVO_BINDING_FUNC(Servo_Element_GetPseudoComputedValues,
                    ComputedStyleStrong,
                    RawGeckoElementBorrowed node, size_t index)
 SERVO_BINDING_FUNC(Servo_Element_IsDisplayNone,
                    bool,
                    RawGeckoElementBorrowed element)
+SERVO_BINDING_FUNC(Servo_Element_IsDisplayContents,
+                   bool,
+                   RawGeckoElementBorrowed element)
 SERVO_BINDING_FUNC(Servo_Element_IsPrimaryStyleReusedViaRuleNode,
                    bool,
                    RawGeckoElementBorrowed element)
 SERVO_BINDING_FUNC(Servo_InvalidateStyleForDocStateChanges,
                    void,
                    RawGeckoElementBorrowed root,
                    RawServoStyleSetBorrowed doc_styles,
                    const nsTArray<RawServoAuthorStylesBorrowed>* non_document_styles,
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1036,28 +1036,45 @@ pub extern "C" fn Servo_Element_GetPseud
 ) -> ComputedStyleStrong {
     let element = GeckoElement(element);
     let data = element.borrow_data().expect("Getting CVs that aren't present");
     data.styles.pseudos.as_array()[index].as_ref().expect("Getting CVs that aren't present")
         .clone().into()
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_Element_IsDisplayNone(element: RawGeckoElementBorrowed) -> bool {
+pub extern "C" fn Servo_Element_IsDisplayNone(
+    element: RawGeckoElementBorrowed,
+) -> bool {
     let element = GeckoElement(element);
-    let data = element.get_data().expect("Invoking Servo_Element_IsDisplayNone on unstyled element");
-
-    // This function is hot, so we bypass the AtomicRefCell. It would be nice to also assert that
-    // we're not in the servo traversal, but this function is called at various intermediate
-    // checkpoints when managing the traversal on the Gecko side.
+    let data = element.get_data()
+        .expect("Invoking Servo_Element_IsDisplayNone on unstyled element");
+
+    // This function is hot, so we bypass the AtomicRefCell.
+    //
+    // It would be nice to also assert that we're not in the servo traversal,
+    // but this function is called at various intermediate checkpoints when
+    // managing the traversal on the Gecko side.
     debug_assert!(is_main_thread());
     unsafe { &*data.as_ptr() }.styles.is_display_none()
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_Element_IsDisplayContents(
+    element: RawGeckoElementBorrowed,
+) -> bool {
+    let element = GeckoElement(element);
+    let data = element.get_data()
+        .expect("Invoking Servo_Element_IsDisplayContents on unstyled element");
+
+    debug_assert!(is_main_thread());
+    unsafe { &*data.as_ptr() }.styles.primary().get_box().clone_display().is_contents()
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_Element_IsPrimaryStyleReusedViaRuleNode(element: RawGeckoElementBorrowed) -> bool {
     let element = GeckoElement(element);
     let data = element.borrow_data()
                       .expect("Invoking Servo_Element_IsPrimaryStyleReusedViaRuleNode on unstyled element");
     data.flags.contains(data::ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE)
 }
 
 #[no_mangle]