Bug 378866, r=peterv,sicking, sr=sicking, a=blocking1.9+
authorOlli.Pettay@helsinki.fi
Fri, 12 Oct 2007 04:07:29 -0700
changeset 6864 3a6cee00e1a5de4d70ac13f92f085993f6201e28
parent 6863 2cd64e0b5382f6bb5fc50711199895de0f05a77d
child 6865 590e0a79aacba64ff0fe9422658ca09902ef3fa3
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv, sicking, sicking, blocking1.9
bugs378866
milestone1.9a9pre
Bug 378866, r=peterv,sicking, sr=sicking, a=blocking1.9+
content/base/public/nsINode.h
content/xbl/src/nsBindingManager.cpp
content/xbl/src/nsBindingManager.h
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLBinding.h
content/xbl/src/nsXBLInsertionPoint.h
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -93,18 +93,20 @@ enum {
   NODE_IS_EDITABLE =             0x00000100U,
 
   // Optimizations to quickly check whether element may have ID, class or style
   // attributes. Not all element implementations may use these!
   NODE_MAY_HAVE_ID =             0x00000200U,
   NODE_MAY_HAVE_CLASS =          0x00000400U,
   NODE_MAY_HAVE_STYLE =          0x00000800U,
 
+  NODE_IS_INSERTION_PARENT =     0x00001000U,
+
   // Four bits for the script-type ID
-  NODE_SCRIPT_TYPE_OFFSET =               12,
+  NODE_SCRIPT_TYPE_OFFSET =               13,
 
   NODE_SCRIPT_TYPE_SIZE =                  4,
 
   // Remaining bits are node type specific.
   NODE_TYPE_SPECIFIC_BITS_OFFSET =
     NODE_SCRIPT_TYPE_OFFSET + NODE_SCRIPT_TYPE_SIZE
 };
 
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -82,36 +82,43 @@
 #include "nsBindingManager.h"
 
 #include "nsThreadUtils.h"
 
 // ==================================================================
 // = nsAnonymousContentList 
 // ==================================================================
 
+#define NS_ANONYMOUS_CONTENT_LIST_IID \
+  { 0xa29df1f8, 0xaeca, 0x4356, \
+    { 0xa8, 0xc2, 0xa7, 0x24, 0xa2, 0x11, 0x73, 0xac } }
+
 class nsAnonymousContentList : public nsIDOMNodeList
 {
 public:
   nsAnonymousContentList(nsInsertionPointList* aElements);
   virtual ~nsAnonymousContentList();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsAnonymousContentList)
   // nsIDOMNodeList interface
   NS_DECL_NSIDOMNODELIST
 
   PRInt32 GetInsertionPointCount() { return mElements->Length(); }
 
   nsXBLInsertionPoint* GetInsertionPointAt(PRInt32 i) { return static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i)); }
   void RemoveInsertionPointAt(PRInt32 i) { mElements->RemoveElementAt(i); }
-
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_ANONYMOUS_CONTENT_LIST_IID)
 private:
   nsInsertionPointList* mElements;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(nsAnonymousContentList,
+                              NS_ANONYMOUS_CONTENT_LIST_IID)
+
 nsAnonymousContentList::nsAnonymousContentList(nsInsertionPointList* aElements)
   : mElements(aElements)
 {
   MOZ_COUNT_CTOR(nsAnonymousContentList);
 
   // We don't reference count our Anonymous reference (to avoid circular
   // references). We'll be told when the Anonymous goes away.
 }
@@ -124,16 +131,17 @@ nsAnonymousContentList::~nsAnonymousCont
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsAnonymousContentList)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList)
 
 NS_INTERFACE_MAP_BEGIN(nsAnonymousContentList)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNodeList)
+  NS_INTERFACE_MAP_ENTRY(nsAnonymousContentList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NodeList)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsAnonymousContentList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsAnonymousContentList)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAnonymousContentList)
   {
@@ -294,18 +302,27 @@ SetOrRemoveObject(PLDHashTable& table, n
       table.ops = nsnull;
       return NS_ERROR_OUT_OF_MEMORY;
     }
     aKey->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
     return AddObjectEntry(table, aKey, aValue);
   }
 
   // no value, so remove the key from the table
-  if (table.ops)
-    RemoveObjectEntry(table, aKey);
+  if (table.ops) {
+    ObjectEntry* entry =
+      static_cast<ObjectEntry*>
+        (PL_DHashTableOperate(&table, aKey, PL_DHASH_LOOKUP));
+    if (entry && PL_DHASH_ENTRY_IS_BUSY(entry)) {
+      // Keep key and value alive while removing the entry.
+      nsCOMPtr<nsISupports> key = entry->GetKey();
+      nsCOMPtr<nsISupports> value = entry->GetValue();
+      RemoveObjectEntry(table, aKey);
+    }
+  }
   return NS_OK;
 }
 
 // Implementation /////////////////////////////////////////////////////////////////
 
 // Static member variable initialization
 
 // Implement our nsISupports methods
@@ -397,22 +414,70 @@ nsBindingManager::nsBindingManager(nsIDo
 }
 
 nsBindingManager::~nsBindingManager(void)
 {
   if (mContentListTable.ops)
     PL_DHashTableFinish(&mContentListTable);
   if (mAnonymousNodesTable.ops)
     PL_DHashTableFinish(&mAnonymousNodesTable);
+  NS_ASSERTION(!mInsertionParentTable.ops || !mInsertionParentTable.entryCount,
+               "Insertion parent table isn't empty!");
   if (mInsertionParentTable.ops)
     PL_DHashTableFinish(&mInsertionParentTable);
   if (mWrapperTable.ops)
     PL_DHashTableFinish(&mWrapperTable);
 }
 
+PLDHashOperator
+PR_CALLBACK RemoveInsertionParentCB(PLDHashTable* aTable, PLDHashEntryHdr* aEntry,
+                                  PRUint32 aNumber, void* aArg)
+{
+  return (static_cast<ObjectEntry*>(aEntry)->GetValue() ==
+          static_cast<nsISupports*>(aArg)) ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
+}
+
+static void
+RemoveInsertionParentForNodeList(nsIDOMNodeList* aList, nsIContent* aParent)
+{
+  nsCOMPtr<nsAnonymousContentList> list = do_QueryInterface(aList);
+  if (list) {
+    PRInt32 count = list->GetInsertionPointCount();
+    for (PRInt32 i = 0; i < count; ++i) {
+      nsRefPtr<nsXBLInsertionPoint> currPoint = list->GetInsertionPointAt(i);
+      nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContent();
+      if (defContent) {
+        defContent->UnbindFromTree();
+      }
+#ifdef DEBUG
+      nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
+      NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
+#endif
+      currPoint->ClearInsertionParent();
+    }
+  }
+}
+
+void
+nsBindingManager::RemoveInsertionParent(nsIContent* aParent)
+{
+  nsCOMPtr<nsIDOMNodeList> contentlist;
+  GetContentListFor(aParent, getter_AddRefs(contentlist));
+  RemoveInsertionParentForNodeList(contentlist, aParent);
+
+  nsCOMPtr<nsIDOMNodeList> anonnodes;
+  GetAnonymousNodesFor(aParent, getter_AddRefs(anonnodes));
+  RemoveInsertionParentForNodeList(anonnodes, aParent);
+
+  if (mInsertionParentTable.ops) {
+    PL_DHashTableEnumerate(&mInsertionParentTable, RemoveInsertionParentCB,
+                           static_cast<nsISupports*>(aParent));
+  }
+}
+
 nsXBLBinding*
 nsBindingManager::GetBinding(nsIContent* aContent)
 {
   if (aContent && aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
       mBindingTable.IsInitialized()) {
     return mBindingTable.GetWeak(aContent);
   }
 
@@ -428,18 +493,28 @@ nsBindingManager::SetBinding(nsIContent*
   }
 
   // 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).
-  nsXBLBinding* oldBinding = mBindingTable.GetWeak(aContent);
+  nsRefPtr<nsXBLBinding> oldBinding = GetBinding(aContent);
   if (oldBinding) {
+    if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
+      nsRefPtr<nsXBLBinding> parentBinding =
+        GetBinding(aContent->GetBindingParent());
+      // Clear insertion parent only if we don't have a parent binding which
+      // marked content to be an insertion parent. See also ChangeDocumentFor().
+      if (!parentBinding || !parentBinding->HasInsertionParent(aContent)) {
+        RemoveInsertionParent(aContent);
+        aContent->UnsetFlags(NODE_IS_INSERTION_PARENT);
+      }
+    }
     mAttachedStack.RemoveElement(oldBinding);
   }
   
   PRBool result = PR_TRUE;
 
   if (aBinding) {
     aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
     result = mBindingTable.Put(aContent, aBinding);
@@ -466,16 +541,18 @@ nsBindingManager::GetInsertionParent(nsI
   }
 
   return nsnull;
 }
 
 nsresult
 nsBindingManager::SetInsertionParent(nsIContent* aContent, nsIContent* aParent)
 {
+  NS_ASSERTION(!aParent || aParent->HasFlag(NODE_IS_INSERTION_PARENT),
+               "Insertion parent should have NODE_IS_INSERTION_PARENT flag!");
   return SetOrRemoveObject(mInsertionParentTable, aContent, aParent);
 }
 
 nsIXPConnectWrappedJS*
 nsBindingManager::GetWrappedJS(nsIContent* aContent)
 { 
   if (mWrapperTable.ops) {
     return static_cast<nsIXPConnectWrappedJS*>(LookupObject(mWrapperTable, aContent));
@@ -500,16 +577,29 @@ nsBindingManager::ChangeDocumentFor(nsIC
   NS_PRECONDITION(!aNewDocument,
                   "Changing to a non-null new document not supported yet");
   if (! aOldDocument)
     return NS_ERROR_NULL_POINTER;
 
   // Hold a ref to the binding so it won't die when we remove it from our
   // table.
   nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
+  if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
+    nsRefPtr<nsXBLBinding> parentBinding = GetBinding(aContent->GetBindingParent());
+    if (parentBinding) {
+      parentBinding->RemoveInsertionParent(aContent);
+      // Clear insertion parent only if we don't have a binding which
+      // marked content to be an insertion parent. See also SetBinding().
+      if (!binding || !binding->HasInsertionParent(aContent)) {
+        RemoveInsertionParent(aContent);
+        aContent->UnsetFlags(NODE_IS_INSERTION_PARENT);
+      }
+    }
+  }
+
   if (binding) {
     binding->ChangeDocument(aOldDocument, aNewDocument);
     SetBinding(aContent, nsnull);
     if (aNewDocument)
       aNewDocument->BindingManager()->SetBinding(aContent, binding);
   }
 
   // Clear out insertion parents and content lists.
--- a/content/xbl/src/nsBindingManager.h
+++ b/content/xbl/src/nsBindingManager.h
@@ -230,16 +230,17 @@ protected:
                                      func_, params_);
 
   // Same as ProcessAttachedQueue, but also nulls out
   // mProcessAttachedQueueEvent
   void DoProcessAttachedQueue();
 
 // MEMBER VARIABLES
 protected: 
+  void RemoveInsertionParent(nsIContent* aParent);
   // A mapping from nsIContent* to the nsXBLBinding* that is
   // installed on that element.
   nsRefPtrHashtable<nsISupportsHashKey,nsXBLBinding> mBindingTable;
 
   // A mapping from nsIContent* to an nsIDOMNodeList*
   // (nsAnonymousContentList*).  This list contains an accurate
   // reflection of our *explicit* children (once intermingled with
   // insertion points) in the altered DOM.
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -437,16 +437,20 @@ BuildContentLists(nsISupports* aKey,
   if (!contentList) {
     data->mRv = NS_ERROR_OUT_OF_MEMORY;
     return PL_DHASH_STOP;
   }
 
   // Figure out the relevant content node.
   nsXBLInsertionPoint* currPoint = aData->ElementAt(0);
   nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
+  if (!parent) {
+    data->mRv = NS_ERROR_FAILURE;
+    return PL_DHASH_STOP;
+  }
   PRInt32 currIndex = currPoint->GetInsertionIndex();
 
   nsCOMPtr<nsIDOMNodeList> nodeList;
   if (parent == boundElement) {
     // We are altering anonymous nodes to accommodate insertion points.
     nodeList = binding->GetAnonymousNodes();
   }
   else {
@@ -521,16 +525,20 @@ RealizeDefaultContent(nsISupports* aKey,
     
     if (insCount == 0) {
       nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
       if (defContent) {
         // We need to take this template and use it to realize the
         // actual default content (through cloning).
         // Clone this insertion point element.
         nsCOMPtr<nsIContent> insParent = currPoint->GetInsertionParent();
+        if (!insParent) {
+          data->mRv = NS_ERROR_FAILURE;
+          return PL_DHASH_STOP;
+        }
         nsIDocument *document = insParent->GetOwnerDoc();
         if (!document) {
           data->mRv = NS_ERROR_FAILURE;
           return PL_DHASH_STOP;
         }
 
         nsCOMPtr<nsIDOMNode> clonedNode;
         nsCOMArray<nsINode> nodesWithProperties;
@@ -1321,16 +1329,57 @@ nsXBLBinding::AllowScripts()
   nsCOMPtr<nsIDocument> ourDocument;
   mPrototypeBinding->XBLDocumentInfo()->GetDocument(getter_AddRefs(ourDocument));
   PRBool canExecute;
   nsresult rv =
     mgr->CanExecuteScripts(cx, ourDocument->NodePrincipal(), &canExecute);
   return NS_SUCCEEDED(rv) && canExecute;
 }
 
+void
+nsXBLBinding::RemoveInsertionParent(nsIContent* aParent)
+{
+  if (mNextBinding) {
+    mNextBinding->RemoveInsertionParent(aParent);
+  }
+  if (mInsertionPointTable) {
+    nsInsertionPointList* list = nsnull;
+    mInsertionPointTable->Get(aParent, &list);
+    if (list) {
+      PRInt32 count = list->Length();
+      for (PRInt32 i = 0; i < count; ++i) {
+        nsRefPtr<nsXBLInsertionPoint> currPoint = list->ElementAt(i);
+        nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContent();
+        if (defContent) {
+          defContent->UnbindFromTree();
+        }
+#ifdef DEBUG
+        nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
+        NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
+#endif
+        currPoint->ClearInsertionParent();
+      }
+      mInsertionPointTable->Remove(aParent);
+    }
+  }
+}
+
+PRBool
+nsXBLBinding::HasInsertionParent(nsIContent* aParent)
+{
+  if (mInsertionPointTable) {
+    nsInsertionPointList* list = nsnull;
+    mInsertionPointTable->Get(aParent, &list);
+    if (list) {
+      return PR_TRUE;
+    }
+  }
+  return mNextBinding ? mNextBinding->HasInsertionParent(aParent) : PR_FALSE;
+}
+
 nsresult
 nsXBLBinding::GetInsertionPointsFor(nsIContent* aParent,
                                     nsInsertionPointList** aResult)
 {
   if (!mInsertionPointTable) {
     mInsertionPointTable =
       new nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>;
     if (!mInsertionPointTable || !mInsertionPointTable->Init(4)) {
@@ -1344,16 +1393,19 @@ nsXBLBinding::GetInsertionPointsFor(nsIC
 
   if (!*aResult) {
     *aResult = new nsInsertionPointList;
     if (!*aResult || !mInsertionPointTable->Put(aParent, *aResult)) {
       delete *aResult;
       *aResult = nsnull;
       return NS_ERROR_OUT_OF_MEMORY;
     }
+    if (aParent) {
+      aParent->SetFlags(NODE_IS_INSERTION_PARENT);
+    }
   }
 
   return NS_OK;
 }
 
 nsInsertionPointList*
 nsXBLBinding::GetExistingInsertionPointsFor(nsIContent* aParent)
 {
--- a/content/xbl/src/nsXBLBinding.h
+++ b/content/xbl/src/nsXBLBinding.h
@@ -159,16 +159,19 @@ public:
 
   static nsresult DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
                                 const nsAFlatCString& aClassName,
                                 nsXBLPrototypeBinding* aProtoBinding,
                                 void **aClassObject);
 
   PRBool AllowScripts();  // XXX make const
 
+  void RemoveInsertionParent(nsIContent* aParent);
+  PRBool HasInsertionParent(nsIContent* aParent);
+
 // MEMBER VARIABLES
 protected:
   nsAutoRefCnt mRefCnt;
   nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
   nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.
   nsRefPtr<nsXBLBinding> mNextBinding; // Strong. The derived binding owns the base class bindings.
   
   nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
--- a/content/xbl/src/nsXBLInsertionPoint.h
+++ b/content/xbl/src/nsXBLInsertionPoint.h
@@ -58,16 +58,18 @@ public:
     return mRefCnt;
   }
 
   nsrefcnt Release();
 
   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPoint)
 
   already_AddRefed<nsIContent> GetInsertionParent();
+  void ClearInsertionParent() { mParentElement = nsnull; }
+
   PRInt32 GetInsertionIndex() { return mIndex; }
 
   void SetDefaultContent(nsIContent* aDefaultContent) { mDefaultContent = aDefaultContent; }
   already_AddRefed<nsIContent> GetDefaultContent();
 
   void SetDefaultContentTemplate(nsIContent* aDefaultContent) { mDefaultContentTemplate = aDefaultContent; }
   already_AddRefed<nsIContent> GetDefaultContentTemplate();