Bug 1377993 - Make node slots less memory hungry in common cases. r=peterv
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 18 Jul 2017 00:25:49 +0200
changeset 417988 5fb7a6989ca46671043f859247d5e3440bc8068b
parent 417987 b37994ac99a3f2f7b63f00cf88c0474bda87e826
child 417989 b8f66d5a9748677dfa266a3d143525da071b730e
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1377993
milestone56.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 1377993 - Make node slots less memory hungry in common cases. r=peterv MozReview-Commit-ID: Lhr1UsCrRTs
dom/base/Element.cpp
dom/base/Element.h
dom/base/FragmentOrElement.cpp
dom/base/FragmentOrElement.h
dom/base/ShadowRoot.cpp
dom/base/nsNodeUtils.cpp
dom/html/nsGenericHTMLElement.cpp
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1544,17 +1544,17 @@ Element::BindToTree(nsIDocument* aDocume
   nsXULElement* xulElem = nsXULElement::FromContent(this);
   if (xulElem) {
     xulElem->SetXULBindingParent(aBindingParent);
   }
   else
 #endif
   {
     if (aBindingParent) {
-      nsDOMSlots *slots = DOMSlots();
+      nsExtendedDOMSlots* slots = ExtendedDOMSlots();
 
       slots->mBindingParent = aBindingParent; // Weak, so no addref happens.
     }
   }
   NS_ASSERTION(!aBindingParent || IsRootOfNativeAnonymousSubtree() ||
                !HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) ||
                (aParent && aParent->IsInNativeAnonymousSubtree()),
                "Trying to re-bind content from native anonymous subtree to "
@@ -1570,17 +1570,17 @@ Element::BindToTree(nsIDocument* aDocume
       aParent->SetMayHaveAnonymousChildren();
     }
     if (aParent->IsInShadowTree()) {
       ClearSubtreeRootPointer();
       SetFlags(NODE_IS_IN_SHADOW_TREE);
     }
     ShadowRoot* parentContainingShadow = aParent->GetContainingShadow();
     if (parentContainingShadow) {
-      DOMSlots()->mContainingShadow = parentContainingShadow;
+      ExtendedDOMSlots()->mContainingShadow = parentContainingShadow;
     }
   }
 
   bool hadForceXBL = HasFlag(NODE_FORCE_XBL_BINDINGS);
 
   bool hadParent = !!GetParentNode();
 
   // Now set the parent and set the "Force attach xbl" flag if needed.
@@ -1960,17 +1960,17 @@ Element::UnbindFromTree(bool aDeep, bool
 #ifdef MOZ_XUL
   nsXULElement* xulElem = nsXULElement::FromContent(this);
   if (xulElem) {
     xulElem->SetXULBindingParent(nullptr);
     clearBindingParent = false;
   }
 #endif
 
-  nsDOMSlots* slots = GetExistingDOMSlots();
+  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   if (slots) {
     if (clearBindingParent) {
       slots->mBindingParent = nullptr;
     }
     if (aNullParent || !mParent->IsInShadowTree()) {
       slots->mContainingShadow = nullptr;
     }
   }
@@ -2008,37 +2008,37 @@ Element::UnbindFromTree(bool aDeep, bool
 
     shadowRoot->SetIsComposedDocParticipant(false);
   }
 }
 
 nsICSSDeclaration*
 Element::GetSMILOverrideStyle()
 {
-  Element::nsDOMSlots *slots = DOMSlots();
+  Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
 
   if (!slots->mSMILOverrideStyle) {
     slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true);
   }
 
   return slots->mSMILOverrideStyle;
 }
 
 DeclarationBlock*
 Element::GetSMILOverrideStyleDeclaration()
 {
-  Element::nsDOMSlots *slots = GetExistingDOMSlots();
+  Element::nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr;
 }
 
 nsresult
 Element::SetSMILOverrideStyleDeclaration(DeclarationBlock* aDeclaration,
                                          bool aNotify)
 {
-  Element::nsDOMSlots *slots = DOMSlots();
+  Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
 
   slots->mSMILOverrideStyleDeclaration = aDeclaration;
 
   if (aNotify) {
     nsIDocument* doc = GetComposedDoc();
     // Only need to request a restyle if we're in a document.  (We might not
     // be in a document, if we're clearing animation effects on a target node
     // that's been detached since the previous animation sample.)
@@ -4082,17 +4082,17 @@ Element::ClearDataset()
   MOZ_ASSERT(slots && slots->mDataset,
              "Slots should exist and dataset should not be null.");
   slots->mDataset = nullptr;
 }
 
 nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>*
 Element::RegisteredIntersectionObservers()
 {
-  nsDOMSlots* slots = DOMSlots();
+  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   return &slots->mRegisteredIntersectionObservers;
 }
 
 enum nsPreviousIntersectionThreshold {
   eUninitialized = -2,
   eNonIntersecting = -1
 };
 
@@ -4133,12 +4133,12 @@ Element::ClearServoData() {
 #else
   MOZ_CRASH("Accessing servo node data in non-stylo build");
 #endif
 }
 
 void
 Element::SetCustomElementData(CustomElementData* aData)
 {
-  nsDOMSlots *slots = DOMSlots();
+  nsExtendedDOMSlots *slots = ExtendedDOMSlots();
   MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
   slots->mCustomElementData = aData;
 }
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -510,17 +510,17 @@ 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
   {
-    nsDOMSlots *slots = GetExistingDOMSlots();
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
       return slots->mCustomElementData;
     }
     return nullptr;
   }
 
   /**
    * Sets the custom element data, ownership of the
@@ -1021,17 +1021,17 @@ public:
   already_AddRefed<DOMRectList> GetClientRects();
   already_AddRefed<DOMRect> GetBoundingClientRect();
 
   already_AddRefed<ShadowRoot> CreateShadowRoot(ErrorResult& aError);
   already_AddRefed<DestinationInsertionPointList> GetDestinationInsertionPoints();
 
   ShadowRoot *FastGetShadowRoot() const
   {
-    nsDOMSlots* slots = GetExistingDOMSlots();
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     return slots ? slots->mShadowRoot.get() : nullptr;
   }
 
   void ScrollIntoView();
   void ScrollIntoView(bool aTop);
   void ScrollIntoView(const ScrollIntoViewOptions &aOptions);
   void Scroll(double aXScroll, double aYScroll);
   void Scroll(const ScrollToOptions& aOptions);
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -655,107 +655,126 @@ nsNodeSupportsWeakRefTearoff::GetWeakRef
   NS_ADDREF(*aInstancePtr = slots->mWeakReference);
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
 FragmentOrElement::nsDOMSlots::nsDOMSlots()
   : nsINode::nsSlots(),
-    mDataset(nullptr),
-    mBindingParent(nullptr)
+    mDataset(nullptr)
 {
 }
 
 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
 {
   if (mAttributeMap) {
     mAttributeMap->DropReference();
   }
 }
 
 void
-FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL)
+FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
   cb.NoteXPCOMChild(mStyle.get());
 
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle");
-  cb.NoteXPCOMChild(mSMILOverrideStyle.get());
-
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
   cb.NoteXPCOMChild(mAttributeMap.get());
 
-  if (aIsXUL) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers");
-    cb.NoteXPCOMChild(mControllers);
-  }
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
-  cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
-  cb.NoteXPCOMChild(mXBLInsertionParent.get());
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
-
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
-
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
 
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mLabelsList");
-  cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mLabelsList));
-
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
   cb.NoteXPCOMChild(mClassList.get());
 
-  if (mCustomElementData) {
-    for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
-      mCustomElementData->mCallbackQueue[i]->Traverse(cb);
+  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(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->mXBLBinding");
+  cb.NoteNativeChild(mExtendedSlots->mXBLBinding,
+                     NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mXBLInsertionParent");
+  cb.NoteXPCOMChild(mExtendedSlots->mXBLInsertionParent.get());
+
+  if (mExtendedSlots->mCustomElementData) {
+    for (uint32_t i = 0;
+         i < mExtendedSlots->mCustomElementData->mCallbackQueue.Length(); i++) {
+      mExtendedSlots->mCustomElementData->mCallbackQueue[i]->Traverse(cb);
     }
   }
 
-  for (auto iter = mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
+  for (auto iter = mExtendedSlots->mRegisteredIntersectionObservers.Iter();
+       !iter.Done(); iter.Next()) {
     DOMIntersectionObserver* observer = iter.Key();
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mRegisteredIntersectionObservers[i]");
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
+                                       "mExtendedSlots->mRegisteredIntersectionObservers[i]");
     cb.NoteXPCOMChild(observer);
   }
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mExtendedSlots->mFrameLoaderOrOpener");
+  cb.NoteXPCOMChild(mExtendedSlots->mFrameLoaderOrOpener);
 }
 
 void
-FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
+FragmentOrElement::nsDOMSlots::Unlink()
 {
   mStyle = nullptr;
-  mSMILOverrideStyle = nullptr;
   if (mAttributeMap) {
     mAttributeMap->DropReference();
     mAttributeMap = nullptr;
   }
-  if (aIsXUL)
-    NS_IF_RELEASE(mControllers);
+  mChildrenList = nullptr;
+  mClassList = nullptr;
 
-  MOZ_ASSERT(!mXBLBinding);
+  if (!mExtendedSlots) {
+    return;
+  }
 
-  mXBLInsertionParent = nullptr;
-  mShadowRoot = nullptr;
-  mContainingShadow = nullptr;
-  mChildrenList = nullptr;
-  mLabelsList = nullptr;
-  mCustomElementData = nullptr;
-  mClassList = nullptr;
-  mRegisteredIntersectionObservers.Clear();
+  mExtendedSlots->mSMILOverrideStyle = nullptr;
+  mExtendedSlots->mControllers = nullptr;
+  mExtendedSlots->mLabelsList = nullptr;
+  mExtendedSlots->mShadowRoot = nullptr;
+  mExtendedSlots->mContainingShadow = nullptr;
+  MOZ_ASSERT(!(mExtendedSlots->mXBLBinding));
+  mExtendedSlots->mXBLInsertionParent = nullptr;
+  mExtendedSlots->mCustomElementData = nullptr;
+  mExtendedSlots->mRegisteredIntersectionObservers.Clear();
+  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());
+  }
 
   if (mAttributeMap) {
     n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
   }
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - Superclass members (nsINode::nsSlots)
@@ -766,16 +785,29 @@ 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()
+{
+  nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(mFrameLoaderOrOpener);
+  if (frameLoader) {
+    static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
+  }
+}
+
 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsIContent(aNodeInfo)
 {
 }
 
 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
   : nsIContent(aNodeInfo)
 {
@@ -1087,29 +1119,29 @@ FragmentOrElement::IsLink(nsIURI** aURI)
 {
   *aURI = nullptr;
   return false;
 }
 
 nsIContent*
 FragmentOrElement::GetBindingParent() const
 {
-  nsDOMSlots *slots = GetExistingDOMSlots();
+  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
 
   if (slots) {
     return slots->mBindingParent;
   }
   return nullptr;
 }
 
 nsXBLBinding*
 FragmentOrElement::GetXBLBinding() const
 {
   if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
-    nsDOMSlots *slots = GetExistingDOMSlots();
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
       return slots->mXBLBinding;
     }
   }
 
   return nullptr;
 }
 
@@ -1134,87 +1166,87 @@ FragmentOrElement::SetXBLBinding(nsXBLBi
   // dies before we process mAttachedStack).
   RefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
   if (oldBinding) {
     bindingManager->RemoveFromAttachedQueue(oldBinding);
   }
 
   if (aBinding) {
     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
-    nsDOMSlots *slots = DOMSlots();
+    nsExtendedDOMSlots* slots = ExtendedDOMSlots();
     slots->mXBLBinding = aBinding;
     bindingManager->AddBoundContent(this);
   } else {
-    nsDOMSlots *slots = GetExistingDOMSlots();
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
       slots->mXBLBinding = nullptr;
     }
     bindingManager->RemoveBoundContent(this);
     if (oldBinding) {
       oldBinding->SetBoundElement(nullptr);
     }
   }
 }
 
 nsIContent*
 FragmentOrElement::GetXBLInsertionParent() const
 {
   if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
-    nsDOMSlots *slots = GetExistingDOMSlots();
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
       return slots->mXBLInsertionParent;
     }
   }
 
   return nullptr;
 }
 
 ShadowRoot*
 FragmentOrElement::GetContainingShadow() const
 {
-  nsDOMSlots *slots = GetExistingDOMSlots();
+  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   if (slots) {
     return slots->mContainingShadow;
   }
   return nullptr;
 }
 
 void
 FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
 {
-  nsDOMSlots *slots = DOMSlots();
+  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   slots->mShadowRoot = aShadowRoot;
 }
 
 nsTArray<nsIContent*>&
 FragmentOrElement::DestInsertionPoints()
 {
-  nsDOMSlots *slots = DOMSlots();
+  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   return slots->mDestInsertionPoints;
 }
 
 nsTArray<nsIContent*>*
 FragmentOrElement::GetExistingDestInsertionPoints() const
 {
-  nsDOMSlots *slots = GetExistingDOMSlots();
+  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   if (slots) {
     return &slots->mDestInsertionPoints;
   }
   return nullptr;
 }
 
 void
 FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
 {
   if (aContent) {
-    nsDOMSlots *slots = DOMSlots();
+    nsExtendedDOMSlots* slots = ExtendedDOMSlots();
     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
     slots->mXBLInsertionParent = aContent;
   } else {
-    nsDOMSlots *slots = GetExistingDOMSlots();
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
       slots->mXBLInsertionParent = 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 (IsStyledByServo() && IsElement() && AsElement()->HasServoData()) {
@@ -1491,24 +1523,25 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Fr
   nsIDocument* doc = tmp->OwnerDoc();
   doc->BindingManager()->RemovedFromDocument(tmp, doc,
                                              nsBindingManager::eDoNotRunDtor);
 
   // Unlink any DOM slots of interest.
   {
     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
     if (slots) {
-      if (tmp->IsElement()) {
+      if (slots->mExtendedSlots && tmp->IsElement()) {
         Element* elem = tmp->AsElement();
-        for (auto iter = slots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
+        for (auto iter = slots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
+             !iter.Done(); iter.Next()) {
           DOMIntersectionObserver* observer = iter.Key();
           observer->UnlinkTarget(*elem);
         }
       }
-      slots->Unlink(tmp->IsXULElement());
+      slots->Unlink();
     }
   }
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
 
 void
@@ -2063,17 +2096,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
       cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
     }
   }
 
   // Traverse any DOM slots of interest.
   {
     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
     if (slots) {
-      slots->Traverse(cb, tmp->IsXULElement());
+      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)
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -10,16 +10,17 @@
  * utility methods for subclasses, and so forth.
  */
 
 #ifndef FragmentOrElement_h___
 #define FragmentOrElement_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
 #include "nsAttrAndChildArray.h"          // member
 #include "nsCycleCollectionParticipant.h" // NS_DECL_CYCLE_*
 #include "nsIContent.h"                   // base class
 #include "nsIWeakReference.h"             // base class
 #include "nsNodeUtils.h"                  // class member nsNodeUtils::CloneNodeImpl
 #include "nsIHTMLCollection.h"
 #include "nsDataHashtable.h"
 #include "nsXBLBinding.h"
@@ -237,91 +238,55 @@ protected:
 
   /**
    * Copy attributes and state to another element
    * @param aDest the object to copy to
    */
   nsresult CopyInnerTo(FragmentOrElement* aDest, bool aPreallocateChildren);
 
 public:
-  // Because of a bug in MS C++ compiler nsDOMSlots must be declared public,
-  // otherwise nsXULElement::nsXULSlots doesn't compile.
   /**
    * 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 nsDOMSlots : public nsINode::nsSlots
+
+  class nsExtendedDOMSlots
   {
   public:
-    nsDOMSlots();
-    virtual ~nsDOMSlots();
-
-    void Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL);
-    void Unlink(bool aIsXUL);
-
-    size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+    nsExtendedDOMSlots();
 
-    /**
-     * The .style attribute (an interface that forwards to the actual
-     * style rules)
-     * @see nsGenericHTMLElement::GetStyle
-     */
-    nsCOMPtr<nsICSSDeclaration> mStyle;
-
-    /**
-     * The .dataset attribute.
-     * @see nsGenericHTMLElement::GetDataset
-     */
-    nsDOMStringMap* mDataset; // [Weak]
+    ~nsExtendedDOMSlots();
 
     /**
      * 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;
 
     /**
-     * An object implementing nsIDOMMozNamedAttrMap for this content (attributes)
-     * @see FragmentOrElement::GetAttributes
-     */
-    RefPtr<nsDOMAttributeMap> mAttributeMap;
-
-    union {
-      /**
-      * The nearest enclosing content node with a binding that created us.
-      * @see FragmentOrElement::GetBindingParent
-      */
-      nsIContent* mBindingParent;  // [Weak]
-
-      /**
-      * The controllers of the XUL Element.
-      */
-      nsIControllers* mControllers; // [OWNER]
-    };
+    * The nearest enclosing content node with a binding that created us.
+    * @see FragmentOrElement::GetBindingParent
+    */
+    nsIContent* mBindingParent;  // [Weak]
 
     /**
-     * An object implementing the .children property for this element.
-     */
-    RefPtr<nsContentList> mChildrenList;
+    * The controllers of the XUL Element.
+    */
+    nsCOMPtr<nsIControllers> mControllers;
 
     /**
-     * An object implementing the .classList property for this element.
-     */
-    RefPtr<nsDOMTokenList> mClassList;
-
-    /*
      * An object implementing the .labels property for this element.
      */
     RefPtr<nsLabelsNodeList> mLabelsList;
 
     /**
      * ShadowRoot bound to the element.
      */
     RefPtr<ShadowRoot> mShadowRoot;
@@ -352,16 +317,65 @@ public:
      */
     RefPtr<CustomElementData> mCustomElementData;
 
     /**
      * Registered Intersection Observers on the element.
      */
     nsDataHashtable<nsRefPtrHashKey<DOMIntersectionObserver>, int32_t>
       mRegisteredIntersectionObservers;
+
+    /**
+     * For XUL to hold either frameloader or opener.
+     */
+    nsCOMPtr<nsISupports> mFrameLoaderOrOpener;
+
+  };
+
+  class nsDOMSlots : public nsINode::nsSlots
+  {
+  public:
+    nsDOMSlots();
+    virtual ~nsDOMSlots();
+
+    void Traverse(nsCycleCollectionTraversalCallback &cb);
+    void Unlink();
+
+    size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+    /**
+     * The .style attribute (an interface that forwards to the actual
+     * style rules)
+     * @see nsGenericHTMLElement::GetStyle
+     */
+    nsCOMPtr<nsICSSDeclaration> mStyle;
+
+    /**
+     * The .dataset attribute.
+     * @see nsGenericHTMLElement::GetDataset
+     */
+    nsDOMStringMap* mDataset; // [Weak]
+
+    /**
+     * An object implementing nsIDOMMozNamedAttrMap for this content (attributes)
+     * @see FragmentOrElement::GetAttributes
+     */
+    RefPtr<nsDOMAttributeMap> mAttributeMap;
+
+    /**
+     * 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;
@@ -371,16 +385,36 @@ protected:
     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();
+  }
+
+  nsExtendedDOMSlots* GetExistingExtendedDOMSlots() const
+  {
+    nsDOMSlots* slots = GetExistingDOMSlots();
+    if (slots) {
+      return slots->mExtendedSlots.get();
+    }
+
+    return nullptr;
+  }
+
   /**
    * 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.
    */
   void SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope);
 
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -70,18 +70,18 @@ ShadowRoot::ShadowRoot(nsIContent* aCont
 
   // Nodes in a shadow tree should never store a value
   // in the subtree root pointer, nodes in the shadow tree
   // track the subtree root using GetContainingShadow().
   ClearSubtreeRootPointer();
 
   SetFlags(NODE_IS_IN_SHADOW_TREE);
 
-  DOMSlots()->mBindingParent = aContent;
-  DOMSlots()->mContainingShadow = this;
+  ExtendedDOMSlots()->mBindingParent = aContent;
+  ExtendedDOMSlots()->mContainingShadow = this;
 
   // Add the ShadowRoot as a mutation observer on the host to watch
   // for mutations because the insertion points in this ShadowRoot
   // may need to be updated when the host children are modified.
   mPoolHost->AddMutationObserver(this);
 }
 
 ShadowRoot::~ShadowRoot()
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -296,19 +296,22 @@ nsNodeUtils::LastRelease(nsINode* aNode)
                                          nsIMutationObserver,
                                          NodeWillBeDestroyed, (aNode));
     }
 
     if (aNode->IsElement()) {
       Element* elem = aNode->AsElement();
       FragmentOrElement::nsDOMSlots* domSlots =
         static_cast<FragmentOrElement::nsDOMSlots*>(slots);
-      for (auto iter = domSlots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
-        DOMIntersectionObserver* observer = iter.Key();
-        observer->UnlinkTarget(*elem);
+      if (domSlots->mExtendedSlots) {
+        for (auto iter = domSlots->mExtendedSlots->mRegisteredIntersectionObservers.Iter();
+             !iter.Done(); iter.Next()) {
+          DOMIntersectionObserver* observer = iter.Key();
+          observer->UnlinkTarget(*elem);
+        }
       }
     }
 
     delete slots;
     aNode->mSlots = nullptr;
   }
 
   // Kill properties first since that may run external code, so we want to
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -498,17 +498,17 @@ nsGenericHTMLElement::BindToTree(nsIDocu
         htmlDocument->ChangeContentEditableCount(this, +1);
       }
     }
   }
 
   // We need to consider a labels element is moved to another subtree
   // with different root, it needs to update labels list and its root
   // as well.
-  nsDOMSlots* slots = GetExistingDOMSlots();
+  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   if (slots && slots->mLabelsList) {
     slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
   }
 
   return rv;
 }
 
 void
@@ -525,17 +525,17 @@ nsGenericHTMLElement::UnbindFromTree(boo
     nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetUncomposedDoc());
     if (htmlDocument) {
       htmlDocument->ChangeContentEditableCount(this, -1);
     }
   }
 
   // We need to consider a labels element is removed from tree,
   // it needs to update labels list and its root as well.
-  nsDOMSlots* slots = GetExistingDOMSlots();
+  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   if (slots && slots->mLabelsList) {
     slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
   }
 
   nsStyledElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 HTMLFormElement*
@@ -1697,17 +1697,17 @@ nsGenericHTMLElement::MatchLabelsElement
   return element && element->GetControl() == aData;
 }
 
 already_AddRefed<nsINodeList>
 nsGenericHTMLElement::Labels()
 {
   MOZ_ASSERT(IsLabelable(),
              "Labels() only allow labelable elements to use it.");
-  nsDOMSlots* slots = DOMSlots();
+  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
 
   if (!slots->mLabelsList) {
     slots->mLabelsList = new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement,
                                               nullptr, this);
   }
 
   RefPtr<nsLabelsNodeList> labels = slots->mLabelsList;
   return labels.forget();
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -171,43 +171,16 @@ nsXULElement::nsXULElement(already_AddRe
         RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY);
     }
 }
 
 nsXULElement::~nsXULElement()
 {
 }
 
-nsXULElement::nsXULSlots::nsXULSlots()
-    : nsXULElement::nsDOMSlots()
-{
-}
-
-nsXULElement::nsXULSlots::~nsXULSlots()
-{
-    NS_IF_RELEASE(mControllers); // Forces release
-    nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(mFrameLoaderOrOpener);
-    if (frameLoader) {
-        static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
-    }
-}
-
-void
-nsXULElement::nsXULSlots::Traverse(nsCycleCollectionTraversalCallback &cb)
-{
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mFrameLoaderOrOpener");
-    cb.NoteXPCOMChild(mFrameLoaderOrOpener);
-}
-
-nsINode::nsSlots*
-nsXULElement::CreateSlots()
-{
-    return new nsXULSlots();
-}
-
 void
 nsXULElement::MaybeUpdatePrivateLifetime()
 {
     if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::windowtype,
                     NS_LITERAL_STRING("navigator:browser"),
                     eCaseMatters)) {
         return;
     }
@@ -323,22 +296,16 @@ NS_TrustedNewXULElement(nsIContent** aRe
 
 //----------------------------------------------------------------------
 // nsISupports interface
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement,
                                                   nsStyledElement)
-    {
-        nsXULSlots* slots = static_cast<nsXULSlots*>(tmp->GetExistingSlots());
-        if (slots) {
-            slots->Traverse(cb);
-        }
-    }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULElement,
                                                 nsStyledElement)
     // Why aren't we unlinking the prototype?
     tmp->ClearHasID();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
@@ -882,19 +849,19 @@ nsXULElement::UnbindFromTree(bool aDeep,
     // object's script object from being garbage collected,
     // which means JS continues to hold an owning reference
     // to the nsGlobalWindow, which owns the document,
     // which owns this content.  That's a cycle, so we break
     // it here.  (It might be better to break this by releasing
     // mDocument in nsGlobalWindow::SetDocShell, but I'm not
     // sure whether that would fix all possible cycles through
     // mControllers.)
-    nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
-        NS_IF_RELEASE(slots->mControllers);
+        slots->mControllers = nullptr;
         RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
         if (frameLoader) {
             frameLoader->Destroy();
         }
         slots->mFrameLoaderOrOpener = nullptr;
     }
 
     nsStyledElement::UnbindFromTree(aDeep, aNullParent);
@@ -1230,19 +1197,19 @@ nsXULElement::RemoveBroadcaster(const ns
               NS_LITERAL_STRING("*"));
         }
     }
 }
 
 void
 nsXULElement::DestroyContent()
 {
-    nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (slots) {
-        NS_IF_RELEASE(slots->mControllers);
+        slots->mControllers = nullptr;
         RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
         if (frameLoader) {
             frameLoader->Destroy();
         }
         slots->mFrameLoaderOrOpener = nullptr;
     }
 
     nsStyledElement::DestroyContent();
@@ -1457,17 +1424,17 @@ nsXULElement::IsAttributeMapped(const ns
 {
     return false;
 }
 
 nsIControllers*
 nsXULElement::GetControllers(ErrorResult& rv)
 {
     if (! Controllers()) {
-        nsDOMSlots* slots = DOMSlots();
+        nsExtendedDOMSlots* slots = ExtendedDOMSlots();
 
         rv = NS_NewXULControllers(nullptr, NS_GET_IID(nsIControllers),
                                   reinterpret_cast<void**>(&slots->mControllers));
 
         NS_ASSERTION(!rv.Failed(), "unable to create a controllers");
         if (rv.Failed()) {
             return nullptr;
         }
@@ -1496,17 +1463,17 @@ nsXULElement::LoadSrc()
         !OwnerDoc()->GetRootElement() ||
         OwnerDoc()->GetRootElement()->
             NodeInfo()->Equals(nsGkAtoms::overlay, kNameSpaceID_XUL)) {
         return NS_OK;
     }
     RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
     if (!frameLoader) {
         // Check if we have an opener we need to be setting
-        nsXULSlots* slots = static_cast<nsXULSlots*>(Slots());
+        nsExtendedDOMSlots* slots = ExtendedDOMSlots();
         nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryInterface(slots->mFrameLoaderOrOpener);
         if (!opener) {
             // If we are a primary xul-browser, we want to take the opener property!
             nsCOMPtr<nsIDOMChromeWindow> chromeWindow = do_QueryInterface(OwnerDoc()->GetWindow());
             if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
                             nsGkAtoms::_true, eIgnoreCase) &&
                 chromeWindow) {
                 nsCOMPtr<mozIDOMWindowProxy> wp;
@@ -1542,44 +1509,44 @@ nsXULElement::GetFrameLoaderXPCOM(nsIFra
 {
     *aFrameLoader = GetFrameLoader().take();
     return NS_OK;
 }
 
 already_AddRefed<nsFrameLoader>
 nsXULElement::GetFrameLoader()
 {
-    nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingSlots());
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     if (!slots)
         return nullptr;
 
     nsCOMPtr<nsIFrameLoader> loader = do_QueryInterface(slots->mFrameLoaderOrOpener);
     return already_AddRefed<nsFrameLoader>(static_cast<nsFrameLoader*>(loader.forget().take()));
 }
 
 void
 nsXULElement::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv)
 {
-    nsXULSlots* slots = static_cast<nsXULSlots*>(Slots());
+    nsExtendedDOMSlots* slots = ExtendedDOMSlots();
     MOZ_ASSERT(!slots->mFrameLoaderOrOpener, "A frameLoader or opener is present when calling PresetOpenerWindow");
 
     slots->mFrameLoaderOrOpener = aWindow;
 }
 
 nsresult
 nsXULElement::SetIsPrerendered()
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::prerendered, nullptr,
                  NS_LITERAL_STRING("true"), true);
 }
 
 void
 nsXULElement::InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader)
 {
-    nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots());
+    nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
     MOZ_ASSERT(slots);
 
     slots->mFrameLoaderOrOpener = aNewFrameLoader;
 }
 
 void
 nsXULElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner,
                                ErrorResult& rv)
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -720,29 +720,16 @@ protected:
     static nsresult
     ExecuteJSCode(nsIDOMElement* anElement, mozilla::WidgetEvent* aEvent);
 
     // Helper routine that crawls a parent chain looking for a tree element.
     NS_IMETHOD GetParentTree(nsIDOMXULMultiSelectControlElement** aTreeElement);
 
     nsresult AddPopupListener(nsIAtom* aName);
 
-    class nsXULSlots : public mozilla::dom::Element::nsDOMSlots
-    {
-    public:
-        nsXULSlots();
-        virtual ~nsXULSlots();
-
-        void Traverse(nsCycleCollectionTraversalCallback &cb);
-
-        nsCOMPtr<nsISupports> mFrameLoaderOrOpener;
-    };
-
-    virtual nsINode::nsSlots* CreateSlots() override;
-
     nsresult LoadSrc();
 
     /**
      * The nearest enclosing content node with a binding
      * that created us. [Weak]
      */
     nsIContent*                         mBindingParent;
 
@@ -790,18 +777,18 @@ protected:
     void UpdateBrightTitlebarForeground(nsIDocument* aDocument);
 
     void RemoveBroadcaster(const nsAString & broadcasterId);
 
 protected:
     // Internal accessor. This shadows the 'Slots', and returns
     // appropriate value.
     nsIControllers *Controllers() {
-      nsDOMSlots* slots = GetExistingDOMSlots();
-      return slots ? slots->mControllers : nullptr;
+      nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
+      return slots ? slots->mControllers.get() : nullptr;
     }
 
     void UnregisterAccessKey(const nsAString& aOldValue);
     bool BoolAttrIsTrue(nsIAtom* aName) const;
 
     friend nsresult
     NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo);
     friend void