Bug 796061 - Part 1: Remove mBindingTable from nsBindingManager. r=mrbkap
authorWilliam Chen <wchen@mozilla.com>
Wed, 17 Jul 2013 09:05:03 -0700
changeset 138871 5e3d564cd1b551eda17fcb7056a91db5dc866306
parent 138870 e6d3454da7a7adb19056383175dfe37416d2e272
child 138872 d810dcc0726f6516571dfa26657b7add26fe4659
push id31159
push userwchen@mozilla.com
push dateWed, 17 Jul 2013 16:05:53 +0000
treeherdermozilla-inbound@d810dcc0726f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs796061
milestone25.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 796061 - Part 1: Remove mBindingTable from nsBindingManager. r=mrbkap
accessible/src/base/AccIterator.cpp
content/base/public/FragmentOrElement.h
content/base/public/nsIContent.h
content/base/src/Element.cpp
content/base/src/FragmentOrElement.cpp
content/base/src/nsContentUtils.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericDOMDataNode.h
content/base/src/nsNodeUtils.cpp
content/base/src/nsReferencedElement.cpp
content/xbl/src/nsBindingManager.cpp
content/xbl/src/nsBindingManager.h
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLService.cpp
layout/inspector/src/inDOMUtils.cpp
--- a/accessible/src/base/AccIterator.cpp
+++ b/accessible/src/base/AccIterator.cpp
@@ -295,17 +295,17 @@ IDRefsIterator::NextElem()
 
 nsIContent*
 IDRefsIterator::GetElem(const nsDependentSubstring& aID)
 {
   // Get elements in DOM tree by ID attribute if this is an explicit content.
   // In case of bound element check its anonymous subtree.
   if (!mContent->IsInAnonymousSubtree()) {
     dom::Element* refElm = mContent->OwnerDoc()->GetElementById(aID);
-    if (refElm || !mContent->OwnerDoc()->BindingManager()->GetBinding(mContent))
+    if (refElm || !mContent->GetXBLBinding())
       return refElm;
   }
 
   // If content is in anonymous subtree or an element having anonymous subtree
   // then use "anonid" attribute to get elements in anonymous subtree.
 
   // Check inside the binding the element is contained in.
   nsIContent* bindingParent = mContent->GetBindingParent();
@@ -313,17 +313,17 @@ IDRefsIterator::GetElem(const nsDependen
     nsIContent* refElm = bindingParent->OwnerDoc()->
       GetAnonymousElementByAttribute(bindingParent, nsGkAtoms::anonid, aID);
 
     if (refElm)
       return refElm;
   }
 
   // Check inside the binding of the element.
-  if (mContent->OwnerDoc()->BindingManager()->GetBinding(mContent)) {
+  if (mContent->GetXBLBinding()) {
     return mContent->OwnerDoc()->
       GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID);
   }
 
   return nullptr;
 }
 
 Accessible*
--- a/content/base/public/FragmentOrElement.h
+++ b/content/base/public/FragmentOrElement.h
@@ -210,16 +210,19 @@ public:
   {
     return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
   }
   virtual nsresult AppendText(const PRUnichar* aBuffer, uint32_t aLength,
                               bool aNotify) MOZ_OVERRIDE;
   virtual bool TextIsOnlyWhitespace() MOZ_OVERRIDE;
   virtual void AppendTextTo(nsAString& aResult) MOZ_OVERRIDE;
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
+  virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
+  virtual void SetXBLBinding(nsXBLBinding* aBinding,
+                             nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
   virtual void DestroyContent() MOZ_OVERRIDE;
   virtual void SaveSubtreeState() MOZ_OVERRIDE;
 
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE;
 
@@ -354,16 +357,21 @@ public:
      * An object implementing the .children property for this element.
      */
     nsRefPtr<nsContentList> mChildrenList;
 
     /**
      * An object implementing the .classList property for this element.
      */
     nsRefPtr<nsDOMTokenList> mClassList;
+
+    /**
+     * XBL binding installed on the element.
+     */
+    nsRefPtr<nsXBLBinding> mXBLBinding;
   };
 
 protected:
   // Override from nsINode
   virtual nsINode::nsSlots* CreateSlots() MOZ_OVERRIDE;
 
   nsDOMSlots *DOMSlots()
   {
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -13,16 +13,17 @@
 class nsAString;
 class nsIAtom;
 class nsIURI;
 class nsRuleWalker;
 class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
 class nsIFrame;
+class nsXBLBinding;
 
 namespace mozilla {
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
 enum nsLinkState {
@@ -611,16 +612,39 @@ public:
    * 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;
 
   /**
+   * Gets the current XBL binding that is bound to this element.
+   *
+   * @return the current binding.
+   */
+  virtual nsXBLBinding *GetXBLBinding() const = 0;
+
+  /**
+   * Sets or unsets an XBL binding for this element. Setting a
+   * binding on an element that already has a binding will remove the
+   * old binding.
+   *
+   * @param aBinding The binding to bind to this content. If nullptr is
+   *        provided as the argument, then existing binding will be
+   *        removed.
+   *
+   * @param aOldBindingManager The old binding manager that contains
+   *                           this content if this content was adopted
+   *                           to another document.
+   */
+  virtual void SetXBLBinding(nsXBLBinding* aBinding,
+                             nsBindingManager* aOldBindingManager = nullptr) = 0;
+
+  /**
    * Returns the content node that is the parent of this node in the flattened
    * tree. For nodes that are not filtered into an insertion point, this
    * simply returns their DOM parent in the original DOM tree.
    *
    * @return the flattened tree parent
    */
   nsIContent *GetFlattenedTreeParent() const;
 
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -358,18 +358,17 @@ Element::WrapObject(JSContext *aCx, JS::
     // There's no baseclass that cares about this call so we just
     // return here.
     return obj;
   }
 
   // We must ensure that the XBL Binding is installed before we hand
   // back this object.
 
-  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
-      doc->BindingManager()->GetBinding(this)) {
+  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && GetXBLBinding()) {
     // There's already a binding for this element so nothing left to
     // be done here.
 
     // In theory we could call ExecuteAttachedHandler here when it's safe to
     // run script if we also removed the binding from the PAQ queue, but that
     // seems like a scary change that would mosly just add more
     // inconsistencies.
     return obj;
@@ -1714,18 +1713,17 @@ Element::SetAttrAndNotify(int32_t aNames
                                                    aNamespaceID,
                                                    nsIDOMNode::ATTRIBUTE_NODE);
 
     rv = mAttrsAndChildren.SetAndTakeAttr(ni, aParsedValue);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
-    nsRefPtr<nsXBLBinding> binding =
-      OwnerDoc()->BindingManager()->GetBinding(this);
+    nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNamespaceID, false, aNotify);
     }
   }
 
   UpdateState(aNotify);
 
   if (aNotify) {
@@ -1898,18 +1896,17 @@ Element::UnsetAttr(int32_t aNameSpaceID,
     hadDirAuto = HasDirAuto(); // already takes bdi into account
   }
 
   nsAttrValue oldValue;
   rv = mAttrsAndChildren.RemoveAttrAt(index, oldValue);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (document || HasFlag(NODE_FORCE_XBL_BINDINGS)) {
-    nsRefPtr<nsXBLBinding> binding =
-      OwnerDoc()->BindingManager()->GetBinding(this);
+    nsRefPtr<nsXBLBinding> binding = GetXBLBinding();
     if (binding) {
       binding->AttributeChanged(aName, aNameSpaceID, true, aNotify);
     }
   }
 
   UpdateState(aNotify);
 
   if (aNotify) {
--- a/content/base/src/FragmentOrElement.cpp
+++ b/content/base/src/FragmentOrElement.cpp
@@ -280,18 +280,17 @@ nsIContent::GetBaseURI() const
   nsAutoTArray<nsString, 5> baseAttrs;
   nsString attr;
   const nsIContent *elem = this;
   do {
     // First check for SVG specialness (why is this SVG specific?)
     if (elem->IsSVG()) {
       nsIContent* bindingParent = elem->GetBindingParent();
       if (bindingParent) {
-        nsXBLBinding* binding =
-          bindingParent->OwnerDoc()->BindingManager()->GetBinding(bindingParent);
+        nsXBLBinding* binding = bindingParent->GetXBLBinding();
         if (binding) {
           // XXX sXBL/XBL2 issue
           // If this is an anonymous XBL element use the binding
           // document for the base URI. 
           // XXX Will fail with xml:base
           base = binding->PrototypeBinding()->DocURI();
           break;
         }
@@ -561,16 +560,19 @@ FragmentOrElement::nsDOMSlots::Traverse(
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mUndoManager");
   cb.NoteXPCOMChild(mUndoManager.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->mChildrenList");
   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
 
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
   cb.NoteXPCOMChild(mClassList.get());
 }
 
 void
@@ -579,16 +581,17 @@ FragmentOrElement::nsDOMSlots::Unlink(bo
   mStyle = nullptr;
   mSMILOverrideStyle = nullptr;
   if (mAttributeMap) {
     mAttributeMap->DropReference();
     mAttributeMap = nullptr;
   }
   if (aIsXUL)
     NS_IF_RELEASE(mControllers);
+  mXBLBinding = nullptr;
   mChildrenList = nullptr;
   mUndoManager = nullptr;
   if (mClassList) {
     mClassList->DropReference();
     mClassList = nullptr;
   }
 }
 
@@ -884,16 +887,67 @@ FragmentOrElement::GetBindingParent() co
   nsDOMSlots *slots = GetExistingDOMSlots();
 
   if (slots) {
     return slots->mBindingParent;
   }
   return nullptr;
 }
 
+nsXBLBinding*
+FragmentOrElement::GetXBLBinding() const
+{
+  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
+    nsDOMSlots *slots = GetExistingDOMSlots();
+    if (slots) {
+      return slots->mXBLBinding;
+    }
+  }
+
+  return nullptr;
+}
+
+void
+FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
+                                 nsBindingManager* aOldBindingManager)
+{
+  nsBindingManager* bindingManager;
+  if (aOldBindingManager) {
+    MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
+                          "when removing a binding.");
+    bindingManager = aOldBindingManager;
+  } else {
+    bindingManager = OwnerDoc()->BindingManager();
+  }
+
+  // After this point, aBinding will be the most-derived binding for aContent.
+  // If we already have a binding for aContent, make sure to
+  // remove it from the attached stack.  Otherwise we might end up firing its
+  // constructor twice (if aBinding inherits from it) or firing its constructor
+  // after aContent has been deleted (if aBinding is null and the content node
+  // dies before we process mAttachedStack).
+  nsRefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
+  if (oldBinding) {
+    bindingManager->RemoveFromAttachedQueue(oldBinding);
+  }
+
+  nsDOMSlots *slots = DOMSlots();
+  if (aBinding) {
+    SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
+    slots->mXBLBinding = aBinding;
+    bindingManager->AddBoundContent(this);
+  } else {
+    slots->mXBLBinding = nullptr;
+    bindingManager->RemoveBoundContent(this);
+    if (oldBinding) {
+      oldBinding->SetBoundElement(nullptr);
+    }
+  }
+}
+
 nsresult
 FragmentOrElement::InsertChildAt(nsIContent* aKid,
                                 uint32_t aIndex,
                                 bool aNotify)
 {
   NS_PRECONDITION(aKid, "null ptr");
 
   return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
@@ -1382,18 +1436,17 @@ NodeHasActiveFrame(nsIDocument* aCurrent
 {
   return aCurrentDoc->GetShell() && aNode->IsElement() &&
          aNode->AsElement()->GetPrimaryFrame();
 }
 
 bool
 OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
 {
-  return aNode->IsElement() &&
-    aCurrentDoc->BindingManager()->GetBinding(aNode->AsElement());
+  return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
 }
 
 // CanSkip checks if aNode is black, and if it is, returns
 // true. If aNode is in a black DOM tree, CanSkip may also remove other objects
 // from purple buffer and unmark event listeners and user data.
 // If the root of the DOM tree is a document, less optimizations are done
 // since checking the blackness of the current document is usually fast and we
 // don't want slow down such common cases.
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1743,18 +1743,17 @@ nsContentUtils::IsImageSrcSetDisabled()
          !IsCallerChrome();
 }
 
 // static
 bool
 nsContentUtils::LookupBindingMember(JSContext* aCx, nsIContent *aContent,
                                     JS::HandleId aId, JSPropertyDescriptor* aDesc)
 {
-  nsXBLBinding* binding = aContent->OwnerDoc()->BindingManager()
-                                  ->GetBinding(aContent);
+  nsXBLBinding* binding = aContent->GetXBLBinding();
   if (!binding)
     return true;
   return binding->LookupMember(aCx, aId, aDesc);
 }
 
 // static
 nsINode*
 nsContentUtils::GetCrossDocParentNode(nsINode* aChild)
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -632,16 +632,28 @@ nsGenericDOMDataNode::RemoveChildAt(uint
 
 nsIContent *
 nsGenericDOMDataNode::GetBindingParent() const
 {
   nsDataSlots *slots = GetExistingDataSlots();
   return slots ? slots->mBindingParent : nullptr;
 }
 
+nsXBLBinding *
+nsGenericDOMDataNode::GetXBLBinding() const
+{
+  return nullptr;
+}
+
+void
+nsGenericDOMDataNode::SetXBLBinding(nsXBLBinding* aBinding,
+                                    nsBindingManager* aOldBindingManager)
+{
+}
+
 bool
 nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const
 {
   return !(aFlags & ~(eCONTENT | eDATA_NODE));
 }
 
 void
 nsGenericDOMDataNode::SaveSubtreeState()
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -136,16 +136,19 @@ public:
   virtual void SaveSubtreeState() MOZ_OVERRIDE;
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const MOZ_OVERRIDE;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const MOZ_OVERRIDE;
 #endif
 
   virtual nsIContent *GetBindingParent() const MOZ_OVERRIDE;
+  virtual nsXBLBinding *GetXBLBinding() const MOZ_OVERRIDE;
+  virtual void SetXBLBinding(nsXBLBinding* aBinding,
+                             nsBindingManager* aOldBindingManager = nullptr) MOZ_OVERRIDE;
   virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE;
   virtual bool IsLink(nsIURI** aURI) const MOZ_OVERRIDE;
 
   virtual nsIAtom* DoGetID() const MOZ_OVERRIDE;
   virtual const nsAttrValue* DoGetClasses() const MOZ_OVERRIDE;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -244,18 +244,17 @@ nsNodeUtils::LastRelease(nsINode* aNode)
   }
 
   if (aNode->IsElement()) {
     nsIDocument* ownerDoc = aNode->OwnerDoc();
     Element* elem = aNode->AsElement();
     ownerDoc->ClearBoxObjectFor(elem);
     
     NS_ASSERTION(aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) ||
-                 !ownerDoc->BindingManager() ||
-                 !ownerDoc->BindingManager()->GetBinding(elem),
+                 !elem->GetXBLBinding(),
                  "Non-forced node has binding on destruction");
 
     // if NODE_FORCE_XBL_BINDINGS is set, the node might still have a binding
     // attached
     if (aNode->HasFlag(NODE_FORCE_XBL_BINDINGS) &&
         ownerDoc->BindingManager()) {
       ownerDoc->BindingManager()->RemovedFromDocument(elem, ownerDoc);
     }
--- a/content/base/src/nsReferencedElement.cpp
+++ b/content/base/src/nsReferencedElement.cpp
@@ -43,17 +43,17 @@ nsReferencedElement::Reset(nsIContent* a
 
   // Get the current document
   nsIDocument *doc = aFromContent->GetCurrentDoc();
   if (!doc)
     return;
 
   nsIContent* bindingParent = aFromContent->GetBindingParent();
   if (bindingParent) {
-    nsXBLBinding* binding = doc->BindingManager()->GetBinding(bindingParent);
+    nsXBLBinding* binding = bindingParent->GetXBLBinding();
     if (binding) {
       bool isEqualExceptRef;
       rv = aURI->EqualsExceptRef(binding->PrototypeBinding()->DocURI(),
                                  &isEqualExceptRef);
       if (NS_SUCCEEDED(rv) && isEqualExceptRef) {
         // XXX sXBL/XBL2 issue
         // Our content is an anonymous XBL element from a binding inside the
         // same document that the referenced URI points to. In order to avoid
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -183,18 +183,18 @@ SetOrRemoveObject(PLDHashTable& table, n
 
 // Static member variable initialization
 
 // Implement our nsISupports methods
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager)
   tmp->mDestroyed = true;
 
-  if (tmp->mBindingTable.IsInitialized())
-    tmp->mBindingTable.Clear();
+  if (tmp->mBoundContentSet.IsInitialized())
+    tmp->mBoundContentSet.Clear();
 
   if (tmp->mDocumentTable.IsInitialized())
     tmp->mDocumentTable.Clear();
 
   if (tmp->mLoadingDocTable.IsInitialized())
     tmp->mLoadingDocTable.Clear();
 
   if (tmp->mInsertionParentTable.ops)
@@ -275,70 +275,41 @@ nsBindingManager::~nsBindingManager(void
                "Insertion parent table isn't empty!");
   if (mInsertionParentTable.ops)
     PL_DHashTableFinish(&mInsertionParentTable);
   if (mWrapperTable.ops)
     PL_DHashTableFinish(&mWrapperTable);
 }
 
 nsXBLBinding*
-nsBindingManager::GetBinding(nsIContent* aContent)
-{
-  if (aContent && aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
-      mBindingTable.IsInitialized()) {
-    return mBindingTable.GetWeak(aContent);
-  }
-
-  return nullptr;
-}
-
-nsXBLBinding*
 nsBindingManager::GetBindingWithContent(nsIContent* aContent)
 {
-  nsXBLBinding* binding = GetBinding(aContent);
+  nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr;
   return binding ? binding->GetBindingWithContent() : nullptr;
 }
 
-nsresult
-nsBindingManager::SetBinding(nsIContent* aContent, nsXBLBinding* aBinding)
+void
+nsBindingManager::AddBoundContent(nsIContent* aContent)
 {
-  if (!mBindingTable.IsInitialized()) {
-    mBindingTable.Init();
+  if (!mBoundContentSet.IsInitialized()) {
+    mBoundContentSet.Init();
   }
 
-  // After this point, aBinding will be the most-derived binding for aContent.
-  // If we already have a binding for aContent in our table, make sure to
-  // remove it from the attached stack.  Otherwise we might end up firing its
-  // constructor twice (if aBinding inherits from it) or firing its constructor
-  // after aContent has been deleted (if aBinding is null and the content node
-  // dies before we process mAttachedStack).
-  nsRefPtr<nsXBLBinding> oldBinding = GetBinding(aContent);
-  if (oldBinding) {
-    // Don't remove items here as that could mess up an executing
-    // ProcessAttachedQueue
-    uint32_t index = mAttachedStack.IndexOf(oldBinding);
-    if (index != mAttachedStack.NoIndex) {
-      mAttachedStack[index] = nullptr;
-    }
+  mBoundContentSet.PutEntry(aContent);
+}
+
+void
+nsBindingManager::RemoveBoundContent(nsIContent* aContent)
+{
+  if (mBoundContentSet.IsInitialized()) {
+    mBoundContentSet.RemoveEntry(aContent);
   }
 
-  if (aBinding) {
-    aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
-    mBindingTable.Put(aContent, aBinding);
-  } else {
-    mBindingTable.Remove(aContent);
-
-    // The death of the bindings means the death of the JS wrapper.
-    SetWrappedJS(aContent, nullptr);
-    if (oldBinding) {
-      oldBinding->SetBoundElement(nullptr);
-    }
-  }
-
-  return NS_OK;
+  // The death of the bindings means the death of the JS wrapper.
+  SetWrappedJS(aContent, nullptr);
 }
 
 nsIContent*
 nsBindingManager::GetInsertionParent(nsIContent* aContent)
 { 
   if (mInsertionParentTable.ops) {
     return static_cast<nsIContent*>
                       (LookupObject(mInsertionParentTable, aContent));
@@ -381,32 +352,32 @@ void
 nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
                                               nsIDocument* aOldDocument)
 {
   NS_PRECONDITION(aOldDocument != nullptr, "no old document");
 
   if (mDestroyed)
     return;
 
-  nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
+  nsRefPtr<nsXBLBinding> binding = aContent->GetXBLBinding();
   if (binding) {
     binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement());
     binding->ChangeDocument(aOldDocument, nullptr);
-    SetBinding(aContent, nullptr);
+    aContent->SetXBLBinding(nullptr, this);
   }
 
   // Clear out insertion parents and content lists.
   SetInsertionParent(aContent, nullptr);
 }
 
 nsIAtom*
 nsBindingManager::ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID)
 {
-  nsXBLBinding *binding = GetBinding(aContent);
-  
+  nsXBLBinding *binding = aContent->GetXBLBinding();
+
   if (binding) {
     nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
 
     if (base) {
       return base;
     }
   }
 
@@ -428,18 +399,19 @@ nsBindingManager::GetAnonymousNodesFor(n
   nsXBLBinding* binding = GetBindingWithContent(aContent);
   return binding ? binding->GetAnonymousNodeList() : nullptr;
 }
 
 nsresult
 nsBindingManager::ClearBinding(nsIContent* aContent)
 {
   // Hold a ref to the binding so it won't die when we remove it from our table
-  nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
-  
+  nsRefPtr<nsXBLBinding> binding =
+    aContent ? aContent->GetXBLBinding() : nullptr;
+
   if (!binding) {
     return NS_OK;
   }
 
   // For now we can only handle removing a binding if it's the only one
   NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE);
 
   // Hold strong ref in case removing the binding tries to close the
@@ -449,17 +421,17 @@ nsBindingManager::ClearBinding(nsIConten
   // ChangeDocument?
   nsCOMPtr<nsIDocument> doc = aContent->OwnerDoc();
   
   // Finally remove the binding...
   // XXXbz this doesn't remove the implementation!  Should fix!  Until
   // then we need the explicit UnhookEventHandlers here.
   binding->UnhookEventHandlers();
   binding->ChangeDocument(doc, nullptr);
-  SetBinding(aContent, nullptr);
+  aContent->SetXBLBinding(nullptr, this);
   binding->MarkForDeath();
   
   // ...and recreate its frames. We need to do this since the frames may have
   // been removed and style may have changed due to the removal of the
   // anonymous children.
   // XXXbz this should be using the current doc (if any), not the owner doc.
   nsIPresShell *presShell = doc->GetShell();
   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
@@ -485,16 +457,27 @@ nsBindingManager::LoadBindingDocument(ns
                                       aOriginPrincipal, true,
                                       getter_AddRefs(info));
   if (!info)
     return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
+void
+nsBindingManager::RemoveFromAttachedQueue(nsXBLBinding* aBinding)
+{
+  // Don't remove items here as that could mess up an executing
+  // ProcessAttachedQueue. Instead, null the entry in the queue.
+  uint32_t index = mAttachedStack.IndexOf(aBinding);
+  if (index != mAttachedStack.NoIndex) {
+    mAttachedStack[index] = nullptr;
+  }
+}
+
 nsresult
 nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
 {
   if (!mAttachedStack.AppendElement(aBinding))
     return NS_ERROR_OUT_OF_MEMORY;
 
   // If we're in the middle of processing our queue already, don't
   // bother posting the event.
@@ -578,36 +561,37 @@ nsBindingManager::ProcessAttachedQueue(u
 // Keep bindings and bound elements alive while executing detached handlers.
 struct BindingTableReadClosure
 {
   nsCOMArray<nsIContent> mBoundElements;
   nsBindingList          mBindings;
 };
 
 static PLDHashOperator
-AccumulateBindingsToDetach(nsISupports *aKey, nsXBLBinding *aBinding,
+AccumulateBindingsToDetach(nsRefPtrHashKey<nsIContent> *aKey,
                            void* aClosure)
- {
+{
+  nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
   BindingTableReadClosure* closure =
     static_cast<BindingTableReadClosure*>(aClosure);
-  if (aBinding && closure->mBindings.AppendElement(aBinding)) {
-    if (!closure->mBoundElements.AppendObject(aBinding->GetBoundElement())) {
+  if (binding && closure->mBindings.AppendElement(binding)) {
+    if (!closure->mBoundElements.AppendObject(binding->GetBoundElement())) {
       closure->mBindings.RemoveElementAt(closure->mBindings.Length() - 1);
     }
   }
   return PL_DHASH_NEXT;
 }
 
 void
 nsBindingManager::ExecuteDetachedHandlers()
 {
   // Walk our hashtable of bindings.
-  if (mBindingTable.IsInitialized()) {
+  if (mBoundContentSet.IsInitialized()) {
     BindingTableReadClosure closure;
-    mBindingTable.EnumerateRead(AccumulateBindingsToDetach, &closure);
+    mBoundContentSet.EnumerateEntries(AccumulateBindingsToDetach, &closure);
     uint32_t i, count = closure.mBindings.Length();
     for (i = 0; i < count; ++i) {
       closure.mBindings[i]->ExecuteDetachedHandler();
     }
   }
 }
 
 nsresult
@@ -667,35 +651,38 @@ void
 nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL)
 {
   if (mLoadingDocTable.IsInitialized()) {
     mLoadingDocTable.Remove(aURL);
   }
 }
 
 static PLDHashOperator
-MarkForDeath(nsISupports *aKey, nsXBLBinding *aBinding, void* aClosure)
+MarkForDeath(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
 {
-  if (aBinding->MarkedForDeath())
+  nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
+
+  if (binding->MarkedForDeath())
     return PL_DHASH_NEXT; // Already marked for death.
 
   nsAutoCString path;
-  aBinding->PrototypeBinding()->DocURI()->GetPath(path);
+  binding->PrototypeBinding()->DocURI()->GetPath(path);
 
   if (!strncmp(path.get(), "/skin", 5))
-    aBinding->MarkForDeath();
-  
+    binding->MarkForDeath();
+
   return PL_DHASH_NEXT;
 }
 
 void
 nsBindingManager::FlushSkinBindings()
 {
-  if (mBindingTable.IsInitialized())
-    mBindingTable.EnumerateRead(MarkForDeath, nullptr);
+  if (mBoundContentSet.IsInitialized()) {
+    mBoundContentSet.EnumerateEntries(MarkForDeath, nullptr);
+  }
 }
 
 // Used below to protect from recurring in QI calls through XPConnect.
 struct AntiRecursionData {
   nsIContent* element; 
   REFNSIID iid; 
   AntiRecursionData* next;
 
@@ -705,17 +692,17 @@ struct AntiRecursionData {
     : element(aElement), iid(aIID), next(aNext) {}
 };
 
 nsresult
 nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
                                            void** aResult)
 {
   *aResult = nullptr;
-  nsXBLBinding *binding = GetBinding(aContent);
+  nsXBLBinding *binding = aContent ? aContent->GetXBLBinding() : nullptr;
   if (binding) {
     // The binding should not be asked for nsISupports
     NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports");
     if (binding->ImplementsInterface(aIID)) {
       nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent);
 
       if (wrappedJS) {
         // Protect from recurring in QI calls through XPConnect. 
@@ -811,17 +798,17 @@ nsBindingManager::WalkRules(nsIStyleRule
   
   NS_ASSERTION(aData->mElement, "How did that happen?");
 
   // Walk the binding scope chain, starting with the binding attached to our
   // content, up till we run out of scopes or we get cut off.
   nsIContent *content = aData->mElement;
   
   do {
-    nsXBLBinding *binding = GetBinding(content);
+    nsXBLBinding *binding = content->GetXBLBinding();
     if (binding) {
       aData->mTreeMatchContext.mScopedRoot = content;
       binding->WalkRules(aFunc, aData);
       // If we're not looking at our original content, allow the binding to cut
       // off style inheritance
       if (content != aData->mElement) {
         if (!binding->InheritsStyle()) {
           // Go no further; we're not inheriting style from anything above here
@@ -845,20 +832,21 @@ nsBindingManager::WalkRules(nsIStyleRule
   aData->mTreeMatchContext.mScopedRoot = nullptr;
 
   return NS_OK;
 }
 
 typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
 
 static PLDHashOperator
-EnumRuleProcessors(nsISupports *aKey, nsXBLBinding *aBinding, void* aClosure)
+EnumRuleProcessors(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
 {
+  nsIContent *boundContent = aKey->GetKey();
   RuleProcessorSet *set = static_cast<RuleProcessorSet*>(aClosure);
-  for (nsXBLBinding *binding = aBinding; binding;
+  for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
        binding = binding->GetBaseBinding()) {
     nsIStyleRuleProcessor *ruleProc =
       binding->PrototypeBinding()->GetRuleProcessor();
     if (ruleProc) {
       if (!set->IsInitialized()) {
         set->Init(16);
       }
       set->PutEntry(ruleProc);
@@ -883,21 +871,22 @@ EnumWalkAllRules(nsPtrHashKey<nsIStyleRu
 
   return PL_DHASH_NEXT;
 }
 
 void
 nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
                                ElementDependentRuleProcessorData* aData)
 {
-  if (!mBindingTable.IsInitialized())
+  if (!mBoundContentSet.IsInitialized()) {
     return;
+  }
 
   RuleProcessorSet set;
-  mBindingTable.EnumerateRead(EnumRuleProcessors, &set);
+  mBoundContentSet.EnumerateEntries(EnumRuleProcessors, &set);
   if (!set.IsInitialized())
     return;
 
   WalkAllRulesData data = { aFunc, aData };
   set.EnumerateEntries(EnumWalkAllRules, &data);
 }
 
 struct MediumFeaturesChangedData {
@@ -919,54 +908,55 @@ EnumMediumFeaturesChanged(nsPtrHashKey<n
   return PL_DHASH_NEXT;
 }
 
 nsresult
 nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
                                         bool* aRulesChanged)
 {
   *aRulesChanged = false;
-  if (!mBindingTable.IsInitialized())
+  if (!mBoundContentSet.IsInitialized()) {
     return NS_OK;
+  }
 
   RuleProcessorSet set;
-  mBindingTable.EnumerateRead(EnumRuleProcessors, &set);
+  mBoundContentSet.EnumerateEntries(EnumRuleProcessors, &set);
   if (!set.IsInitialized())
     return NS_OK;
 
   MediumFeaturesChangedData data = { aPresContext, aRulesChanged };
   set.EnumerateEntries(EnumMediumFeaturesChanged, &data);
   return NS_OK;
 }
 
 static PLDHashOperator
-EnumAppendAllSheets(nsISupports *aKey, nsXBLBinding *aBinding, void* aClosure)
+EnumAppendAllSheets(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
 {
+  nsIContent *boundContent = aKey->GetKey();
   nsTArray<nsCSSStyleSheet*>* array =
     static_cast<nsTArray<nsCSSStyleSheet*>*>(aClosure);
-  for (nsXBLBinding *binding = aBinding; binding;
+  for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
        binding = binding->GetBaseBinding()) {
     nsXBLPrototypeResources::sheet_array_type* sheets =
       binding->PrototypeBinding()->GetStyleSheets();
     if (sheets) {
       // Copy from nsTArray<nsRefPtr<nsCSSStyleSheet> > to
       // nsTArray<nsCSSStyleSheet*>.
       array->AppendElements(*sheets);
     }
   }
   return PL_DHASH_NEXT;
 }
 
 void
 nsBindingManager::AppendAllSheets(nsTArray<nsCSSStyleSheet*>& aArray)
 {
-  if (!mBindingTable.IsInitialized())
-    return;
-
-  mBindingTable.EnumerateRead(EnumAppendAllSheets, &aArray);
+  if (mBoundContentSet.IsInitialized()) {
+    mBoundContentSet.EnumerateEntries(EnumAppendAllSheets, &aArray);
+  }
 }
 
 static void
 InsertAppendedContent(nsBindingManager* aManager,
                       XBLChildrenElement* aPoint,
                       nsIContent* aFirstNewContent)
 {
   uint32_t insertionIndex;
@@ -1133,18 +1123,19 @@ nsBindingManager::DropDocumentReference(
   if (mProcessAttachedQueueEvent) {
     mProcessAttachedQueueEvent->Revoke();
   }
 
   if (mInsertionParentTable.ops)
     PL_DHashTableFinish(&(mInsertionParentTable));
   mInsertionParentTable.ops = nullptr;
 
-  if (mBindingTable.IsInitialized())
-    mBindingTable.Clear();
+  if (mBoundContentSet.IsInitialized()) {
+    mBoundContentSet.Clear();
+  }
 
   mDocument = nullptr;
 }
 
 void
 nsBindingManager::Traverse(nsIContent *aContent,
                            nsCycleCollectionTraversalCallback &cb)
 {
@@ -1161,21 +1152,19 @@ nsBindingManager::Traverse(nsIContent *a
     cb.NoteXPCOMChild(value);
   }
 
   // XXXbz how exactly would NODE_MAY_BE_IN_BINDING_MNGR end up on non-elements?
   if (!aContent->IsElement()) {
     return;
   }
 
-  nsXBLBinding *binding = GetBinding(aContent);
-  if (binding) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBindingTable key");
+  if (mBoundContentSet.Contains(aContent)) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBoundContentSet entry");
     cb.NoteXPCOMChild(aContent);
-    CycleCollectionNoteChild(cb, binding, "[via binding manager] mBindingTable value");
   }
   if (mWrapperTable.ops &&
       (value = LookupObject(mWrapperTable, aContent))) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
     cb.NoteXPCOMChild(aContent);
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
     cb.NoteXPCOMChild(value);
   }
--- a/content/xbl/src/nsBindingManager.h
+++ b/content/xbl/src/nsBindingManager.h
@@ -38,19 +38,20 @@ public:
 
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   nsBindingManager(nsIDocument* aDocument);
   ~nsBindingManager();
 
-  nsXBLBinding* GetBinding(nsIContent* aContent);
   nsXBLBinding* GetBindingWithContent(nsIContent* aContent);
-  nsresult SetBinding(nsIContent* aContent, nsXBLBinding* aBinding);
+
+  void AddBoundContent(nsIContent* aContent);
+  void RemoveBoundContent(nsIContent* aContent);
 
   nsIContent* GetInsertionParent(nsIContent* aContent);
   nsresult SetInsertionParent(nsIContent* aContent, nsIContent* aResult);
 
   /**
    * Notify the binding manager that an element
    * has been removed from its document,
    * so that it can update any bindings or
@@ -80,16 +81,17 @@ public:
   nsresult GetAnonymousNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult);
   nsINodeList* GetAnonymousNodesFor(nsIContent* aContent);
 
   nsresult ClearBinding(nsIContent* aContent);
   nsresult LoadBindingDocument(nsIDocument* aBoundDoc, nsIURI* aURL,
                                nsIPrincipal* aOriginPrincipal);
 
   nsresult AddToAttachedQueue(nsXBLBinding* aBinding);
+  void RemoveFromAttachedQueue(nsXBLBinding* aBinding);
   void ProcessAttachedQueue(uint32_t aSkipSize = 0);
 
   void ExecuteDetachedHandlers();
 
   nsresult PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo);
   nsXBLDocumentInfo* GetXBLDocumentInfo(nsIURI* aURI);
   void RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo);
 
@@ -155,19 +157,18 @@ protected:
   // mProcessAttachedQueueEvent
   void DoProcessAttachedQueue();
 
   // Post an event to process the attached queue.
   void PostProcessAttachedQueueEvent();
 
 // MEMBER VARIABLES
 protected: 
-  // A mapping from nsIContent* to the nsXBLBinding* that is
-  // installed on that element.
-  nsRefPtrHashtable<nsISupportsHashKey,nsXBLBinding> mBindingTable;
+  // A set of nsIContent that currently have a binding installed.
+  nsTHashtable<nsRefPtrHashKey<nsIContent> > mBoundContentSet;
 
   // A mapping from nsIContent* to nsIContent*.  The insertion parent
   // is our one true parent in the transformed DOM.  This gives us a
   // more-or-less O(1) way of obtaining our transformed parent.
   PLDHashTable mInsertionParentTable;
 
   // A mapping from nsIContent* to nsIXPWrappedJS* (an XPConnect
   // wrapper for JS objects).  For XBL bindings that implement XPIDL
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -1168,16 +1168,25 @@ nsXBLBinding::LookupMemberInternal(JSCon
   }
 
   // Find our class object. It's in a protected scope and permanent just in case,
   // so should be there no matter what.
   JS::RootedValue classObject(aCx);
   if (!JS_GetProperty(aCx, aXBLScope, mJSClass->name, classObject.address())) {
     return false;
   }
+
+  // The bound element may have been adoped by a document and have a different
+  // wrapper (and different xbl scope) than when the binding was applied, in
+  // this case getting the class object will fail. Behave as if the class
+  // object did not exist.
+  if (classObject.isUndefined()) {
+    return true;
+  }
+
   MOZ_ASSERT(classObject.isObject());
 
   // Look for the property on this binding. If it's not there, try the next
   // binding on the chain.
   nsXBLProtoImpl* impl = mPrototypeBinding->GetImplementation();
   if (impl && !impl->LookupMember(aCx, aName, aNameAsId, aDesc,
                                   &classObject.toObject()))
   {
--- a/content/xbl/src/nsXBLService.cpp
+++ b/content/xbl/src/nsXBLService.cpp
@@ -67,21 +67,20 @@ IsAncestorBinding(nsIDocument* aDocument
                   nsIURI* aChildBindingURI,
                   nsIContent* aChild)
 {
   NS_ASSERTION(aDocument, "expected a document");
   NS_ASSERTION(aChildBindingURI, "expected a binding URI");
   NS_ASSERTION(aChild, "expected a child content");
 
   uint32_t bindingRecursion = 0;
-  nsBindingManager* bindingManager = aDocument->BindingManager();
   for (nsIContent *bindingParent = aChild->GetBindingParent();
        bindingParent;
        bindingParent = bindingParent->GetBindingParent()) {
-    nsXBLBinding* binding = bindingManager->GetBinding(bindingParent);
+    nsXBLBinding* binding = bindingParent->GetXBLBinding();
     if (!binding) {
       continue;
     }
 
     if (binding->PrototypeBinding()->CompareBindingURI(aChildBindingURI)) {
       ++bindingRecursion;
       if (bindingRecursion < NS_MAX_XBL_BINDING_RECURSION) {
         continue;
@@ -446,19 +445,17 @@ nsXBLService::LoadBindings(nsIContent* a
   nsAutoCString urlspec;
   if (nsContentUtils::GetWrapperSafeScriptFilename(document, aURL, urlspec)) {
     // Block an attempt to load a binding that has special wrapper
     // automation needs.
 
     return NS_OK;
   }
 
-  nsBindingManager *bindingManager = document->BindingManager();
-  
-  nsXBLBinding *binding = bindingManager->GetBinding(aContent);
+  nsXBLBinding *binding = aContent->GetXBLBinding();
   if (binding) {
     if (binding->MarkedForDeath()) {
       FlushStyleBindings(aContent);
       binding = nullptr;
     }
     else {
       // See if the URIs match.
       if (binding->PrototypeBinding()->CompareBindingURI(aURL))
@@ -491,17 +488,17 @@ nsXBLService::LoadBindings(nsIContent* a
 
   // We loaded a style binding.  It goes on the end.
   if (binding) {
     // Get the last binding that is in the append layer.
     binding->RootBinding()->SetBaseBinding(newBinding);
   }
   else {
     // Install the binding on the content node.
-    bindingManager->SetBinding(aContent, newBinding);
+    aContent->SetXBLBinding(newBinding);
   }
 
   {
     nsAutoScriptBlocker scriptBlocker;
 
     // Set the binding's bound element.
     newBinding->SetBoundElement(aContent);
 
@@ -524,25 +521,22 @@ nsXBLService::LoadBindings(nsIContent* a
   return NS_OK; 
 }
 
 nsresult
 nsXBLService::FlushStyleBindings(nsIContent* aContent)
 {
   nsCOMPtr<nsIDocument> document = aContent->OwnerDoc();
 
-  nsBindingManager *bindingManager = document->BindingManager();
-  
-  nsXBLBinding *binding = bindingManager->GetBinding(aContent);
-  
+  nsXBLBinding *binding = aContent->GetXBLBinding();
   if (binding) {
     // Clear out the script references.
     binding->ChangeDocument(document, nullptr);
 
-    bindingManager->SetBinding(aContent, nullptr); // Flush old style bindings
+    aContent->SetXBLBinding(nullptr); // Flush old style bindings
   }
    
   return NS_OK;
 }
 
 //
 // AttachGlobalKeyHandler
 //
--- a/layout/inspector/src/inDOMUtils.cpp
+++ b/layout/inspector/src/inDOMUtils.cpp
@@ -541,18 +541,17 @@ inDOMUtils::GetBindingURLs(nsIDOMElement
 
   nsCOMPtr<nsIMutableArray> urls = do_CreateInstance(NS_ARRAY_CONTRACTID);
   if (!urls)
     return NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
   NS_ENSURE_ARG_POINTER(content);
 
-  nsIDocument *ownerDoc = content->OwnerDoc();
-  nsXBLBinding *binding = ownerDoc->BindingManager()->GetBinding(content);
+  nsXBLBinding *binding = content->GetXBLBinding();
 
   while (binding) {
     urls->AppendElement(binding->PrototypeBinding()->BindingURI(), false);
     binding = binding->GetBaseBinding();
   }
 
   NS_ADDREF(*_retval = urls);
   return NS_OK;