Bug 1427001: Stop duplicating slots. r?smaug draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sat, 23 Dec 2017 09:23:42 +0100
changeset 714779 cb842b53f36e8fcb7b66b4d941ba16b2bd1f9f0f
parent 714778 8b91d15bf5d0cf25ea961149c511b56d65e27b05
child 714780 ca2cfa730f8ead1c0462ec9d6f8ea0aee7ad6383
push id94027
push userbmo:emilio@crisal.io
push dateFri, 29 Dec 2017 16:26:14 +0000
reviewerssmaug
bugs1427001
milestone59.0a1
Bug 1427001: Stop duplicating slots. r?smaug MozReview-Commit-ID: Cq647BcOzbe
dom/base/Element.h
dom/base/FragmentOrElement.cpp
dom/base/FragmentOrElement.h
dom/base/nsGenericDOMDataNode.cpp
dom/base/nsGenericDOMDataNode.h
dom/base/nsIContent.h
dom/base/nsINode.h
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
servo/components/style/gecko/wrapper.rs
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -547,23 +547,21 @@ public:
   /**
    * Gets the custom element data used by web components custom element.
    * Custom element data is created at the first attempt to enqueue a callback.
    *
    * @return The custom element data or null if none.
    */
   inline CustomElementData* GetCustomElementData() const
   {
-    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
-    if (slots) {
-      return slots->mCustomElementData;
-    }
-    return nullptr;
+    const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
+    return slots ? slots->mCustomElementData.get() : nullptr;
   }
 
+
   /**
    * Sets the custom element data, ownership of the
    * callback data is taken by this element.
    *
    * @param aData The custom element data.
    */
   void SetCustomElementData(CustomElementData* aData);
 
@@ -1290,19 +1288,21 @@ 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);
 
-  ShadowRoot *FastGetShadowRoot() const
+  // FIXME(emilio): Should just shadow GetShadowRoot(), that way we get the fast
+  // version by default everywhere we already have an Element...
+  ShadowRoot* FastGetShadowRoot() const
   {
-    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
+    const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     return slots ? slots->mShadowRoot.get() : nullptr;
   }
 
 private:
   void ScrollIntoView(const ScrollIntoViewOptions &aOptions);
 public:
   void ScrollIntoView(const BooleanOrScrollIntoViewOptions& aObject);
   MOZ_CAN_RUN_SCRIPT void Scroll(double aXScroll, double aYScroll);
@@ -1476,19 +1476,19 @@ public:
     return mAttrsAndChildren.GetAttr(aAttr, aNameSpaceID);
   }
 
   /**
    * Returns the attribute map, if there is one.
    *
    * @return existing attribute map or nullptr.
    */
-  nsDOMAttributeMap *GetAttributeMap()
+  nsDOMAttributeMap* GetAttributeMap()
   {
-    nsDOMSlots *slots = GetExistingDOMSlots();
+    nsDOMSlots* slots = GetExistingDOMSlots();
 
     return slots ? slots->mAttributeMap.get() : nullptr;
   }
 
   virtual void RecompileScriptEventListeners()
   {
   }
 
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -705,17 +705,17 @@ nsParentNodeChildContentList::ValidateCa
   return true;
 }
 
 //----------------------------------------------------------------------
 
 nsIHTMLCollection*
 FragmentOrElement::Children()
 {
-  FragmentOrElement::nsDOMSlots *slots = DOMSlots();
+  nsDOMSlots* slots = DOMSlots();
 
   if (!slots->mChildrenList) {
     slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard,
                                              nsGkAtoms::_asterisk, nsGkAtoms::_asterisk,
                                              false);
   }
 
   return slots->mChildrenList;
@@ -783,114 +783,86 @@ 128;
 64;
 #endif
 
 static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed,
               "DOM slots cannot be grown without consideration");
 static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed,
               "DOM slots cannot be grown without consideration");
 
+void
+nsIContent::nsExtendedContentSlots::Unlink()
+{
+  mXBLInsertionPoint = nullptr;
+  mContainingShadow = nullptr;
+  mAssignedSlot = nullptr;
+}
+
+void
+nsIContent::nsExtendedContentSlots::Traverse(nsCycleCollectionTraversalCallback& aCb)
+{
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mContainingShadow");
+  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mAssignedSlot");
+  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get()));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mXBLInsertionPoint");
+  aCb.NoteXPCOMChild(mXBLInsertionPoint.get());
+}
+
+nsIContent::nsExtendedContentSlots::nsExtendedContentSlots()
+  : mBindingParent(nullptr)
+{
+}
+
+nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() = default;
+
 FragmentOrElement::nsDOMSlots::nsDOMSlots()
-  : nsINode::nsSlots(),
+  : nsIContent::nsContentSlots(),
     mDataset(nullptr)
 {
 }
 
 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
 {
   if (mAttributeMap) {
     mAttributeMap->DropReference();
   }
 }
 
 void
-FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
+FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback& aCb)
 {
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
-  cb.NoteXPCOMChild(mStyle.get());
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
-  cb.NoteXPCOMChild(mAttributeMap.get());
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
+  nsIContent::nsContentSlots::Traverse(aCb);
 
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
-  cb.NoteXPCOMChild(mClassList.get());
-
-  if (!mExtendedSlots) {
-    return;
-  }
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mSMILOverrideStyle");
-  cb.NoteXPCOMChild(mExtendedSlots->mSMILOverrideStyle.get());
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mControllers");
-  cb.NoteXPCOMChild(mExtendedSlots->mControllers);
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mStyle");
+  aCb.NoteXPCOMChild(mStyle.get());
 
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mLabelsList");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*,mExtendedSlots-> mLabelsList));
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mShadowRoot");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mExtendedSlots->mShadowRoot));
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mContainingShadow");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mExtendedSlots->mContainingShadow));
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mAssignedSlot");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mExtendedSlots->mAssignedSlot.get()));
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeMap");
+  aCb.NoteXPCOMChild(mAttributeMap.get());
 
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mXBLBinding");
-  cb.NoteNativeChild(mExtendedSlots->mXBLBinding,
-                     NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mXBLInsertionPoint");
-  cb.NoteXPCOMChild(mExtendedSlots->mXBLInsertionPoint.get());
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mChildrenList");
+  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
 
-  if (mExtendedSlots->mCustomElementData) {
-    mExtendedSlots->mCustomElementData->Traverse(cb);
-  }
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mFrameLoaderOrOpener");
-  cb.NoteXPCOMChild(mExtendedSlots->mFrameLoaderOrOpener);
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mClassList");
+  aCb.NoteXPCOMChild(mClassList.get());
 }
 
 void
 FragmentOrElement::nsDOMSlots::Unlink()
 {
+  nsIContent::nsContentSlots::Unlink();
   mStyle = nullptr;
   if (mAttributeMap) {
     mAttributeMap->DropReference();
     mAttributeMap = nullptr;
   }
   mChildrenList = nullptr;
   mClassList = nullptr;
-
-  if (!mExtendedSlots) {
-    return;
-  }
-
-  mExtendedSlots->mSMILOverrideStyle = nullptr;
-  mExtendedSlots->mControllers = nullptr;
-  mExtendedSlots->mLabelsList = nullptr;
-  mExtendedSlots->mShadowRoot = nullptr;
-  mExtendedSlots->mContainingShadow = nullptr;
-  mExtendedSlots->mAssignedSlot = nullptr;
-  MOZ_ASSERT(!(mExtendedSlots->mXBLBinding));
-  mExtendedSlots->mXBLInsertionPoint = nullptr;
-  if (mExtendedSlots->mCustomElementData) {
-    mExtendedSlots->mCustomElementData->Unlink();
-    mExtendedSlots->mCustomElementData = nullptr;
-  }
-  nsCOMPtr<nsIFrameLoader> frameLoader =
-    do_QueryInterface(mExtendedSlots->mFrameLoaderOrOpener);
-  if (frameLoader) {
-    static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
-  }
-  mExtendedSlots->mFrameLoaderOrOpener = nullptr;
 }
 
 size_t
 FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
   if (mExtendedSlots) {
     n += aMallocSizeOf(mExtendedSlots.get());
@@ -910,29 +882,78 @@ FragmentOrElement::nsDOMSlots::SizeOfInc
   // - mChildrenList
   // - mClassList
 
   // The following members are not measured:
   // - mBindingParent / mControllers: because they're   non-owning
   return n;
 }
 
-FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots()
-  : mBindingParent(nullptr)
-{
-}
+FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default;
 
 FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots()
 {
   nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(mFrameLoaderOrOpener);
   if (frameLoader) {
     static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
   }
 }
 
+void
+FragmentOrElement::nsExtendedDOMSlots::Unlink()
+{
+  nsIContent::nsExtendedContentSlots::Unlink();
+
+  // Don't clear mXBLBinding, it'll be done in
+  // BindingManager::RemovedFromDocument from FragmentOrElement::Unlink.
+  mSMILOverrideStyle = nullptr;
+  mControllers = nullptr;
+  mLabelsList = nullptr;
+  mShadowRoot = nullptr;
+  if (mCustomElementData) {
+    mCustomElementData->Unlink();
+    mCustomElementData = nullptr;
+  }
+  nsCOMPtr<nsIFrameLoader> frameLoader =
+    do_QueryInterface(mFrameLoaderOrOpener);
+  if (frameLoader) {
+    static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
+  }
+  mFrameLoaderOrOpener = nullptr;
+}
+
+void
+FragmentOrElement::nsExtendedDOMSlots::Traverse(nsCycleCollectionTraversalCallback& aCb)
+{
+  nsIContent::nsExtendedContentSlots::Traverse(aCb);
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mSMILOverrideStyle");
+  aCb.NoteXPCOMChild(mSMILOverrideStyle.get());
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mControllers");
+  aCb.NoteXPCOMChild(mControllers);
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mLabelsList");
+  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mLabelsList));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mShadowRoot");
+  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mXBLBinding");
+  aCb.NoteNativeChild(mXBLBinding,
+                     NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
+
+  if (mCustomElementData) {
+    mCustomElementData->Traverse(aCb);
+  }
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mFrameLoaderOrOpener");
+  aCb.NoteXPCOMChild(mFrameLoaderOrOpener);
+}
+
 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsIContent(aNodeInfo)
 {
 }
 
 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
   : nsIContent(aNodeInfo)
 {
@@ -1157,35 +1178,22 @@ nsIContent::IsFocusableInternal(int32_t*
 
 bool
 FragmentOrElement::IsLink(nsIURI** aURI) const
 {
   *aURI = nullptr;
   return false;
 }
 
-nsIContent*
-FragmentOrElement::GetBindingParent() const
-{
-  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
-
-  if (slots) {
-    return slots->mBindingParent;
-  }
-  return nullptr;
-}
-
 nsXBLBinding*
 FragmentOrElement::DoGetXBLBinding() const
 {
   MOZ_ASSERT(HasFlag(NODE_MAY_BE_IN_BINDING_MNGR));
-  if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
-    return slots->mXBLBinding;
-  }
-  return nullptr;
+  const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
+  return slots ? slots->mXBLBinding.get() : nullptr;
 }
 
 void
 FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
                                  nsBindingManager* aOldBindingManager)
 {
   nsBindingManager* bindingManager;
   if (aOldBindingManager) {
@@ -1219,71 +1227,40 @@ FragmentOrElement::SetXBLBinding(nsXBLBi
     }
     bindingManager->RemoveBoundContent(this);
     if (oldBinding) {
       oldBinding->SetBoundElement(nullptr);
     }
   }
 }
 
-nsIContent*
-FragmentOrElement::GetXBLInsertionPoint() const
-{
-  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
-    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
-    if (slots) {
-      return slots->mXBLInsertionPoint;
-    }
-  }
-
-  return nullptr;
-}
-
-ShadowRoot*
-FragmentOrElement::GetContainingShadow() const
-{
-  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
-  if (slots) {
-    return slots->mContainingShadow;
-  }
-  return nullptr;
-}
-
 void
 FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
 {
   nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   slots->mShadowRoot = aShadowRoot;
 }
 
-HTMLSlotElement*
-FragmentOrElement::GetAssignedSlot() const
+void
+nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot)
 {
-  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
-  return slots ? slots->mAssignedSlot.get() : nullptr;
+  ExtendedContentSlots()->mAssignedSlot = aSlot;
 }
 
 void
-FragmentOrElement::SetAssignedSlot(HTMLSlotElement* aSlot)
-{
-  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
-  slots->mAssignedSlot = aSlot;
-}
-
-void
-FragmentOrElement::SetXBLInsertionPoint(nsIContent* aContent)
+nsIContent::SetXBLInsertionPoint(nsIContent* aContent)
 {
   nsCOMPtr<nsIContent> oldInsertionPoint = nullptr;
   if (aContent) {
-    nsExtendedDOMSlots* slots = ExtendedDOMSlots();
+    nsExtendedContentSlots* slots = ExtendedContentSlots();
     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
     oldInsertionPoint = slots->mXBLInsertionPoint.forget();
     slots->mXBLInsertionPoint = aContent;
   } else {
-    if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
+    if (nsExtendedContentSlots* slots = GetExistingExtendedContentSlots()) {
       oldInsertionPoint = slots->mXBLInsertionPoint.forget();
       slots->mXBLInsertionPoint = nullptr;
     }
   }
 
   // We just changed the flattened tree, so any Servo style data is now invalid.
   // We rely on nsXBLService::LoadBindings to re-traverse the subtree afterwards.
   if (oldInsertionPoint != aContent &&
@@ -1565,25 +1542,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Fr
 
   // Clear flag here because unlinking slots will clear the
   // containing shadow root pointer.
   tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
   nsIDocument* doc = tmp->OwnerDoc();
   doc->BindingManager()->RemovedFromDocument(tmp, doc,
                                              nsBindingManager::eDoNotRunDtor);
-
-  // Unlink any DOM slots of interest.
-  {
-    nsDOMSlots *slots = tmp->GetExistingDOMSlots();
-    if (slots) {
-      slots->Unlink();
-    }
-  }
-
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
 
 void
 FragmentOrElement::MarkUserData(void* aObject, nsAtom* aKey, void* aChild,
                                void* aData)
 {
@@ -2143,24 +2111,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     }
 
     uint32_t kids = tmp->mAttrsAndChildren.ChildCount();
     for (i = 0; i < kids; i++) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
       cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
     }
   }
-
-  // Traverse any DOM slots of interest.
-  {
-    nsDOMSlots *slots = tmp->GetExistingDOMSlots();
-    if (slots) {
-      slots->Traverse(cb);
-    }
-  }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
   NS_INTERFACE_MAP_ENTRY(Element)
   NS_INTERFACE_MAP_ENTRY(nsIContent)
@@ -2496,22 +2456,16 @@ FragmentOrElement::SetInnerHTMLInternal(
       nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
       static_cast<nsINode*>(target)->AppendChild(*df, aError);
       mb.NodesAdded();
     }
   }
 }
 
-nsINode::nsSlots*
-FragmentOrElement::CreateSlots()
-{
-  return new nsDOMSlots();
-}
-
 void
 FragmentOrElement::FireNodeRemovedForChildren()
 {
   nsIDocument* doc = OwnerDoc();
   // Optimize the common case
   if (!nsContentUtils::
         HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
     return;
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -143,26 +143,20 @@ public:
   virtual nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
                               bool aNotify) override;
   virtual bool TextIsOnlyWhitespace() override;
   virtual bool ThreadSafeTextIsOnlyWhitespace() const override;
   virtual bool HasTextForTranslation() override;
   virtual void AppendTextTo(nsAString& aResult) override;
   MOZ_MUST_USE
   virtual bool AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) override;
-  virtual nsIContent* GetBindingParent() const override;
   virtual nsXBLBinding* DoGetXBLBinding() const override;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) override;
-  virtual ShadowRoot *GetContainingShadow() const override;
   virtual void SetShadowRoot(ShadowRoot* aBinding) override;
-  virtual mozilla::dom::HTMLSlotElement* GetAssignedSlot() const override;
-  virtual void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot) override;
-  virtual nsIContent *GetXBLInsertionPoint() const override;
-  virtual void SetXBLInsertionPoint(nsIContent* aContent) override;
   virtual bool IsLink(nsIURI** aURI) const override;
 
   virtual void DestroyContent() override;
   virtual void SaveSubtreeState() override;
 
   nsIHTMLCollection* Children();
   uint32_t ChildElementCount()
   {
@@ -241,95 +235,76 @@ public:
    * There are a set of DOM- and scripting-specific instance variables
    * that may only be instantiated when a content object is accessed
    * through the DOM. Rather than burn actual slots in the content
    * objects for each of these instance variables, we put them off
    * in a side structure that's only allocated when the content is
    * accessed through the DOM.
    */
 
-  class nsExtendedDOMSlots
+  class nsExtendedDOMSlots final : public nsIContent::nsExtendedContentSlots
   {
   public:
     nsExtendedDOMSlots();
+    ~nsExtendedDOMSlots() final;
 
-    ~nsExtendedDOMSlots();
+    void Traverse(nsCycleCollectionTraversalCallback&) final;
+    void Unlink() final;
 
     /**
      * SMIL Overridde style rules (for SMIL animation of CSS properties)
      * @see Element::GetSMILOverrideStyle
      */
     nsCOMPtr<nsICSSDeclaration> mSMILOverrideStyle;
 
     /**
      * Holds any SMIL override style declaration for this element.
      */
     RefPtr<mozilla::DeclarationBlock> mSMILOverrideStyleDeclaration;
 
     /**
-    * The nearest enclosing content node with a binding that created us.
-    * @see FragmentOrElement::GetBindingParent
-    */
-    nsIContent* mBindingParent;  // [Weak]
-
-    /**
     * The controllers of the XUL Element.
     */
     nsCOMPtr<nsIControllers> mControllers;
 
     /**
      * An object implementing the .labels property for this element.
      */
     RefPtr<nsLabelsNodeList> mLabelsList;
 
     /**
      * ShadowRoot bound to the element.
      */
     RefPtr<ShadowRoot> mShadowRoot;
 
     /**
-     * The root ShadowRoot of this element if it is in a shadow tree.
-     */
-    RefPtr<ShadowRoot> mContainingShadow;
-
-    /**
-     * The assigned slot associated with this element.
-     */
-    RefPtr<mozilla::dom::HTMLSlotElement> mAssignedSlot;
-
-    /**
      * XBL binding installed on the element.
      */
     RefPtr<nsXBLBinding> mXBLBinding;
 
     /**
-     * XBL binding insertion point.
-     */
-    nsCOMPtr<nsIContent> mXBLInsertionPoint;
-
-    /**
      * Web components custom element data.
      */
     RefPtr<CustomElementData> mCustomElementData;
 
     /**
      * For XUL to hold either frameloader or opener.
      */
     nsCOMPtr<nsISupports> mFrameLoaderOrOpener;
 
   };
 
-  class nsDOMSlots : public nsINode::nsSlots
+  class nsDOMSlots final : public nsIContent::nsContentSlots
   {
   public:
     nsDOMSlots();
-    virtual ~nsDOMSlots();
+    ~nsDOMSlots() final;
 
-    void Traverse(nsCycleCollectionTraversalCallback &cb);
-    void Unlink();
+    void Traverse(nsCycleCollectionTraversalCallback&) final;
+    void Unlink() final;
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
     /**
      * The .style attribute (an interface that forwards to the actual
      * style rules)
      * @see nsGenericHTMLElement::GetStyle
      */
@@ -351,55 +326,57 @@ public:
      * An object implementing the .children property for this element.
      */
     RefPtr<nsContentList> mChildrenList;
 
     /**
      * An object implementing the .classList property for this element.
      */
     RefPtr<nsDOMTokenList> mClassList;
-
-    mozilla::UniquePtr<nsExtendedDOMSlots> mExtendedSlots;
   };
 
 protected:
   void GetMarkup(bool aIncludeSelf, nsAString& aMarkup);
   void SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError);
 
   // Override from nsINode
-  virtual nsINode::nsSlots* CreateSlots() override;
+  nsIContent::nsContentSlots* CreateSlots() override
+  {
+    return new nsDOMSlots();
+  }
 
-  nsDOMSlots *DOMSlots()
+  nsIContent::nsExtendedContentSlots* CreateExtendedSlots() final
+  {
+    return new nsExtendedDOMSlots();
+  }
+
+  nsDOMSlots* DOMSlots()
   {
     return static_cast<nsDOMSlots*>(Slots());
   }
 
   nsDOMSlots *GetExistingDOMSlots() const
   {
     return static_cast<nsDOMSlots*>(GetExistingSlots());
   }
 
   nsExtendedDOMSlots* ExtendedDOMSlots()
   {
-    nsDOMSlots* slots = DOMSlots();
-    if (!slots->mExtendedSlots) {
-      slots->mExtendedSlots = MakeUnique<nsExtendedDOMSlots>();
-    }
-
-    return slots->mExtendedSlots.get();
+    return static_cast<nsExtendedDOMSlots*>(ExtendedContentSlots());
   }
 
-  nsExtendedDOMSlots* GetExistingExtendedDOMSlots() const
+  const nsExtendedDOMSlots* GetExistingExtendedDOMSlots() const
   {
-    nsDOMSlots* slots = GetExistingDOMSlots();
-    if (slots) {
-      return slots->mExtendedSlots.get();
-    }
+    return static_cast<const nsExtendedDOMSlots*>(
+      GetExistingExtendedContentSlots());
+  }
 
-    return nullptr;
+  nsExtendedDOMSlots* GetExistingExtendedDOMSlots()
+  {
+    return static_cast<nsExtendedDOMSlots*>(GetExistingExtendedContentSlots());
   }
 
   /**
    * Calls SetIsElementInStyleScopeFlagOnSubtree for each shadow tree attached
    * to this node, which is assumed to be an Element.
    *
    * @param aInStyleScope The IsElementInStyleScope flag value to set.
    */
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -94,34 +94,29 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     char name[40];
     SprintfLiteral(name, "nsGenericDOMDataNode (len=%d)",
                    tmp->mText.GetLength());
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   } else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGenericDOMDataNode, tmp->mRefCnt.get())
   }
 
-  if (!nsINode::Traverse(tmp, cb)) {
+  if (!nsIContent::Traverse(tmp, cb)) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
-
-  nsDataSlots *slots = tmp->GetExistingDataSlots();
-  if (slots) {
-    slots->Traverse(cb);
-  }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode)
-  nsINode::Unlink(tmp);
+  nsIContent::Unlink(tmp);
 
   // Clear flag here because unlinking slots will clear the
   // containing shadow root pointer.
   tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
-  nsDataSlots *slots = tmp->GetExistingDataSlots();
+  nsContentSlots* slots = tmp->GetExistingContentSlots();
   if (slots) {
     slots->Unlink();
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsGenericDOMDataNode)
@@ -503,33 +498,33 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
 
   // First set the binding parent
   if (aBindingParent) {
     NS_ASSERTION(IsRootOfNativeAnonymousSubtree() ||
                  !HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) ||
                  (aParent && aParent->IsInNativeAnonymousSubtree()),
                  "Trying to re-bind content from native anonymous subtree to "
                  "non-native anonymous parent!");
-    DataSlots()->mBindingParent = aBindingParent; // Weak, so no addref happens.
+    ExtendedContentSlots()->mBindingParent = aBindingParent; // Weak, so no addref happens.
     if (aParent->IsInNativeAnonymousSubtree()) {
       SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
     }
     if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) {
       SetFlags(NODE_CHROME_ONLY_ACCESS);
     }
     if (HasFlag(NODE_IS_ANONYMOUS_ROOT)) {
       aParent->SetMayHaveAnonymousChildren();
     }
     if (aParent->IsInShadowTree()) {
       ClearSubtreeRootPointer();
       SetFlags(NODE_IS_IN_SHADOW_TREE);
     }
     ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
     if (parentContainingShadow) {
-      DataSlots()->mContainingShadow = parentContainingShadow;
+      ExtendedContentSlots()->mContainingShadow = parentContainingShadow;
     }
   }
 
   bool hadParent = !!GetParentNode();
 
   // Set parent
   if (aParent) {
     if (!GetParent()) {
@@ -615,17 +610,17 @@ nsGenericDOMDataNode::UnbindFromTree(boo
     // do not get uninstalled.
     if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
       nsContentUtils::AddScriptRunner(
         new RemoveFromBindingManagerRunnable(document->BindingManager(), this,
                                              document));
     }
   }
 
-  nsDataSlots *slots = GetExistingDataSlots();
+  nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
   if (slots) {
     slots->mBindingParent = nullptr;
     if (aNullParent || !mParent->IsInShadowTree()) {
       slots->mContainingShadow = nullptr;
     }
   }
 
   nsNodeUtils::ParentChainChanged(this);
@@ -663,90 +658,31 @@ nsGenericDOMDataNode::InsertChildAt(nsIC
   return NS_OK;
 }
 
 void
 nsGenericDOMDataNode::RemoveChildAt(uint32_t aIndex, bool aNotify)
 {
 }
 
-nsIContent *
-nsGenericDOMDataNode::GetBindingParent() const
-{
-  nsDataSlots *slots = GetExistingDataSlots();
-  return slots ? slots->mBindingParent : nullptr;
-}
-
-ShadowRoot *
-nsGenericDOMDataNode::GetContainingShadow() const
-{
-  nsDataSlots *slots = GetExistingDataSlots();
-  if (!slots) {
-    return nullptr;
-  }
-  return slots->mContainingShadow;
-}
-
-void
-nsGenericDOMDataNode::SetShadowRoot(ShadowRoot* aShadowRoot)
-{
-}
-
-HTMLSlotElement*
-nsGenericDOMDataNode::GetAssignedSlot() const
-{
-  nsDataSlots *slots = GetExistingDataSlots();
-  return slots ? slots->mAssignedSlot.get() : nullptr;
-}
-
-void
-nsGenericDOMDataNode::SetAssignedSlot(HTMLSlotElement* aSlot)
-{
-  nsDataSlots *slots = DataSlots();
-  slots->mAssignedSlot = aSlot;
-}
-
 nsXBLBinding *
 nsGenericDOMDataNode::DoGetXBLBinding() const
 {
   return nullptr;
 }
 
 void
 nsGenericDOMDataNode::SetXBLBinding(nsXBLBinding* aBinding,
                                     nsBindingManager* aOldBindingManager)
 {
 }
 
-nsIContent *
-nsGenericDOMDataNode::GetXBLInsertionPoint() const
+void
+nsGenericDOMDataNode::SetShadowRoot(ShadowRoot* aShadowRoot)
 {
-  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
-    nsDataSlots *slots = GetExistingDataSlots();
-    if (slots) {
-      return slots->mXBLInsertionPoint;
-    }
-  }
-
-  return nullptr;
-}
-
-void
-nsGenericDOMDataNode::SetXBLInsertionPoint(nsIContent* aContent)
-{
-  if (aContent) {
-    nsDataSlots *slots = DataSlots();
-    SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
-    slots->mXBLInsertionPoint = aContent;
-  } else {
-    nsDataSlots *slots = GetExistingDataSlots();
-    if (slots) {
-      slots->mXBLInsertionPoint = nullptr;
-    }
-  }
 }
 
 bool
 nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const
 {
   return !(aFlags & ~eDATA_NODE);
 }
 
@@ -770,48 +706,16 @@ nsGenericDOMDataNode::DumpContent(FILE* 
 
 bool
 nsGenericDOMDataNode::IsLink(nsIURI** aURI) const
 {
   *aURI = nullptr;
   return false;
 }
 
-nsINode::nsSlots*
-nsGenericDOMDataNode::CreateSlots()
-{
-  return new nsDataSlots();
-}
-
-nsGenericDOMDataNode::nsDataSlots::nsDataSlots()
-  : nsINode::nsSlots(), mBindingParent(nullptr)
-{
-}
-
-void
-nsGenericDOMDataNode::nsDataSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
-{
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionPoint");
-  cb.NoteXPCOMChild(mXBLInsertionPoint.get());
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAssignedSlot");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get()));
-}
-
-void
-nsGenericDOMDataNode::nsDataSlots::Unlink()
-{
-  mXBLInsertionPoint = nullptr;
-  mContainingShadow = nullptr;
-  mAssignedSlot = nullptr;
-}
-
 //----------------------------------------------------------------------
 
 // Implementation of the nsIDOMText interface
 
 nsresult
 nsGenericDOMDataNode::SplitData(uint32_t aOffset, nsIContent** aReturn,
                                 bool aCloneAfterOriginal)
 {
--- a/dom/base/nsGenericDOMDataNode.h
+++ b/dom/base/nsGenericDOMDataNode.h
@@ -153,26 +153,20 @@ public:
                             const mozilla::fallible_t&) override;
   virtual void SaveSubtreeState() override;
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const override;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override;
 #endif
 
-  virtual nsIContent* GetBindingParent() const override;
   virtual nsXBLBinding* DoGetXBLBinding() const override;
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) override;
-  virtual mozilla::dom::ShadowRoot *GetContainingShadow() const override;
   virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) override;
-  virtual mozilla::dom::HTMLSlotElement* GetAssignedSlot() const override;
-  virtual void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot) override;
-  virtual nsIContent *GetXBLInsertionPoint() const override;
-  virtual void SetXBLInsertionPoint(nsIContent* aContent) override;
   virtual bool IsNodeOfType(uint32_t aFlags) const override;
   virtual bool IsLink(nsIURI** aURI) const override;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override
   {
     nsCOMPtr<nsINode> result = CloneDataNode(aNodeInfo, true);
     result.forget(aResult);
@@ -233,67 +227,16 @@ protected:
 
   virtual mozilla::dom::Element* GetNameSpaceElement() override
   {
     nsINode *parent = GetParentNode();
 
     return parent && parent->IsElement() ? parent->AsElement() : nullptr;
   }
 
-  /**
-   * There are a set of DOM- and scripting-specific instance variables
-   * that may only be instantiated when a content object is accessed
-   * through the DOM. Rather than burn actual slots in the content
-   * objects for each of these instance variables, we put them off
-   * in a side structure that's only allocated when the content is
-   * accessed through the DOM.
-   */
-  class nsDataSlots : public nsINode::nsSlots
-  {
-  public:
-    nsDataSlots();
-
-    void Traverse(nsCycleCollectionTraversalCallback &cb);
-    void Unlink();
-
-    /**
-     * The nearest enclosing content node with a binding that created us.
-     * @see nsIContent::GetBindingParent
-     */
-    nsIContent* mBindingParent;  // [Weak]
-
-    /**
-     * @see nsIContent::GetXBLInsertionPoint
-     */
-    nsCOMPtr<nsIContent> mXBLInsertionPoint;
-
-    /**
-     * @see nsIContent::GetContainingShadow
-     */
-    RefPtr<mozilla::dom::ShadowRoot> mContainingShadow;
-
-    /**
-     * @see nsIContent::GetAssignedSlot
-     */
-    RefPtr<mozilla::dom::HTMLSlotElement> mAssignedSlot;
-  };
-
-  // Override from nsINode
-  virtual nsINode::nsSlots* CreateSlots() override;
-
-  nsDataSlots* DataSlots()
-  {
-    return static_cast<nsDataSlots*>(Slots());
-  }
-
-  nsDataSlots *GetExistingDataSlots() const
-  {
-    return static_cast<nsDataSlots*>(GetExistingSlots());
-  }
-
   nsresult SplitText(uint32_t aOffset, nsIDOMText** aReturn);
 
   nsresult GetWholeText(nsAString& aWholeText);
 
   static int32_t FirstLogicallyAdjacentTextNode(nsIContent* aParent,
                                                 int32_t aIndex);
 
   static int32_t LastLogicallyAdjacentTextNode(nsIContent* aParent,
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -502,17 +502,21 @@ public:
    * frame) responsible for our construction (and existence).  Used by
    * anonymous content (both XBL-generated and native-anonymous).
    *
    * null for all explicit content (i.e., content reachable from the top
    * of its GetParent() chain via child lists).
    *
    * @return the binding parent
    */
-  virtual nsIContent* GetBindingParent() const = 0;
+  virtual nsIContent* GetBindingParent() const
+  {
+    const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
+    return slots ? slots->mBindingParent : nullptr;
+  }
 
   /**
    * Gets the current XBL binding that is bound to this element.
    *
    * @return the current binding.
    */
   nsXBLBinding* GetXBLBinding() const
   {
@@ -541,16 +545,18 @@ public:
   virtual void SetXBLBinding(nsXBLBinding* aBinding,
                              nsBindingManager* aOldBindingManager = nullptr) = 0;
 
   /**
    * Sets the ShadowRoot binding for this element. The contents of the
    * binding is rendered in place of this node's children.
    *
    * @param aShadowRoot The ShadowRoot to be bound to this element.
+   *
+   * FIXME(emilio): No reason this lives in nsIContent, should move to Element.
    */
   virtual void SetShadowRoot(mozilla::dom::ShadowRoot* aShadowRoot) = 0;
 
   /**
    * Gets the ShadowRoot binding for this element.
    *
    * @return The ShadowRoot currently bound to this element.
    */
@@ -558,31 +564,39 @@ public:
 
   /**
    * 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.
    */
-  virtual mozilla::dom::ShadowRoot *GetContainingShadow() const = 0;
+  mozilla::dom::ShadowRoot* GetContainingShadow() const
+  {
+    const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
+    return slots ? slots->mContainingShadow.get() : nullptr;
+  }
 
   /**
    * Gets the assigned slot associated with this content.
    *
    * @return The assigned slot element or null.
    */
-  virtual mozilla::dom::HTMLSlotElement* GetAssignedSlot() const = 0;
+  mozilla::dom::HTMLSlotElement* GetAssignedSlot() const
+  {
+    const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
+    return slots ? slots->mAssignedSlot.get() : nullptr;
+  }
 
   /**
    * Sets the assigned slot associated with this content.
    *
    * @param aSlot The assigned slot.
    */
-  virtual void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot) = 0;
+  void SetAssignedSlot(mozilla::dom::HTMLSlotElement* aSlot);
 
   /**
    * Gets the assigned slot associated with this content based on parent's
    * shadow root mode. Returns null if parent's shadow root is "closed".
    * https://dom.spec.whatwg.org/#dom-slotable-assignedslot
    *
    * @return The assigned slot element or null.
    */
@@ -595,24 +609,28 @@ public:
   }
 
   /**
    * Gets the insertion parent element of the XBL binding.
    * The insertion parent is our one true parent in the transformed DOM.
    *
    * @return the insertion parent element.
    */
-  virtual nsIContent* GetXBLInsertionPoint() const = 0;
+  nsIContent* GetXBLInsertionPoint() const
+  {
+    const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
+    return slots ? slots->mXBLInsertionPoint.get() : nullptr;
+  }
 
   /**
    * Sets the insertion parent element of the XBL binding.
    *
    * @param aContent The insertion parent element.
    */
-  virtual void SetXBLInsertionPoint(nsIContent* aContent) = 0;
+  void SetXBLInsertionPoint(nsIContent* aContent);
 
   /**
    * Same as GetFlattenedTreeParentNode, but returns null if the parent is
    * non-nsIContent.
    */
   inline nsIContent *GetFlattenedTreeParent() const;
 
   // Helper method, which we leave public so that it's accessible from nsINode.
@@ -845,16 +863,124 @@ public:
 
   virtual already_AddRefed<nsITextControlElement> GetAsTextControlElement()
   {
     return nullptr;
   }
 
 protected:
   /**
+   * Lazily allocated extended slots to avoid
+   * that may only be instantiated when a content object is accessed
+   * through the DOM. Rather than burn actual slots in the content
+   * objects for each of these instance variables, we put them off
+   * in a side structure that's only allocated when the content is
+   * accessed through the DOM.
+   */
+  class nsExtendedContentSlots
+  {
+  public:
+    nsExtendedContentSlots();
+    virtual ~nsExtendedContentSlots();
+
+    virtual void Traverse(nsCycleCollectionTraversalCallback&);
+    virtual void Unlink();
+
+    /**
+     * The nearest enclosing content node with a binding that created us.
+     * @see nsIContent::GetBindingParent
+     */
+    nsIContent* mBindingParent;  // [Weak]
+
+    /**
+     * @see nsIContent::GetXBLInsertionPoint
+     */
+    nsCOMPtr<nsIContent> mXBLInsertionPoint;
+
+    /**
+     * @see nsIContent::GetContainingShadow
+     */
+    RefPtr<mozilla::dom::ShadowRoot> mContainingShadow;
+
+    /**
+     * @see nsIContent::GetAssignedSlot
+     */
+    RefPtr<mozilla::dom::HTMLSlotElement> mAssignedSlot;
+  };
+
+  class nsContentSlots : public nsINode::nsSlots
+  {
+  public:
+    void Traverse(nsCycleCollectionTraversalCallback& aCb) override
+    {
+      nsINode::nsSlots::Traverse(aCb);
+      if (mExtendedSlots) {
+        mExtendedSlots->Traverse(aCb);
+      }
+    }
+
+    void Unlink() override
+    {
+      nsINode::nsSlots::Unlink();
+      if (mExtendedSlots) {
+        mExtendedSlots->Unlink();
+      }
+    }
+
+    mozilla::UniquePtr<nsExtendedContentSlots> mExtendedSlots;
+  };
+
+  // Override from nsINode
+  nsContentSlots* CreateSlots() override
+  {
+    return new nsContentSlots();
+  }
+
+  nsContentSlots* ContentSlots()
+  {
+    return static_cast<nsContentSlots*>(Slots());
+  }
+
+  const nsContentSlots* GetExistingContentSlots() const
+  {
+    return static_cast<nsContentSlots*>(GetExistingSlots());
+  }
+
+  nsContentSlots* GetExistingContentSlots()
+  {
+    return static_cast<nsContentSlots*>(GetExistingSlots());
+  }
+
+  virtual nsExtendedContentSlots* CreateExtendedSlots()
+  {
+    return new nsExtendedContentSlots();
+  }
+
+  const nsExtendedContentSlots* GetExistingExtendedContentSlots() const
+  {
+    const nsContentSlots* slots = GetExistingContentSlots();
+    return slots ? slots->mExtendedSlots.get() : nullptr;
+  }
+
+  nsExtendedContentSlots* GetExistingExtendedContentSlots()
+  {
+    nsContentSlots* slots = GetExistingContentSlots();
+    return slots ? slots->mExtendedSlots.get() : nullptr;
+  }
+
+  nsExtendedContentSlots* ExtendedContentSlots()
+  {
+    nsContentSlots* slots = ContentSlots();
+    if (!slots->mExtendedSlots) {
+      slots->mExtendedSlots.reset(CreateExtendedSlots());
+    }
+    return slots->mExtendedSlots.get();
+  }
+
+  /**
    * 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.
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -1107,18 +1107,18 @@ public:
   {
   public:
     nsSlots();
 
     // If needed we could remove the vtable pointer this dtor causes by
     // putting a DestroySlots function on nsINode
     virtual ~nsSlots();
 
-    void Traverse(nsCycleCollectionTraversalCallback &cb);
-    void Unlink();
+    virtual void Traverse(nsCycleCollectionTraversalCallback&);
+    virtual void Unlink();
 
     /**
      * A list of mutation observers
      */
     nsAutoTObserverArray<nsIMutationObserver*, 1> mMutationObservers;
 
     /**
      * An object implementing nsIDOMNodeList for this content (childNodes)
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1675,22 +1675,16 @@ void
 nsXULElement::DoCommand()
 {
     nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // strong just in case
     if (doc) {
         nsContentUtils::DispatchXULCommand(this, true);
     }
 }
 
-nsIContent *
-nsXULElement::GetBindingParent() const
-{
-    return mBindingParent;
-}
-
 bool
 nsXULElement::IsNodeOfType(uint32_t aFlags) const
 {
     return false;
 }
 
 nsresult
 nsXULElement::AddPopupListener(nsAtom* aName)
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -379,17 +379,21 @@ public:
     {
     }
 #endif
 
     virtual bool PerformAccesskey(bool aKeyCausesActivation,
                                   bool aIsTrustedEvent) override;
     void ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent);
 
-    virtual nsIContent *GetBindingParent() const override;
+    nsIContent* GetBindingParent() const final
+    {
+      return mBindingParent;
+    }
+
     virtual bool IsNodeOfType(uint32_t aFlags) const override;
     virtual bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
 
     NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override;
     virtual nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute,
                                                 int32_t aModType) const override;
     NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
 
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -531,18 +531,19 @@ impl<'le> GeckoElement<'le> {
         let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;
         unsafe { slots.as_ref() }
     }
 
     /// Returns a reference to the extended DOM slots for this Element.
     fn get_extended_slots(
         &self,
     ) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {
-        self.get_dom_slots()
-            .and_then(|s| unsafe { s.mExtendedSlots.mPtr.as_ref() })
+        self.get_dom_slots().and_then(|s| unsafe {
+            (s._base.mExtendedSlots.mPtr as *const _).as_ref()
+        })
     }
 
     #[inline]
     fn may_be_in_binding_manager(&self) -> bool {
         self.flags() & (structs::NODE_MAY_BE_IN_BINDING_MNGR as u32) != 0
     }
 
     #[inline]
@@ -587,17 +588,17 @@ impl<'le> GeckoElement<'le> {
             });
             binding_parent
         }
     }
 
     fn get_non_xul_xbl_binding_parent_raw_content(&self) -> *mut nsIContent {
         debug_assert!(!self.is_xul_element());
         self.get_extended_slots()
-            .map_or(ptr::null_mut(), |slots| slots.mBindingParent)
+            .map_or(ptr::null_mut(), |slots| slots._base.mBindingParent)
     }
 
     fn has_xbl_binding_parent(&self) -> bool {
         if self.is_xul_element() {
             // FIXME(heycam): Having trouble with bindgen on nsXULElement,
             // where the binding parent is stored in a member variable
             // rather than in slots.  So just get it through FFI for now.
             unsafe { bindings::Gecko_GetBindingParent(self.0).is_some() }