Part 1 of fix for bug 379718 (using trace API for reference counts) and bug 386912 (cycle collector faults after tracing "JS object but unknown to the JS GC"). r=igor/jst, sr=jst, a=blocking1.9+/M9 (for bug 386912).
authorpeterv@propagandism.org
Sun, 28 Oct 2007 04:14:40 -0700
changeset 7226 a363325649d920a3203129c75443120e6263f48e
parent 7225 4e469097f82c20909895acb32db473672e84f4dc
child 7227 1fbd3e916a36fb7f83db8a832b95d6e4cd3b1eef
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersigor, jst, jst, blocking1.9
bugs379718, 386912
milestone1.9a9pre
Part 1 of fix for bug 379718 (using trace API for reference counts) and bug 386912 (cycle collector faults after tracing "JS object but unknown to the JS GC"). r=igor/jst, sr=jst, a=blocking1.9+/M9 (for bug 386912).
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLDocumentInfo.cpp
content/xbl/src/nsXBLDocumentInfo.h
content/xbl/src/nsXBLInsertionPoint.cpp
content/xbl/src/nsXBLProtoImpl.cpp
content/xbl/src/nsXBLProtoImpl.h
content/xbl/src/nsXBLProtoImplMember.h
content/xbl/src/nsXBLProtoImplMethod.cpp
content/xbl/src/nsXBLProtoImplMethod.h
content/xbl/src/nsXBLProtoImplProperty.cpp
content/xbl/src/nsXBLProtoImplProperty.h
content/xbl/src/nsXBLPrototypeBinding.cpp
content/xbl/src/nsXBLPrototypeBinding.h
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
content/xul/document/src/nsXULDocument.cpp
dom/src/base/nsJSEnvironment.cpp
dom/src/base/nsJSTimeoutHandler.cpp
dom/src/events/nsJSEventListener.cpp
dom/src/events/nsJSEventListener.h
js/src/xpconnect/idl/nsIXPCScriptable.idl
js/src/xpconnect/idl/nsIXPConnect.idl
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcwrappedjs.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativejsops.cpp
js/src/xpconnect/src/xpcwrappednativescope.cpp
xpcom/base/nsAgg.h
xpcom/glue/nsCycleCollectionParticipant.cpp
xpcom/glue/nsCycleCollectionParticipant.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -748,52 +748,16 @@ public:
   }
 
   /**
    * Return the content policy service
    */
   static nsIContentPolicy *GetContentPolicy();
 
   /**
-   * Make sure that whatever value *aPtr contains at any given moment is
-   * protected from JS GC until we remove the GC root.  A call to this that
-   * succeeds MUST be matched by a call to RemoveJSGCRoot to avoid leaking.
-   */
-  static nsresult AddJSGCRoot(jsval* aPtr, const char* aName) {
-    return AddJSGCRoot((void*)aPtr, aName);
-  }
-
-  /**
-   * Make sure that whatever object *aPtr is pointing to at any given moment is
-   * protected from JS GC until we remove the GC root.  A call to this that
-   * succeeds MUST be matched by a call to RemoveJSGCRoot to avoid leaking.
-   */
-  static nsresult AddJSGCRoot(JSObject** aPtr, const char* aName) {
-    return AddJSGCRoot((void*)aPtr, aName);
-  }
-
-  /**
-   * Make sure that whatever object *aPtr is pointing to at any given moment is
-   * protected from JS GC until we remove the GC root.  A call to this that
-   * succeeds MUST be matched by a call to RemoveJSGCRoot to avoid leaking.
-   */
-  static nsresult AddJSGCRoot(void* aPtr, const char* aName);  
-
-  /**
-   * Remove aPtr as a JS GC root
-   */
-  static nsresult RemoveJSGCRoot(jsval* aPtr) {
-    return RemoveJSGCRoot((void*)aPtr);
-  }
-  static nsresult RemoveJSGCRoot(JSObject** aPtr) {
-    return RemoveJSGCRoot((void*)aPtr);
-  }
-  static nsresult RemoveJSGCRoot(void* aPtr);
-
-  /**
    * Quick helper to determine whether there are any mutation listeners
    * of a given type that apply to this content or any of its ancestors.
    * The method has the side effect to call document's MayDispatchMutationEvent
    * using aTargetForSubtreeModified as the parameter.
    *
    * @param aNode  The node to search for listeners
    * @param aType  The type of listener (NS_EVENT_BITS_MUTATION_*)
    * @param aTargetForSubtreeModified The node which is the target of the
@@ -997,50 +961,83 @@ public:
     }
   }
 
   /**
    * Unbinds the content from the tree and nulls it out if it's not null.
    */
   static void DestroyAnonymousContent(nsCOMPtr<nsIContent>* aContent);
 
-  static nsresult HoldScriptObject(PRUint32 aLangID, void *aObject);
-  static nsresult DropScriptObject(PRUint32 aLangID, void *aObject);
-
-  class ScriptObjectHolder
+  /**
+   * Keep script object aNewObject, held by aScriptObjectHolder, alive.
+   *
+   * NOTE: This currently only supports objects that hold script objects of one
+   *       scripting language.
+   *
+   * @param aLangID script language ID of aNewObject
+   * @param aScriptObjectHolder the object that holds aNewObject
+   * @param aTracer the tracer for aScriptObject
+   * @param aNewObject the script object to hold
+   * @param aWasHoldingObjects whether aScriptObjectHolder was already holding
+   *                           script objects (ie. HoldScriptObject was called
+   *                           on it before, without a corresponding call to
+   *                           DropScriptObjects)
+   */
+  static nsresult HoldScriptObject(PRUint32 aLangID, void* aScriptObjectHolder,
+                                   nsScriptObjectTracer* aTracer,
+                                   void* aNewObject, PRBool aWasHoldingObjects)
   {
-  public:
-    ScriptObjectHolder(PRUint32 aLangID) : mLangID(aLangID),
-                                           mObject(nsnull)
-    {
-      MOZ_COUNT_CTOR(ScriptObjectHolder);
-    }
-    ~ScriptObjectHolder()
-    {
-      MOZ_COUNT_DTOR(ScriptObjectHolder);
-      if (mObject)
-        DropScriptObject(mLangID, mObject);
+    if (aLangID == nsIProgrammingLanguage::JAVASCRIPT) {
+      return aWasHoldingObjects ? NS_OK :
+                                  HoldJSObjects(aScriptObjectHolder, aTracer);
     }
-    nsresult set(void *aObject)
-    {
-      NS_ASSERTION(aObject, "unexpected null object");
-      NS_ASSERTION(!mObject, "already have an object");
-      nsresult rv = HoldScriptObject(mLangID, aObject);
-      if (NS_SUCCEEDED(rv)) {
-        mObject = aObject;
-      }
-      return rv;
+
+    return HoldScriptObject(aLangID, aNewObject);
+  }
+
+  /**
+   * Drop any script objects that aScriptObjectHolder is holding.
+   *
+   * NOTE: This currently only supports objects that hold script objects of one
+   *       scripting language.
+   *
+   * @param aLangID script language ID of the objects that 
+   * @param aScriptObjectHolder the object that holds script object that we want
+   *                            to drop
+   * @param aTracer the tracer for aScriptObject
+   */
+  static nsresult DropScriptObjects(PRUint32 aLangID, void* aScriptObjectHolder,
+                                    nsScriptObjectTracer* aTracer)
+  {
+    if (aLangID == nsIProgrammingLanguage::JAVASCRIPT) {
+      return DropJSObjects(aScriptObjectHolder);
     }
-    void traverse(nsCycleCollectionTraversalCallback &cb)
-    {
-      cb.NoteScriptChild(mLangID, mObject);
-    }
-    PRUint32 mLangID;
-    void *mObject;
-  };
+
+    aTracer->Trace(aScriptObjectHolder, DropScriptObject, nsnull);
+
+    return NS_OK;
+  }
+
+  /**
+   * Keep the JS objects held by aScriptObjectHolder alive.
+   *
+   * @param aScriptObjectHolder the object that holds JS objects that we want to
+   *                            keep alive
+   * @param aTracer the tracer for aScriptObject
+   */
+  static nsresult HoldJSObjects(void* aScriptObjectHolder,
+                                nsScriptObjectTracer* aTracer);
+
+  /**
+   * Drop the JS objects held by aScriptObjectHolder.
+   *
+   * @param aScriptObjectHolder the object that holds JS objects that we want to
+   *                            drop
+   */
+  static nsresult DropJSObjects(void* aScriptObjectHolder);
 
   /**
    * Convert nsIContent::IME_STATUS_* to nsIKBStateControll::IME_STATUS_*
    */
   static PRUint32 GetKBStateControlStatusFromIMEStatus(PRUint32 aState);
 
   /*
    * Notify when the first XUL menu is opened and when the all XUL menus are
@@ -1117,16 +1114,20 @@ private:
                                            JSObject *aNewGlobal,
                                            nsIDocument *aOldDocument,
                                            nsIDocument *aNewDocument);
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static nsIDOMScriptObjectFactory *GetDOMScriptObjectFactory();
 
+  static nsresult HoldScriptObject(PRUint32 aLangID, void* aObject);
+  PR_STATIC_CALLBACK(void) DropScriptObject(PRUint32 aLangID, void *aObject,
+                                            void *aClosure);
+
   static nsIDOMScriptObjectFactory *sDOMScriptObjectFactory;
 
   static nsIXPConnect *sXPConnect;
 
   static nsIScriptSecurityManager *sSecurityManager;
 
   static nsIThreadJSContextStack *sThreadJSContextStack;
 
@@ -1158,33 +1159,36 @@ private:
 
   static nsILineBreaker* sLineBreaker;
   static nsIWordBreaker* sWordBreaker;
   static nsICaseConversion* sCaseConv;
 
   // Holds pointers to nsISupports* that should be released at shutdown
   static nsVoidArray* sPtrsToPtrsToRelease;
 
-  // For now, we don't want to automatically clean this up in Shutdown(), since
-  // consumers might unfortunately end up wanting to use it after that
-  static nsIJSRuntimeService* sJSRuntimeService;
-  static JSRuntime* sJSScriptRuntime;
-  static PRInt32 sJSScriptRootCount;
-
   static nsIScriptRuntime* sScriptRuntimes[NS_STID_ARRAY_UBOUND];
   static PRInt32 sScriptRootCount[NS_STID_ARRAY_UBOUND];
+  static PRUint32 sJSGCThingRootCount;
 
 #ifdef IBMBIDI
   static nsIBidiKeyboard* sBidiKeyboard;
 #endif
 
   static PRBool sInitialized;
 };
 
 
+#define NS_HOLD_JS_OBJECTS(obj, clazz)                                         \
+  nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz),        \
+                                &NS_CYCLE_COLLECTION_NAME(clazz))
+
+#define NS_DROP_JS_OBJECTS(obj, clazz)                                         \
+  nsContentUtils::DropJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz))
+
+
 class nsCxPusher
 {
 public:
   nsCxPusher();
   ~nsCxPusher(); // Calls Pop();
 
   // Returns PR_FALSE if something erroneous happened.
   PRBool Push(nsISupports *aCurrentTarget);
@@ -1197,43 +1201,48 @@ private:
 };
 
 class nsAutoGCRoot {
 public:
   // aPtr should be the pointer to the jsval we want to protect
   nsAutoGCRoot(jsval* aPtr, nsresult* aResult) :
     mPtr(aPtr)
   {
-    mResult = *aResult =
-      nsContentUtils::AddJSGCRoot(aPtr, "nsAutoGCRoot");
+    mResult = *aResult = AddJSGCRoot(aPtr, "nsAutoGCRoot");
   }
 
   // aPtr should be the pointer to the JSObject* we want to protect
   nsAutoGCRoot(JSObject** aPtr, nsresult* aResult) :
     mPtr(aPtr)
   {
-    mResult = *aResult =
-      nsContentUtils::AddJSGCRoot(aPtr, "nsAutoGCRoot");
+    mResult = *aResult = AddJSGCRoot(aPtr, "nsAutoGCRoot");
   }
 
   // aPtr should be the pointer to the thing we want to protect
   nsAutoGCRoot(void* aPtr, nsresult* aResult) :
     mPtr(aPtr)
   {
-    mResult = *aResult =
-      nsContentUtils::AddJSGCRoot(aPtr, "nsAutoGCRoot");
+    mResult = *aResult = AddJSGCRoot(aPtr, "nsAutoGCRoot");
   }
 
   ~nsAutoGCRoot() {
     if (NS_SUCCEEDED(mResult)) {
-      nsContentUtils::RemoveJSGCRoot(mPtr);
+      RemoveJSGCRoot(mPtr);
     }
   }
 
+  static void Shutdown();
+
 private:
+  static nsresult AddJSGCRoot(void *aPtr, const char* aName);
+  static nsresult RemoveJSGCRoot(void *aPtr);
+
+  static nsIJSRuntimeService* sJSRuntimeService;
+  static JSRuntime* sJSScriptRuntime;
+
   void* mPtr;
   nsresult mResult;
 };
 
 #define NS_AUTO_GCROOT_PASTE2(tok,line) tok##line
 #define NS_AUTO_GCROOT_PASTE(tok,line) \
   NS_AUTO_GCROOT_PASTE2(tok,line)
 #define NS_AUTO_GCROOT(ptr, result) \ \
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -175,25 +175,25 @@ nsDataHashtable<nsISupportsHashKey, Even
 nsIStringBundleService *nsContentUtils::sStringBundleService;
 nsIStringBundle *nsContentUtils::sStringBundles[PropertiesFile_COUNT];
 nsIContentPolicy *nsContentUtils::sContentPolicyService;
 PRBool nsContentUtils::sTriedToGetContentPolicy = PR_FALSE;
 nsILineBreaker *nsContentUtils::sLineBreaker;
 nsIWordBreaker *nsContentUtils::sWordBreaker;
 nsICaseConversion *nsContentUtils::sCaseConv;
 nsVoidArray *nsContentUtils::sPtrsToPtrsToRelease;
-nsIJSRuntimeService *nsContentUtils::sJSRuntimeService;
-JSRuntime *nsContentUtils::sJSScriptRuntime;
-PRInt32 nsContentUtils::sJSScriptRootCount = 0;
 nsIScriptRuntime *nsContentUtils::sScriptRuntimes[NS_STID_ARRAY_UBOUND];
 PRInt32 nsContentUtils::sScriptRootCount[NS_STID_ARRAY_UBOUND];
+PRUint32 nsContentUtils::sJSGCThingRootCount;
 #ifdef IBMBIDI
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull;
 #endif
 
+nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService;
+JSRuntime *nsAutoGCRoot::sJSScriptRuntime;
 
 PRBool nsContentUtils::sInitialized = PR_FALSE;
 
 static PLDHashTable sEventListenerManagersHash;
 
 class EventListenerManagerMapEntry : public PLDHashEntryHdr
 {
 public:
@@ -666,17 +666,18 @@ nsContentUtils::Shutdown()
   NS_IF_RELEASE(sContentPolicyService);
   sTriedToGetContentPolicy = PR_FALSE;
   PRInt32 i;
   for (i = 0; i < PRInt32(PropertiesFile_COUNT); ++i)
     NS_IF_RELEASE(sStringBundles[i]);
   NS_IF_RELEASE(sStringBundleService);
   NS_IF_RELEASE(sConsoleService);
   NS_IF_RELEASE(sDOMScriptObjectFactory);
-  NS_IF_RELEASE(sXPConnect);
+  if (sJSGCThingRootCount == 0 && sXPConnect)
+    NS_RELEASE(sXPConnect);
   NS_IF_RELEASE(sSecurityManager);
   NS_IF_RELEASE(sThreadJSContextStack);
   NS_IF_RELEASE(sNameSpaceManager);
   NS_IF_RELEASE(sParserService);
   NS_IF_RELEASE(sIOService);
   NS_IF_RELEASE(sLineBreaker);
   NS_IF_RELEASE(sWordBreaker);
   NS_IF_RELEASE(sCaseConv);
@@ -716,16 +717,18 @@ nsContentUtils::Shutdown()
     // it could leave dangling references in DOMClassInfo's preserved
     // wrapper table.
 
     if (sEventListenerManagersHash.entryCount == 0) {
       PL_DHashTableFinish(&sEventListenerManagersHash);
       sEventListenerManagersHash.ops = nsnull;
     }
   }
+
+  nsAutoGCRoot::Shutdown();
 }
 
 static PRBool IsCallerTrustedForCapability(const char* aCapability)
 {
   // The secman really should handle UniversalXPConnect case, since that
   // should include UniversalBrowserRead... doesn't right now, though.
   PRBool hasCap;
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
@@ -2667,17 +2670,17 @@ nsContentUtils::GetContentPolicy()
     sTriedToGetContentPolicy = PR_TRUE;
   }
 
   return sContentPolicyService;
 }
 
 // static
 nsresult
-nsContentUtils::AddJSGCRoot(void* aPtr, const char* aName)
+nsAutoGCRoot::AddJSGCRoot(void* aPtr, const char* aName)
 {
   if (!sJSScriptRuntime) {
     nsresult rv = CallGetService("@mozilla.org/js/xpc/RuntimeService;1",
                                  &sJSRuntimeService);
     NS_ENSURE_TRUE(sJSRuntimeService, rv);
 
     sJSRuntimeService->GetRuntime(&sJSScriptRuntime);
     if (!sJSScriptRuntime) {
@@ -2685,48 +2688,34 @@ nsContentUtils::AddJSGCRoot(void* aPtr, 
       NS_WARNING("Unable to get JS runtime from JS runtime service");
       return NS_ERROR_FAILURE;
     }
   }
 
   PRBool ok;
   ok = ::JS_AddNamedRootRT(sJSScriptRuntime, aPtr, aName);
   if (!ok) {
-    if (sJSScriptRootCount == 0) {
-      // We just got the runtime... Just null things out, since no
-      // one's expecting us to have a runtime yet
-      NS_RELEASE(sJSRuntimeService);
-      sJSScriptRuntime = nsnull;
-    }
     NS_WARNING("JS_AddNamedRootRT failed");
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  // We now have one more root we added to the runtime
-  ++sJSScriptRootCount;
-
   return NS_OK;
 }
 
 /* static */
 nsresult
-nsContentUtils::RemoveJSGCRoot(void* aPtr)
+nsAutoGCRoot::RemoveJSGCRoot(void* aPtr)
 {
   if (!sJSScriptRuntime) {
     NS_NOTREACHED("Trying to remove a JS GC root when none were added");
     return NS_ERROR_UNEXPECTED;
   }
 
   ::JS_RemoveRootRT(sJSScriptRuntime, aPtr);
 
-  if (--sJSScriptRootCount == 0) {
-    NS_RELEASE(sJSRuntimeService);
-    sJSScriptRuntime = nsnull;
-  }
-
   return NS_OK;
 }
 
 // static
 PRBool
 nsContentUtils::IsEventAttributeName(nsIAtom* aName, PRInt32 aType)
 {
   const char* name;
@@ -3520,16 +3509,18 @@ nsContentUtils::GetDOMScriptObjectFactor
   return sDOMScriptObjectFactory;
 }
 
 /* static */
 nsresult
 nsContentUtils::HoldScriptObject(PRUint32 aLangID, void *aObject)
 {
   NS_ASSERTION(aObject, "unexpected null object");
+  NS_ASSERTION(aLangID != nsIProgrammingLanguage::JAVASCRIPT,
+               "Should use HoldJSObjects.");
   nsresult rv;
 
   PRUint32 langIndex = NS_STID_INDEX(aLangID);
   nsIScriptRuntime *runtime = sScriptRuntimes[langIndex];
   if (!runtime) {
     nsIDOMScriptObjectFactory *factory = GetDOMScriptObjectFactory();
     NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
 
@@ -3546,27 +3537,57 @@ nsContentUtils::HoldScriptObject(PRUint3
   ++sScriptRootCount[langIndex];
   NS_LOG_ADDREF(sScriptRuntimes[langIndex], sScriptRootCount[langIndex],
                 "HoldScriptObject", sizeof(void*));
 
   return NS_OK;
 }
 
 /* static */
-nsresult
-nsContentUtils::DropScriptObject(PRUint32 aLangID, void *aObject)
+void
+nsContentUtils::DropScriptObject(PRUint32 aLangID, void *aObject,
+                                 void *aClosure)
 {
   NS_ASSERTION(aObject, "unexpected null object");
+  NS_ASSERTION(aLangID != nsIProgrammingLanguage::JAVASCRIPT,
+               "Should use DropJSObjects.");
   PRUint32 langIndex = NS_STID_INDEX(aLangID);
   NS_LOG_RELEASE(sScriptRuntimes[langIndex], sScriptRootCount[langIndex] - 1,
                  "HoldScriptObject");
-  nsresult rv = sScriptRuntimes[langIndex]->DropScriptObject(aObject);
+  sScriptRuntimes[langIndex]->DropScriptObject(aObject);
   if (--sScriptRootCount[langIndex] == 0) {
     NS_RELEASE(sScriptRuntimes[langIndex]);
   }
+}
+
+/* static */
+nsresult
+nsContentUtils::HoldJSObjects(void* aScriptObjectHolder,
+                              nsScriptObjectTracer* aTracer)
+{
+  PRBool newHolder;
+  nsresult rv = sXPConnect->AddJSHolder(aScriptObjectHolder, aTracer);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  ++sJSGCThingRootCount;
+  NS_LOG_ADDREF(sXPConnect, sJSGCThingRootCount, "HoldJSObjects",
+                sizeof(void*));
+
+  return NS_OK;
+}
+
+/* static */
+nsresult
+nsContentUtils::DropJSObjects(void* aScriptObjectHolder)
+{
+  NS_LOG_RELEASE(sXPConnect, sJSGCThingRootCount - 1, "HoldJSObjects");
+  nsresult rv = sXPConnect->RemoveJSHolder(aScriptObjectHolder);
+  if (--sJSGCThingRootCount == 0 && !sInitialized) {
+    NS_RELEASE(sXPConnect);
+  }
   return rv;
 }
 
 /* static */
 PRUint32
 nsContentUtils::GetKBStateControlStatusFromIMEStatus(PRUint32 aState)
 {
   switch (aState & nsIContent::IME_STATUS_MASK_ENABLED) {
@@ -3712,8 +3733,15 @@ nsContentUtils::IsNativeAnonymous(nsICon
                   aContent->GetParent()->NodeInfo()->
                     Equals(nsGkAtoms::use, kNameSpaceID_SVG)),
                  "Native anonymous node with wrong binding parent");
     aContent = bindingParent;
   }
 
   return PR_FALSE;
 }
+
+/* static */
+void
+nsAutoGCRoot::Shutdown()
+{
+  NS_IF_RELEASE(sJSRuntimeService);
+}
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -301,17 +301,17 @@ TraverseKey(nsISupports* aKey, nsInserti
 
   cb.NoteXPCOMChild(aKey);
   if (aData) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY(*aData, nsXBLInsertionPoint)
   }
   return PL_DHASH_NEXT;
 }
 
-NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLBinding)
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLBinding)
   // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
   //     mPrototypeBinding is weak.
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContent)
   // XXX What about mNextBinding and mInsertionPointTable?
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLBinding)
   cb.NoteXPCOMChild(tmp->mPrototypeBinding->XBLDocumentInfo());
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -447,31 +447,53 @@ TraverseProtos(nsHashKey *aKey, void *aD
 static PRIntn PR_CALLBACK
 UnlinkProtos(nsHashKey *aKey, void *aData, void* aClosure)
 {
   nsXBLPrototypeBinding *proto = static_cast<nsXBLPrototypeBinding*>(aData);
   proto->Unlink();
   return kHashEnumerateNext;
 }
 
+struct ProtoTracer
+{
+  TraceCallback mCallback;
+  void *mClosure;
+};
+
+static PRIntn PR_CALLBACK
+TraceProtos(nsHashKey *aKey, void *aData, void* aClosure)
+{
+  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)
   if (tmp->mBindingTable) {
     tmp->mBindingTable->Enumerate(UnlinkProtos, nsnull);
   }
   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);
   }
   cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObject*>(tmp->mGlobalObject));
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
+  if (tmp->mBindingTable) {
+    ProtoTracer closure = { aCallback, aClosure };
+    tmp->mBindingTable->Enumerate(TraceProtos, &closure);
+  }
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo)
   NS_INTERFACE_MAP_ENTRY(nsIXBLDocumentInfo)
   NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXBLDocumentInfo)
 NS_INTERFACE_MAP_END
 
@@ -502,17 +524,20 @@ nsXBLDocumentInfo::nsXBLDocumentInfo(nsI
 nsXBLDocumentInfo::~nsXBLDocumentInfo()
 {
   /* destructor code */
   if (mGlobalObject) {
     // remove circular reference
     mGlobalObject->SetScriptContext(nsIProgrammingLanguage::JAVASCRIPT, nsnull);
     mGlobalObject->ClearGlobalObjectOwner(); // just in case
   }
-  delete mBindingTable;
+  if (mBindingTable) {
+    NS_DROP_JS_OBJECTS(this, nsXBLDocumentInfo);
+    delete mBindingTable;
+  }
 }
 
 NS_IMETHODIMP
 nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding** aResult)
 {
   *aResult = nsnull;
   if (!mBindingTable)
     return NS_OK;
@@ -536,18 +561,23 @@ DeletePrototypeBinding(nsHashKey* aKey, 
   nsXBLPrototypeBinding* binding = static_cast<nsXBLPrototypeBinding*>(aData);
   delete binding;
   return PR_TRUE;
 }
 
 NS_IMETHODIMP
 nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding)
 {
-  if (!mBindingTable)
+  if (!mBindingTable) {
     mBindingTable = new nsObjectHashtable(nsnull, nsnull, DeletePrototypeBinding, nsnull);
+    if (!mBindingTable)
+      return NS_ERROR_OUT_OF_MEMORY;
+
+    NS_HOLD_JS_OBJECTS(this, nsXBLDocumentInfo);
+  }
 
   const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
   nsCStringKey key(flat.get());
   mBindingTable->Put(&key, aBinding);
 
   return NS_OK;
 }
 
--- a/content/xbl/src/nsXBLDocumentInfo.h
+++ b/content/xbl/src/nsXBLDocumentInfo.h
@@ -67,18 +67,18 @@ public:
   
   NS_IMETHOD FlushSkinStylesheets();
 
   NS_IMETHOD_(PRBool) IsChrome() { return mIsChrome; }
 
   // nsIScriptGlobalObjectOwner methods
   virtual nsIScriptGlobalObject* GetScriptGlobalObject();
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXBLDocumentInfo,
-                                           nsIXBLDocumentInfo)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsXBLDocumentInfo,
+                                                         nsIXBLDocumentInfo)
 
 private:
   nsCOMPtr<nsIDocument> mDocument;
   PRPackedBool mScriptAccess;
   PRPackedBool mIsChrome;
   // the binding table owns each nsXBLPrototypeBinding
   nsObjectHashtable* mBindingTable;
   // non-owning pointer to the first binding in the table
--- a/content/xbl/src/nsXBLInsertionPoint.cpp
+++ b/content/xbl/src/nsXBLInsertionPoint.cpp
@@ -59,17 +59,17 @@ nsXBLInsertionPoint::Release()
   if (mRefCnt == 0) {
     mRefCnt = 1;
     delete this;
     return 0;
   }
   return mRefCnt;
 }
 
-NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPoint)
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLInsertionPoint)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLInsertionPoint)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mElements)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDefaultContentTemplate)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDefaultContent)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLInsertionPoint)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mElements)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDefaultContentTemplate)
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -195,28 +195,28 @@ nsXBLProtoImpl::CompilePrototypeMembers(
       DestroyMembers(curr);
       return rv;
     }
   }
   return NS_OK;
 }
 
 void
-nsXBLProtoImpl::Traverse(nsCycleCollectionTraversalCallback &cb) const
+nsXBLProtoImpl::Trace(TraceCallback aCallback, void *aClosure) const
 {
   // If we don't have a class object then we either didn't compile members
   // or we only have fields, in both cases there are no cycles through our
   // members.
   if (!mClassObject) {
     return;
   }
 
   nsXBLProtoImplMember *member;
   for (member = mMembers; member; member = member->GetNext()) {
-    member->Traverse(cb);
+    member->Trace(aCallback, aClosure);
   }
 }
 
 void
 nsXBLProtoImpl::Unlink()
 {
   if (mClassObject) {
     DestroyMembers(nsnull);
--- a/content/xbl/src/nsXBLProtoImpl.h
+++ b/content/xbl/src/nsXBLProtoImpl.h
@@ -85,17 +85,17 @@ public:
   }
 
   void SetFieldList(nsXBLProtoImplField* aFieldList)
   {
     delete mFields;
     mFields = aFieldList;
   }
 
-  void Traverse(nsCycleCollectionTraversalCallback &cb) const;
+  void Trace(TraceCallback aCallback, void *aClosure) const;
   void Unlink();
 
   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;
 
--- a/content/xbl/src/nsXBLProtoImplMember.h
+++ b/content/xbl/src/nsXBLProtoImplMember.h
@@ -42,21 +42,21 @@
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "jsapi.h"
 #include "nsIContent.h"
 #include "nsString.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIServiceManager.h"
 #include "nsReadableUtils.h"
+#include "nsCycleCollectionParticipant.h"
 
 class nsIScriptContext;
 struct JSRuntime;
 class nsIJSRuntimeService;
-class nsCycleCollectionTraversalCallback;
 
 struct nsXBLTextWithLineNumber
 {
   PRUnichar* mText;
   PRUint32 mLineNumber;
 
   nsXBLTextWithLineNumber() :
     mText(nsnull),
@@ -109,17 +109,17 @@ public:
                                  nsIContent* aBoundElement, 
                                  void* aScriptObject,
                                  void* aTargetClassObject,
                                  const nsCString& aClassStr) = 0;
   virtual nsresult CompileMember(nsIScriptContext* aContext,
                                  const nsCString& aClassStr,
                                  void* aClassObject)=0;
 
-  virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const = 0;
+  virtual void Trace(TraceCallback aCallback, void *aClosure) const = 0;
 
 protected:
   friend class nsAutoGCRoot;
   
   nsXBLProtoImplMember* mNext;  // The members of an implementation are chained.
   PRUnichar* mName;               // The name of the field, method, or property.
 };
 
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -67,18 +67,16 @@ nsXBLProtoImplMethod::~nsXBLProtoImplMet
 }
 
 void
 nsXBLProtoImplMethod::Destroy(PRBool aIsCompiled)
 {
   NS_PRECONDITION(aIsCompiled == mIsCompiled,
                   "Incorrect aIsCompiled in nsXBLProtoImplMethod::Destroy");
   if (aIsCompiled) {
-    if (mJSMethodObject)
-      nsContentUtils::RemoveJSGCRoot(&mJSMethodObject);
     mJSMethodObject = nsnull;
   }
   else {
     delete mUncompiledMethod;
     mUncompiledMethod = nsnull;
   }
 }
 
@@ -258,35 +256,35 @@ nsXBLProtoImplMethod::CompileMember(nsIS
     mUncompiledMethod = nsnull;
     return rv;
   }
 
   mJSMethodObject = methodObject;
 
   if (methodObject) {
     // Root the compiled prototype script object.
-    rv = nsContentUtils::AddJSGCRoot(&mJSMethodObject,
-                                     "nsXBLProtoImplMethod::mJSMethodObject");
     if (NS_FAILED(rv)) {
       mJSMethodObject = nsnull;
     }
   }
   
 #ifdef DEBUG
   mIsCompiled = NS_SUCCEEDED(rv);
 #endif
   return rv;
 }
 
 void
-nsXBLProtoImplMethod::Traverse(nsCycleCollectionTraversalCallback &cb) const
+nsXBLProtoImplMethod::Trace(TraceCallback aCallback, void *aClosure) const
 {
   NS_ASSERTION(mIsCompiled, "Shouldn't traverse uncompiled method");
 
-  cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject);
+  if (mJSMethodObject) {
+    aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject, aClosure);
+  }
 }
 
 nsresult
 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
 {
   NS_PRECONDITION(mIsCompiled, "Can't execute uncompiled method");
   
   if (!mJSMethodObject) {
--- a/content/xbl/src/nsXBLProtoImplMethod.h
+++ b/content/xbl/src/nsXBLProtoImplMethod.h
@@ -124,17 +124,17 @@ public:
                                  nsIContent* aBoundElement, 
                                  void* aScriptObject,
                                  void* aTargetClassObject,
                                  const nsCString& aClassStr);
   virtual nsresult CompileMember(nsIScriptContext* aContext,
                                  const nsCString& aClassStr,
                                  void* aClassObject);
 
-  virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const;
+  virtual void Trace(TraceCallback aCallback, void *aClosure) const;
 
 protected:
   union {
     nsXBLUncompiledMethod* mUncompiledMethod; // An object that represents the method before being compiled.
     JSObject * mJSMethodObject;               // The JS object for the method (after compilation)
   };
 
 #ifdef DEBUG
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -82,24 +82,24 @@ nsXBLProtoImplProperty::~nsXBLProtoImplP
 
 void
 nsXBLProtoImplProperty::Destroy(PRBool aIsCompiled)
 {
   NS_PRECONDITION(aIsCompiled == mIsCompiled,
                   "Incorrect aIsCompiled in nsXBLProtoImplProperty::Destroy");
 
   if ((mJSAttributes & JSPROP_GETTER) && mJSGetterObject) {
-    nsContentUtils::RemoveJSGCRoot(&mJSGetterObject);
+    mJSGetterObject = nsnull;
   }
   else {
     delete mGetterText;
   }
 
   if ((mJSAttributes & JSPROP_SETTER) && mJSSetterObject) {
-    nsContentUtils::RemoveJSGCRoot(&mJSSetterObject);
+    mJSSetterObject = nsnull;
   }
   else {
     delete mSetterText;
   }
 
   mGetterText = mSetterText = nsnull;
 }
 
@@ -263,19 +263,16 @@ nsXBLProtoImplProperty::CompileMember(ns
       // Make sure we free mGetterText here before setting mJSGetterObject, since
       // that'll overwrite mGetterText
       delete mGetterText;
       deletedGetter = PR_TRUE;
       mJSGetterObject = getterObject;
     
       if (mJSGetterObject && NS_SUCCEEDED(rv)) {
         mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
-        // Root the compiled prototype script object.
-        rv = nsContentUtils::AddJSGCRoot(&mJSGetterObject,
-                                         "nsXBLProtoImplProperty::mJSGetterObject");
       }
       if (NS_FAILED(rv)) {
         mJSGetterObject = nsnull;
         mJSAttributes &= ~JSPROP_GETTER;
         /*chaining to return failure*/
       }
     }
   } // if getter is not empty
@@ -315,19 +312,16 @@ nsXBLProtoImplProperty::CompileMember(ns
       // Make sure we free mSetterText here before setting mJSGetterObject, since
       // that'll overwrite mSetterText
       delete mSetterText;
       deletedSetter = PR_TRUE;
       mJSSetterObject = setterObject;
 
       if (mJSSetterObject && NS_SUCCEEDED(rv)) {
         mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
-        // Root the compiled prototype script object.
-        rv = nsContentUtils::AddJSGCRoot(&mJSSetterObject,
-                                         "nsXBLProtoImplProperty::mJSSetterObject");
       }
       if (NS_FAILED(rv)) {
         mJSSetterObject = nsnull;
         mJSAttributes &= ~JSPROP_SETTER;
         /*chaining to return failure*/
       }
     }
   } // if setter wasn't empty....
@@ -340,20 +334,20 @@ nsXBLProtoImplProperty::CompileMember(ns
 #ifdef DEBUG
   mIsCompiled = NS_SUCCEEDED(rv);
 #endif
   
   return rv;
 }
 
 void
-nsXBLProtoImplProperty::Traverse(nsCycleCollectionTraversalCallback &cb) const
+nsXBLProtoImplProperty::Trace(TraceCallback aCallback, void *aClosure) const
 {
   NS_ASSERTION(mIsCompiled, "Shouldn't traverse uncompiled method");
 
-  if (mJSAttributes & JSPROP_GETTER) {
-    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSGetterObject);
+  if ((mJSAttributes & JSPROP_GETTER) && mJSGetterObject) {
+    aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSGetterObject, aClosure);
   }
 
-  if (mJSAttributes & JSPROP_SETTER) {
-    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSSetterObject);
+  if ((mJSAttributes & JSPROP_SETTER) && mJSSetterObject) {
+    aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSSetterObject, aClosure);
   }
 }
--- a/content/xbl/src/nsXBLProtoImplProperty.h
+++ b/content/xbl/src/nsXBLProtoImplProperty.h
@@ -67,17 +67,17 @@ public:
                                  nsIContent* aBoundElement, 
                                  void* aScriptObject,
                                  void* aTargetClassObject,
                                  const nsCString& aClassStr);
   virtual nsresult CompileMember(nsIScriptContext* aContext,
                                  const nsCString& aClassStr,
                                  void* aClassObject);
 
-  virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const;
+  virtual void Trace(TraceCallback aCallback, void *aClosure) const;
 
 protected:
   union {
     // The raw text for the getter (prior to compilation).
     nsXBLTextWithLineNumber* mGetterText;
     // The JS object for the getter (after compilation)
     JSObject *               mJSGetterObject;
   };
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -239,17 +239,17 @@ private:
 
   static nsFixedSizeAllocator* kPool;
   static PRUint32 gRefCnt;
 };
 
 PRUint32 nsXBLInsertionPointEntry::gRefCnt = 0;
 nsFixedSizeAllocator* nsXBLInsertionPointEntry::kPool;
 
-NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPointEntry)
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLInsertionPointEntry)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLInsertionPointEntry)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInsertionParent)
   if (tmp->mDefaultContent) {
     // mDefaultContent is a sort of anonymous content within the XBL
     // document, and we own and manage it.  Unhook it here, since we're going
     // away.
     tmp->mDefaultContent->UnbindFromTree();
     tmp->mDefaultContent = nsnull;
@@ -350,34 +350,39 @@ TraverseBinding(nsHashKey *aKey, void *a
   cb->NoteXPCOMChild(static_cast<nsISupports*>(aData));
   return kHashEnumerateNext;
 }
 
 void
 nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
 {
   cb.NoteXPCOMChild(mBinding);
-  if (mImplementation)
-    mImplementation->Traverse(cb);
   if (mResources)
     cb.NoteXPCOMChild(mResources->mLoader);
   if (mInsertionPointTable)
     mInsertionPointTable->Enumerate(TraverseInsertionPoint, &cb);
   if (mInterfaceTable)
     mInterfaceTable->Enumerate(TraverseBinding, &cb);
 }
 
 void
 nsXBLPrototypeBinding::Unlink()
 {
   if (mImplementation)
     mImplementation->Unlink();
 }
 
 void
+nsXBLPrototypeBinding::Trace(TraceCallback aCallback, void *aClosure) const
+{
+  if (mImplementation)
+    mImplementation->Trace(aCallback, aClosure);
+}
+
+void
 nsXBLPrototypeBinding::Initialize()
 {
   nsIContent* content = GetImmediateChild(nsGkAtoms::content);
   if (content) {
     // Make sure to construct the attribute table first, since constructing the
     // insertion point table removes some of the subtrees, which makes them
     // unreachable by walking our DOM.
     ConstructAttributeTable(content);
--- a/content/xbl/src/nsXBLPrototypeBinding.h
+++ b/content/xbl/src/nsXBLPrototypeBinding.h
@@ -193,16 +193,17 @@ public:
   // 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 Trace(TraceCallback aCallback, void *aClosure) const;
 
 // Static members
   static PRUint32 gRefCnt;
  
   static nsFixedSizeAllocator* kAttrPool;
 
 // Internal member functions.
 // XXXbz GetImmediateChild needs to be public to be called by SetAttrs,
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -50,17 +50,16 @@
  * Modifications to Mozilla code or documentation
  * identified per MPL Section 3.3
  *
  * Date         Modified by     Description of modification
  * 03/27/2000   IBM Corp.       Added PR_CALLBACK for Optlink
  *                               use in OS2
  */
 
-#include "jsapi.h"      // for JS_AddNamedRoot and JS_RemoveRootRT
 #include "nsCOMPtr.h"
 #include "nsDOMCID.h"
 #include "nsDOMError.h"
 #include "nsDOMString.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsHashtable.h"
 #include "nsIAtom.h"
@@ -697,17 +696,18 @@ nsScriptEventHandlerOwnerTearoff::Compil
     nsresult rv;
 
     XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheSets);
 
     // XXX sXBL/XBL2 issue! Owner or current document?
     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mElement->GetOwnerDoc());
 
     nsIScriptContext *context;
-    if (mElement->mPrototype && xuldoc) {
+    nsXULPrototypeElement *elem = mElement->mPrototype;
+    if (elem && xuldoc) {
         // It'll be shared among the instances of the prototype.
 
         // Use the prototype document's special context.  Because
         // scopeObject is null, the JS engine has no other source of
         // <the-new-shared-event-handler>.__proto__ than to look in
         // cx->globalObject for Function.prototype.  That prototype
         // keeps the global object alive, so if we use this document's
         // global object, we'll be putting something in the prototype
@@ -750,19 +750,26 @@ nsScriptEventHandlerOwnerTearoff::Compil
     if (NS_FAILED(rv)) return rv;
 
     nsXULPrototypeAttribute *attr =
         mElement->FindPrototypeAttribute(kNameSpaceID_None, aName);
     if (attr) {
         XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheFills);
         // take a copy of the event handler, and tell the language about it.
         if (aHandler) {
+            NS_ASSERTION(!attr->mEventHandler, "Leaking handler.");
+
             rv = nsContentUtils::HoldScriptObject(aContext->GetScriptTypeID(),
-                                                  aHandler);
+                                                  elem,
+                                                  &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode),
+                                                  aHandler,
+                                                  elem->mHoldsScriptObject);
             if (NS_FAILED(rv)) return rv;
+
+            elem->mHoldsScriptObject = PR_TRUE;
         }
         attr->mEventHandler = (void *)aHandler;
     }
 
     return NS_OK;
 }
 
 void
@@ -2346,58 +2353,61 @@ nsXULElement::RecompileScriptEventListen
 
             nsAutoString value;
             GetAttr(kNameSpaceID_None, attr, value);
             AddScriptEventListener(attr, value, PR_TRUE);
         }
     }
 }
 
-NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXULPrototypeNode)
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_NATIVE_0(nsXULPrototypeNode)
 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->mNumAttributes; ++i) {
-            cb.NoteScriptChild(elem->mScriptTypeID,
-                               elem->mAttributes[i].mEventHandler);
-        }
         for (i = 0; i < elem->mNumChildren; ++i) {
             NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i],
                                                          nsXULPrototypeNode)
         }
     }
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BEGIN(nsXULPrototypeNode)
+    if (tmp->mType == nsXULPrototypeNode::eType_Element) {
+        nsXULPrototypeElement *elem =
+            static_cast<nsXULPrototypeElement*>(tmp);
+        if (elem->mHoldsScriptObject) {
+            PRUint32 i;
+            for (i = 0; i < elem->mNumAttributes; ++i) {
+                void *handler = elem->mAttributes[i].mEventHandler;
+                NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(elem->mScriptTypeID,
+                                                        handler)
+            }
+        }
+    }
     else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
-        static_cast<nsXULPrototypeScript*>(tmp)->mScriptObject.traverse(cb);
+        nsXULPrototypeScript *script =
+            static_cast<nsXULPrototypeScript*>(tmp);
+        NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(script->mScriptObject.mLangID,
+                                                script->mScriptObject.mObject)
     }
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release)
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeAttribute
 //
 
 nsXULPrototypeAttribute::~nsXULPrototypeAttribute()
 {
     MOZ_COUNT_DTOR(nsXULPrototypeAttribute);
-    NS_ASSERTION(!mEventHandler, "Finalize not called - language object leak!");
-}
-
-void
-nsXULPrototypeAttribute::Finalize(PRUint32 aLangID)
-{
-    if (mEventHandler) {
-        if (NS_FAILED(nsContentUtils::DropScriptObject(aLangID, mEventHandler)))
-            NS_ERROR("Failed to drop script object");
-        mEventHandler = nsnull;
-    }
 }
 
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeElement
 //
 
@@ -2674,16 +2684,29 @@ nsXULPrototypeElement::SetAttrAt(PRUint3
         // Don't abort if parsing failed, it could just be malformed css.
     }
 
     mAttributes[aPos].mValue.ParseStringOrAtom(aValue);
 
     return NS_OK;
 }
 
+void
+nsXULPrototypeElement::Unlink()
+{
+    if (mHoldsScriptObject) {
+        nsContentUtils::DropScriptObjects(mScriptTypeID, this,
+                                          &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode));
+        mHoldsScriptObject = PR_FALSE;
+    }
+    mNumAttributes = 0;
+    delete[] mAttributes;
+    mAttributes = nsnull;
+}
+
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeScript
 //
 
 nsXULPrototypeScript::nsXULPrototypeScript(PRUint32 aLangID, PRUint32 aLineNo, PRUint32 aVersion)
     : nsXULPrototypeNode(eType_Script),
       mLineNo(aLineNo),
@@ -2696,16 +2719,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();
 }
 
 nsresult
 nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
                                 nsIScriptGlobalObject* aGlobal,
                                 const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
     nsIScriptContext *context = aGlobal->GetScriptContext(
@@ -2813,17 +2837,17 @@ nsXULPrototypeScript::Deserialize(nsIObj
     NS_ASSERTION(context != nsnull, "Have no context for deserialization");
     NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
     nsScriptObjectHolder newScriptObject(context);
     rv = context->Deserialize(aStream, newScriptObject);
     if (NS_FAILED(rv)) {
         NS_WARNING("Language deseralization failed");
         return rv;
     }
-    mScriptObject.set(newScriptObject);
+    Set(newScriptObject);
     return NS_OK;
 }
 
 
 nsresult
 nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput,
                                            nsIScriptGlobalObject* aGlobal)
 {
@@ -2866,17 +2890,17 @@ nsXULPrototypeScript::DeserializeOutOfLi
                     // setting langID to UNKNOWN in the nsXULPrototypeScript
                     // ctor and not setting it until the scriptObject is set -
                     // code that pre-fetches these globals will then start
                     // asserting.)
                     if (mScriptObject.mLangID != newLangID) {
                         NS_ERROR("XUL cache gave different language?");
                         return NS_ERROR_UNEXPECTED;
                     }
-                    mScriptObject.set(newScriptObject);
+                    Set(newScriptObject);
                 }
             }
         }
 
         if (! mScriptObject.mObject) {
             nsCOMPtr<nsIURI> oldURI;
 
             if (mSrcURI) {
@@ -2992,17 +3016,17 @@ nsXULPrototypeScript::Compile(const PRUn
                                 aDocument->NodePrincipal(),
                                 urlspec.get(),
                                 aLineNo,
                                 mLangVersion,
                                 newScriptObject);
     if (NS_FAILED(rv))
         return rv;
 
-    mScriptObject.set(newScriptObject);
+    Set(newScriptObject);
     return rv;
 }
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeText
 //
 
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -119,19 +119,16 @@ public:
 
     nsAttrName mName;
     nsAttrValue mValue;
     // mEventHandler is only valid for the language ID specified in the
     // containing nsXULPrototypeElement.  We would ideally use
     // nsScriptObjectHolder, but want to avoid the extra lang ID.
     void* mEventHandler;
 
-    // Containing element must tell us the langID so we can cleanup.
-    void Finalize(PRUint32 aLangID);
-
 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
     /**
       If enough attributes, on average, are event handlers, it pays to keep
       mEventHandler here, instead of maintaining a separate mapping in each
       nsXULElement associating those mName values with their mEventHandlers.
       Assume we don't need to keep mNameSpaceID along with mName in such an
       event-handler-only name-to-function-pointer mapping.
 
@@ -225,17 +222,17 @@ public:
      * away.  This makes the parents through the tree stop owning their
      * children, whether or not the parent's reference count is zero.
      * Individual elements may still own individual prototypes, but
      * those prototypes no longer remember their children to allow them
      * to be constructed.
      */
     virtual void ReleaseSubtree() { Release(); }
 
-    NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXULPrototypeNode)
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsXULPrototypeNode)
 
 protected:
     nsXULPrototypeNode(Type aType)
         : mType(aType), mRefCnt(1) {}
 };
 
 class nsXULPrototypeElement : public nsXULPrototypeNode
 {
@@ -244,27 +241,25 @@ public:
         : nsXULPrototypeNode(eType_Element),
           mNumChildren(0),
           mChildren(nsnull),
           mNumAttributes(0),
           mAttributes(nsnull),
           mHasIdAttribute(PR_FALSE),
           mHasClassAttribute(PR_FALSE),
           mHasStyleAttribute(PR_FALSE),
+          mHoldsScriptObject(PR_FALSE),
           mScriptTypeID(nsIProgrammingLanguage::UNKNOWN)
     {
         NS_LOG_ADDREF(this, 1, ClassName(), ClassSize());
     }
 
     virtual ~nsXULPrototypeElement()
     {
-        PRUint32 i;
-        for (i = 0; i < mNumAttributes; i++)
-            mAttributes[i].Finalize(mScriptTypeID);
-        delete[] mAttributes;
+        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); }
 #endif
@@ -289,27 +284,30 @@ 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 Unlink();
+
     PRUint32                 mNumChildren;
     nsXULPrototypeNode**     mChildren;           // [OWNER]
 
     nsCOMPtr<nsINodeInfo>    mNodeInfo;           // [OWNER]
 
     PRUint32                 mNumAttributes;
     nsXULPrototypeAttribute* mAttributes;         // [OWNER]
     
     PRPackedBool             mHasIdAttribute:1;
     PRPackedBool             mHasClassAttribute:1;
     PRPackedBool             mHasStyleAttribute:1;
+    PRPackedBool             mHoldsScriptObject:1;
 
     // The language ID can not be set on a per-node basis, but is tracked
     // so that the language ID from the originating root can be used
     // (eg, when a node from an overlay ends up in our document, that node
     // must use its original script language, not our document's default.
     PRUint16                 mScriptTypeID;
     static void ReleaseGlobals()
     {
@@ -356,23 +354,62 @@ public:
     nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput,
                                   nsIScriptGlobalObject* aGlobal);
 
     nsresult Compile(const PRUnichar* aText, PRInt32 aTextLength,
                      nsIURI* aURI, PRUint32 aLineNo,
                      nsIDocument* aDocument,
                      nsIScriptGlobalObjectOwner* aGlobalOwner);
 
+    void Unlink()
+    {
+        if (mScriptObject.mObject) {
+            nsContentUtils::DropScriptObjects(mScriptObject.mLangID, this,
+                                              &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode));
+            mScriptObject.mObject = nsnull;
+        }
+    }
+
+    void Set(nsScriptObjectHolder &aHolder)
+    {
+        NS_ASSERTION(mScriptObject.mLangID == aHolder.getScriptTypeID(),
+                     "Wrong language, this will leak the previous object.");
+
+        mScriptObject.mLangID = aHolder.getScriptTypeID();
+        Set((void*)aHolder);
+    }
+    void Set(void *aObject)
+    {
+        NS_ASSERTION(!mScriptObject.mObject, "Leaking script object.");
+
+        nsresult rv = nsContentUtils::HoldScriptObject(mScriptObject.mLangID,
+                                                       this,
+                                                       &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode),
+                                                       aObject, PR_FALSE);
+        if (NS_SUCCEEDED(rv)) {
+            mScriptObject.mObject = aObject;
+        }
+    }
+
+    struct ScriptObjectHolder
+    {
+        ScriptObjectHolder(PRUint32 aLangID) : mLangID(aLangID),
+                                               mObject(nsnull)
+        {
+        }
+        PRUint32 mLangID;
+        void* mObject;
+    };
     nsCOMPtr<nsIURI>         mSrcURI;
     PRUint32                 mLineNo;
     PRPackedBool             mSrcLoading;
     PRPackedBool             mOutOfLine;
     nsXULDocument*           mSrcLoadWaiters;   // [OWNER] but not COMPtr
     PRUint32                 mLangVersion;
-    nsContentUtils::ScriptObjectHolder mScriptObject;
+    ScriptObjectHolder       mScriptObject;
 };
 
 class nsXULPrototypeText : public nsXULPrototypeNode
 {
 public:
     nsXULPrototypeText()
         : nsXULPrototypeNode(eType_Text)
     {
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -3204,17 +3204,17 @@ nsXULDocument::LoadScript(nsXULPrototype
                                    &fetchedLang);
         if (newScriptObject) {
             // The script language for a proto must remain constant - we
             // can't just change it for this unexpected language.
             if (aScriptProto->mScriptObject.mLangID != fetchedLang) {
                 NS_ERROR("XUL cache gave me an incorrect script language");
                 return NS_ERROR_UNEXPECTED;
             }
-            aScriptProto->mScriptObject.set(newScriptObject);
+            aScriptProto->Set(newScriptObject);
         }
 
         if (aScriptProto->mScriptObject.mObject) {
             rv = ExecuteScript(aScriptProto);
 
             // Ignore return value from execution, and don't block
             *aBlock = PR_FALSE;
             return NS_OK;
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -3724,17 +3724,18 @@ nsresult NS_CreateJSRuntime(nsIScriptRun
 // When consumed by non-JS (eg, another script language), conversion is done
 // on-the-fly.
 class nsJSArgArray : public nsIJSArgArray, public nsIArray {
 public:
   nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv, nsresult *prv);
   ~nsJSArgArray();
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
+                                                         nsIJSArgArray)
 
   // nsIArray
   NS_DECL_NSIARRAY
 
   // nsIJSArgArray
   nsresult GetArgs(PRUint32 *argc, void **argv);
 
   void ReleaseJSObjects();
@@ -3754,65 +3755,62 @@ nsJSArgArray::nsJSArgArray(JSContext *aC
   // copy the array - we don't know its lifetime, and ours is tied to xpcom
   // refcounting.  Alloc zero'd array so cleanup etc is safe.
   mArgv = (jsval *) PR_CALLOC(argc * sizeof(jsval));
   if (!mArgv) {
     *prv = NS_ERROR_OUT_OF_MEMORY;
     return;
   }
 
-  JSAutoRequest ar(aContext);
-  for (PRUint32 i = 0; i < argc; ++i) {
-    if (argv)
+  // Callers are allowed to pass in a null argv even for argc > 0. They can
+  // then use GetArgs to initialize the values.
+  if (argv) {
+    for (PRUint32 i = 0; i < argc; ++i)
       mArgv[i] = argv[i];
-    if (!::JS_AddNamedRoot(aContext, &mArgv[i], "nsJSArgArray.mArgv[i]")) {
-      *prv = NS_ERROR_UNEXPECTED;
-      return;
-    }
   }
-  *prv = NS_OK;
+  if (argc > 0)
+    *prv = NS_HOLD_JS_OBJECTS(this, nsJSArgArray);
 }
 
 nsJSArgArray::~nsJSArgArray()
 {
   ReleaseJSObjects();
 }
 
 void
 nsJSArgArray::ReleaseJSObjects()
 {
+  if (mArgc > 0)
+    NS_DROP_JS_OBJECTS(this, nsJSArgArray);
   if (mArgv) {
-    NS_ASSERTION(nsJSRuntime::sRuntime, "Where's the runtime gone?");
-    if (nsJSRuntime::sRuntime) {
-      for (PRUint32 i = 0; i < mArgc; ++i) {
-        ::JS_RemoveRootRT(nsJSRuntime::sRuntime, &mArgv[i]);
-      }
-    }
     PR_DELETE(mArgv);
   }
   mArgc = 0;
 }
 
 // QueryInterface implementation for nsJSArgArray
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
   tmp->ReleaseJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
-  {
-    jsval *argv = tmp->mArgv;
-    if (argv) {
-      jsval *end;
-      for (end = argv + tmp->mArgc; argv < end; ++argv) {
-        if (JSVAL_IS_OBJECT(*argv))
-          cb.NoteScriptChild(JAVASCRIPT, JSVAL_TO_OBJECT(*argv));
-      }
+  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;
+    for (end = argv + tmp->mArgc; argv < end; ++argv) {
+	    if (JSVAL_IS_GCTHING(*argv))
+        NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(JAVASCRIPT,
+                                                JSVAL_TO_GCTHING(*argv))
     }
   }
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
   NS_INTERFACE_MAP_ENTRY(nsIArray)
   NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSArgArray, nsIJSArgArray)
--- a/dom/src/base/nsJSTimeoutHandler.cpp
+++ b/dom/src/base/nsJSTimeoutHandler.cpp
@@ -54,17 +54,17 @@ static const char kSetIntervalStr[] = "s
 static const char kSetTimeoutStr[] = "setTimeout";
 
 // Our JS nsIScriptTimeoutHandler implementation.
 class nsJSScriptTimeoutHandler: public nsIScriptTimeoutHandler
 {
 public:
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler)
 
   nsJSScriptTimeoutHandler();
   ~nsJSScriptTimeoutHandler();
 
   virtual const PRUnichar *GetHandlerText();
   virtual void *GetScriptObject() {
     return mFunObj;
   }
@@ -113,19 +113,24 @@ private:
 // QueryInterface implementation for nsJSScriptTimeoutHandler
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
   tmp->ReleaseJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSScriptTimeoutHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgv)
-  cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, tmp->mFunObj);
+  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)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFunObj)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
   NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
 
@@ -141,59 +146,21 @@ nsJSScriptTimeoutHandler::~nsJSScriptTim
 {
   ReleaseJSObjects();
 }
 
 void
 nsJSScriptTimeoutHandler::ReleaseJSObjects()
 {
   if (mExpr || mFunObj) {
-    nsCOMPtr<nsIScriptContext> scx = mContext;
-    JSRuntime *rt = nsnull;
-
-    if (scx) {
-      JSContext *cx;
-      cx = (JSContext *)scx->GetNativeContext();
-      rt = ::JS_GetRuntime(cx);
-      mContext = nsnull;
-    } else {
-      // XXX The timeout *must* be unrooted, even if !scx. This can be
-      // done without a JS context using the JSRuntime. This is safe
-      // enough, but it would be better to drop all a window's
-      // timeouts before its context is cleared. Bug 50705 describes a
-      // situation where we're not. In that case, at the time the
-      // context is cleared, a timeout (actually an Interval) is still
-      // active, but temporarily removed from the window's list of
-      // timers (placed instead on the timer manager's list). This
-      // makes the nearly handy ClearAllTimeouts routine useless, so
-      // we settled on using the JSRuntime rather than relying on the
-      // window having a context. It would be good to remedy this
-      // workable but clumsy situation someday.
-
-      nsCOMPtr<nsIJSRuntimeService> rtsvc =
-        do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
-
-      if (rtsvc) {
-        rtsvc->GetRuntime(&rt);
-      }
-    }
-
-    if (!rt) {
-      // most unexpected. not much choice but to bail.
-
-      NS_ERROR("nsTimeout::Release() with no JSRuntime. eek!");
-
-      return;
-    }
-
     if (mExpr) {
-      ::JS_RemoveRootRT(rt, &mExpr);
+      NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
       mExpr = nsnull;
     } else if (mFunObj) {
-      ::JS_RemoveRootRT(rt, &mFunObj);
+      NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
       mFunObj = nsnull;
     } else {
       NS_WARNING("No func and no expr - roots may not have been removed");
     }
   }
 }
 
 nsresult
@@ -275,31 +242,29 @@ nsJSScriptTimeoutHandler::Init(nsIScript
                      *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
 
     // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
     ncc->SetExceptionWasThrown(PR_TRUE);
     return NS_ERROR_DOM_TYPE_ERR;
   }
 
   if (expr) {
-    if (!::JS_AddNamedRoot(cx, &mExpr, "timeout.mExpr")) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+    rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     mExpr = expr;
 
     // Get the calling location.
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
-    if (!::JS_AddNamedRoot(cx, &mFunObj, "timeout.mFunObj")) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+    rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     mFunObj = funobj;
 
     // Create our arg array - leave an extra slot for a secret final argument
     // that indicates to the called function how "late" the timeout is.  We
     // will fill that in when SetLateness is called.
     nsCOMPtr<nsIArray> array;
     rv = NS_CreateJSArgv(cx, (argc > 1) ? argc - 1 : argc, nsnull,
--- a/dom/src/events/nsJSEventListener.cpp
+++ b/dom/src/events/nsJSEventListener.cpp
@@ -76,34 +76,40 @@ 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?");
-  aContext->HoldScriptObject(aScopeObject);
+  NS_HOLD_JS_OBJECTS(this, nsJSEventListener);
 }
 
 nsJSEventListener::~nsJSEventListener() 
 {
-  mContext->DropScriptObject(mScopeObject);
+  if (mContext)
+    NS_DROP_JS_OBJECTS(this, nsJSEventListener);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSEventListener)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSEventListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTarget)
 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)
-  cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, tmp->mScopeObject);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSEventListener)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_MEMBER_CALLBACK(tmp->mContext->GetScriptTypeID(),
+                                                 mScopeObject)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
   NS_INTERFACE_MAP_ENTRY(nsIJSEventListener)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsJSEventListener, nsIDOMEventListener)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsJSEventListener, nsIDOMEventListener)
--- a/dom/src/events/nsJSEventListener.h
+++ b/dom/src/events/nsJSEventListener.h
@@ -60,18 +60,18 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   // nsIDOMEventListener interface
   NS_DECL_NSIDOMEVENTLISTENER
 
   // nsIJSEventListener interface
   virtual void SetEventName(nsIAtom* aName);
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsJSEventListener,
-                                           nsIDOMEventListener)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSEventListener,
+                                                         nsIDOMEventListener)
 protected:
   nsCOMPtr<nsIAtom> mEventName;
   
   enum nsReturnResult {
      nsReturnResult_eNotSet,
      nsReturnResult_eReverseReturnResult,
      nsReturnResult_eDoNotReverseReturnResult
   };
--- a/js/src/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/src/xpconnect/idl/nsIXPCScriptable.idl
@@ -36,16 +36,18 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIXPConnect.idl"
 
+[ptr] native JSTracerPtr(JSTracer);
+
 %{ C++
 #define NS_SUCCESS_I_DID_SOMETHING \
    (NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_XPCONNECT,1))
 %}
 
 /**
  * Note: This is not really an XPCOM interface.  For example, callers must
  * guarantee that they set the *_retval of the various methods that return a
--- a/js/src/xpconnect/idl/nsIXPConnect.idl
+++ b/js/src/xpconnect/idl/nsIXPConnect.idl
@@ -59,17 +59,17 @@
 /***************************************************************************/
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native JSValPtr(jsval);
       native JSVal(jsval);
       native JSID(jsid);
 [ptr] native voidPtrPtr(void*);
-[ptr] native JSTracerPtr(JSTracer);
+[ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer);
 
 /***************************************************************************/
 
 %{ C++
 /***************************************************************************/
 #define GENERATE_XPC_FAILURE(x) \
             (NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCONNECT,x))
 
@@ -145,17 +145,17 @@
 interface nsIXPCScriptable;
 interface nsIXPConnect;
 interface nsIXPConnectWrappedNative;
 interface nsIInterfaceInfo;
 interface nsIXPCSecurityManager;
 interface nsIPrincipal;
 
 %{C++
-class nsCycleCollectionTraversalCallback;
+class nsScriptObjectTracer;
 %}
 
 /***************************************************************************/
 [uuid(8916a320-d118-11d3-8f3a-0010a4e73d9a)]
 interface nsIXPConnectJSObjectHolder : nsISupports
 {
     readonly attribute JSObjectPtr      JSObject;
 };
@@ -438,17 +438,17 @@ interface nsIXPCFunctionThisTranslator :
 %{ C++
 // For use with the service manager
 // {CB6593E0-F9B2-11d2-BDD6-000064657374}
 #define NS_XPCONNECT_CID \
 { 0xcb6593e0, 0xf9b2, 0x11d2, \
     { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
 %}
 
-[uuid(52fc2ff3-c0ea-46c1-9105-655283c361ff)]
+[uuid(3eb7f5fc-1325-43af-aead-6033162e04af)]
 interface nsIXPConnect : nsISupports
 {
 %{ C++
   NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
 %}
 
     void
     initClasses(in JSContextPtr aJSContext,
@@ -722,9 +722,23 @@ interface nsIXPConnect : nsISupports
      * Wrap a jsval in a cross origin wrapper.
      * @param aJSContext A context to use to create objects.
      * @param aParent The parent to create the wrapper with.
      * @param aWrappedObj The object to wrap.
      */
     [noscript] JSVal getCrossOriginWrapperForObject(in JSContextPtr aJSContext,
                                                     in JSObjectPtr aParent,
                                                     in JSObjectPtr aWrappedObj);
+
+    /**
+     * Root JS objects held by aHolder.
+     * @param aHolder The object that hold the JS objects that should be rooted.
+     * @param aTrace The tracer for aHolder.
+     */
+    [noscript] void addJSHolder(in voidPtr aHolder,
+                                in nsScriptObjectTracerPtr aTracer);
+
+    /**
+     * Stop rooting the JS objects held by aHolder.
+     * @param aHolder The object that hold the rooted JS objects.
+     */
+    [noscript] void removeJSHolder(in voidPtr aHolder);
 };
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -690,38 +690,53 @@ NoteJSChild(JSTracer *trc, void *thing, 
     {
         ContextCallbackItem *item = static_cast<ContextCallbackItem*>(trc);
         item->cb->NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, thing);
     }
 }
 
 static uint8 GCTypeToTraceKindMap[GCX_NTYPES] = {
     JSTRACE_OBJECT,     /* GCX_OBJECT */
-    JSTRACE_STRING,     /* GCX_STRING (unused) */
-    JSTRACE_DOUBLE,     /* GCX_DOUBLE (unused) */
-    JSTRACE_STRING,     /* GCX_MUTABLE_STRING (unused) */
-    JSTRACE_FUNCTION,   /* GCX_FUNCTION (unused) */
+    JSTRACE_STRING,     /* GCX_STRING */
+    JSTRACE_DOUBLE,     /* GCX_DOUBLE */
+    JSTRACE_FUNCTION,   /* GCX_FUNCTION */
     JSTRACE_NAMESPACE,  /* GCX_NAMESPACE */
     JSTRACE_QNAME,      /* GCX_QNAME */
-    JSTRACE_XML         /* GCX_XML */
-    // We don't care about JSTRACE_STRING, so stop here
+    JSTRACE_XML,        /* GCX_XML */
+    (uint8)-1,         /* unused */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 0 */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 1 */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 2 */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 3 */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 4 */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 5 */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 6 */
+    JSTRACE_STRING,     /* GCX_EXTERNAL_STRING + 7 */
 };
 
+// static
+uint8
+nsXPConnect::GetTraceKind(void *thing)
+{
+    uint8 type = *js_GetGCThingFlags(thing) & GCF_TYPEMASK;
+    return GCTypeToTraceKindMap[type];
+}
+
 NS_IMETHODIMP
 nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
 {
     if(!mCycleCollectionContext)
         return NS_ERROR_FAILURE;
 
     JSContext *cx = mCycleCollectionContext->GetJSContext();
 
     PRUint32 refcount = mObjRefcounts->Get(p);
     NS_ASSERTION(refcount > 0, "JS object but unknown to the JS GC?");
 
-    uint8 ty = *js_GetGCThingFlags(p) & GCF_TYPEMASK;
+    uint8 ty = GetTraceKind(p);
     if(ty != GCX_OBJECT && ty != GCX_NAMESPACE && ty != GCX_QNAME &&
        ty != GCX_XML)
         return NS_OK;
 
 #ifdef DEBUG_CC
     if(ty == GCX_OBJECT)
     {
         JSObject *obj = static_cast<JSObject*>(p);
@@ -2092,16 +2107,28 @@ nsXPConnect::AfterProcessNextEvent(nsITh
 
 NS_IMETHODIMP
 nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread)
 {
     NS_NOTREACHED("Why tell us?");
     return NS_ERROR_UNEXPECTED;
 }
 
+NS_IMETHODIMP
+nsXPConnect::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
+{
+    return mRuntime->AddJSHolder(aHolder, aTracer);
+}
+
+NS_IMETHODIMP
+nsXPConnect::RemoveJSHolder(void* aHolder)
+{
+    return mRuntime->RemoveJSHolder(aHolder);
+}
+
 #ifdef DEBUG
 /* These are here to be callable from a debugger */
 JS_BEGIN_EXTERN_C
 void DumpJSStack()
 {
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if(NS_SUCCEEDED(rv) && xpc)
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -246,16 +246,52 @@ ContextCallback(JSContext *cx, uintN ope
         }
     }
 
     return gOldJSContextCallback
            ? gOldJSContextCallback(cx, operation)
            : JS_TRUE;
 }
 
+struct ObjectHolder : public JSDHashEntryHdr
+{
+    void *holder;
+    nsScriptObjectTracer* tracer;
+};
+
+nsresult
+XPCJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
+{
+    if(!mJSHolders.ops)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    ObjectHolder *entry =
+        reinterpret_cast<ObjectHolder*>(JS_DHashTableOperate(&mJSHolders,
+                                                             aHolder,
+                                                             JS_DHASH_ADD));
+    if(!entry)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    entry->holder = aHolder;
+    entry->tracer = aTracer;
+
+    return NS_OK;
+}
+
+nsresult
+XPCJSRuntime::RemoveJSHolder(void* aHolder)
+{
+    if(!mJSHolders.ops)
+        return NS_ERROR_OUT_OF_MEMORY;
+
+    JS_DHashTableOperate(&mJSHolders, aHolder, JS_DHASH_REMOVE);
+
+    return NS_OK;
+}
+
 // static
 void XPCJSRuntime::TraceJS(JSTracer* trc, void* data)
 {
     XPCJSRuntime* self = (XPCJSRuntime*)data;
 
     // Skip this part if XPConnect is shutting down. We get into
     // bad locking problems with the thread iteration otherwise.
     if(!self->GetXPConnect()->IsShuttingDown())
@@ -272,26 +308,58 @@ void XPCJSRuntime::TraceJS(JSTracer* trc
                              XPCPerThreadData::IterateThreads(&iterp)))
             {
                 // Trace those AutoMarkingPtr lists!
                 thread->TraceJS(trc);
             }
         }
     }
 
-    XPCWrappedNativeScope::TraceJS(trc, self);
+    // XPCJSObjectHolders don't participate in cycle collection, so always trace
+    // them here.
+    for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot())
+        static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
+        
+    self->TraceXPConnectRoots(trc);
+}
 
-    for (XPCRootSetElem *e = self->mVariantRoots; e ; e = e->GetNextRoot())
+PR_STATIC_CALLBACK(void)
+TraceJSObject(PRUint32 aLangID, void *aScriptThing, void *aClosure)
+{
+    if(aLangID == nsIProgrammingLanguage::JAVASCRIPT)
+    {
+        JS_CALL_TRACER(static_cast<JSTracer*>(aClosure), aScriptThing,
+                       nsXPConnect::GetXPConnect()->GetTraceKind(aScriptThing),
+                       "JSObjectHolder");
+    }
+}
+
+JS_STATIC_DLL_CALLBACK(JSDHashOperator)
+TraceJSHolder(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number,
+              void *arg)
+{
+    ObjectHolder* entry = reinterpret_cast<ObjectHolder*>(hdr);
+
+    entry->tracer->Trace(entry->holder, TraceJSObject, arg);
+
+    return JS_DHASH_NEXT;
+}
+
+void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc)
+{
+    XPCWrappedNativeScope::TraceJS(trc, this);
+
+    for(XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot())
         static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
 
-    for (XPCRootSetElem *e = self->mWrappedJSRoots; e ; e = e->GetNextRoot())
+    for(XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
         static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
 
-    for (XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot())
-        static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
+    if(mJSHolders.ops)
+        JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc);
 }
 
 // static
 JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
 {
     nsVoidArray* dyingWrappedJSArray;
 
     XPCJSRuntime* self = nsXPConnect::GetRuntime();
@@ -804,16 +872,22 @@ XPCJSRuntime::~XPCJSRuntime()
 
     // unwire the readable/JSString sharing magic
     XPCStringConvert::ShutdownDOMStringFinalizer();
 
     XPCConvert::RemoveXPCOMUCStringFinalizer();
 
     gOldJSGCCallback = NULL;
     gOldJSContextCallback = NULL;
+
+    if(mJSHolders.ops)
+    {
+        JS_DHashTableFinish(&mJSHolders);
+        mJSHolders.ops = nsnull;
+    }
 }
 
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect,
                            nsIJSRuntimeService* aJSRuntimeService)
  : mXPConnect(aXPConnect),
    mJSRuntime(nsnull),
    mJSRuntimeService(aJSRuntimeService),
    mContextMap(JSContext2XPCContextMap::newMap(XPC_CONTEXT_MAP_SIZE)),
@@ -857,16 +931,20 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     if(mJSRuntime)
     {
         gOldJSContextCallback = JS_SetContextCallback(mJSRuntime,
                                                       ContextCallback);
         gOldJSGCCallback = JS_SetGCCallbackRT(mJSRuntime, GCCallback);
         JS_SetExtraGCRoots(mJSRuntime, TraceJS, this);
     }
 
+    if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull,
+                          sizeof(ObjectHolder), 512))
+        mJSHolders.ops = nsnull;
+
     // Install a JavaScript 'debugger' keyword handler in debug builds only
 #ifdef DEBUG
     if(mJSRuntime && !JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler)
         xpc_InstallJSDebuggerKeywordHandler(mJSRuntime);
 #endif
 }
 
 // static
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -493,16 +493,19 @@ public:
     virtual nsresult FinishCycleCollection();
     virtual nsCycleCollectionParticipant *ToParticipant(void *p);
 #ifdef DEBUG_CC
     virtual void PrintAllReferencesTo(void *p);
     virtual void SuspectExtraPointers();
 #endif
 
     JSObjectRefcounts* GetJSObjectRefcounts() {return mObjRefcounts;}
+
+    static uint8 GetTraceKind(void *thing);
+
 #ifndef XPCONNECT_STANDALONE
     void RecordTraversal(void *p, nsISupports *s);
 #endif
 
 #ifdef XPC_IDISPATCH_SUPPORT
 public:
     static PRBool IsIDispatchEnabled();
 #endif
@@ -671,23 +674,29 @@ public:
     }
     const char* GetStringName(uintN index) const
     {
         NS_ASSERTION(index < IDX_TOTAL_COUNT, "index out of range");
         return mStrings[index];
     }
 
     static void JS_DLL_CALLBACK TraceJS(JSTracer* trc, void* data);
+    void TraceXPConnectRoots(JSTracer *trc);
+    void AddXPConnectRoots(JSContext* cx,
+                           nsCycleCollectionTraversalCallback& cb);
 
     static JSBool JS_DLL_CALLBACK GCCallback(JSContext *cx, JSGCStatus status);
 
     inline void AddVariantRoot(XPCTraceableVariant* variant);
     inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
     inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
 
+    nsresult AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
+    nsresult RemoveJSHolder(void* aHolder);
+
     void DebugDump(PRInt16 depth);
 
     void SystemIsBeingShutDown(JSContext* cx);
 
     PRThread* GetThreadRunningGC() const {return mThreadRunningGC;}
 
     ~XPCJSRuntime();
 
@@ -741,16 +750,17 @@ private:
     nsVoidArray mWrappedJSToReleaseArray;
     nsVoidArray mNativesToReleaseArray;
     JSBool mMainThreadOnlyGC;
     JSBool mDeferReleases;
     JSBool mDoingFinalization;
     XPCRootSetElem *mVariantRoots;
     XPCRootSetElem *mWrappedJSRoots;
     XPCRootSetElem *mObjectHolderRoots;
+    JSDHashTable mJSHolders;
 };
 
 /***************************************************************************/
 /***************************************************************************/
 // XPCContext is mostly a dumb class to hold JSContext specific data and
 // maps that let us find wrappers created for the given JSContext.
 
 // no virtuals
@@ -1186,18 +1196,16 @@ public:
     static JSBool
     IsDyingScope(XPCWrappedNativeScope *scope);
 
     void SetComponents(nsXPCComponents* aComponents);
     void SetGlobal(XPCCallContext& ccx, JSObject* aGlobal);
 
     static void InitStatics() { gScopes = nsnull; gDyingScopes = nsnull; }
 
-    void Traverse(nsCycleCollectionTraversalCallback &cb);
-
 #ifndef XPCONNECT_STANDALONE
     /**
      * Fills the hash mapping global object to principal.
      */
     static void TraverseScopes(XPCCallContext& ccx);
 #endif
 
 protected:
@@ -1926,20 +1934,21 @@ private:
 
 /***************************************************************************/
 // XPCWrappedNative the wrapper around one instance of a native xpcom object
 // to be used from JavaScript.
 
 class XPCWrappedNative : public nsIXPConnectWrappedNative
 {
 public:
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
     NS_DECL_NSIXPCONNECTWRAPPEDNATIVE
     NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
+    NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(XPCWrappedNative)
 
 #ifndef XPCONNECT_STANDALONE
     virtual nsIPrincipal* GetObjectPrincipal() const;
 #endif
 
     JSBool
     IsValid() const {return nsnull != mFlatJSObject;}
 
@@ -2039,17 +2048,17 @@ public:
     static nsresult
     ReparentWrapperIfFound(XPCCallContext& ccx,
                            XPCWrappedNativeScope* aOldScope,
                            XPCWrappedNativeScope* aNewScope,
                            JSObject* aNewParent,
                            nsISupports* aCOMObj,
                            XPCWrappedNative** aWrapper);
 
-    void FlatJSObjectFinalized(JSContext *cx, JSObject *obj);
+    void FlatJSObjectFinalized(JSContext *cx);
 
     void SystemIsBeingShutDown(JSContext* cx);
 
 #ifdef XPC_DETECT_LEADING_UPPERCASE_ACCESS_ERRORS
     // This will try to find a member that is of the form "camelCased"
     // but was accessed from JS using "CamelCased". This is here to catch
     // mistakes caused by the confusion magnet that JS methods are by
     // convention 'foo' while C++ members are by convention 'Foo'.
@@ -2191,18 +2200,20 @@ private:
         XPCWrappedNativeProto*   mMaybeProto;
     };
     XPCNativeSet*                mSet;
     JSObject*                    mFlatJSObject;
     XPCNativeScriptableInfo*     mScriptableInfo;
     XPCWrappedNativeTearOffChunk mFirstChunk;
     JSObject*                    mWrapper;
 
+#ifdef XPC_CHECK_WRAPPER_THREADSAFETY
 public:
     nsCOMPtr<nsIThread>          mThread; // Don't want to overload _mOwningThread
+#endif
 };
 
 /***************************************************************************
 ****************************************************************************
 *
 * Core classes for wrapped JSObject for use from native code...
 *
 ****************************************************************************
@@ -3023,17 +3034,16 @@ private:
     AutoMarkingPtr*      mAutoRoots;
 
     jsuword              mStackLimit;
 
 #ifdef XPC_CHECK_WRAPPER_THREADSAFETY
     JSUint32             mWrappedNativeThreadsafetyReportDepth;
 #endif
     PRThread*            mThread;
-    nsVoidArray          mNativesToReleaseArray;
 
     static PRLock*           gLock;
     static XPCPerThreadData* gThreads;
     static PRUintn           gTLSIndex;
 };
 
 /**************************************************************/
 
--- a/js/src/xpconnect/src/xpcwrappedjs.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjs.cpp
@@ -584,20 +584,16 @@ nsXPCWrappedJS::SystemIsBeingShutDown(JS
     // mJSObj == nsnull is used to indicate that the wrapper is no longer valid
     // and that calls should fail without trying to use any of the
     // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
 
     // NOTE: that mClass is retained so that GetInterfaceInfo can continue to
     // work (and avoid crashing some platforms).
     mJSObj = nsnull;
 
-    // There is no reason to keep this root any longer. Since we've cleared
-    // mJSObj our dtor will not remove the root later. So, we do it now.
-    JS_RemoveRootRT(rt, &mJSObj);
-
     // Notify other wrappers in the chain.
     if(mNext)
         mNext->SystemIsBeingShutDown(rt);
 }
 
 /***************************************************************************/
 
 /* readonly attribute nsISimpleEnumerator enumerator; */
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -956,17 +956,17 @@ NS_IMPL_THREADSAFE_RELEASE(XPCWrappedNat
  *    mJSObject we set it's parent to be mFlatJSObject. This way we know that
  *    when mFlatJSObject get's collected there are no outstanding reachable
  *    tearoff mJSObjects. Note that we must clear the private of any lingering
  *    mJSObjects at this point because we have no guarentee of the *order* of
  *    finalization within a given gc cycle.
  */
 
 void
-XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx, JSObject *obj)
+XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx)
 {
     if(!IsValid())
         return;
 
     // Iterate the tearoffs and null out each of their JSObject's privates.
     // This will keep them from trying to access their pointers to the
     // dying tearoff object. We can safely assume that those remaining
     // JSObjects are about to be finalized too.
--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
@@ -642,28 +642,28 @@ XPC_WN_Shared_Enumerate(JSContext *cx, J
 /***************************************************************************/
 
 JS_STATIC_DLL_CALLBACK(void)
 XPC_WN_NoHelper_Finalize(JSContext *cx, JSObject *obj)
 {
     XPCWrappedNative* p = (XPCWrappedNative*) JS_GetPrivate(cx, obj);
     if(!p)
         return;
-    p->FlatJSObjectFinalized(cx, obj);
+    p->FlatJSObjectFinalized(cx);
 }
 
 static void
 TraceScopeJSObjects(JSTracer *trc, XPCWrappedNativeScope* scope)
 {
     NS_ASSERTION(scope, "bad scope");
 
     JSObject* obj;
 
     obj = scope->GetGlobalJSObject();
-    NS_ASSERTION(scope, "bad scope JSObject");
+    NS_ASSERTION(obj, "bad scope JSObject");
     JS_CALL_OBJECT_TRACER(trc, obj, "XPCWrappedNativeScope::mGlobalJSObject");
 
     obj = scope->GetPrototypeJSObject();
     if(obj)
     {
         JS_CALL_OBJECT_TRACER(trc, obj,
                               "XPCWrappedNativeScope::mPrototypeJSObject");
     }
@@ -1030,17 +1030,17 @@ XPC_WN_Helper_HasInstance(JSContext *cx,
 
 JS_STATIC_DLL_CALLBACK(void)
 XPC_WN_Helper_Finalize(JSContext *cx, JSObject *obj)
 {
     XPCWrappedNative* wrapper = (XPCWrappedNative*) JS_GetPrivate(cx, obj);
     if(!wrapper)
         return;
     wrapper->GetScriptableCallback()->Finalize(wrapper, cx, obj);
-    wrapper->FlatJSObjectFinalized(cx, obj);
+    wrapper->FlatJSObjectFinalized(cx);
 }
 
 JS_STATIC_DLL_CALLBACK(void)
 XPC_WN_Helper_Trace(JSTracer *trc, JSObject *obj)
 {
     XPCWrappedNative* wrapper =
         XPCWrappedNative::GetWrappedNativeOfJSObject(trc->context, obj);
     if(wrapper && wrapper->IsValid())
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -846,26 +846,16 @@ XPCWrappedNativeScope::DebugDump(PRInt16
             XPC_LOG_INDENT();
             mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth);
             XPC_LOG_OUTDENT();
         }
     XPC_LOG_OUTDENT();
 #endif
 }
 
-void
-XPCWrappedNativeScope::Traverse(nsCycleCollectionTraversalCallback &cb)
-{
-    // See TraceScopeJSObjects.
-    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mGlobalJSObject);
-    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mPrototypeJSObject);
-    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
-                       mPrototypeJSFunction);
-}
-
 #ifndef XPCONNECT_STANDALONE
 // static
 void
 XPCWrappedNativeScope::TraverseScopes(XPCCallContext& ccx)
 {
     // Hold the lock throughout.
     XPCAutoLock lock(ccx.GetRuntime()->GetMapLock());
 
--- a/xpcom/base/nsAgg.h
+++ b/xpcom/base/nsAgg.h
@@ -113,17 +113,18 @@ public:                                 
   static _class* Downcast(nsISupports* s)                                   \
   {                                                                         \
     return (_class*)((char*)(s) - offsetof(_class, fAggregated));           \
   }                                                                         \
   static nsISupports* Upcast(_class *p)                                     \
   {                                                                         \
     return p->InnerObject();                                                \
   }                                                                         \
-};                                                           
+};                                                                          \
+NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 // Put this in your class's constructor:
 #define NS_INIT_AGGREGATED(outer)                                           \
   PR_BEGIN_MACRO                                                            \
     fOuter = outer ? outer : &fAggregated;                                  \
   PR_END_MACRO
 
 
--- a/xpcom/glue/nsCycleCollectionParticipant.cpp
+++ b/xpcom/glue/nsCycleCollectionParticipant.cpp
@@ -33,16 +33,31 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 
+PR_STATIC_CALLBACK(void)
+NoteChild(PRUint32 aLangID, void *aScriptThing, void *aClosure)
+{
+  nsCycleCollectionTraversalCallback *cb =
+    static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
+  cb->NoteScriptChild(aLangID, aScriptThing);
+}
+
+void
+nsScriptObjectTracer::TraverseScriptObjects(void *p,
+                                        nsCycleCollectionTraversalCallback &cb)
+{
+  Trace(p, NoteChild, &cb);
+}
+
 nsresult
 nsXPCOMCycleCollectionParticipant::Root(void *p)
 {
     nsISupports *s = static_cast<nsISupports*>(p);
     NS_ADDREF(s);
     return NS_OK;
 }
 
@@ -67,16 +82,22 @@ nsXPCOMCycleCollectionParticipant::Trave
   return NS_OK;
 }
 
 void
 nsXPCOMCycleCollectionParticipant::UnmarkPurple(nsISupports *n)
 {
 }
 
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::Trace(void *p, TraceCallback cb,
+                                         void *closure)
+{
+}
+
 PRBool
 nsXPCOMCycleCollectionParticipant::CheckForRightISupports(nsISupports *s)
 {
     nsCOMPtr<nsISupports> foo;
     s->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
                       getter_AddRefs(foo));
     return s == foo;
 }
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -120,26 +120,39 @@ public:
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionParticipant, 
                               NS_CYCLECOLLECTIONPARTICIPANT_IID)
 
 #undef IMETHOD_VISIBILITY
 #define IMETHOD_VISIBILITY NS_COM_GLUE
 
+typedef void
+(* PR_CALLBACK TraceCallback)(PRUint32 langID, void *p, void *closure);
+
+class NS_NO_VTABLE nsScriptObjectTracer : public nsCycleCollectionParticipant
+{
+public:
+    NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure) = 0;
+    void NS_COM_GLUE TraverseScriptObjects(void *p,
+                                        nsCycleCollectionTraversalCallback &cb);
+};
+
 class NS_COM_GLUE nsXPCOMCycleCollectionParticipant
-    : public nsCycleCollectionParticipant
+    : public nsScriptObjectTracer
 {
 public:
     NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb);
 
     NS_IMETHOD Root(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);
 };
 
 #undef IMETHOD_VISIBILITY
 #define IMETHOD_VISIBILITY NS_VISIBILITY_HIDDEN
 
@@ -148,18 +161,21 @@ public:
 ///////////////////////////////////////////////////////////////////////////////
 
 #define NS_CYCLE_COLLECTION_INNERCLASS                                         \
         cycleCollection
 
 #define NS_CYCLE_COLLECTION_CLASSNAME(_class)                                  \
         _class::NS_CYCLE_COLLECTION_INNERCLASS
 
+#define NS_CYCLE_COLLECTION_INNERNAME                                          \
+        _cycleCollectorGlobal
+
 #define NS_CYCLE_COLLECTION_NAME(_class)                                       \
-        _class##_cycleCollectorGlobal
+        _class::NS_CYCLE_COLLECTION_INNERNAME
 
 #define NS_IMPL_QUERY_CYCLE_COLLECTION(_class)                                 \
   if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {          \
     *aInstancePtr = & NS_CYCLE_COLLECTION_NAME(_class);                        \
     return NS_OK;                                                              \
   } else
 
 #define NS_IMPL_QUERY_CYCLE_COLLECTION_ISUPPORTS(_class)                       \
@@ -196,16 +212,18 @@ public:
     NS_PRECONDITION(aInstancePtr, "null out param");                          \
                                                                               \
     if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {       \
       *aInstancePtr = &NS_CYCLE_COLLECTION_NAME(_class);                      \
       return NS_OK;                                                           \
     }                                                                         \
     nsresult rv;
 
+#define NS_CYCLE_COLLECTION_UPCAST(obj, clazz)                                 \
+  NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj)
 
 ///////////////////////////////////////////////////////////////////////////////
 // Helpers for implementing nsCycleCollectionParticipant::Unlink
 ///////////////////////////////////////////////////////////////////////////////
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)                          \
   NS_IMETHODIMP                                                                \
   NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p)                       \
@@ -318,133 +336,198 @@ public:
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(_field)                   \
     {                                                                          \
       PRInt32 i;                                                               \
       for (i = 0; i < tmp->_field.Count(); ++i)                                \
         cb.NoteXPCOMChild(tmp->_field[i]);                                     \
     }
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(_ptr, _ptr_class)         \
-  cb.NoteNativeChild(_ptr, &NS_CYCLE_COLLECTION_NATIVE_NAME(_ptr_class));
+  cb.NoteNativeChild(_ptr, &NS_CYCLE_COLLECTION_NAME(_ptr_class));
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(_field, _field_class)  \
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->_field, _field_class)
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY(_array, _element_class)     \
     {                                                                          \
       PRUint32 i, length = (_array).Length();                                  \
       for (i = 0; i < length; ++i)                                             \
         NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR((_array)[i],              \
                                                      _element_class);          \
     }
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(_field,              \
                                                           _element_class)      \
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY(tmp->_field, _element_class)
 
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS                       \
+    TraverseScriptObjects(tmp, cb);
+
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END                                  \
     return NS_OK;                                                              \
   }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Helpers for implementing nsScriptObjectTracer::Trace
+///////////////////////////////////////////////////////////////////////////////
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class)                           \
+  void                                                                         \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p,                        \
+                                               TraceCallback aCallback,        \
+                                               void *aClosure)                 \
+  {                                                                            \
+    nsISupports *s = static_cast<nsISupports*>(p);                             \
+    NS_ASSERTION(CheckForRightISupports(s),                                    \
+                 "not the nsISupports pointer we expect");                     \
+    _class *tmp = Downcast(s);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BEGIN(_class)                    \
+  void                                                                         \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p,                        \
+                                               TraceCallback aCallback,        \
+                                               void *aClosure)                 \
+  {                                                                            \
+    _class *tmp = static_cast<_class*>(p);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(_langID, _object)              \
+  if (_object)                                                                 \
+    aCallback(_langID, _object, aClosure);
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_MEMBER_CALLBACK(_langID, _field)        \
+  NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(_langID, tmp->_field)
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(_object)                    \
+  NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(nsIProgrammingLanguage::JAVASCRIPT,  \
+                                          _object)
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(_field)              \
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->_field)
+
+#define NS_IMPL_CYCLE_COLLECTION_TRACE_END                                     \
+  }
+
+///////////////////////////////////////////////////////////////////////////////
 // Helpers for implementing a concrete nsCycleCollectionParticipant 
 ///////////////////////////////////////////////////////////////////////////////
 
-#define NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _base)                \
-class NS_CYCLE_COLLECTION_INNERCLASS                                           \
- : public nsXPCOMCycleCollectionParticipant                                    \
-{                                                                              \
+#define NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE                               \
+  static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME;
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base)                     \
 public:                                                                        \
   NS_IMETHOD Unlink(void *p);                                                  \
   NS_IMETHOD Traverse(void *p,                                                 \
                       nsCycleCollectionTraversalCallback &cb);                 \
   NS_IMETHOD_(void) UnmarkPurple(nsISupports *s)                               \
   {                                                                            \
     Downcast(s)->UnmarkPurple();                                               \
   }                                                                            \
   static _class* Downcast(nsISupports* s)                                      \
   {                                                                            \
     return static_cast<_class*>(static_cast<_base*>(s));                       \
   }                                                                            \
   static nsISupports* Upcast(_class *p)                                        \
   {                                                                            \
     return NS_ISUPPORTS_CAST(_base*, p);                                       \
-  }                                                                            \
-};                                                           
+  }
+
+#define NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _base)                \
+class NS_CYCLE_COLLECTION_INNERCLASS                                           \
+ : public nsXPCOMCycleCollectionParticipant                                    \
+{                                                                              \
+  NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base)                           \
+};                                                                             \
+NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 #define NS_DECL_CYCLE_COLLECTION_CLASS(_class)                                 \
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class)
 
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _base)  \
+class NS_CYCLE_COLLECTION_INNERCLASS                                           \
+ : public nsXPCOMCycleCollectionParticipant                                    \
+{                                                                              \
+  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)
+
 #define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(_class, _base_class)          \
 class NS_CYCLE_COLLECTION_INNERCLASS                                           \
  : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class)                           \
 {                                                                              \
 public:                                                                        \
   NS_IMETHOD Unlink(void *p);                                                  \
   NS_IMETHOD Traverse(void *p,                                                 \
                       nsCycleCollectionTraversalCallback &cb);                 \
   static _class* Downcast(nsISupports* s)                                      \
   {                                                                            \
     return static_cast<_class*>(static_cast<_base_class*>(                     \
       NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s)));               \
   }                                                                            \
-};
+};                                                                             \
+NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 #define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(_class,             \
                                                            _base_class)        \
 class NS_CYCLE_COLLECTION_INNERCLASS                                           \
  : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class)                           \
 {                                                                              \
 public:                                                                        \
   NS_IMETHOD Traverse(void *p,                                                 \
                       nsCycleCollectionTraversalCallback &cb);                 \
   static _class* Downcast(nsISupports* s)                                      \
   {                                                                            \
     return static_cast<_class*>(static_cast<_base_class*>(                     \
       NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s)));               \
   }                                                                            \
-};
+};                                                                             \
+NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
 /**
  * This implements a stub UnmarkPurple function for classes that want to be
  * traversed but whose AddRef/Release functions don't add/remove them to/from
  * the purple buffer. If you're just using NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  * then you don't need this.
  */
 #define NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(_class)                    \
   NS_IMETHODIMP_(void) UnmarkPurple()                                          \
   {                                                                            \
   }                                                                            \
 
 #define NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                                 \
-  static NS_CYCLE_COLLECTION_CLASSNAME(_class)                                 \
-    NS_CYCLE_COLLECTION_NAME(_class);
+  NS_CYCLE_COLLECTION_CLASSNAME(_class) NS_CYCLE_COLLECTION_NAME(_class);
 
-#define NS_CYCLE_COLLECTION_NATIVE_INNERNAME                                   \
-        _cycleCollectorGlobal
-
-#define NS_CYCLE_COLLECTION_NATIVE_NAME(_class)                                \
-        _class::NS_CYCLE_COLLECTION_NATIVE_INNERNAME
+#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY                             \
+  public:                                                                      \
+    NS_IMETHOD Root(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                                       \
   {                                                                            \
-  public:                                                                      \
-    NS_IMETHOD Root(void *n);                                                  \
-    NS_IMETHOD Unlink(void *n);                                                \
-    NS_IMETHOD Unroot(void *n);                                                \
-    NS_IMETHOD Traverse(void *n,                                               \
-                      nsCycleCollectionTraversalCallback &cb);                 \
+     NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY                                \
   };                                                                           \
-  static NS_CYCLE_COLLECTION_INNERCLASS                                        \
-      NS_CYCLE_COLLECTION_NATIVE_INNERNAME;
+  NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
 
-#define NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(_class)                          \
-  NS_CYCLE_COLLECTION_CLASSNAME(_class) NS_CYCLE_COLLECTION_NATIVE_NAME(_class);
+#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(_class)            \
+  class NS_CYCLE_COLLECTION_INNERCLASS                                         \
+   : public nsScriptObjectTracer                                               \
+  {                                                                            \
+    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)                         \
   {                                                                            \
     _class *tmp = static_cast<_class*>(p);                                     \
     tmp->_root_function();                                                     \
     return NS_OK;                                                              \