Fix for bug 407034 (JS_Assert "!rt->gcRunning" unbinding link elements in cycle collector with JS protocol handlers), r/sr=dbaron.
authorpeterv@propagandism.org
Tue, 29 Jan 2008 18:05:43 -0800
changeset 10981 d953e8396f168886c313d6b870f65fbea108fcb0
parent 10980 8e641dbfcd400269e60ffe20221bd83ae018bb8b
child 10982 b2e6aa3448fc4ce9a15b8c43904a78bbae65dcf1
push idunknown
push userunknown
push dateunknown
bugs407034
milestone1.9b3pre
Fix for bug 407034 (JS_Assert "!rt->gcRunning" unbinding link elements in cycle collector with JS protocol handlers), r/sr=dbaron.
content/xbl/src/nsXBLDocumentInfo.cpp
content/xbl/src/nsXBLProtoImpl.cpp
content/xbl/src/nsXBLProtoImpl.h
content/xbl/src/nsXBLPrototypeBinding.cpp
content/xbl/src/nsXBLPrototypeBinding.h
content/xbl/src/nsXBLPrototypeHandler.cpp
content/xbl/src/nsXBLPrototypeHandler.h
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
dom/src/base/nsJSEnvironment.cpp
dom/src/base/nsJSTimeoutHandler.cpp
dom/src/events/nsJSEventListener.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcwrappedjs.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/glue/nsCycleCollectionParticipant.cpp
xpcom/glue/nsCycleCollectionParticipant.h
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -440,20 +440,20 @@ TraverseProtos(nsHashKey *aKey, void *aD
   nsCycleCollectionTraversalCallback *cb = 
     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
   nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
   proto->Traverse(*cb);
   return kHashEnumerateNext;
 }
 
 static PRIntn PR_CALLBACK
-UnlinkProtos(nsHashKey *aKey, void *aData, void* aClosure)
+UnlinkProtoJSObjects(nsHashKey *aKey, void *aData, void* aClosure)
 {
   nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
-  proto->Unlink();
+  proto->UnlinkJSObjects();
   return kHashEnumerateNext;
 }
 
 struct ProtoTracer
 {
   TraceCallback mCallback;
   void *mClosure;
 };
@@ -463,20 +463,22 @@ TraceProtos(nsHashKey *aKey, void *aData
 {
   ProtoTracer* closure = static_cast<ProtoTracer*>(aClosure);
   nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
   proto->Trace(closure->mCallback, closure->mClosure);
   return kHashEnumerateNext;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo)
+NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsXBLDocumentInfo)
   if (tmp->mBindingTable) {
-    tmp->mBindingTable->Enumerate(UnlinkProtos, nsnull);
+    tmp->mBindingTable->Enumerate(UnlinkProtoJSObjects, nsnull);
   }
+NS_IMPL_CYCLE_COLLECTION_ROOT_END
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mGlobalObject)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
   if (tmp->mBindingTable) {
     tmp->mBindingTable->Enumerate(TraverseProtos, &cb);
   }
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -211,17 +211,17 @@ nsXBLProtoImpl::Trace(TraceCallback aCal
 
   nsXBLProtoImplMember *member;
   for (member = mMembers; member; member = member->GetNext()) {
     member->Trace(aCallback, aClosure);
   }
 }
 
 void
-nsXBLProtoImpl::Unlink()
+nsXBLProtoImpl::UnlinkJSObjects()
 {
   if (mClassObject) {
     DestroyMembers(nsnull);
   }
 }
 
 nsXBLProtoImplField*
 nsXBLProtoImpl::FindField(const nsString& aFieldName) const
--- a/content/xbl/src/nsXBLProtoImpl.h
+++ b/content/xbl/src/nsXBLProtoImpl.h
@@ -86,17 +86,17 @@ public:
 
   void SetFieldList(nsXBLProtoImplField* aFieldList)
   {
     delete mFields;
     mFields = aFieldList;
   }
 
   void Trace(TraceCallback aCallback, void *aClosure) const;
-  void Unlink();
+  void UnlinkJSObjects();
 
   nsXBLProtoImplField* FindField(const nsString& aFieldName) const;
 
   // Resolve all the fields for this implementation on the object |obj| False
   // return means a JS exception was set.
   PRBool ResolveAllFields(JSContext *cx, JSObject *obj) const;
 
   // Undefine all our fields from object |obj| (which should be a
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -359,24 +359,24 @@ nsXBLPrototypeBinding::Traverse(nsCycleC
     cb.NoteXPCOMChild(mResources->mLoader);
   if (mInsertionPointTable)
     mInsertionPointTable->Enumerate(TraverseInsertionPoint, &cb);
   if (mInterfaceTable)
     mInterfaceTable->Enumerate(TraverseBinding, &cb);
 }
 
 void
-nsXBLPrototypeBinding::Unlink()
+nsXBLPrototypeBinding::UnlinkJSObjects()
 {
   if (mImplementation)
-    mImplementation->Unlink();
+    mImplementation->UnlinkJSObjects();
 
   nsXBLPrototypeHandler* curr = mPrototypeHandler;
   while (curr) {
-    curr->Unlink();
+    curr->UnlinkJSObjects();
     curr = curr->GetNextHandler();
   }
 }
 
 void
 nsXBLPrototypeBinding::Trace(TraceCallback aCallback, void *aClosure) const
 {
   if (mImplementation)
--- a/content/xbl/src/nsXBLPrototypeBinding.h
+++ b/content/xbl/src/nsXBLPrototypeBinding.h
@@ -192,17 +192,17 @@ public:
   // binding.  It may well throw errors (eg on out-of-memory).  Do not confuse
   // this with the Initialize() method, which must be called after the
   // binding's handlers, properties, etc are all set.
   nsresult Init(const nsACString& aRef,
                 nsIXBLDocumentInfo* aInfo,
                 nsIContent* aElement);
 
   void Traverse(nsCycleCollectionTraversalCallback &cb) const;
-  void Unlink();
+  void UnlinkJSObjects();
   void Trace(TraceCallback aCallback, void *aClosure) const;
 
 // Static members
   static PRUint32 gRefCnt;
  
   static nsFixedSizeAllocator* kAttrPool;
 
 // Internal member functions.
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -164,17 +164,17 @@ nsXBLPrototypeHandler::~nsXBLPrototypeHa
 void
 nsXBLPrototypeHandler::Trace(TraceCallback aCallback, void *aClosure) const
 {
   if (mCachedHandler)
     aCallback(nsIProgrammingLanguage::JAVASCRIPT, mCachedHandler, aClosure);
 }
 
 void
-nsXBLPrototypeHandler::Unlink()
+nsXBLPrototypeHandler::UnlinkJSObjects()
 {
   ForgetCachedHandler();
 }
 
 already_AddRefed<nsIContent>
 nsXBLPrototypeHandler::GetHandlerElement()
 {
   if (mType & NS_HANDLER_TYPE_XUL) {
--- a/content/xbl/src/nsXBLPrototypeHandler.h
+++ b/content/xbl/src/nsXBLPrototypeHandler.h
@@ -149,17 +149,17 @@ public:
   // This returns a valid value only if HasAllowUntrustedEventsAttr returns
   // PR_TRUE.
   PRBool AllowUntrustedEvents()
   {
     return (mType & NS_HANDLER_ALLOW_UNTRUSTED) != 0;
   }
 
   void Trace(TraceCallback aCallback, void *aClosure) const;
-  void Unlink();
+  void UnlinkJSObjects();
 	
 public:
   static PRUint32 gRefCnt;
   
 protected:
   already_AddRefed<nsIController> GetController(nsPIDOMEventTarget* aTarget);
   
   inline PRInt32 GetMatchingKeyCode(const nsAString& aKeyName);
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2411,19 +2411,16 @@ nsXULElement::RecompileScriptEventListen
     }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXULPrototypeNode)
     if (tmp->mType == nsXULPrototypeNode::eType_Element) {
         static_cast<nsXULPrototypeElement*>(tmp)->Unlink();
     }
-    else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
-        static_cast<nsXULPrototypeScript*>(tmp)->Unlink();
-    }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXULPrototypeNode)
     if (tmp->mType == nsXULPrototypeNode::eType_Element) {
         nsXULPrototypeElement *elem =
             static_cast<nsXULPrototypeElement*>(tmp);
         PRUint32 i;
         for (i = 0; i < elem->mNumChildren; ++i) {
             NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i],
@@ -2447,17 +2444,25 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BE
     }
     else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
         nsXULPrototypeScript *script =
             static_cast<nsXULPrototypeScript*>(tmp);
         NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(script->mScriptObject.mLangID,
                                                 script->mScriptObject.mObject)
     }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
+NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN_NATIVE(nsXULPrototypeNode, AddRef)
+    if (tmp->mType == nsXULPrototypeNode::eType_Element) {
+        static_cast<nsXULPrototypeElement*>(tmp)->UnlinkJSObjects();
+    }
+    else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
+        static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects();
+    }
+NS_IMPL_CYCLE_COLLECTION_ROOT_END
+//NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release)
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeAttribute
 //
 
 nsXULPrototypeAttribute::~nsXULPrototypeAttribute()
@@ -2745,23 +2750,28 @@ nsXULPrototypeElement::SetAttrAt(PRUint3
     }
 
     mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
 
     return NS_OK;
 }
 
 void
-nsXULPrototypeElement::Unlink()
+nsXULPrototypeElement::UnlinkJSObjects()
 {
     if (mHoldsScriptObject) {
         nsContentUtils::DropScriptObjects(mScriptTypeID, this,
                                           &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode));
         mHoldsScriptObject = PR_FALSE;
     }
+}
+
+void
+nsXULPrototypeElement::Unlink()
+{
     mNumAttributes = 0;
     delete[] mAttributes;
     mAttributes = nsnull;
 }
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeScript
@@ -2779,17 +2789,17 @@ nsXULPrototypeScript::nsXULPrototypeScri
     NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
     NS_ASSERTION(aLangID != nsIProgrammingLanguage::UNKNOWN,
                  "The language ID must be known and constant");
 }
 
 
 nsXULPrototypeScript::~nsXULPrototypeScript()
 {
-    Unlink();
+    UnlinkJSObjects();
 }
 
 nsresult
 nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
                                 nsIScriptGlobalObject* aGlobal,
                                 const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     nsIScriptContext *context = aGlobal->GetScriptContext(
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -249,16 +249,17 @@ public:
           mHoldsScriptObject(PR_FALSE),
           mScriptTypeID(nsIProgrammingLanguage::UNKNOWN)
     {
         NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
     }
 
     virtual ~nsXULPrototypeElement()
     {
+        UnlinkJSObjects();
         Unlink();
         NS_ASSERTION(!mChildren && mNumChildren == 0,
                      "ReleaseSubtree not called");
     }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
     virtual const char* ClassName() { return "nsXULPrototypeElement"; }
     virtual PRUint32 ClassSize() { return sizeof(*this); }
@@ -284,16 +285,17 @@ public:
                                const nsCOMArray<nsINodeInfo> *aNodeInfos);
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
                                  nsIScriptGlobalObject* aGlobal,
                                  nsIURI* aDocumentURI,
                                  const nsCOMArray<nsINodeInfo> *aNodeInfos);
 
     nsresult SetAttrAt(PRUint32 aPos, const nsAString& aValue, nsIURI* aDocumentURI);
 
+    void UnlinkJSObjects();
     void Unlink();
 
     PRUint32                 mNumChildren;
     nsXULPrototypeNode**     mChildren;           // [OWNER]
 
     nsCOMPtr<nsINodeInfo>    mNodeInfo;           // [OWNER]
 
     PRUint32                 mNumAttributes;
@@ -354,17 +356,17 @@ public:
     nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput,
                                   nsIScriptGlobalObject* aGlobal);
 
     nsresult Compile(const PRUnichar* aText, PRInt32 aTextLength,
                      nsIURI* aURI, PRUint32 aLineNo,
                      nsIDocument* aDocument,
                      nsIScriptGlobalObjectOwner* aGlobalOwner);
 
-    void Unlink()
+    void UnlinkJSObjects()
     {
         if (mScriptObject.mObject) {
             nsContentUtils::DropScriptObjects(mScriptObject.mLangID, this,
                                               &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode));
             mScriptObject.mObject = nsnull;
         }
     }
 
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -3836,19 +3836,20 @@ nsJSArgArray::ReleaseJSObjects()
   if (mArgv) {
     PR_DELETE(mArgv);
   }
   mArgc = 0;
 }
 
 // QueryInterface implementation for nsJSArgArray
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
+NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSArgArray)
   tmp->ReleaseJSObjects();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_ROOT_END
+NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSArgArray)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
   jsval *argv = tmp->mArgv;
   if (argv) {
     jsval *end;
--- a/dom/src/base/nsJSTimeoutHandler.cpp
+++ b/dom/src/base/nsJSTimeoutHandler.cpp
@@ -107,19 +107,20 @@ private:
   JSString *mExpr;
   JSObject *mFunObj;
 };
 
 
 // nsJSScriptTimeoutHandler
 // QueryInterface implementation for nsJSScriptTimeoutHandler
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
+NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSScriptTimeoutHandler)
   tmp->ReleaseJSObjects();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_ROOT_END
+NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSScriptTimeoutHandler)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSScriptTimeoutHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgv)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpr)
--- a/dom/src/events/nsJSEventListener.cpp
+++ b/dom/src/events/nsJSEventListener.cpp
@@ -76,31 +76,44 @@ nsJSEventListener::nsJSEventListener(nsI
                                      nsISupports *aTarget)
   : nsIJSEventListener(aContext, aScopeObject, aTarget),
     mReturnResult(nsReturnResult_eNotSet)
 {
   // aScopeObject is the inner window's JS object, which we need to lock
   // until we are done with it.
   NS_ASSERTION(aScopeObject && aContext,
                "EventListener with no context or scope?");
-  NS_HOLD_JS_OBJECTS(this, nsJSEventListener);
+  nsContentUtils::HoldScriptObject(aContext->GetScriptTypeID(), this,
+                                   &NS_CYCLE_COLLECTION_NAME(nsJSEventListener),
+                                   aScopeObject, PR_FALSE);
 }
 
 nsJSEventListener::~nsJSEventListener() 
 {
   if (mContext)
-    NS_DROP_JS_OBJECTS(this, nsJSEventListener);
+    nsContentUtils::DropScriptObjects(mContext->GetScriptTypeID(), this,
+                                &NS_CYCLE_COLLECTION_NAME(nsJSEventListener));
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSEventListener)
+NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSEventListener)
+  if (tmp->mContext &&
+      tmp->mContext->GetScriptTypeID() == nsIProgrammingLanguage::JAVASCRIPT) {
+    NS_DROP_JS_OBJECTS(tmp, nsJSEventListener);
+    tmp->mScopeObject = nsnull;
+  }
+NS_IMPL_CYCLE_COLLECTION_ROOT_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSEventListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTarget)
   if (tmp->mContext) {
-    tmp->mScopeObject = nsnull;
-    NS_DROP_JS_OBJECTS(tmp, nsJSEventListener);
+    if (tmp->mScopeObject) {
+      nsContentUtils::DropScriptObjects(tmp->mContext->GetScriptTypeID(), this,
+                                  &NS_CYCLE_COLLECTION_NAME(nsJSEventListener));
+      tmp->mScopeObject = nsnull;
+    }
     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSEventListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -674,17 +674,17 @@ nsXPConnect::FinishCycleCollection()
 
 nsCycleCollectionParticipant *
 nsXPConnect::ToParticipant(void *p)
 {
     return this;
 }
 
 NS_IMETHODIMP
-nsXPConnect::Root(void *p)
+nsXPConnect::RootAndUnlinkJSObjects(void *p)
 {
     return NS_OK;
 }
 
 #ifdef DEBUG_CC
 void
 nsXPConnect::PrintAllReferencesTo(void *p)
 {
@@ -932,17 +932,17 @@ nsXPConnect::GetRequestDepth(JSContext* 
         // collection.
         --requestDepth;
     return requestDepth;
 }
 
 class JSContextParticipant : public nsCycleCollectionParticipant
 {
 public:
-    NS_IMETHOD Root(void *n)
+    NS_IMETHOD RootAndUnlinkJSObjects(void *n)
     {
         return NS_OK;
     }
     NS_IMETHOD Unlink(void *n)
     {
         // We must not unlink a JSContext because Root/Unroot don't ensure that
         // the pointer is still valid.
         return NS_OK;
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -492,17 +492,17 @@ public:
     virtual ~nsXPConnect();
 
     JSBool IsShuttingDown() const {return mShuttingDown;}
 
     nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
     nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
 
     // nsCycleCollectionParticipant
-    NS_IMETHOD Root(void *p);
+    NS_IMETHOD RootAndUnlinkJSObjects(void *p);
     NS_IMETHOD Unlink(void *p);
     NS_IMETHOD Unroot(void *p);
     NS_IMETHOD Traverse(void *p,
                         nsCycleCollectionTraversalCallback &cb);
     
     // nsCycleCollectionLanguageRuntime
     virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb);
     virtual nsresult FinishCycleCollection();
@@ -2483,18 +2483,24 @@ class nsXPCWrappedJS : protected nsAutoX
                        public XPCRootSetElem
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
     NS_DECL_NSIXPCONNECTWRAPPEDJS
     NS_DECL_NSISUPPORTSWEAKREFERENCE
     NS_DECL_NSIPROPERTYBAG
-    NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS,
-                                             nsIXPConnectWrappedJS)
+
+    class NS_CYCLE_COLLECTION_INNERCLASS
+     : public nsXPCOMCycleCollectionParticipant
+    {
+      NS_IMETHOD RootAndUnlinkJSObjects(void *p);
+      NS_DECL_CYCLE_COLLECTION_CLASS_BODY(nsXPCWrappedJS, nsIXPConnectWrappedJS)
+    };
+    NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
     NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(nsXPCWrappedJS)
 
     NS_IMETHOD CallMethod(PRUint16 methodIndex,
                           const XPTMethodDescriptor *info,
                           nsXPTCMiniVariant* params);
 
     /*
     * This is rarely called directly. Instead one usually calls
--- a/js/src/xpconnect/src/xpcwrappedjs.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjs.cpp
@@ -107,27 +107,45 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrapp
         cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
     else
         // Non-root wrappers keep their root alive.
         cb.NoteXPCOMChild(static_cast<nsIXPConnectWrappedJS*>(root));
 
     return NS_OK;
 }
 
+NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsXPCWrappedJS)
+    if(tmp->mRoot && !tmp->mRoot->HasWeakReferences() && tmp->IsValid())
+    {
+        XPCJSRuntime* rt = nsXPConnect::GetRuntime();
+        if(rt)
+        {
+            if(tmp->mRoot == tmp)
+            {
+                // remove this root wrapper from the map
+                JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
+                if(map)
+                {
+                    XPCAutoLock lock(rt->GetMapLock());
+                    map->Remove(tmp);
+                }
+            }
+
+            if(tmp->mRefCnt > 1)
+                tmp->RemoveFromRootSet(rt->GetJSRuntime());
+        }
+
+        tmp->mJSObj = nsnull;
+    }
+NS_IMPL_CYCLE_COLLECTION_ROOT_END
+
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
     if(tmp->mRoot && !tmp->mRoot->HasWeakReferences())
     {
         tmp->Unlink();
-        if(tmp->IsValid())
-        {
-            XPCJSRuntime* rt = nsXPConnect::GetRuntime();
-            if(tmp->mRefCnt > 1)
-                tmp->RemoveFromRootSet(rt->GetJSRuntime());
-            tmp->mJSObj = nsnull;
-        }
     }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMETHODIMP
 nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
     NS_ASSERTION(IsAggregatedToNative(), "bad AggregatedQueryInterface call");
 
@@ -146,19 +164,16 @@ nsXPCWrappedJS::AggregatedQueryInterface
     }
 
     return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
 }
 
 NS_IMETHODIMP
 nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
-    if(!IsValid())
-        return NS_ERROR_UNEXPECTED;
-
     if(nsnull == aInstancePtr)
     {
         NS_PRECONDITION(0, "null pointer");
         return NS_ERROR_NULL_POINTER;
     }
 
     if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
         *aInstancePtr = & NS_CYCLE_COLLECTION_NAME(nsXPCWrappedJS);
@@ -168,16 +183,19 @@ nsXPCWrappedJS::QueryInterface(REFNSIID 
     if(aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)))
     {
         NS_ADDREF(this);
         *aInstancePtr =
             NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
         return NS_OK;
     }
 
+    if(!IsValid())
+        return NS_ERROR_UNEXPECTED;
+
     // Always check for this first so that our 'outer' can get this interface
     // from us without recurring into a call to the outer's QI!
     if(aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS)))
     {
         NS_ADDREF(this);
         *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
         return NS_OK;
     }
@@ -448,38 +466,36 @@ nsXPCWrappedJS::nsXPCWrappedJS(XPCCallCo
 nsXPCWrappedJS::~nsXPCWrappedJS()
 {
     NS_PRECONDITION(0 == mRefCnt, "refcounting error");
 
     if(mRoot == this)
     {
         // Let the nsWeakReference object (if present) know of our demise.
         ClearWeakReferences();
-    }
-    Unlink();
-}
 
-void
-nsXPCWrappedJS::Unlink()
-{
-    XPCJSRuntime* rt = nsXPConnect::GetRuntime();
-    if(mRoot == this)
-    {
-        // remove this root wrapper from the map
+        // Remove this root wrapper from the map
+        XPCJSRuntime* rt = nsXPConnect::GetRuntime();
         if(rt)
         {
             JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
             if(map)
             {
                 XPCAutoLock lock(rt->GetMapLock());
                 map->Remove(this);
             }
         }
     }
-    else if(mRoot)
+    Unlink();
+}
+
+void
+nsXPCWrappedJS::Unlink()
+{
+    if(mRoot != this && mRoot)
     {
         // unlink this wrapper
         nsXPCWrappedJS* cur = mRoot;
         while(1)
         {
             if(cur->mNext == this)
             {
                 cur->mNext = mNext;
@@ -487,30 +503,28 @@ nsXPCWrappedJS::Unlink()
             }
             cur = cur->mNext;
             NS_ASSERTION(cur, "failed to find wrapper in its own chain");
         }
         // let the root go
         NS_RELEASE(mRoot);
     }
 
-    if(IsValid())
+    NS_IF_RELEASE(mClass);
+    if (mOuter)
     {
-        NS_IF_RELEASE(mClass);
-        if (mOuter)
+        XPCJSRuntime* rt = nsXPConnect::GetRuntime();
+        if (rt && rt->GetThreadRunningGC())
         {
-            if (rt && rt->GetThreadRunningGC())
-            {
-                rt->DeferredRelease(mOuter);
-                mOuter = nsnull;
-            }
-            else
-            {
-                NS_RELEASE(mOuter);
-            }
+            rt->DeferredRelease(mOuter);
+            mOuter = nsnull;
+        }
+        else
+        {
+            NS_RELEASE(mOuter);
         }
     }
 }
 
 nsXPCWrappedJS*
 nsXPCWrappedJS::Find(REFNSIID aIID)
 {
     if(aIID.Equals(NS_GET_IID(nsISupports)))
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -873,18 +873,18 @@ struct nsCycleCollector
 
     void RegisterRuntime(PRUint32 langID, 
                          nsCycleCollectionLanguageRuntime *rt);
     void ForgetRuntime(PRUint32 langID);
 
     void SelectPurple();
     void MarkRoots(GCGraphBuilder &builder);
     void ScanRoots();
-    void CollectWhite();
-    PRBool UnrootWhite(); // returns whether anything was collected
+    void RootWhite();
+    PRBool CollectWhite(); // returns whether anything was collected
 
     nsCycleCollector();
     ~nsCycleCollector();
 
     PRBool Suspect(nsISupports *n);
     PRBool Forget(nsISupports *n);
     PRBool Collect(PRUint32 aTryCollections = 1);
     PRBool BeginCollection();
@@ -1520,17 +1520,17 @@ nsCycleCollector::ScanRoots()
 }
 
 
 ////////////////////////////////////////////////////////////////////////
 // Bacon & Rajan's |CollectWhite| routine, somewhat modified.
 ////////////////////////////////////////////////////////////////////////
 
 void
-nsCycleCollector::CollectWhite()
+nsCycleCollector::RootWhite()
 {
     // Explanation of "somewhat modified": we have no way to collect the
     // set of whites "all at once", we have to ask each of them to drop
     // their outgoing links and assume this will cause the garbage cycle
     // to *mostly* self-destruct (except for the reference we continue
     // to hold). 
     // 
     // To do this "safely" we must make sure that the white nodes we're
@@ -1557,43 +1557,43 @@ nsCycleCollector::CollectWhite()
         if (pinfo->mColor == white) {
             mBuf.Push(pinfo);
         }
     }
 
     PRUint32 i, count = mBuf.GetSize();
     for (i = 0; i < count; ++i) {
         PtrInfo *pinfo = static_cast<PtrInfo*>(mBuf.ObjectAt(i));
-        rv = pinfo->mParticipant->Root(pinfo->mPointer);
+        rv = pinfo->mParticipant->RootAndUnlinkJSObjects(pinfo->mPointer);
         if (NS_FAILED(rv))
             Fault("Failed root call while unlinking", pinfo);
     }
-
+}
+
+PRBool
+nsCycleCollector::CollectWhite()
+{
+    nsresult rv;
+    PRUint32 i, count = mBuf.GetSize();
     for (i = 0; i < count; ++i) {
         PtrInfo *pinfo = static_cast<PtrInfo*>(mBuf.ObjectAt(i));
         rv = pinfo->mParticipant->Unlink(pinfo->mPointer);
         if (NS_FAILED(rv)) {
             Fault("Failed unlink call while unlinking", pinfo);
 #ifdef DEBUG_CC
             mStats.mFailedUnlink++;
 #endif
         }
         else {
 #ifdef DEBUG_CC
             ++mStats.mCollectedNode;
 #endif
         }
     }
-}
-
-PRBool
-nsCycleCollector::UnrootWhite()
-{
-    nsresult rv;
-    PRUint32 i, count = mBuf.GetSize();
+
     for (i = 0; i < count; ++i) {
         PtrInfo *pinfo = static_cast<PtrInfo*>(mBuf.ObjectAt(i));
         rv = pinfo->mParticipant->Unroot(pinfo->mPointer);
         if (NS_FAILED(rv))
             Fault("Failed unroot call while unlinking", pinfo);
     }
 
     mBuf.Empty();
@@ -2281,31 +2281,31 @@ nsCycleCollector::BeginCollection()
                 }
             }
         }
 #endif
 
 #ifdef COLLECT_TIME_DEBUG
         now = PR_Now();
 #endif
-        CollectWhite();
+        RootWhite();
 
 #ifdef COLLECT_TIME_DEBUG
         printf("cc: CollectWhite() took %lldms\n",
                (PR_Now() - now) / PR_USEC_PER_MSEC);
 #endif
     }
 
     return PR_TRUE;
 }
 
 PRBool
 nsCycleCollector::FinishCollection()
 {
-    PRBool collected = UnrootWhite();
+    PRBool collected = CollectWhite();
 
 #ifdef DEBUG_CC
     mStats.mCollection++;
     if (mParams.mReportStats)
         mStats.Dump();
 #endif
 
     for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
--- a/xpcom/glue/nsCycleCollectionParticipant.cpp
+++ b/xpcom/glue/nsCycleCollectionParticipant.cpp
@@ -49,17 +49,17 @@ NoteChild(PRUint32 aLangID, void *aScrip
 void
 nsScriptObjectTracer::TraverseScriptObjects(void *p,
                                         nsCycleCollectionTraversalCallback &cb)
 {
   Trace(p, NoteChild, &cb);
 }
 
 nsresult
-nsXPCOMCycleCollectionParticipant::Root(void *p)
+nsXPCOMCycleCollectionParticipant::RootAndUnlinkJSObjects(void *p)
 {
     nsISupports *s = static_cast<nsISupports*>(p);
     NS_ADDREF(s);
     return NS_OK;
 }
 
 nsresult
 nsXPCOMCycleCollectionParticipant::Unlink(void *p)
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -117,17 +117,17 @@ public:
 
 class NS_NO_VTABLE nsCycleCollectionParticipant
 {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_CYCLECOLLECTIONPARTICIPANT_IID)
 
     NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) = 0;
 
-    NS_IMETHOD Root(void *p) = 0;
+    NS_IMETHOD RootAndUnlinkJSObjects(void *p) = 0;
     NS_IMETHOD Unlink(void *p) = 0;
     NS_IMETHOD Unroot(void *p) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionParticipant, 
                               NS_CYCLECOLLECTIONPARTICIPANT_IID)
 
 #undef IMETHOD_VISIBILITY
@@ -145,17 +145,17 @@ public:
 };
 
 class NS_COM_GLUE nsXPCOMCycleCollectionParticipant
     : public nsScriptObjectTracer
 {
 public:
     NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb);
 
-    NS_IMETHOD Root(void *p);
+    NS_IMETHOD RootAndUnlinkJSObjects(void *p);
     NS_IMETHOD Unlink(void *p);
     NS_IMETHOD Unroot(void *p);
 
     NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure);
 
     NS_IMETHOD_(void) UnmarkPurple(nsISupports *p);
 
     PRBool CheckForRightISupports(nsISupports *s);
@@ -224,16 +224,41 @@ public:
       return NS_OK;                                                           \
     }                                                                         \
     nsresult rv;
 
 #define NS_CYCLE_COLLECTION_UPCAST(obj, clazz)                                 \
   NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj)
 
 ///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsCycleCollectionParticipant::RootAndUnlinkJSObjects
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(_class)                            \
+  NS_IMETHODIMP                                                                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::RootAndUnlinkJSObjects(void *p)       \
+  {                                                                            \
+    nsISupports *s = static_cast<nsISupports*>(p);                             \
+    NS_ASSERTION(CheckForRightISupports(s),                                    \
+                 "not the nsISupports pointer we expect");                     \
+    nsXPCOMCycleCollectionParticipant::RootAndUnlinkJSObjects(s);              \
+    _class *tmp = Downcast(s);
+
+#define NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN_NATIVE(_class, _root_function)     \
+  NS_IMETHODIMP                                                                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::RootAndUnlinkJSObjects(void *p)       \
+  {                                                                            \
+    _class *tmp = static_cast<_class*>(p);                                     \
+    tmp->_root_function();
+
+#define NS_IMPL_CYCLE_COLLECTION_ROOT_END                                      \
+    return NS_OK;                                                              \
+  }
+
+///////////////////////////////////////////////////////////////////////////////
 // Helpers for implementing nsCycleCollectionParticipant::Unlink
 ///////////////////////////////////////////////////////////////////////////////
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)                          \
   NS_IMETHODIMP                                                                \
   NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p)                       \
   {                                                                            \
     nsISupports *s = static_cast<nsISupports*>(p);                             \
@@ -449,23 +474,23 @@ class NS_CYCLE_COLLECTION_INNERCLASS    
 };                                                                             \
 NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 #define NS_DECL_CYCLE_COLLECTION_CLASS(_class)                                 \
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class)
 
 // Cycle collector helper for classes that don't want to unlink anything.
 // Note: if this is used a lot it might make sense to have a base class that
-//       doesn't do anything in Root/Unlink/Unroot.
+//       doesn't do anything in RootAndUnlinkJSObjects/Unlink/Unroot.
 #define NS_DECL_CYCLE_COLLECTION_CLASS_NO_UNLINK(_class)                       \
 class NS_CYCLE_COLLECTION_INNERCLASS                                           \
  : public nsXPCOMCycleCollectionParticipant                                    \
 {                                                                              \
   NS_DECL_CYCLE_COLLECTION_CLASS_BODY_NO_UNLINK(_class, _class)                \
-  NS_IMETHOD Root(void *p)                                                     \
+  NS_IMETHOD RootAndUnlinkJSObjects(void *p)                                   \
   {                                                                            \
     return NS_OK;                                                              \
   }                                                                            \
   NS_IMETHOD Unlink(void *p)                                                   \
   {                                                                            \
     return NS_OK;                                                              \
   }                                                                            \
   NS_IMETHOD Unroot(void *p)                                                   \
@@ -474,16 +499,17 @@ class NS_CYCLE_COLLECTION_INNERCLASS    
   }                                                                            \
 };                                                                             \
 NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 #define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _base)  \
 class NS_CYCLE_COLLECTION_INNERCLASS                                           \
  : public nsXPCOMCycleCollectionParticipant                                    \
 {                                                                              \
+  NS_IMETHOD RootAndUnlinkJSObjects(void *p);                                  \
   NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base)                           \
   NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure);           \
 };                                                                             \
 NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 #define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(_class)  \
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _class)
 
@@ -530,17 +556,17 @@ NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
   {                                                                            \
   }                                                                            \
 
 #define NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                                 \
   NS_CYCLE_COLLECTION_CLASSNAME(_class) NS_CYCLE_COLLECTION_NAME(_class);
 
 #define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY                             \
   public:                                                                      \
-    NS_IMETHOD Root(void *n);                                                  \
+    NS_IMETHOD RootAndUnlinkJSObjects(void *n);                                \
     NS_IMETHOD Unlink(void *n);                                                \
     NS_IMETHOD Unroot(void *n);                                                \
     NS_IMETHOD Traverse(void *n,                                               \
                       nsCycleCollectionTraversalCallback &cb);
 
 #define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(_class)                          \
   class NS_CYCLE_COLLECTION_INNERCLASS                                         \
    : public nsCycleCollectionParticipant                                       \
@@ -555,17 +581,17 @@ NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
   {                                                                            \
     NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY                                 \
     NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure);         \
   };                                                                           \
   NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 #define NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(_class, _root_function)           \
   NS_IMETHODIMP                                                                \
-  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Root(void *p)                         \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::RootAndUnlinkJSObjects(void *p)       \
   {                                                                            \
     _class *tmp = static_cast<_class*>(p);                                     \
     tmp->_root_function();                                                     \
     return NS_OK;                                                              \
   }
 
 #define NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(_class, _unroot_function)       \
   NS_IMETHODIMP                                                                \