Bug 1427511: Make GetFlattenedTreeParent more straight-forward. r=smaug
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sun, 31 Dec 2017 20:57:32 +0100
changeset 397561 b8656e137157fb746b333a53db06def134d4b108
parent 397560 030a95a498c6f77e34b1f2ded81924f98c6aff2e
child 397562 9a95b205f3e8df9cd967c3f8a0fae52a54dac6a9
push id33178
push useraiakab@mozilla.com
push dateWed, 03 Jan 2018 09:47:24 +0000
treeherdermozilla-central@66a4f3cefc64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1427511
milestone59.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 1427511: Make GetFlattenedTreeParent more straight-forward. r=smaug Now that accessing nsIContent slots is not a blob of virtual function calls, we should be able to unify logic here, and speed up the not-so-rare case for chrome, while keeping the usual case fast. MozReview-Commit-ID: 87iY5Cbhx4T
dom/base/Element.cpp
dom/base/Element.h
dom/base/FragmentOrElement.cpp
dom/base/ShadowRoot.cpp
dom/base/ShadowRoot.h
dom/base/nsDocument.cpp
dom/base/nsDocumentEncoder.cpp
dom/base/nsIContent.h
dom/base/nsIContentInlines.h
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/base/nsNodeUtils.cpp
dom/html/HTMLLabelElement.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/forms/nsComboboxControlFrame.cpp
layout/generic/nsFrameSelection.cpp
layout/generic/nsSubDocumentFrame.cpp
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1729,22 +1729,19 @@ Element::BindToTree(nsIDocument* aDocume
         // Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert
         nsContentUtils::TryToUpgradeElement(this);
       }
     }
   }
 
   // Propagate scoped style sheet tracking bit.
   if (mParent->IsContent()) {
-    nsIContent* parent;
-    ShadowRoot* shadowRootParent = ShadowRoot::FromNode(mParent);
-    if (shadowRootParent) {
+    nsIContent* parent = mParent->AsContent();
+    if (ShadowRoot* shadowRootParent = ShadowRoot::FromNode(parent)) {
       parent = shadowRootParent->GetHost();
-    } else {
-      parent = mParent->AsContent();
     }
 
     bool inStyleScope = parent->IsElementInStyleScope();
 
     SetIsElementInStyleScope(inStyleScope);
     SetIsElementInStyleScopeFlagOnShadowTree(inStyleScope);
   }
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1312,19 +1312,17 @@ public:
                                             ErrorResult& aError);
   ShadowRoot* GetShadowRootByMode() const;
   void SetSlot(const nsAString& aName, ErrorResult& aError);
   void GetSlot(nsAString& aName);
 
   // [deprecated] Shadow DOM v0
   already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
 
-  // FIXME(emilio): Should just shadow GetShadowRoot(), that way we get the fast
-  // version by default everywhere we already have an Element...
-  ShadowRoot* FastGetShadowRoot() const
+  ShadowRoot* GetShadowRoot() const
   {
     const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     return slots ? slots->mShadowRoot.get() : nullptr;
   }
 
 private:
   void ScrollIntoView(const ScrollIntoViewOptions &aOptions);
 public:
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -179,118 +179,45 @@ nsIContent::GetAssignedSlotByMode() cons
   if (GetParent()->GetShadowRoot()->IsClosed()) {
     return nullptr;
   }
 
   return slot;
 }
 
 nsINode*
-nsIContent::GetFlattenedTreeParentForMaybeAssignedNode() const
-{
-  if (HTMLSlotElement* assignedSlot = GetAssignedSlot()) {
-    return assignedSlot;
-  }
-
-  HTMLSlotElement* parentSlot = HTMLSlotElement::FromContent(GetParent());
-  if (!parentSlot) {
-    return nullptr;
-  }
-
-  // If this is not an unassigned node, then it must be a fallback content.
-  MOZ_ASSERT(parentSlot->AssignedNodes().IsEmpty());
-
-  return parentSlot;
-}
-
-nsINode*
-nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const
+nsIContent::GetFlattenedTreeParentForDocumentElementNAC() const
 {
-  nsINode* parentNode = GetParentNode();
-  if (!parentNode || !parentNode->IsContent()) {
-    MOZ_ASSERT(!parentNode || parentNode == OwnerDoc());
-    return parentNode;
-  }
-  nsIContent* parent = parentNode->AsContent();
+  MOZ_ASSERT(IsRootOfNativeAnonymousSubtree());
+  MOZ_ASSERT(GetParent());
+  MOZ_ASSERT(GetParent() == OwnerDoc()->GetRootElement());
+  MOZ_ASSERT(!IsGeneratedContentContainerForAfter());
+  MOZ_ASSERT(!IsGeneratedContentContainerForBefore());
 
-  // When getting the flattened tree parent for style, we return null
-  // for any "document level" native anonymous content subtree root.
-  // This is NAC generated by an ancestor frame of the document element's
-  // primary frame, and includes scrollbar elements created by the root
-  // scroll frame, and the "custom content container" and accessible caret
-  // generated by the nsCanvasFrame.  We distinguish document level NAC
-  // from NAC generated by the root element's primary frame below.
-  if (aType == eForStyle &&
-      IsRootOfNativeAnonymousSubtree() &&
-      OwnerDoc()->GetRootElement() == parent) {
-    // First, check if this is generated ::before/::after content for the root.
-    // If it is, we know what to do.
-    if (IsGeneratedContentContainerForBefore() ||
-        IsGeneratedContentContainerForAfter()) {
-      return parent;
-    }
-
-    AutoTArray<nsIContent*, 8> rootElementNAC;
-    nsContentUtils::AppendNativeAnonymousChildren(
-        parent, rootElementNAC, nsIContent::eSkipDocumentLevelNativeAnonymousContent);
-    bool isDocLevelNAC = !rootElementNAC.Contains(this);
+  nsIContent* parent = GetParent();
+  AutoTArray<nsIContent*, 8> rootElementNAC;
+  nsContentUtils::AppendNativeAnonymousChildren(
+    parent, rootElementNAC, nsIContent::eSkipDocumentLevelNativeAnonymousContent);
+  const bool isDocLevelNAC = !rootElementNAC.Contains(this);
 
 #ifdef DEBUG
-    {
-      // The code below would be slightly more direct, but it gets the wrong
-      // answer when the root scrollframe is being bootstrapped and we're
-      // trying to style the scrollbars (since GetRootScrollFrame() still returns
-      // null at that point). Verify that the results match otherwise.
-      AutoTArray<nsIContent*, 8> docLevelNAC;
-      nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(OwnerDoc(), docLevelNAC);
-      nsIPresShell* shell = OwnerDoc()->GetShell();
-      MOZ_ASSERT_IF(shell && shell->GetRootScrollFrame(),
-                    isDocLevelNAC == docLevelNAC.Contains(this));
-    }
+  {
+    // The code below would be slightly more direct, but it gets the wrong
+    // answer when the root scrollframe is being bootstrapped and we're
+    // trying to style the scrollbars (since GetRootScrollFrame() still returns
+    // null at that point). Verify that the results match otherwise.
+    AutoTArray<nsIContent*, 8> docLevelNAC;
+    nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(OwnerDoc(), docLevelNAC);
+    nsIPresShell* shell = OwnerDoc()->GetShell();
+    MOZ_ASSERT_IF(shell && shell->GetRootScrollFrame(),
+                  isDocLevelNAC == docLevelNAC.Contains(this));
+  }
 #endif
 
-    if (isDocLevelNAC) {
-      return OwnerDoc();
-    }
-  }
-
-  if (IsRootOfAnonymousSubtree()) {
-    return parent;
-  }
-
-  if (nsContentUtils::HasDistributedChildren(parent)) {
-    return GetFlattenedTreeParentForMaybeAssignedNode();
-  }
-
-  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
-      parent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
-    // We need to check `parent` to properly handle the unassigned child case
-    // below, since if we were never assigned we would never have the flag set.
-    //
-    // Note that unassigned child nodes _could_ have the flag set, if they were
-    // ever assigned, or if they have a binding themselves.
-    if (nsIContent* insertionPoint = GetXBLInsertionPoint()) {
-      parent = insertionPoint->GetParent();
-      MOZ_ASSERT(parent);
-    } else if (parent->OwnerDoc()->BindingManager()->GetBindingWithContent(parent)) {
-      // This is an unassigned node child of the bound element, so it isn't part
-      // of the flat tree.
-      return nullptr;
-    }
-  }
-
-  // Shadow roots never shows up in the flattened tree. Return the host
-  // instead.
-  if (parent->IsInShadowTree()) {
-    if (ShadowRoot* parentShadowRoot = ShadowRoot::FromNode(parent)) {
-      return parentShadowRoot->GetHost();
-    }
-  }
-
-  return parent;
+  return isDocLevelNAC ? OwnerDocAsNode() : parent;
 }
 
 nsIContent::IMEState
 nsIContent::GetDesiredIMEState()
 {
   if (!IsEditableInternal()) {
     // Check for the special case where we're dealing with elements which don't
     // have the editable flag set, but are readwrite (such as text controls).
@@ -2373,18 +2300,17 @@ FragmentOrElement::SetInnerHTMLInternal(
   }
   mb.RemovalDone();
 
   nsAutoScriptLoaderDisabler sld(doc);
 
   nsAtom* contextLocalName = NodeInfo()->NameAtom();
   int32_t contextNameSpaceID = GetNameSpaceID();
 
-  ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
-  if (shadowRoot) {
+  if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
     // Fix up the context to be the host of the ShadowRoot.
     contextLocalName = shadowRoot->GetHost()->NodeInfo()->NameAtom();
     contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
   }
 
   if (doc->IsHTMLDocument()) {
     int32_t oldChildCount = target->GetChildCount();
     aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -95,28 +95,16 @@ ShadowRoot::~ShadowRoot()
 }
 
 JSObject*
 ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
 }
 
-ShadowRoot*
-ShadowRoot::FromNode(nsINode* aNode)
-{
-  if (aNode->IsInShadowTree() && !aNode->GetParentNode()) {
-    MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE,
-               "ShadowRoot is a document fragment.");
-    return static_cast<ShadowRoot*>(aNode);
-  }
-
-  return nullptr;
-}
-
 void
 ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
 {
   MOZ_ASSERT(aSlot);
 
   // Note that if name attribute missing, the slot is a default slot.
  nsAutoString name;
   aSlot->GetName(name);
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_shadowroot_h__
 #define mozilla_dom_shadowroot_h__
 
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DocumentOrShadowRoot.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIContentInlines.h"
 #include "nsIdentifierMapEntry.h"
 #include "nsTHashtable.h"
 
 class nsAtom;
 class nsIContent;
 class nsXBLPrototypeBinding;
 
 namespace mozilla {
@@ -27,16 +26,36 @@ namespace dom {
 
 class Element;
 
 class ShadowRoot final : public DocumentFragment,
                          public DocumentOrShadowRoot,
                          public nsStubMutationObserver
 {
 public:
+  static ShadowRoot* FromNode(nsINode* aNode)
+  {
+    return aNode->IsShadowRoot() ? static_cast<ShadowRoot*>(aNode) : nullptr;
+  }
+
+  static const ShadowRoot* FromNode(const nsINode* aNode)
+  {
+    return aNode->IsShadowRoot() ? static_cast<const ShadowRoot*>(aNode) : nullptr;
+  }
+
+  static ShadowRoot* FromNodeOrNull(nsINode* aNode)
+  {
+    return aNode ? FromNode(aNode) : nullptr;
+  }
+
+  static const ShadowRoot* FromNodeOrNull(const nsINode* aNode)
+  {
+    return aNode ? FromNode(aNode) : nullptr;
+  }
+
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRoot,
                                            DocumentFragment)
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
@@ -108,18 +127,16 @@ public:
   void RemoveSlot(HTMLSlotElement* aSlot);
 
   void SetInsertionPointChanged() { mInsertionPointChanged = true; }
 
   void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  static ShadowRoot* FromNode(nsINode* aNode);
-
   void AddToIdTable(Element* aElement, nsAtom* aId);
   void RemoveFromIdTable(Element* aElement, nsAtom* aId);
 
   // WebIDL methods.
   using mozilla::dom::DocumentOrShadowRoot::GetElementById;
   void GetInnerHTML(nsAString& aInnerHTML);
   void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
   void StyleSheetChanged();
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6537,17 +6537,17 @@ nsIDocument::ImportNode(nsINode& aNode, 
 
   switch (imported->NodeType()) {
     case nsIDOMNode::DOCUMENT_NODE:
     {
       break;
     }
     case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
     {
-      if (ShadowRoot::FromNode(imported)) {
+      if (imported->IsShadowRoot()) {
         break;
       }
       MOZ_FALLTHROUGH;
     }
     case nsIDOMNode::ATTRIBUTE_NODE:
     case nsIDOMNode::ELEMENT_NODE:
     case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
     case nsIDOMNode::TEXT_NODE:
@@ -7705,17 +7705,17 @@ nsIDocument::AdoptNode(nsINode& aAdopted
 
         newAttr.swap(adoptedAttr);
       }
 
       break;
     }
     case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
     {
-      if (ShadowRoot::FromNode(adoptedNode)) {
+      if (adoptedNode->IsShadowRoot()) {
         rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
         return nullptr;
       }
       MOZ_FALLTHROUGH;
     }
     case nsIDOMNode::ELEMENT_NODE:
     case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
     case nsIDOMNode::TEXT_NODE:
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -105,22 +105,19 @@ protected:
 
   bool IsVisibleNode(nsINode* aNode)
   {
     NS_PRECONDITION(aNode, "");
 
     if (mFlags & SkipInvisibleContent) {
       // Treat the visibility of the ShadowRoot as if it were
       // the host content.
-      nsCOMPtr<nsIContent> content;
-      ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode);
-      if (shadowRoot) {
+      nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+      if (ShadowRoot* shadowRoot = ShadowRoot::FromNodeOrNull(content)) {
         content = shadowRoot->GetHost();
-      } else {
-        content = do_QueryInterface(aNode);
       }
 
       if (content) {
         nsIFrame* frame = content->GetPrimaryFrame();
         if (!frame) {
           if (aNode->IsNodeOfType(nsINode::eTEXT)) {
             // We have already checked that our parent is visible.
             return true;
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -529,17 +529,17 @@ public:
 
   virtual nsXBLBinding* DoGetXBLBinding() const = 0;
 
   /**
    * Gets the ShadowRoot binding for this element.
    *
    * @return The ShadowRoot currently bound to this element.
    */
-  inline mozilla::dom::ShadowRoot *GetShadowRoot() const;
+  inline mozilla::dom::ShadowRoot* GetShadowRoot() const;
 
   /**
    * Gets the root of the node tree for this content if it is in a shadow tree.
    * This method is called |GetContainingShadow| instead of |GetRootShadowRoot|
    * to avoid confusion with |GetShadowRoot|.
    *
    * @return The ShadowRoot that is the root of the node tree.
    */
@@ -600,21 +600,27 @@ public:
    * @param aContent The insertion parent element.
    */
   void SetXBLInsertionPoint(nsIContent* aContent);
 
   /**
    * Same as GetFlattenedTreeParentNode, but returns null if the parent is
    * non-nsIContent.
    */
-  inline nsIContent *GetFlattenedTreeParent() const;
+  inline nsIContent* GetFlattenedTreeParent() const;
 
-  // Helper method, which we leave public so that it's accessible from nsINode.
-  enum FlattenedParentType { eNotForStyle, eForStyle };
-  nsINode* GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const;
+  /**
+   * Get the flattened tree parent for NAC holding from the document element,
+   * from the point of view of the style system.
+   *
+   * Document-level anonymous content holds from the document element, even
+   * though they should not be treated as such (they should be parented to the
+   * document instead, and shouldn't inherit from the document element).
+   */
+  nsINode* GetFlattenedTreeParentForDocumentElementNAC() const;
 
   /**
    * API to check if this is a link that's traversed in response to user input
    * (e.g. a click event). Specializations for HTML/SVG/generic XML allow for
    * different types of link in different types of content.
    *
    * @param aURI Required out param. If this content is a link, a new nsIURI
    *             set to this link's URI will be passed out.
@@ -950,22 +956,16 @@ protected:
   }
 
   /**
    * Hook for implementing GetID.  This is guaranteed to only be
    * called if HasID() is true.
    */
   nsAtom* DoGetID() const;
 
-  /**
-   * Returns the assigned slot, if it exists, or the direct parent, if it's a
-   * fallback content of a slot.
-   */
-  nsINode* GetFlattenedTreeParentForMaybeAssignedNode() const;
-
 public:
 #ifdef DEBUG
   /**
    * List the content (and anything it contains) out to the given
    * file stream. Use aIndent as the base indent during formatting.
    */
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
 
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -8,16 +8,18 @@
 #define nsIContentInlines_h
 
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsAtom.h"
 #include "nsIFrame.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLSlotElement.h"
+#include "mozilla/dom/ShadowRoot.h"
 
 inline bool
 nsIContent::IsInHTMLDocument() const
 {
   return OwnerDoc()->IsHTMLDocument();
 }
 
 inline bool
@@ -49,75 +51,91 @@ nsIContent::SetPrimaryFrame(nsIFrame* aF
 }
 
 inline mozilla::dom::ShadowRoot* nsIContent::GetShadowRoot() const
 {
   if (!IsElement()) {
     return nullptr;
   }
 
-  return AsElement()->FastGetShadowRoot();
+  return AsElement()->GetShadowRoot();
 }
 
-template<nsIContent::FlattenedParentType Type>
-static inline bool FlattenedTreeParentIsParent(const nsINode* aNode)
-{
-  // Try to short-circuit past the complicated and not-exactly-fast logic for
-  // computing the flattened parent.
-  //
-  // WARNING! This logic is replicated in Servo to avoid out of line calls.
-  // If you change the cases here, you need to change them in Servo as well!
-
-  // Check if the node is an explicit child of an XBL-bound element, re-bound to
-  // an XBL insertion point, or is a top-level element in a shadow tree, whose
-  // flattened parent is the host element (as opposed to the actual parent which
-  // is the shadow root).
-  if (aNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR | NODE_IS_IN_SHADOW_TREE)) {
-    return false;
-  }
-
-  // Check if we want the flattened parent for style, and the node is the root
-  // of a native anonymous content subtree parented to the document's root
-  // element.
-  if (Type == nsIContent::eForStyle &&
-      aNode->HasFlag(NODE_IS_NATIVE_ANONYMOUS_ROOT) &&
-      aNode->OwnerDoc()->GetRootElement() == aNode->GetParentNode())
-  {
-    return false;
-  }
-
-  // Check if the node is an explicit child of an element with a shadow root or
-  // an element with an XBL binding, which may be re-bound to an insertion
-  // point, or not be bound to any, and thus won't appear on the flattened tree
-  // at all.
-  nsIContent* parent = aNode->GetParent();
-  if (parent && (parent->GetShadowRoot() || parent->GetXBLBinding())) {
-    return false;
-  }
-
-  // Common case.
-  return true;
-}
-
-template<nsIContent::FlattenedParentType Type>
+template<nsINode::FlattenedParentType aType>
 static inline nsINode*
 GetFlattenedTreeParentNode(const nsINode* aNode)
 {
-  if (MOZ_LIKELY(FlattenedTreeParentIsParent<Type>(aNode))) {
-    return aNode->GetParentNode();
+  if (!aNode->IsContent()) {
+    return nullptr;
+  }
+
+  nsINode* parent = aNode->GetParentNode();
+  if (!parent || !parent->IsContent()) {
+    return parent;
+  }
+
+  const nsIContent* content = aNode->AsContent();
+  nsIContent* parentAsContent = parent->AsContent();
+
+  if (aType == nsINode::eForStyle &&
+      content->IsRootOfNativeAnonymousSubtree() &&
+      parentAsContent == content->OwnerDoc()->GetRootElement() &&
+      !content->IsGeneratedContentContainerForAfter() &&
+      !content->IsGeneratedContentContainerForBefore()) {
+    return content->GetFlattenedTreeParentForDocumentElementNAC();
+  }
+
+  if (content->IsRootOfAnonymousSubtree()) {
+    return parent;
+  }
+
+  if (parentAsContent->GetShadowRoot()) {
+    // If it's not assigned to any slot it's not part of the flat tree, and thus
+    // we return null.
+    return content->GetAssignedSlot();
   }
 
-  MOZ_ASSERT(aNode->IsContent());
-  return aNode->AsContent()->GetFlattenedTreeParentNodeInternal(Type);
+  if (parentAsContent->IsInShadowTree()) {
+    if (auto* slot = mozilla::dom::HTMLSlotElement::FromContent(parentAsContent)) {
+      // If the assigned nodes list is empty, we're fallback content which is
+      // active, otherwise we are not part of the flat tree.
+      return slot->AssignedNodes().IsEmpty()
+        ? parent
+        : nullptr;
+    }
+
+    if (auto* shadowRoot = mozilla::dom::ShadowRoot::FromNode(parentAsContent)) {
+      return shadowRoot->GetHost();
+    }
+  }
+
+  if (content->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
+      parent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
+    if (nsIContent* xblInsertionPoint = content->GetXBLInsertionPoint()) {
+      return xblInsertionPoint->GetParent();
+    }
+
+    if (parent->OwnerDoc()->BindingManager()->GetBindingWithContent(parentAsContent)) {
+      // This is an unassigned node child of the bound element, so it isn't part
+      // of the flat tree.
+      return nullptr;
+    }
+  }
+
+  MOZ_ASSERT(!parentAsContent->IsActiveChildrenElement(),
+             "<xbl:children> isn't in the flattened tree");
+
+  // Common case.
+  return parent;
 }
 
 inline nsINode*
 nsINode::GetFlattenedTreeParentNode() const
 {
-  return ::GetFlattenedTreeParentNode<nsIContent::eNotForStyle>(this);
+  return ::GetFlattenedTreeParentNode<nsINode::eNotForStyle>(this);
 }
 
 inline nsIContent*
 nsIContent::GetFlattenedTreeParent() const
 {
   nsINode* parent = GetFlattenedTreeParentNode();
   return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
 }
@@ -131,17 +149,17 @@ nsIContent::IsEventAttributeName(nsAtom*
   }
 
   return IsEventAttributeNameInternal(aName);
 }
 
 inline nsINode*
 nsINode::GetFlattenedTreeParentNodeForStyle() const
 {
-  return ::GetFlattenedTreeParentNode<nsIContent::eForStyle>(this);
+  return ::GetFlattenedTreeParentNode<nsINode::eForStyle>(this);
 }
 
 inline bool
 nsINode::NodeOrAncestorHasDirAuto() const
 {
   return AncestorHasDirAuto() || (IsElement() && AsElement()->HasDirAuto());
 }
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -255,24 +255,23 @@ nsINode::GetTextEditorRootContent(TextEd
 nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
 {
   if (aOptions.mComposed) {
     if (IsInComposedDoc() && GetComposedDoc()) {
       return OwnerDoc();
     }
 
     nsINode* node = this;
-    ShadowRoot* shadowRootParent = nullptr;
     while(node) {
       node = node->SubtreeRoot();
-      shadowRootParent = ShadowRoot::FromNode(node);
-      if (!shadowRootParent) {
-         break;
+      ShadowRoot* shadow = ShadowRoot::FromNode(node);
+      if (!shadow) {
+        break;
       }
-      node = shadowRootParent->GetHost();
+      node = shadow->GetHost();
     }
 
     return node;
   }
 
   return SubtreeRoot();
 }
 
@@ -390,18 +389,17 @@ nsINode::GetSelectionRootContent(nsIPres
 
   // This node might be in another subtree, if so, we should find this subtree's
   // root.  Otherwise, we can return the content simply.
   NS_ENSURE_TRUE(content, nullptr);
   if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
     content = GetRootForContentSubtree(static_cast<nsIContent*>(this));
     // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
     // Use the host as the root.
-    ShadowRoot* shadowRoot = ShadowRoot::FromNode(content);
-    if (shadowRoot) {
+    if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) {
       content = shadowRoot->GetHost();
     }
   }
 
   return content;
 }
 
 nsINodeList*
@@ -3057,22 +3055,19 @@ nsINode::GetParentElementCrossingShadowR
   if (!mParent) {
     return nullptr;
   }
 
   if (mParent->IsElement()) {
     return mParent->AsElement();
   }
 
-  ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent);
-  if (shadowRoot) {
-    nsIContent* host = shadowRoot->GetHost();
-    MOZ_ASSERT(host, "ShowRoots should always have a host");
-    MOZ_ASSERT(host->IsElement(), "ShadowRoot hosts should always be Elements");
-    return host->AsElement();
+  if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent)) {
+    MOZ_ASSERT(shadowRoot->GetHost(), "ShowRoots should always have a host");
+    return shadowRoot->GetHost();
   }
 
   return nullptr;
 }
 
 bool
 nsINode::HasBoxQuadsSupport(JSContext* aCx, JSObject* /* unused */)
 {
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -700,16 +700,24 @@ public:
   }
 
   template<typename First, typename... Args>
   inline bool IsAnyOfMathMLElements(First aFirst, Args... aArgs) const
   {
     return IsMathMLElement() && IsNodeInternal(aFirst, aArgs...);
   }
 
+  bool IsShadowRoot() const
+  {
+    const bool isShadowRoot = IsInShadowTree() && !GetParentNode();
+    MOZ_ASSERT_IF(isShadowRoot,
+                  NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE);
+    return isShadowRoot;
+  }
+
   /**
    * Insert a content node at a particular index.  This method handles calling
    * BindToTree on the child appropriately.
    *
    * @param aKid the content to insert
    * @param aIndex the index it is being inserted at (the index it will have
    *        after it is inserted)
    * @param aNotify whether to notify the document (current document for
@@ -937,31 +945,33 @@ public:
    * an nsIDocument or an nsIAttribute.
    * @return the parent node
    */
   nsINode* GetParentNode() const
   {
     return mParent;
   }
 
+  enum FlattenedParentType { eNotForStyle, eForStyle };
+
   /**
    * Returns the node that is the parent of this node in the flattened
    * tree. This differs from the normal parent if the node is filtered
    * into an insertion point, or if the node is a direct child of a
    * shadow root.
    *
    * @return the flattened tree parent
    */
   inline nsINode* GetFlattenedTreeParentNode() const;
 
   /**
-   * Like GetFlattenedTreeParentNode, but returns null for any native
-   * anonymous content that was generated for ancestor frames of the
-   * root element's primary frame, such as scrollbar elements created
-   * by the root scroll frame.
+   * Like GetFlattenedTreeParentNode, but returns the document for any native
+   * anonymous content that was generated for ancestor frames of the document
+   * element's primary frame, such as scrollbar elements created by the root
+   * scroll frame.
    */
   inline nsINode* GetFlattenedTreeParentNodeForStyle() const;
 
   inline mozilla::dom::Element* GetFlattenedTreeParentElement() const;
   inline mozilla::dom::Element* GetFlattenedTreeParentElementForStyle() const;
 
   /**
    * Get the parent nsINode for this node if it is an Element.
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsNodeUtils.h"
 #include "nsContentUtils.h"
 #include "nsCSSPseudoElements.h"
 #include "nsINode.h"
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "mozilla/dom/Element.h"
 #include "nsIMutationObserver.h"
 #include "nsIDocument.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsIXPConnect.h"
 #include "PLDHashTable.h"
 #include "nsIDOMAttr.h"
 #include "nsCOMArray.h"
@@ -112,18 +113,17 @@ enum class IsRemoveNotification
   nsINode* node = content_;                                       \
   do {                                                            \
     nsINode::nsSlots* slots = node->GetExistingSlots();           \
     if (slots && !slots->mMutationObservers.IsEmpty()) {          \
       NS_OBSERVER_AUTO_ARRAY_NOTIFY_OBSERVERS_WITH_QI(            \
         slots->mMutationObservers, nsIMutationObserver, 1,        \
         nsIAnimationObserver, func_, params_);                    \
     }                                                             \
-    ShadowRoot* shadow = ShadowRoot::FromNode(node);              \
-    if (shadow) {                                                 \
+    if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {        \
       node = shadow->GetHost();                                   \
     } else {                                                      \
       node = node->GetParentNode();                               \
     }                                                             \
   } while (node);                                                 \
   if (needsEnterLeave) {                                          \
     nsDOMMutationObserver::LeaveMutationHandling();               \
   }                                                               \
--- a/dom/html/HTMLLabelElement.cpp
+++ b/dom/html/HTMLLabelElement.cpp
@@ -7,16 +7,17 @@
 /**
  * Implementation of HTML <label> elements.
  */
 #include "HTMLLabelElement.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/dom/HTMLLabelElementBinding.h"
 #include "nsFocusManager.h"
+#include "nsContentUtils.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsQueryObject.h"
 #include "mozilla/dom/ShadowRoot.h"
 
 // construction, destruction
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Label)
 
@@ -232,29 +233,24 @@ HTMLLabelElement::GetLabeledElement() co
   if (!GetAttr(kNameSpaceID_None, nsGkAtoms::_for, elementId)) {
     // No @for, so we are a label for our first form control element.
     // Do a depth-first traversal to look for the first form control element.
     return GetFirstLabelableDescendant();
   }
 
   // We have a @for. The id has to be linked to an element in the same tree
   // and this element should be a labelable form control.
-  nsINode* root = SubtreeRoot();
-  ShadowRoot* shadow = ShadowRoot::FromNode(root);
   Element* element = nullptr;
 
-  if (shadow) {
-    element = shadow->GetElementById(elementId);
+  if (ShadowRoot* shadowRoot = GetContainingShadow()) {
+    element = shadowRoot->GetElementById(elementId);
+  } else if (nsIDocument* doc = GetUncomposedDoc()) {
+    element = doc->GetElementById(elementId);
   } else {
-    nsIDocument* doc = GetUncomposedDoc();
-    if (doc) {
-      element = doc->GetElementById(elementId);
-    } else {
-      element = nsContentUtils::MatchElementId(root->AsContent(), elementId);
-    }
+    element = nsContentUtils::MatchElementId(SubtreeRoot()->AsContent(), elementId);
   }
 
   if (element && element->IsLabelable()) {
     return static_cast<nsGenericHTMLElement*>(element);
   }
 
   return nullptr;
 }
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7985,30 +7985,29 @@ nsCSSFrameConstructor::ContentRangeInser
     return;
   }
 
   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() ||
-        ShadowRoot::FromNode(aContainer))) {
+      !(aContainer->IsActiveChildrenElement() || aContainer->IsShadowRoot())) {
     // We're punting on frame construction because there's no container frame.
     // The Servo-backed style system handles this case like the lazy frame
     // construction case, except when we're already constructing frames, in
     // which case we shouldn't need to do anything else.
     if (aContainer->IsStyledByServo() &&
         aInsertionKind == InsertionKind::Async) {
       LazilyStyleNewChildRange(aStartChild, aEndChild);
     }
     return;
   }
 
-  MOZ_ASSERT_IF(ShadowRoot::FromNode(aContainer), !parentFrame);
+  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!");
 
   if (aInsertionKind == InsertionKind::Async &&
       MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
     if (aContainer->IsStyledByServo()) {
@@ -9325,22 +9324,24 @@ nsCSSFrameConstructor::GetInsertionPoint
     // anonymous.
     if (aChild->GetBindingParent() == aContainer) {
       // This child content is anonymous. Don't use the insertion
       // point, since that's only for the explicit kids.
       return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
     }
 
     if (nsContentUtils::HasDistributedChildren(aContainer) ||
-        ShadowRoot::FromNode(aContainer)) {
+        aContainer->IsShadowRoot()) {
       // The container distributes nodes or is a shadow root, 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.
+      //
+      // FIXME(emilio): We should be able to use this path all the time.
       nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
       if (flattenedParent) {
         return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
                               flattenedParent);
       }
       return InsertionPoint();
     }
 
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -19,16 +19,17 @@
 #include "nsIFormControl.h"
 #include "nsNameSpaceManager.h"
 #include "nsIListControlFrame.h"
 #include "nsPIDOMWindow.h"
 #include "nsIPresShell.h"
 #include "nsPresState.h"
 #include "nsView.h"
 #include "nsViewManager.h"
+#include "nsIContentInlines.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMNode.h"
 #include "nsISelectControlFrame.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/HTMLSelectElement.h"
 #include "nsIDocument.h"
 #include "nsIScrollableFrame.h"
 #include "nsListControlFrame.h"
--- a/layout/generic/nsFrameSelection.cpp
+++ b/layout/generic/nsFrameSelection.cpp
@@ -1696,20 +1696,18 @@ nsFrameSelection::GetFrameForNodeOffset(
           }
         }
       }
     }
 
     // If the node is a ShadowRoot, the frame needs to be adjusted,
     // because a ShadowRoot does not get a frame. Its children are rendered
     // as children of the host.
-    mozilla::dom::ShadowRoot* shadowRoot =
-      mozilla::dom::ShadowRoot::FromNode(theNode);
-    if (shadowRoot) {
-      theNode = shadowRoot->GetHost();
+    if (ShadowRoot* shadow = ShadowRoot::FromNode(theNode)) {
+      theNode = shadow->GetHost();
     }
 
     returnFrame = theNode->GetPrimaryFrame();
     if (!returnFrame) {
       if (aHint == CARET_ASSOCIATE_BEFORE) {
         if (aOffset > 0) {
           --aOffset;
           continue;
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/layout/RenderFrameParent.h"
 
 #include "nsCOMPtr.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsIDocShell.h"
 #include "nsIContentViewer.h"
+#include "nsIContentInlines.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsIDocument.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
 #include "nsFrameSetFrame.h"
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -148,26 +148,16 @@ Gecko_RecordTraversalStatistics(uint32_t
 }
 
 bool
 Gecko_IsInDocument(RawGeckoNodeBorrowed aNode)
 {
   return aNode->IsInComposedDoc();
 }
 
-#ifdef MOZ_DEBUG_RUST
-bool
-Gecko_FlattenedTreeParentIsParent(RawGeckoNodeBorrowed aNode)
-{
-  // Servo calls this in debug builds to verify the result of its own
-  // flattened_tree_parent_is_parent() function.
-  return FlattenedTreeParentIsParent<nsIContent::eForStyle>(aNode);
-}
-#endif
-
 /*
  * Does this child count as significant for selector matching?
  *
  * See nsStyleUtil::IsSignificantChild for details.
  */
 bool
 Gecko_IsSignificantChild(RawGeckoNodeBorrowed aNode, bool aTextIsSignificant,
                          bool aWhitespaceIsSignificant)
@@ -181,21 +171,17 @@ RawGeckoNodeBorrowedOrNull
 Gecko_GetLastChild(RawGeckoNodeBorrowed aNode)
 {
   return aNode->GetLastChild();
 }
 
 RawGeckoNodeBorrowedOrNull
 Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed aNode)
 {
-  MOZ_ASSERT(!FlattenedTreeParentIsParent<nsIContent::eForStyle>(aNode),
-             "Should have taken the inline path");
-  MOZ_ASSERT(aNode->IsContent(), "Slow path only applies to content");
-  const nsIContent* c = aNode->AsContent();
-  return c->GetFlattenedTreeParentNodeInternal(nsIContent::eForStyle);
+  return aNode->GetFlattenedTreeParentNodeForStyle();
 }
 
 RawGeckoElementBorrowedOrNull
 Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed aElement, bool aIsBefore)
 {
   MOZ_ASSERT(aElement);
   MOZ_ASSERT(aElement->HasProperties());
 
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -147,17 +147,16 @@ struct FontSizePrefs
   nscoord mDefaultFantasySize;
 };
 
 // DOM Traversal.
 void Gecko_RecordTraversalStatistics(uint32_t total, uint32_t parallel,
                                      uint32_t total_t, uint32_t parallel_t,
                                      uint32_t total_s, uint32_t parallel_s);
 bool Gecko_IsInDocument(RawGeckoNodeBorrowed node);
-bool Gecko_FlattenedTreeParentIsParent(RawGeckoNodeBorrowed node);
 bool Gecko_IsSignificantChild(RawGeckoNodeBorrowed node,
                               bool text_is_significant,
                               bool whitespace_is_significant);
 RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed node);
 RawGeckoNodeBorrowedOrNull Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed node);
 RawGeckoElementBorrowedOrNull Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed element, bool is_before);
 nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(RawGeckoElementBorrowed element);
 void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* anon_content);