Fix for bug 368774 (Make cycle collector work with refcounted non-XPCOM objects). r=dbaron, sr=sicking.
authorpeterv@propagandism.org
Thu, 24 May 2007 07:10:02 -0700
changeset 1829 b42f911e329e41ee710ff7450f34968c5af71b04
parent 1828 d7f479aa690ad3a3f952fbf9e5575a5f910dda2d
child 1830 5dec6e7f7a569a9011d80e6a31d03b81da59ffc7
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron, sicking
bugs368774
milestone1.9a5pre
Fix for bug 368774 (Make cycle collector work with refcounted non-XPCOM objects). r=dbaron, sr=sicking.
content/base/public/nsContentUtils.h
content/html/content/src/nsGenericHTMLElement.h
content/xbl/src/nsBindingManager.cpp
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLBinding.h
content/xbl/src/nsXBLInsertionPoint.cpp
content/xbl/src/nsXBLInsertionPoint.h
content/xbl/src/nsXBLProtoImpl.cpp
content/xbl/src/nsXBLProtoImpl.h
content/xbl/src/nsXBLProtoImplField.cpp
content/xbl/src/nsXBLProtoImplField.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/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
content/xul/document/src/nsXULContentSink.cpp
content/xul/document/src/nsXULPrototypeDocument.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcwrappedjs.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
xpcom/base/nsAgg.h
xpcom/base/nsCycleCollectionParticipant.cpp
xpcom/base/nsCycleCollectionParticipant.h
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
xpcom/build/dlldeps.cpp
xpcom/ds/nsVariant.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -991,16 +991,20 @@ public:
     nsresult set(void *aObject)
     {
       nsresult rv = HoldScriptObject(mLangID, aObject);
       if (NS_SUCCEEDED(rv)) {
         mObject = aObject;
       }
       return rv;
     }
+    void traverse(nsCycleCollectionTraversalCallback &cb)
+    {
+      cb.NoteScriptChild(mLangID, mObject);
+    }
     PRUint32 mLangID;
     void *mObject;
   };
 
   /**
    * Convert nsIContent::IME_STATUS_* to nsIKBStateControll::IME_STATUS_*
    */
   static PRUint32 GetKBStateControlStatusFromIMEStatus(PRUint32 aState);
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1057,17 +1057,17 @@ NS_NewHTML##_elementName##Element(nsINod
                                                nsIDOMHTMLElement)
 
 #define NS_HTML_CONTENT_CC_INTERFACE_MAP_AMBIGUOUS_BEGIN(_class, _base,       \
                                                         _base_if)             \
   NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr)    \
   {                                                                           \
     NS_ENSURE_ARG_POINTER(aInstancePtr);                                      \
                                                                               \
-    if ( aIID.Equals(NS_GET_IID(nsCycleCollectionParticipant)) ) {            \
+    if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {       \
       *aInstancePtr = &NS_CYCLE_COLLECTION_NAME(_class);                      \
       return NS_OK;                                                           \
     }                                                                         \
                                                                               \
     *aInstancePtr = nsnull;                                                   \
                                                                               \
     nsresult rv;                                                              \
                                                                               \
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -82,22 +82,24 @@
 #include "nsBindingManager.h"
 
 #include "nsThreadUtils.h"
 
 // ==================================================================
 // = nsAnonymousContentList 
 // ==================================================================
 
-class nsAnonymousContentList : public nsGenericDOMNodeList
+class nsAnonymousContentList : public nsIDOMNodeList
 {
 public:
   nsAnonymousContentList(nsInsertionPointList* aElements);
   virtual ~nsAnonymousContentList();
 
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsAnonymousContentList)
   // nsIDOMNodeList interface
   NS_DECL_NSIDOMNODELIST
 
   PRInt32 GetInsertionPointCount() { return mElements->Length(); }
 
   nsXBLInsertionPoint* GetInsertionPointAt(PRInt32 i) { return NS_STATIC_CAST(nsXBLInsertionPoint*, mElements->ElementAt(i)); }
   void RemoveInsertionPointAt(PRInt32 i) { mElements->RemoveElementAt(i); }
 
@@ -115,16 +117,39 @@ nsAnonymousContentList::nsAnonymousConte
 }
 
 nsAnonymousContentList::~nsAnonymousContentList()
 {
   MOZ_COUNT_DTOR(nsAnonymousContentList);
   delete mElements;
 }
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsAnonymousContentList)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList)
+
+NS_INTERFACE_MAP_BEGIN(nsAnonymousContentList)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMNodeList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NodeList)
+  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsAnonymousContentList)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsAnonymousContentList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAnonymousContentList)
+  {
+    PRInt32 i, count = tmp->mElements->Length();
+    for (i = 0; i < count; ++i) {
+      NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mElements->ElementAt(i),
+                                                      nsXBLInsertionPoint);
+    }
+  }
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMETHODIMP
 nsAnonymousContentList::GetLength(PRUint32* aLength)
 {
   NS_ASSERTION(aLength != nsnull, "null ptr");
   if (! aLength)
       return NS_ERROR_NULL_POINTER;
 
   PRInt32 cnt = mElements->Length();
@@ -304,17 +329,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   if (tmp->mInsertionParentTable.ops)
     PL_DHashTableFinish(&(tmp->mInsertionParentTable));
   tmp->mInsertionParentTable.ops = nsnull;
 
   if (tmp->mWrapperTable.ops)
     PL_DHashTableFinish(&(tmp->mWrapperTable));
   tmp->mWrapperTable.ops = nsnull;
 
-  tmp->mAttachedStack.Clear();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mAttachedStack)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 static PLDHashOperator
 DocumentInfoHashtableTraverser(nsIURI* key,
                                nsIXBLDocumentInfo* di,
                                void* userArg)
 {
@@ -336,16 +361,18 @@ LoadingDocHashtableTraverser(nsIURI* key
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager)
   // The hashes keyed on nsIContent are traversed from the nsIContent itself.
   if (tmp->mDocumentTable.IsInitialized())
       tmp->mDocumentTable.EnumerateRead(&DocumentInfoHashtableTraverser, &cb);
   if (tmp->mLoadingDocTable.IsInitialized())
       tmp->mLoadingDocTable.EnumerateRead(&LoadingDocHashtableTraverser, &cb);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mAttachedStack,
+                                                    nsXBLBinding)
   // No need to traverse mProcessAttachedQueueEvent, since it'll just
   // fire at some point.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager)
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
@@ -1360,19 +1387,18 @@ nsBindingManager::Traverse(nsIContent *a
                            nsCycleCollectionTraversalCallback &cb)
 {
   if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
     return;
   }
 
   nsXBLBinding *binding = GetBinding(aContent);
   if (binding) {
-    // XXX nsXBLBinding isn't nsISupports but it is refcounted, so we can't
-    //     traverse it.
     cb.NoteXPCOMChild(aContent);
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(binding, nsXBLBinding)
   }
   nsISupports *value;
   if (mContentListTable.ops &&
       (value = LookupObject(mContentListTable, aContent))) {
     cb.NoteXPCOMChild(aContent);
     cb.NoteXPCOMChild(value);
   }
   if (mAnonymousNodesTable.ops &&
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -168,16 +168,46 @@ nsXBLBinding::nsXBLBinding(nsXBLPrototyp
 
 nsXBLBinding::~nsXBLBinding(void)
 {
   delete mInsertionPointTable;
   nsIXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
   NS_RELEASE(info);
 }
 
+PR_STATIC_CALLBACK(PLDHashOperator)
+TraverseKey(nsISupports* aKey, nsInsertionPointList* aData, void* aClosure)
+{
+  nsCycleCollectionTraversalCallback &cb = 
+    *NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, aClosure);
+
+  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_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());
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNextBinding, nsXBLBinding)
+  if (tmp->mInsertionPointTable)
+    tmp->mInsertionPointTable->EnumerateRead(TraverseKey, &cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release)
+
 void
 nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding)
 {
   if (mNextBinding) {
     NS_ERROR("Base XBL binding is already defined!");
     return;
   }
 
--- a/content/xbl/src/nsXBLBinding.h
+++ b/content/xbl/src/nsXBLBinding.h
@@ -40,16 +40,17 @@
 #define nsXBLBinding_h_
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsIDOMNodeList.h"
 #include "nsIStyleRuleProcessor.h"
 #include "nsClassHashtable.h"
 #include "nsTArray.h"
+#include "nsCycleCollectionParticipant.h"
 
 class nsXBLPrototypeBinding;
 class nsIContent;
 class nsIAtom;
 class nsIDocument;
 class nsIScriptContext;
 class nsObjectHashtable;
 class nsXBLInsertionPoint;
@@ -90,16 +91,18 @@ public:
     if (mRefCnt == 0) {
       mRefCnt = 1;
       delete this;
       return 0;
     }
     return mRefCnt;
   }
 
+  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLBinding)
+
   nsXBLPrototypeBinding* PrototypeBinding() { return mPrototypeBinding; }
   nsIContent* GetAnonymousContent() { return mContent.get(); }
 
   nsXBLBinding* GetBaseBinding() { return mNextBinding; }
   void SetBaseBinding(nsXBLBinding *aBinding);
 
   nsIContent* GetBoundElement() { return mBoundElement; }
   void SetBoundElement(nsIContent *aElement);
--- a/content/xbl/src/nsXBLInsertionPoint.cpp
+++ b/content/xbl/src/nsXBLInsertionPoint.cpp
@@ -59,16 +59,30 @@ nsXBLInsertionPoint::Release()
   if (mRefCnt == 0) {
     mRefCnt = 1;
     delete this;
     return 0;
   }
   return mRefCnt;
 }
 
+NS_IMPL_CYCLE_COLLECTION_NATIVE_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)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDefaultContent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLInsertionPoint, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLInsertionPoint, Release)
+
 already_AddRefed<nsIContent>
 nsXBLInsertionPoint::GetInsertionParent()
 {
   NS_IF_ADDREF(mParentElement);
   return mParentElement;
 }
 
 already_AddRefed<nsIContent>
--- a/content/xbl/src/nsXBLInsertionPoint.h
+++ b/content/xbl/src/nsXBLInsertionPoint.h
@@ -37,16 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsXBLInsertionPoint_h__
 #define nsXBLInsertionPoint_h__
 
 #include "nsCOMArray.h"
 #include "nsIContent.h"
 #include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
 
 class nsXBLInsertionPoint
 {
 public:
   nsXBLInsertionPoint(nsIContent* aParentElement, PRUint32 aIndex, nsIContent* aDefContent);
   ~nsXBLInsertionPoint();
 
   nsrefcnt AddRef()
@@ -54,16 +55,18 @@ public:
     ++mRefCnt;
     NS_LOG_ADDREF(this, mRefCnt, "nsXBLInsertionPoint",
                   sizeof(nsXBLInsertionPoint));
     return mRefCnt;
   }
 
   nsrefcnt Release();
 
+  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPoint)
+
   already_AddRefed<nsIContent> GetInsertionParent();
   PRInt32 GetInsertionIndex() { return mIndex; }
 
   void SetDefaultContent(nsIContent* aDefaultContent) { mDefaultContent = aDefaultContent; }
   already_AddRefed<nsIContent> GetDefaultContent();
 
   void SetDefaultContentTemplate(nsIContent* aDefaultContent) { mDefaultContentTemplate = aDefaultContent; }
   already_AddRefed<nsIContent> GetDefaultContentTemplate();
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -194,16 +194,25 @@ nsXBLProtoImpl::CompilePrototypeMembers(
       DestroyMembers(curr);
       return rv;
     }
   }
   return NS_OK;
 }
 
 void
+nsXBLProtoImpl::Traverse(nsCycleCollectionTraversalCallback &cb) const
+{
+  nsXBLProtoImplMember *member;
+  for (member = mMembers; member; member = member->GetNext()) {
+    member->Traverse(cb);
+  }
+}
+
+void
 nsXBLProtoImpl::DestroyMembers(nsXBLProtoImplMember* aBrokenMember)
 {
   NS_ASSERTION(mClassObject, "This should never be called when there is no class object");
   PRBool compiled = PR_TRUE;
   for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) {
     if (curr == aBrokenMember) {
       compiled = PR_FALSE;
     }
--- a/content/xbl/src/nsXBLProtoImpl.h
+++ b/content/xbl/src/nsXBLProtoImpl.h
@@ -71,16 +71,18 @@ public:
   nsresult InitTargetObjects(nsXBLPrototypeBinding* aBinding, nsIScriptContext* aContext, 
                              nsIContent* aBoundElement, 
                              nsIXPConnectJSObjectHolder** aScriptObjectHolder,
                              void** aTargetClassObject);
   nsresult CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding);
 
   void SetMemberList(nsXBLProtoImplMember* aMemberList) { delete mMembers; mMembers = aMemberList; }
 
+  void Traverse(nsCycleCollectionTraversalCallback &cb) const;
+
 protected:
   // Function to call if compilation of a member fails.  When this is called,
   // all members before aBrokenMember are compiled, compilation of
   // aBrokenMember failed, and members after aBrokenMember are uncompiled.
   // This function assumes that aBrokenMember is _not_ compiled.
   void DestroyMembers(nsXBLProtoImplMember* aBrokenMember);
   
 public:
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -146,8 +146,13 @@ nsXBLProtoImplField::InstallMember(nsISc
 }
 
 nsresult 
 nsXBLProtoImplField::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
                                    void* aClassObject)
 {
   return NS_OK;
 }
+
+void
+nsXBLProtoImplField::Traverse(nsCycleCollectionTraversalCallback &cb) const
+{
+}
--- a/content/xbl/src/nsXBLProtoImplField.h
+++ b/content/xbl/src/nsXBLProtoImplField.h
@@ -62,16 +62,18 @@ 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;
+
 protected:
   PRUnichar* mFieldText;
   PRUint32 mFieldTextLength;
   PRUint32 mLineNumber;
   uintN mJSAttributes;
 };
 
 #endif // nsXBLProtoImplField_h__
--- a/content/xbl/src/nsXBLProtoImplMember.h
+++ b/content/xbl/src/nsXBLProtoImplMember.h
@@ -46,16 +46,17 @@
 #include "nsString.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIServiceManager.h"
 #include "nsReadableUtils.h"
 
 class nsIScriptContext;
 struct JSRuntime;
 class nsIJSRuntimeService;
+struct nsCycleCollectionTraversalCallback;
 
 struct nsXBLTextWithLineNumber
 {
   PRUnichar* mText;
   PRUint32 mLineNumber;
 
   nsXBLTextWithLineNumber() :
     mText(nsnull),
@@ -108,16 +109,18 @@ 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;
+
 protected:
   friend class nsAutoGCRoot;
   
   nsXBLProtoImplMember* mNext;  // The members of an implementation are chained.
   PRUnichar* mName;               // The name of the field, method, or property.
 };
 
 #endif // nsXBLProtoImplMember_h__
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -271,16 +271,24 @@ nsXBLProtoImplMethod::CompileMember(nsIS
   }
   
 #ifdef DEBUG
   mIsCompiled = NS_SUCCEEDED(rv);
 #endif
   return rv;
 }
 
+void
+nsXBLProtoImplMethod::Traverse(nsCycleCollectionTraversalCallback &cb) const
+{
+  if (mIsCompiled) {
+    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject);
+  }
+}
+
 nsresult
 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
 {
   NS_PRECONDITION(mIsCompiled, "Can't execute uncompiled method");
   
   if (!mJSMethodObject) {
     // Nothing to do here
     return NS_OK;
--- a/content/xbl/src/nsXBLProtoImplMethod.h
+++ b/content/xbl/src/nsXBLProtoImplMethod.h
@@ -124,16 +124,18 @@ 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;
+
 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
   PRBool mIsCompiled;
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -338,8 +338,20 @@ nsXBLProtoImplProperty::CompileMember(ns
   }
 
 #ifdef DEBUG
   mIsCompiled = NS_SUCCEEDED(rv);
 #endif
   
   return rv;
 }
+
+void
+nsXBLProtoImplProperty::Traverse(nsCycleCollectionTraversalCallback &cb) const
+{
+  if (mJSAttributes & JSPROP_GETTER) {
+    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSGetterObject);
+  }
+
+  if (mJSAttributes & JSPROP_SETTER) {
+    cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSSetterObject);
+  }
+}
--- a/content/xbl/src/nsXBLProtoImplProperty.h
+++ b/content/xbl/src/nsXBLProtoImplProperty.h
@@ -67,16 +67,18 @@ 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;
+
 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
@@ -142,17 +142,19 @@ public:
   ~nsXBLInsertionPointEntry() {
     if (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.
       mDefaultContent->UnbindFromTree();
     }      
   }
-  
+
+  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPointEntry)
+
   nsIContent* GetInsertionParent() { return mInsertionParent; }
   PRUint32 GetInsertionIndex() { return mInsertionIndex; }
   void SetInsertionIndex(PRUint32 aIndex) { mInsertionIndex = aIndex; }
 
   nsIContent* GetDefaultContent() { return mDefaultContent; }
   void SetDefaultContent(nsIContent* aChildren) { mDefaultContent = aChildren; }
 
 
@@ -183,30 +185,47 @@ public:
     }
     return mRefCnt;
   }
 
 protected:
   nsCOMPtr<nsIContent> mInsertionParent;
   nsCOMPtr<nsIContent> mDefaultContent;
   PRUint32 mInsertionIndex;
-  nsrefcnt mRefCnt;
+  nsAutoRefCnt mRefCnt;
 
   nsXBLInsertionPointEntry(nsIContent* aParent)
     : mInsertionParent(aParent),
-      mInsertionIndex(0),
-      mRefCnt(0) { }
+      mInsertionIndex(0) { }
 
 private:
   // Hide so that only Create() and Destroy() can be used to
   // allocate and deallocate from the heap
   static void* operator new(size_t) CPP_THROW_NEW { return 0; }
   static void operator delete(void*, size_t) {}
 };
 
+NS_IMPL_CYCLE_COLLECTION_NATIVE_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;
+  }      
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLInsertionPointEntry)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInsertionParent)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDefaultContent)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLInsertionPointEntry, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLInsertionPointEntry, Release)
+
 // =============================================================================
 
 // Static initialization
 PRUint32 nsXBLPrototypeBinding::gRefCnt = 0;
 
 nsFixedSizeAllocator* nsXBLPrototypeBinding::kAttrPool;
 nsFixedSizeAllocator* nsXBLPrototypeBinding::kInsPool;
 
@@ -274,23 +293,49 @@ nsXBLPrototypeBinding::Init(const nsACSt
     bindingURL->SetRef(aID);
 
   mXBLDocInfoWeak = aInfo;
 
   SetBindingElement(aElement);
   return NS_OK;
 }
 
+PR_STATIC_CALLBACK(PRIntn)
+TraverseInsertionPoint(nsHashKey* aKey, void* aData, void* aClosure)
+{
+  nsCycleCollectionTraversalCallback &cb = 
+    *NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, aClosure);
+  nsXBLInsertionPointEntry* entry =
+    NS_STATIC_CAST(nsXBLInsertionPointEntry*, aData);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(entry,
+                                               nsXBLInsertionPointEntry)
+  return kHashEnumerateNext;
+}
+
+PR_STATIC_CALLBACK(PRBool)
+TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure)
+{
+  nsCycleCollectionTraversalCallback *cb = 
+    NS_STATIC_CAST(nsCycleCollectionTraversalCallback*, aClosure);
+  cb->NoteXPCOMChild(NS_STATIC_CAST(nsISupports*, aData));
+  return kHashEnumerateNext;
+}
+
 void
 nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
 {
   cb.NoteXPCOMChild(mBinding);
-  // XXX mInsertionPointTable!
+  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::Initialize()
 {
   nsIContent* content = GetImmediateChild(nsGkAtoms::content);
   if (content) {
     // Make sure to construct the attribute table first, since constructing the
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -348,25 +348,36 @@ NS_NewXULElement(nsIContent** aResult, n
     NS_ADDREF(*aResult = element);
 
     return NS_OK;
 }
 
 //----------------------------------------------------------------------
 // nsISupports interface
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement,
+                                                  nsGenericElement)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mPrototype,
+                                                  nsXULPrototypeElement)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_IMPL_ADDREF_INHERITED(nsXULElement, nsGenericElement)
 NS_IMPL_RELEASE_INHERITED(nsXULElement, nsGenericElement)
 
 NS_IMETHODIMP
 nsXULElement::QueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
     NS_ENSURE_ARG_POINTER(aInstancePtr);
     *aInstancePtr = nsnull;
 
+    if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
+      *aInstancePtr = &NS_CYCLE_COLLECTION_NAME(nsXULElement);
+      return NS_OK;
+    }
 
     nsresult rv = nsGenericElement::QueryInterface(aIID, aInstancePtr);
     if (NS_SUCCEEDED(rv))
         return rv;
 
     nsISupports *inst = nsnull;
 
     if (aIID.Equals(NS_GET_IID(nsIDOMNode))) {
@@ -2273,16 +2284,39 @@ 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_UNLINK_NATIVE_0(nsXULPrototypeNode)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXULPrototypeNode)
+    if (tmp->mType == nsXULPrototypeNode::eType_Element) {
+        nsXULPrototypeElement *elem =
+            NS_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)
+        }
+    }
+    else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
+        NS_STATIC_CAST(nsXULPrototypeScript*, tmp)->mScriptObject.traverse(cb);
+    }
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release)
+
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeAttribute
 //
 
 nsXULPrototypeAttribute::~nsXULPrototypeAttribute()
 {
     MOZ_COUNT_DTOR(nsXULPrototypeAttribute);
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -188,17 +188,17 @@ public:
 
 class nsXULPrototypeNode
 {
 public:
     enum Type { eType_Element, eType_Script, eType_Text, eType_PI };
 
     Type                     mType;
 
-    PRInt32                  mRefCnt;
+    nsAutoRefCnt             mRefCnt;
 
     virtual ~nsXULPrototypeNode() {}
     virtual nsresult Serialize(nsIObjectOutputStream* aStream,
                                nsIScriptGlobalObject* aGlobal,
                                const nsCOMArray<nsINodeInfo> *aNodeInfos) = 0;
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
                                  nsIScriptGlobalObject* aGlobal,
                                  nsIURI* aDocumentURI,
@@ -217,16 +217,18 @@ public:
     {
         --mRefCnt;
         NS_LOG_RELEASE(this, mRefCnt, ClassName());
         if (mRefCnt == 0)
             delete this;
     }
     virtual void ReleaseSubtree() { Release(); }
 
+    NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXULPrototypeNode)
+
 protected:
     nsXULPrototypeNode(Type aType)
         : mType(aType), mRefCnt(1) {}
 };
 
 class nsXULPrototypeElement : public nsXULPrototypeNode
 {
 public:
@@ -471,16 +473,18 @@ protected:
 
 public:
     static nsresult
     Create(nsXULPrototypeElement* aPrototype, nsIDocument* aDocument,
            PRBool aIsScriptable, nsIContent** aResult);
 
     // nsISupports
     NS_DECL_ISUPPORTS_INHERITED
+    NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsXULElement,
+                                                       nsGenericElement)
 
     // nsINode
     virtual PRUint32 GetChildCount() const;
     virtual nsIContent *GetChildAt(PRUint32 aIndex) const;
     virtual PRInt32 IndexOf(nsINode* aPossibleChild) const;
     virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
     virtual nsresult InsertChildAt(nsIContent* aKid, PRUint32 aIndex,
                                    PRBool aNotify);
--- a/content/xul/document/src/nsXULContentSink.cpp
+++ b/content/xul/document/src/nsXULContentSink.cpp
@@ -235,23 +235,24 @@ XULContentSinkImpl::~XULContentSinkImpl(
 
         nsVoidArray* children;
         rv = mContextStack.GetTopChildren(&children);
         if (NS_SUCCEEDED(rv)) {
             for (PRInt32 i = children->Count() - 1; i >= 0; --i) {
                 nsXULPrototypeNode* child =
                     NS_REINTERPRET_CAST(nsXULPrototypeNode*, children->ElementAt(i));
 
-                delete child;
+                child->Release();
             }
         }
 
         nsXULPrototypeNode* node;
         rv = mContextStack.GetTopNode(&node);
-        if (NS_SUCCEEDED(rv)) delete node;
+        if (NS_SUCCEEDED(rv))
+            node->Release();
 
         State state;
         mContextStack.Pop(&state);
     }
 
     PR_FREEIF(mText);
 }
 
@@ -756,17 +757,17 @@ XULContentSinkImpl::ReportError(const PR
   while (mContextStack.Depth()) {
     nsVoidArray* children;
     rv = mContextStack.GetTopChildren(&children);
     if (NS_SUCCEEDED(rv)) {
       for (PRInt32 i = children->Count() - 1; i >= 0; --i) {
         nsXULPrototypeNode* child =
             NS_REINTERPRET_CAST(nsXULPrototypeNode*, children->ElementAt(i));
 
-        delete child;
+        child->Release();
       }
     }
 
     State state;
     mContextStack.Pop(&state);
   }
 
   mState = eInProlog;
@@ -904,17 +905,17 @@ XULContentSinkImpl::OpenRoot(const PRUni
     // Set the correct script-type for the element.
     rv = SetElementScriptType(element, aAttributes, aAttrLen);
     if (NS_FAILED(rv)) return rv;
 
     // Push the element onto the context stack, so that child
     // containers will hook up to us as their parent.
     rv = mContextStack.Push(element, mState);
     if (NS_FAILED(rv)) {
-        delete element;
+        element->Release();
         return rv;
     }
 
     // Add the attributes
     rv = AddAttributes(aAttributes, aAttrLen, element);
     if (NS_FAILED(rv)) return rv;
 
     mState = eInDocumentElement;
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -191,18 +191,19 @@ nsXULPrototypeDocument::~nsXULPrototypeD
         NS_IF_RELEASE(gSystemPrincipal);
         NS_IF_RELEASE(gSystemGlobal);
     }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsXULPrototypeDocument)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument)
-  // XXX Can't traverse tmp->mRoot, non-XPCOM refcounted object
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalObject)
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mRoot,
+                                                    nsXULPrototypeElement)
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobalObject)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument)
   NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
   NS_INTERFACE_MAP_ENTRY(nsISerializable)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObjectOwner)
 NS_INTERFACE_MAP_END
 
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -594,69 +594,41 @@ nsXPConnect::FinishCycleCollection()
 {
     delete mCycleCollectionContext;
     mCycleCollectionContext = nsnull;
     if (mObjRefcounts)
         mObjRefcounts->Finish();
     return NS_OK;
 }
 
-nsresult 
-nsXPConnect::Root(const nsDeque &nodes)
+NS_IMETHODIMP
+nsXPConnect::Root(void *p)
 {
-    if(!mCycleCollectionContext)
+    if(!mCycleCollectionContext ||
+       !JS_LockGCThing(*mCycleCollectionContext, NS_STATIC_CAST(JSObject*, p)))
         return NS_ERROR_FAILURE;
-
-    JSContext *cx = mCycleCollectionContext->GetJSContext();
-    for (PRInt32 i = 0; i < nodes.GetSize(); ++i)
-    {
-        void *p = nodes.ObjectAt(i);
-        if (!p)
-            continue;
-        JSObject *obj = NS_STATIC_CAST(JSObject*, p);
-        if (!JS_LockGCThing(cx, obj))
-            return NS_ERROR_FAILURE;
-    }
     return NS_OK;
 }
 
-nsresult 
-nsXPConnect::Unlink(const nsDeque &nodes)
+NS_IMETHODIMP
+nsXPConnect::Unlink(void *p)
 {
     if(!mCycleCollectionContext)
         return NS_ERROR_FAILURE;
-
-    JSContext *cx = mCycleCollectionContext->GetJSContext();
-    for (PRInt32 i = 0; i < nodes.GetSize(); ++i)
-    {
-        void *p = nodes.ObjectAt(i);
-        if (!p)
-            continue;
-        JSObject *obj = NS_STATIC_CAST(JSObject*, p);
-        JS_ClearScope(cx, obj);
-    }
+    JS_ClearScope(*mCycleCollectionContext, NS_STATIC_CAST(JSObject*, p));
     return NS_OK;
 }
 
-nsresult 
-nsXPConnect::Unroot(const nsDeque &nodes)
+NS_IMETHODIMP
+nsXPConnect::Unroot(void *p)
 {
-    if(!mCycleCollectionContext)
+    if(!mCycleCollectionContext ||
+       !JS_UnlockGCThing(*mCycleCollectionContext,
+                         NS_STATIC_CAST(JSObject*, p)))
         return NS_ERROR_FAILURE;
-
-    JSContext *cx = mCycleCollectionContext->GetJSContext();
-    for (PRInt32 i = 0; i < nodes.GetSize(); ++i)
-    {
-        void *p = nodes.ObjectAt(i);
-        if (!p)
-            continue;
-        JSObject *obj = NS_STATIC_CAST(JSObject*, p);
-        if (!JS_UnlockGCThing(cx, obj))
-            return NS_ERROR_FAILURE;
-    }
     return NS_OK;
 }
 
 static void
 TraverseJSScript(JSScript* script, nsCycleCollectionTraversalCallback& cb)
 {
     JSAtomMap* map = &script->atomMap;
     uintN i, length = map->length;
@@ -666,18 +638,19 @@ TraverseJSScript(JSScript* script, nsCyc
     {
         JSAtom* atom = vector[i];
         if(ATOM_IS_OBJECT(atom))
             cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
                                ATOM_TO_OBJECT(atom));
     }
 }
 
-nsresult 
-nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
+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?");
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -422,17 +422,18 @@ private:
 // to this rule are noted explicitly.
 
 const PRBool OBJ_IS_GLOBAL = PR_TRUE;
 const PRBool OBJ_IS_NOT_GLOBAL = PR_FALSE;
 struct JSObjectRefcounts;
 
 class nsXPConnect : public nsIXPConnect,
                     public nsSupportsWeakReference,
-                    public nsCycleCollectionLanguageRuntime
+                    public nsCycleCollectionLanguageRuntime,
+                    public nsCycleCollectionParticipant
 {
 public:
     // all the interface method declarations...
     NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCONNECT
 
     // non-interface implementation
 public:
@@ -470,23 +471,26 @@ public:
 
     virtual ~nsXPConnect();
 
     JSBool IsShuttingDown() const {return mShuttingDown;}
 
     nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
     nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
 
-    // from nsCycleCollectionLanguageRuntime
+    // from nsCycleCollectionLanguageRuntime and nsCycleCollectionParticipant
     nsresult BeginCycleCollection();
-    nsresult Root(const nsDeque &nodes);
-    nsresult Unlink(const nsDeque &nodes);
-    nsresult Unroot(const nsDeque &nodes);
-    nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb);
+    NS_IMETHOD Root(void *p);
+    NS_IMETHOD Unlink(void *p);
+    NS_IMETHOD Unroot(void *p);
+    NS_IMETHOD Traverse(void *p,
+                        nsCycleCollectionTraversalCallback &cb);
     nsresult FinishCycleCollection();
+    nsCycleCollectionParticipant *ToParticipant(void *p) {return this;}
+
     JSObjectRefcounts* GetJSObjectRefcounts() {return mObjRefcounts;}
 #ifndef XPCONNECT_STANDALONE
     void RecordTraversal(void *p, nsISupports *s);
 #endif
 
 #ifdef XPC_IDISPATCH_SUPPORT
 public:
     static PRBool IsIDispatchEnabled();
--- a/js/src/xpconnect/src/xpcwrappedjs.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjs.cpp
@@ -44,17 +44,17 @@
 #include "xpcprivate.h"
 
 // NOTE: much of the fancy footwork is done in xpcstubs.cpp
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
 
 NS_IMETHODIMP
 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
-   (nsISupports *s, nsCycleCollectionTraversalCallback &cb)
+   (void *p, nsCycleCollectionTraversalCallback &cb)
 {
     // REVIEW ME PLEASE: this is a very odd area and it's easy to get
     // it wrong. I'm not sure I got it right.
     //
     // We *might* have a stub that's not actually connected to an
     // nsXPCWrappedJS, so we begin by QI'ing over to a "real"
     // nsIXPConnectWrappedJS. Since that's a mostly-empty class, we
     // then downcast from there to the "true" nsXPCWrappedJS.
@@ -63,17 +63,18 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrapp
     // knows for how long. 
 
     nsresult rv;
     nsIXPConnectWrappedJS *base;
     nsXPCWrappedJS *tmp;
     {
         // Put the nsCOMPtr in a local scope, to avoid messing up the refcount
         // below.
-        nsCOMPtr<nsIXPConnectWrappedJS> owner = do_QueryInterface(s, &rv);
+        nsCOMPtr<nsIXPConnectWrappedJS> owner =
+            do_QueryInterface(NS_STATIC_CAST(nsISupports*, p), &rv);
         if (NS_FAILED(rv))
             return rv;
 
         base = owner.get();
         tmp = NS_STATIC_CAST(nsXPCWrappedJS*, base);
         NS_ASSERTION(tmp->mRefCnt.get() > 2,
                      "How can this be, no one else holds a strong ref?");
     }
@@ -107,17 +108,17 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrapp
     else
         // Non-root wrappers keep their root alive.
         cb.NoteXPCOMChild(NS_STATIC_CAST(nsIXPConnectWrappedJS*, root));
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Unlink(nsISupports *s)
+NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Unlink(void *p)
 {
     // NB: We might unlink our outgoing references in the future; for
     // now we do nothing. This is a harmless conservative behavior; it
     // just means that we rely on the cycle being broken by some of
     // the external XPCOM objects' unlink() methods, not our
     // own. Typically *any* unlinking will break the cycle.
     return NS_OK;
 }
@@ -151,17 +152,17 @@ nsXPCWrappedJS::QueryInterface(REFNSIID 
         return NS_ERROR_UNEXPECTED;
 
     if(nsnull == aInstancePtr)
     {
         NS_PRECONDITION(0, "null pointer");
         return NS_ERROR_NULL_POINTER;
     }
 
-    if ( aIID.Equals(NS_GET_IID(nsCycleCollectionParticipant)) ) {
+    if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
         *aInstancePtr = & NS_CYCLE_COLLECTION_NAME(nsXPCWrappedJS);
         return NS_OK;
     }
 
     if(aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)))
     {
         NS_ADDREF(this);
         *aInstancePtr =
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -45,20 +45,20 @@
 #include "nsCRT.h"
 #include "XPCNativeWrapper.h"
 
 /***************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
 
 NS_IMETHODIMP
-NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(nsISupports *s,
+NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(void *p,
                                                           nsCycleCollectionTraversalCallback &cb)
 {
-    XPCWrappedNative *tmp = NS_STATIC_CAST(XPCWrappedNative*, s);
+    XPCWrappedNative *tmp = NS_STATIC_CAST(XPCWrappedNative*, p);
     if(!tmp->IsValid())
         return NS_OK;
 
 #ifdef DEBUG_CC
     char name[72];
     XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
     if(si)
         JS_snprintf(name, sizeof(name), "XPCWrappedNative (%s)",
@@ -108,17 +108,17 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrapped
 
     // XPCWrappedNative keeps its native object alive.
     cb.NoteXPCOMChild(tmp->GetIdentityObject());
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(nsISupports *s)
+NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(void *p)
 {
     // NB: We might unlink our outgoing references in the future; for
     // now we do nothing. This is a harmless conservative behavior; it
     // just means that we rely on the cycle being broken by some of
     // the external XPCOM objects' unlink() methods, not our
     // own. Typically *any* unlinking will break the cycle.
     return NS_OK;
 }
--- a/xpcom/base/nsAgg.h
+++ b/xpcom/base/nsAgg.h
@@ -95,21 +95,21 @@ private:                                
                                                                             \
     nsISupports*        fOuter;                                             \
     Internal            fAggregated;                                        \
                                                                             \
 public:                                                                     \
 
 #define NS_DECL_AGGREGATED_CYCLE_COLLECTION_CLASS(_class)                   \
 class NS_CYCLE_COLLECTION_INNERCLASS                                        \
- : public nsCycleCollectionParticipant                                      \
+ : public nsXPCOMCycleCollectionParticipant                                 \
 {                                                                           \
 public:                                                                     \
-  NS_IMETHOD Unlink(nsISupports *p);                                        \
-  NS_IMETHOD Traverse(nsISupports *p,                                       \
+  NS_IMETHOD Unlink(void *p);                                               \
+  NS_IMETHOD Traverse(void *p,                                              \
                       nsCycleCollectionTraversalCallback &cb);              \
   NS_IMETHOD_(void) UnmarkPurple(nsISupports *p)                            \
   {                                                                         \
     Downcast(p)->UnmarkPurple();                                            \
   }                                                                         \
   static _class* Downcast(nsISupports* s)                                   \
   {                                                                         \
     return (_class*)((char*)(s) - offsetof(_class, fAggregated));           \
@@ -292,22 +292,23 @@ nsresult                                
                   NS_GET_IID(nsCycleCollectionParticipant) :                \
                   NS_GET_IID(nsAggregatedCycleCollectionParticipant)))      \
     foundInterface = & NS_CYCLE_COLLECTION_NAME(_class);                    \
   else
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_AGGREGATED(_class)          \
   NS_IMETHODIMP                                                             \
   NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse                           \
-                         (nsISupports *p,                                   \
+                         (void *p,                                          \
                           nsCycleCollectionTraversalCallback &cb)           \
   {                                                                         \
-    NS_ASSERTION(CheckForRightISupports(p),                                 \
+    nsISupports *s = NS_STATIC_CAST(nsISupports*, p);                       \
+    NS_ASSERTION(CheckForRightISupports(s),                                 \
                  "not the nsISupports pointer we expect");                  \
-    _class *tmp = NS_STATIC_CAST(_class*, Downcast(p));                     \
+    _class *tmp = NS_STATIC_CAST(_class*, Downcast(s));                     \
     if (!tmp->IsPartOfAggregated())                                         \
         NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class)
 
 #define NS_GENERIC_AGGREGATED_CONSTRUCTOR(_InstanceClass)                   \
 static NS_METHOD                                                            \
 _InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID,             \
                             void **aResult)                                 \
 {                                                                           \
--- a/xpcom/base/nsCycleCollectionParticipant.cpp
+++ b/xpcom/base/nsCycleCollectionParticipant.cpp
@@ -33,50 +33,52 @@
  * 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"
 
-NS_INTERFACE_MAP_BEGIN(nsCycleCollectionParticipant)
-  NS_INTERFACE_MAP_ENTRY(nsCycleCollectionParticipant)
-NS_INTERFACE_MAP_END
-
-NS_IMETHODIMP_(nsrefcnt) nsCycleCollectionParticipant::AddRef(void)
+NS_IMETHODIMP
+nsXPCOMCycleCollectionParticipant::Root(void *p)
 {
-  // Do nothing, it's a singleton.
-  return 1;
+    nsISupports *s = NS_STATIC_CAST(nsISupports*, p);
+    NS_ADDREF(s);
+    return NS_OK;
 }
 
-NS_IMETHODIMP_(nsrefcnt) nsCycleCollectionParticipant::Release(void)
-{
-  // Do nothing, it's a singleton.
-  return 1;
-}
-
-NS_IMETHODIMP nsCycleCollectionParticipant::Unlink(nsISupports *n)
+NS_IMETHODIMP
+nsXPCOMCycleCollectionParticipant::Unlink(void *p)
 {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXPCOMCycleCollectionParticipant::Unroot(void *p)
+{
+    nsISupports *s = NS_STATIC_CAST(nsISupports*, p);
+    NS_RELEASE(s);
+    return NS_OK;
+}
+
 NS_IMETHODIMP 
-nsCycleCollectionParticipant::Traverse(nsISupports *n, 
-                                       nsCycleCollectionTraversalCallback &cb)
+nsXPCOMCycleCollectionParticipant::Traverse
+    (void *p, nsCycleCollectionTraversalCallback &cb)
 {
   return NS_OK;
 }
 
-NS_IMETHODIMP_(void) nsCycleCollectionParticipant::UnmarkPurple(nsISupports *n)
+NS_IMETHODIMP_(void)
+nsXPCOMCycleCollectionParticipant::UnmarkPurple(nsISupports *n)
 {
 }
 
 #ifdef DEBUG
 PRBool
-nsCycleCollectionParticipant::CheckForRightISupports(nsISupports *s)
+nsXPCOMCycleCollectionParticipant::CheckForRightISupports(nsISupports *s)
 {
     nsCOMPtr<nsISupports> foo;
     s->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
                       getter_AddRefs(foo));
     return s == foo;
 }
 #endif
--- a/xpcom/base/nsCycleCollectionParticipant.h
+++ b/xpcom/base/nsCycleCollectionParticipant.h
@@ -73,71 +73,88 @@ public:
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionISupports, 
                               NS_CYCLECOLLECTIONISUPPORTS_IID)
 
 #undef  IMETHOD_VISIBILITY
 #define IMETHOD_VISIBILITY NS_VISIBILITY_DEFAULT
 
+struct nsCycleCollectionParticipant;
+
 struct nsCycleCollectionTraversalCallback
 {
     // You must call DescribeNode() with an accurate refcount,
     // otherwise cycle collection will fail, and probably crash.
 #ifdef DEBUG_CC
     virtual void DescribeNode(size_t refcount,
                               size_t objsz,
                               const char *objname) = 0;
 #else
     virtual void DescribeNode(size_t refcount) = 0;
 #endif
     virtual void NoteScriptChild(PRUint32 langID, void *child) = 0;
     virtual void NoteXPCOMChild(nsISupports *child) = 0;
+    virtual void NoteNativeChild(void *child,
+                                 nsCycleCollectionParticipant *helper) = 0;
 };
 
-class NS_COM nsCycleCollectionParticipant
-    : public nsISupports
+class NS_NO_VTABLE nsCycleCollectionParticipant
+{
+public:
+    NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb) = 0;
+
+    NS_IMETHOD Root(void *p) = 0;
+    NS_IMETHOD Unlink(void *p) = 0;
+    NS_IMETHOD Unroot(void *p) = 0;
+};
+
+class NS_COM nsXPCOMCycleCollectionParticipant
+    : public nsCycleCollectionParticipant
 {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_CYCLECOLLECTIONPARTICIPANT_IID)
-    NS_DECL_ISUPPORTS
+
+    NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb);
 
-    NS_IMETHOD Unlink(nsISupports *p);
-    NS_IMETHOD Traverse(nsISupports *p, 
-                        nsCycleCollectionTraversalCallback &cb);
+    NS_IMETHOD Root(void *p);
+    NS_IMETHOD Unlink(void *p);
+    NS_IMETHOD Unroot(void *p);
+
     NS_IMETHOD_(void) UnmarkPurple(nsISupports *p);
 
 #ifdef DEBUG
     NS_EXTERNAL_VIS_(PRBool) CheckForRightISupports(nsISupports *s);
 #endif
 };
 
-NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionParticipant, 
+NS_DEFINE_STATIC_IID_ACCESSOR(nsXPCOMCycleCollectionParticipant, 
                               NS_CYCLECOLLECTIONPARTICIPANT_IID)
 
 #undef  IMETHOD_VISIBILITY
 #define IMETHOD_VISIBILITY NS_VISIBILITY_HIDDEN
 
 
 ///////////////////////////////////////////////////////////////////////////////
-// Helpers for implementing a QI to nsCycleCollectionParticipant
+// Helpers for implementing a QI to nsXPCOMCycleCollectionParticipant
 ///////////////////////////////////////////////////////////////////////////////
 
 #define NS_CYCLE_COLLECTION_INNERCLASS                                         \
         cycleCollection
 
 #define NS_CYCLE_COLLECTION_CLASSNAME(_class)                                  \
         _class::NS_CYCLE_COLLECTION_INNERCLASS
 
 #define NS_CYCLE_COLLECTION_NAME(_class)                                       \
         _class##_cycleCollectorGlobal
 
 #define NS_IMPL_QUERY_CYCLE_COLLECTION(_class)                                 \
-  if ( aIID.Equals(NS_GET_IID(nsCycleCollectionParticipant)) ) {               \
-    foundInterface = & NS_CYCLE_COLLECTION_NAME(_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)                       \
   if ( aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) )                   \
     foundInterface = NS_CYCLE_COLLECTION_CLASSNAME(_class)::Upcast(this);      \
   else
 
 #define NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)                        \
@@ -160,120 +177,167 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCol
     NS_INTERFACE_MAP_ENTRY_CYCLE_COLLECTION(_class)
 
 ///////////////////////////////////////////////////////////////////////////////
 // Helpers for implementing nsCycleCollectionParticipant::Unlink
 ///////////////////////////////////////////////////////////////////////////////
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)                          \
   NS_IMETHODIMP                                                                \
-  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(nsISupports *s)                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p)                       \
   {                                                                            \
+    nsISupports *s = NS_STATIC_CAST(nsISupports*, p);                          \
     NS_ASSERTION(CheckForRightISupports(s),                                    \
                  "not the nsISupports pointer we expect");                     \
     _class *tmp = Downcast(s);
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base_class)   \
   NS_IMETHODIMP                                                                \
-  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(nsISupports *s)                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p)                       \
   {                                                                            \
+    nsISupports *s = NS_STATIC_CAST(nsISupports*, p);                          \
     NS_ASSERTION(CheckForRightISupports(s),                                    \
                  "not the nsISupports pointer we expect");                     \
     _class *tmp = NS_STATIC_CAST(_class*, Downcast(s));                        \
     NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Unlink(s);
 
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(_class)                   \
+  NS_IMETHODIMP                                                                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p)                       \
+  {                                                                            \
+    _class *tmp = NS_STATIC_CAST(_class*, p);
+
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(_field)                       \
     tmp->_field = NULL;    
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(_field)                     \
     tmp->_field.Clear();
 
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(_field)                       \
+    tmp->_field.Clear();
+
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                    \
     return NS_OK;                                                              \
   }
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_0(_class)                              \
   NS_IMETHODIMP                                                                \
-  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(nsISupports *s)                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p)                       \
   {                                                                            \
-    NS_ASSERTION(CheckForRightISupports(s),                                    \
+    NS_ASSERTION(CheckForRightISupports(NS_STATIC_CAST(nsISupports*, p)),      \
                  "not the nsISupports pointer we expect");                     \
     return NS_OK;                                                              \
   }
 
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_NATIVE_0(_class)                       \
+  NS_IMETHODIMP                                                                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(void *p)                       \
+  {                                                                            \
+    return NS_OK;                                                              \
+  }
+
 
 ///////////////////////////////////////////////////////////////////////////////
 // Helpers for implementing nsCycleCollectionParticipant::Traverse
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifdef DEBUG_CC
 #define NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class)                              \
     cb.DescribeNode(tmp->mRefCnt.get(), sizeof(_class), #_class);
 #else
 #define NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class)                              \
     cb.DescribeNode(tmp->mRefCnt.get());
 #endif
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)                        \
   NS_IMETHODIMP                                                                \
   NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse                              \
-                         (nsISupports *s,                                      \
+                         (void *p,                                             \
                           nsCycleCollectionTraversalCallback &cb)              \
   {                                                                            \
+    nsISupports *s = NS_STATIC_CAST(nsISupports*, p);                          \
     NS_ASSERTION(CheckForRightISupports(s),                                    \
                  "not the nsISupports pointer we expect");                     \
     _class *tmp = Downcast(s);                                                 \
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class)
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base_class) \
   NS_IMETHODIMP                                                                \
   NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse                              \
-                         (nsISupports *s,                                      \
+                         (void *p,                                             \
                           nsCycleCollectionTraversalCallback &cb)              \
   {                                                                            \
+    nsISupports *s = NS_STATIC_CAST(nsISupports*, p);                          \
     NS_ASSERTION(CheckForRightISupports(s),                                    \
                  "not the nsISupports pointer we expect");                     \
     _class *tmp = NS_STATIC_CAST(_class*, Downcast(s));                        \
     NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Traverse(s, cb);
 
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(_class)                 \
+  NS_IMETHODIMP                                                                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Traverse                              \
+                         (void *p,                                             \
+                          nsCycleCollectionTraversalCallback &cb)              \
+  {                                                                            \
+    _class *tmp = NS_STATIC_CAST(_class*, p);                                  \
+    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(_class)
+
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(_field)                       \
     cb.NoteXPCOMChild(tmp->_field);
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(_field)                     \
     cb.NoteXPCOMChild(tmp->_field.get());
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(_field, _base)    \
     cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(_base*, tmp->_field));
 
 #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));
+
+#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_END                                  \
     return NS_OK;                                                              \
   }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Helpers for implementing a concrete nsCycleCollectionParticipant 
 ///////////////////////////////////////////////////////////////////////////////
 
 #define NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _base)                \
 class NS_CYCLE_COLLECTION_INNERCLASS                                           \
- : public nsCycleCollectionParticipant                                         \
+ : public nsXPCOMCycleCollectionParticipant                                    \
 {                                                                              \
 public:                                                                        \
-  NS_IMETHOD Unlink(nsISupports *n);                                           \
-  NS_IMETHOD Traverse(nsISupports *n,                                          \
+  NS_IMETHOD Unlink(void *p);                                                  \
+  NS_IMETHOD Traverse(void *p,                                                 \
                       nsCycleCollectionTraversalCallback &cb);                 \
-  NS_IMETHOD_(void) UnmarkPurple(nsISupports *n)                               \
+  NS_IMETHOD_(void) UnmarkPurple(nsISupports *s)                               \
   {                                                                            \
-    Downcast(n)->UnmarkPurple();                                               \
+    Downcast(s)->UnmarkPurple();                                               \
   }                                                                            \
   static _class* Downcast(nsISupports* s)                                      \
   {                                                                            \
     return NS_STATIC_CAST(_class*, NS_STATIC_CAST(_base*, s));                 \
   }                                                                            \
   static nsISupports* Upcast(_class *p)                                        \
   {                                                                            \
     return NS_ISUPPORTS_CAST(_base*, p);                                       \
@@ -283,56 +347,97 @@ public:                                 
 #define NS_DECL_CYCLE_COLLECTION_CLASS(_class)                                 \
   NS_DECL_CYCLE_COLLECTION_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(nsISupports *n);                                           \
-  NS_IMETHOD Traverse(nsISupports *n,                                          \
+  NS_IMETHOD Unlink(void *p);                                                  \
+  NS_IMETHOD Traverse(void *p,                                                 \
                       nsCycleCollectionTraversalCallback &cb);                 \
   static _class* Downcast(nsISupports* s)                                      \
   {                                                                            \
     return NS_STATIC_CAST(_class*, NS_STATIC_CAST(_base_class*,                \
       NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s)));               \
   }                                                                            \
 };
 
 #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(nsISupports *n,                                          \
+  NS_IMETHOD Traverse(void *p,                                                 \
                       nsCycleCollectionTraversalCallback &cb);                 \
   static _class* Downcast(nsISupports* s)                                      \
   {                                                                            \
     return NS_STATIC_CAST(_class*, NS_STATIC_CAST(_base_class*,                \
       NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s)));               \
   }                                                                            \
 };
 
 /**
  * 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)                    \
-  void UnmarkPurple()                                                          \
+  NS_IMETHODIMP_(void) UnmarkPurple()                                          \
   {                                                                            \
   }                                                                            \
 
 #define NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                                 \
   static 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(_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);                 \
+  };                                                                           \
+  static NS_CYCLE_COLLECTION_INNERCLASS                                        \
+      NS_CYCLE_COLLECTION_NATIVE_INNERNAME;
+
+#define NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(_class)                          \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class) NS_CYCLE_COLLECTION_NATIVE_NAME(_class);
+
+#define NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(_class, _root_function)           \
+  NS_IMETHODIMP                                                                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Root(void *p)                         \
+  {                                                                            \
+    _class *tmp = NS_STATIC_CAST(_class*, p);                                  \
+    tmp->_root_function();                                                     \
+    return NS_OK;                                                              \
+  }
+
+#define NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(_class, _unroot_function)       \
+  NS_IMETHODIMP                                                                \
+  NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unroot(void *p)                       \
+  {                                                                            \
+    _class *tmp = NS_STATIC_CAST(_class*, p);                                  \
+    tmp->_unroot_function();                                                   \
+    return NS_OK;                                                              \
+  }
+
 #define NS_IMPL_CYCLE_COLLECTION_0(_class)                                     \
  NS_IMPL_CYCLE_COLLECTION_CLASS(_class)                                        \
  NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(_class)                                 \
  NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                           \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(_class)                               \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 #define NS_IMPL_CYCLE_COLLECTION_1(_class, _f)                                 \
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -75,20 +75,20 @@
 
 // Safety:
 //
 // An XPCOM object is either scan-safe or scan-unsafe, purple-safe or
 // purple-unsafe.
 //
 // An object is scan-safe if:
 //
-//  - It can be QI'ed to |nsCycleCollectionParticipant|, though this
+//  - It can be QI'ed to |nsXPCOMCycleCollectionParticipant|, though this
 //    operation loses ISupports identity (like nsIClassInfo).
 //  - The operation |traverse| on the resulting
-//    nsCycleCollectionParticipant does not cause *any* refcount
+//    nsXPCOMCycleCollectionParticipant does not cause *any* refcount
 //    adjustment to occur (no AddRef / Release calls).
 //
 // An object is purple-safe if it satisfies the following properties:
 //
 //  - The object is scan-safe.  
 //  - If the object calls |nsCycleCollector::suspect(this)|, 
 //    it will eventually call |nsCycleCollector::forget(this)|, 
 //    exactly once per call to |suspect|, before being destroyed.
@@ -274,18 +274,32 @@ struct nsCycleCollectorStats
         DUMP(mFreedWhilePurple);
     
         DUMP(mCollection);
 #undef DUMP
     }
 };
 #endif
 
-static void
-ToParticipant(nsISupports *s, nsCycleCollectionParticipant **cp);
+static inline void
+ToParticipant(nsISupports *s, nsXPCOMCycleCollectionParticipant **cp)
+{
+    // We use QI to move from an nsISupports to an
+    // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper
+    // object that implements traversal and unlinking logic for the nsISupports
+    // in question.
+    CallQueryInterface(s, cp);
+#ifdef DEBUG_CC
+    if (cp)
+        ++sCollector->mStats.mSuccessfulQI;
+    else
+        ++sCollector->mStats.mFailedQI;
+#endif
+}
+
 
 #ifdef DEBUG_CC
 static PRBool
 nsCycleCollector_shouldSuppress(nsISupports *s);
 #endif
 
 ////////////////////////////////////////////////////////////////////////
 // Base types
@@ -424,35 +438,36 @@ enum NodeColor { black, white, grey };
 
 // This structure should be kept as small as possible; we may expect
 // a million of them to be allocated and touched repeatedly during
 // each cycle collection.
 
 struct PtrInfo
 {
     void *mPointer;
+    nsCycleCollectionParticipant *mParticipant;
     PRUint32 mColor : 2;
     PRUint32 mInternalRefs : 30;
-    // FIXME: mLang expands back to a full word when bug 368774 lands.
-    PRUint32 mLang : 2;
-    PRUint32 mRefCount : 30;
+    PRUint32 mRefCount : 31;
+    PRUint32 mWasPurple : 1;
     EdgePool::Iterator mFirstChild; // first
     EdgePool::Iterator mLastChild; // one after last
 
 #ifdef DEBUG_CC
     size_t mBytes;
     const char *mName;
 #endif
 
-    PtrInfo(void *aPointer)
+    PtrInfo(void *aPointer, nsCycleCollectionParticipant *aParticipant)
         : mPointer(aPointer),
+          mParticipant(aParticipant),
           mColor(grey),
           mInternalRefs(0),
-          mLang(nsIProgrammingLanguage::CPLUSPLUS),
           mRefCount(0),
+          mWasPurple(PR_FALSE),
           mFirstChild(),
           mLastChild()
 #ifdef DEBUG_CC
         , mBytes(0),
           mName(nsnull)
 #endif
     {
     }
@@ -501,27 +516,27 @@ public:
         Builder(NodePool& aPool)
             : mNextBlock(&aPool.mBlocks),
               mNext(aPool.mLast),
               mBlockEnd(nsnull)
         {
             NS_ASSERTION(aPool.mBlocks == nsnull && aPool.mLast == nsnull,
                          "pool not empty");
         }
-        PtrInfo *Add(void *aPointer)
+        PtrInfo *Add(void *aPointer, nsCycleCollectionParticipant *aParticipant)
         {
             if (mNext == mBlockEnd) {
                 Block *block;
                 if (!(*mNextBlock = block = new Block()))
                     return nsnull;
                 mNext = block->mEntries;
                 mBlockEnd = block->mEntries + BlockSize;
                 mNextBlock = &block->mNext;
             }
-            return new (mNext++) PtrInfo(aPointer);
+            return new (mNext++) PtrInfo(aPointer, aParticipant);
         }
     private:
         Block **mNextBlock;
         PtrInfo *&mNext;
         PtrInfo *mBlockEnd;
     };
 
     class Enumerator;
@@ -770,103 +785,42 @@ nsPurpleBuffer::SelectAgedPointers(nsDeq
 struct nsCycleCollectionXPCOMRuntime : 
     public nsCycleCollectionLanguageRuntime 
 {
     nsresult BeginCycleCollection() 
     {
         return NS_OK;
     }
 
-    nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) 
+    nsresult FinishCycleCollection() 
     {
-        nsresult rv;
-
-        nsISupports *s = NS_STATIC_CAST(nsISupports *, p);        
-        nsCycleCollectionParticipant *cp;
-        ToParticipant(s, &cp);
-        if (!cp) {
-            Fault("walking wrong type of pointer", s);
-            return NS_ERROR_FAILURE;
-        }
-
-        rv = cp->Traverse(s, cb);
-        if (NS_FAILED(rv)) {
-            Fault("XPCOM pointer traversal failed", s);
-            return NS_ERROR_FAILURE;
-        }
-        return NS_OK;
-    }
-
-    nsresult Root(const nsDeque &nodes)
-    {
-        for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
-            void *p = nodes.ObjectAt(i);
-            nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
-            NS_ADDREF(s);
-        }
         return NS_OK;
     }
 
-    nsresult Unlink(const nsDeque &nodes)
+    nsCycleCollectionParticipant *ToParticipant(void *p)
     {
-        nsresult rv;
-
-        for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
-            void *p = nodes.ObjectAt(i);
-
-            nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
-            nsCycleCollectionParticipant *cp;
-            ToParticipant(s, &cp);
-            if (!cp) {
-                Fault("unlinking wrong kind of pointer", s);
-                return NS_ERROR_FAILURE;
-            }
-
-            rv = cp->Unlink(s);
-
-            if (NS_FAILED(rv)) {
-                Fault("failed unlink", s);
-                return NS_ERROR_FAILURE;
-            }
-        }
-        return NS_OK;
-    }
-
-    nsresult Unroot(const nsDeque &nodes)
-    {
-        for (PRInt32 i = 0; i < nodes.GetSize(); ++i) {
-            void *p = nodes.ObjectAt(i);
-            nsISupports *s = NS_STATIC_CAST(nsISupports *, p);
-            NS_RELEASE(s);
-        }
-        return NS_OK;
-    }
-
-    nsresult FinishCycleCollection() 
-    {
-        return NS_OK;
+        nsXPCOMCycleCollectionParticipant *cp;
+        ::ToParticipant(NS_STATIC_CAST(nsISupports*, p), &cp);
+        return cp;
     }
 };
 
 struct nsCycleCollector
 {
     PRBool mCollectionInProgress;
     PRBool mScanInProgress;
 
     nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1];
     nsCycleCollectionXPCOMRuntime mXPCOMRuntime;
 
-    // The set of buffers |mBufs| serves a variety of purposes; mostly
-    // involving the transfer of pointers from a hashtable iterator
-    // routine to some outer logic that might also need to mutate the
-    // hashtable. In some contexts, only buffer 0 is used (as a
-    // set-of-all-pointers); in other contexts, one buffer is used
-    // per-language (as a set-of-pointers-in-language-N).
+    // The buffer |mBuf| serves a variety of purposes; mostly involving the
+    // transfer of pointers from a hashtable iterator routine to some outer
+    // logic that might also need to mutate the hashtable.
 
-    nsDeque mBufs[nsIProgrammingLanguage::MAX + 1];
+    nsDeque mBuf;
     
     nsCycleCollectorParams mParams;
 
     nsPurpleBuffer mPurpleBuf;
 
     void RegisterRuntime(PRUint32 langID, 
                          nsCycleCollectionLanguageRuntime *rt);
     void ForgetRuntime(PRUint32 langID);
@@ -1057,27 +1011,29 @@ private:
 
 public:
     GCGraphBuilder(GCGraph &aGraph,
                    nsCycleCollectionLanguageRuntime **aRuntimes);
     ~GCGraphBuilder();
 
     PRUint32 Count() const { return mPtrToNodeMap.entryCount; }
 
-    PtrInfo* AddNode(void *s);
+    PtrInfo* AddNode(void *s, nsCycleCollectionParticipant *aParticipant);
     void Traverse(PtrInfo* aPtrInfo);
 
 private:
     // nsCycleCollectionTraversalCallback methods.
 #ifdef DEBUG_CC
     void DescribeNode(size_t refCount, size_t objSz, const char *objName);
 #else
     void DescribeNode(size_t refCount);
 #endif
     void NoteXPCOMChild(nsISupports *child);
+    void NoteNativeChild(void *child,
+                         nsCycleCollectionParticipant *participant);
     void NoteScriptChild(PRUint32 langID, void *child);
 };
 
 GCGraphBuilder::GCGraphBuilder(GCGraph &aGraph,
                                nsCycleCollectionLanguageRuntime **aRuntimes)
     : mNodeBuilder(aGraph.mNodes),
       mEdgeBuilder(aGraph.mEdges),
       mRuntimes(aRuntimes)
@@ -1089,56 +1045,52 @@ GCGraphBuilder::GCGraphBuilder(GCGraph &
 
 GCGraphBuilder::~GCGraphBuilder()
 {
     if (mPtrToNodeMap.ops)
         PL_DHashTableFinish(&mPtrToNodeMap);
 }
 
 PtrInfo*
-GCGraphBuilder::AddNode(void *s)
+GCGraphBuilder::AddNode(void *s, nsCycleCollectionParticipant *aParticipant)
 {
     PtrToNodeEntry *e = NS_STATIC_CAST(PtrToNodeEntry*, 
         PL_DHashTableOperate(&mPtrToNodeMap, s, PL_DHASH_ADD));
     PtrInfo *result;
     if (!e->mNode) {
         // New entry.
-        result = mNodeBuilder.Add(s);
+        result = mNodeBuilder.Add(s, aParticipant);
         if (!result) {
             PL_DHashTableRawRemove(&mPtrToNodeMap, e);
             return nsnull;
         }
         e->mNode = result;
     } else {
         result = e->mNode;
+        NS_ASSERTION(result->mParticipant == aParticipant,
+                     "nsCycleCollectionParticipant shouldn't change!");
     }
     return result;
 }
 
 void
 GCGraphBuilder::Traverse(PtrInfo* aPtrInfo)
 {
     mCurrPi = aPtrInfo;
 
 #ifdef DEBUG_CC
-    if (mCurrPi->mLang > nsIProgrammingLanguage::MAX ) {
-        Fault("unknown language during walk");
-        return;
-    }
-
-    if (!mRuntimes[mCurrPi->mLang]) {
-        Fault("script pointer for unregistered language");
+    if (!mCurrPi->mParticipant) {
+        Fault("unknown pointer during walk");
         return;
     }
 #endif
 
     mCurrPi->mFirstChild = mEdgeBuilder.Mark();
     
-    nsresult rv =
-        mRuntimes[aPtrInfo->mLang]->Traverse(aPtrInfo->mPointer, *this);
+    nsresult rv = aPtrInfo->mParticipant->Traverse(aPtrInfo->mPointer, *this);
     if (NS_FAILED(rv)) {
         Fault("script pointer traversal failed", aPtrInfo->mPointer);
     }
 
     mCurrPi->mLastChild = mEdgeBuilder.Mark();
 }
 
 void 
@@ -1154,80 +1106,106 @@ GCGraphBuilder::DescribeNode(size_t refC
 #ifdef DEBUG_CC
     mCurrPi->mBytes = objSz;
     mCurrPi->mName = objName;
 #endif
 
     mCurrPi->mRefCount = refCount;
 #ifdef DEBUG_CC
     sCollector->mStats.mVisitedNode++;
-    if (mCurrPi->mLang == nsIProgrammingLanguage::JAVASCRIPT)
+    if (mCurrPi->mParticipant == mRuntimes[nsIProgrammingLanguage::JAVASCRIPT])
         sCollector->mStats.mVisitedJSNode++;
 #endif
 }
 
 void 
 GCGraphBuilder::NoteXPCOMChild(nsISupports *child) 
 {
-    if (!child)
+    if (!child || !(child = canonicalize(child)))
         return; 
-   
-    child = canonicalize(child);
 
-    PRBool scanSafe = nsCycleCollector_isScanSafe(child);
 #ifdef DEBUG_CC
-    scanSafe &= !nsCycleCollector_shouldSuppress(child);
+    if (nsCycleCollector_shouldSuppress(child))
+        return;
 #endif
-    if (scanSafe) {
-        PtrInfo *childPi = AddNode(child);
+    
+    nsXPCOMCycleCollectionParticipant *cp;
+    ToParticipant(child, &cp);
+    if (cp) {
+        PtrInfo *childPi = AddNode(child, cp);
         if (!childPi)
             return;
         mEdgeBuilder.Add(childPi);
         ++childPi->mInternalRefs;
     }
 }
 
 void
+GCGraphBuilder::NoteNativeChild(void *child,
+                                nsCycleCollectionParticipant *participant)
+{
+    if (!child)
+        return;
+
+    NS_ASSERTION(participant, "Need a nsCycleCollectionParticipant!");
+
+    PtrInfo *childPi = AddNode(child, participant);
+    if (!childPi)
+        return;
+    mEdgeBuilder.Add(childPi);
+    ++childPi->mInternalRefs;
+}
+
+void
 GCGraphBuilder::NoteScriptChild(PRUint32 langID, void *child) 
 {
     if (!child)
         return;
 
     if (langID > nsIProgrammingLanguage::MAX || !mRuntimes[langID]) {
         Fault("traversing pointer for unregistered language", child);
         return;
     }
 
-    PtrInfo *childPi = AddNode(child);
+    nsCycleCollectionParticipant *cp = mRuntimes[langID]->ToParticipant(child);
+    if (!cp)
+        return;
+
+    PtrInfo *childPi = AddNode(child, cp);
     if (!childPi)
         return;
     mEdgeBuilder.Add(childPi);
     ++childPi->mInternalRefs;
-    childPi->mLang = langID;
 }
 
 
 void 
 nsCycleCollector::CollectPurple()
 {
-    mPurpleBuf.SelectAgedPointers(&mBufs[0]);
+    mPurpleBuf.SelectAgedPointers(&mBuf);
 }
 
 void
 nsCycleCollector::MarkRoots(GCGraph &graph)
 {
-    if (mBufs[0].GetSize() == 0)
+    if (mBuf.GetSize() == 0)
         return;
 
     GCGraphBuilder builder(graph, mRuntimes);
 
     int i;
-    for (i = 0; i < mBufs[0].GetSize(); ++i) {
-        nsISupports *s = NS_STATIC_CAST(nsISupports *, mBufs[0].ObjectAt(i));
-        PtrInfo *pi = builder.AddNode(canonicalize(s));
+    for (i = 0; i < mBuf.GetSize(); ++i) {
+        nsISupports *s = NS_STATIC_CAST(nsISupports *, mBuf.ObjectAt(i));
+        nsXPCOMCycleCollectionParticipant *cp;
+        ToParticipant(s, &cp);
+        if (cp) {
+            PtrInfo *pinfo = builder.AddNode(canonicalize(s), cp);
+            if (pinfo)
+                pinfo->mWasPurple = PR_TRUE;
+        }
     }
 
     graph.mRootCount = builder.Count();
 
     // read the PtrInfo out of the graph that we are building
     NodePool::Enumerator queue(graph.mNodes);
     while (!queue.IsDone()) {
         PtrInfo *pi = queue.GetNext();
@@ -1325,94 +1303,87 @@ nsCycleCollector::CollectWhite(GCGraph &
     // To do this "safely" we must make sure that the white nodes we're
     // operating on are stable for the duration of our operation. So we
     // make 3 sets of calls to language runtimes:
     //
     //   - Root(whites), which should pin the whites in memory.
     //   - Unlink(whites), which drops outgoing links on each white.
     //   - Unroot(whites), which returns the whites to normal GC.
 
-    PRUint32 i;
     nsresult rv;
 
-    for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
-        mBufs[i].Empty();
+    mBuf.Empty();
 
 #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
     struct _CrtMemState ms1, ms2;
     _CrtMemCheckpoint(&ms1);
 #endif
 
     NodePool::Enumerator etor(graph.mNodes);
     while (!etor.IsDone())
     {
         PtrInfo *pinfo = etor.GetNext();
         void *p = pinfo->mPointer;
 
-        NS_ASSERTION(pinfo->mLang == nsIProgrammingLanguage::CPLUSPLUS ||
-                     !mPurpleBuf.Exists(p),
-                     "Need to remove non-CPLUSPLUS objects from purple buffer!");
         if (pinfo->mColor == white) {
-            if (pinfo->mLang > nsIProgrammingLanguage::MAX)
-                Fault("White node has bad language ID", p);
-            else
-                mBufs[pinfo->mLang].Push(p);
+            mBuf.Push(pinfo);
 
-            if (pinfo->mLang == nsIProgrammingLanguage::CPLUSPLUS) {
+            if (pinfo->mWasPurple) {
                 nsISupports* s = NS_STATIC_CAST(nsISupports*, p);
                 Forget(s);
             }
         }
-        else if (pinfo->mLang == nsIProgrammingLanguage::CPLUSPLUS) {
+        else if (pinfo->mWasPurple) {
             nsISupports* s = NS_STATIC_CAST(nsISupports*, p);
-            nsCycleCollectionParticipant* cp;
-            CallQueryInterface(s, &cp);
-            if (cp)
-                cp->UnmarkPurple(s);
+            nsXPCOMCycleCollectionParticipant* cp =
+                NS_STATIC_CAST(nsXPCOMCycleCollectionParticipant*,
+                               pinfo->mParticipant);
+#ifdef DEBUG
+            nsXPCOMCycleCollectionParticipant* checkcp;
+            CallQueryInterface(s, &checkcp);
+            NS_ASSERTION(checkcp == cp,
+                         "QI should return the same participant!");
+#endif
+            cp->UnmarkPurple(s);
             Forget(s);
         }
     }
 
-    for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
-        if (mRuntimes[i] &&
-            mBufs[i].GetSize() > 0) {
-            rv = mRuntimes[i]->Root(mBufs[i]);
-            if (NS_FAILED(rv))
-                Fault("Failed root call while unlinking");
+    PRUint32 i, count = mBuf.GetSize();
+    for (i = 0; i < count; ++i) {
+        PtrInfo *pinfo = NS_STATIC_CAST(PtrInfo*, mBuf.ObjectAt(i));
+        rv = pinfo->mParticipant->Root(pinfo->mPointer);
+        if (NS_FAILED(rv))
+            Fault("Failed root call while unlinking");
+    }
+
+    for (i = 0; i < count; ++i) {
+        PtrInfo *pinfo = NS_STATIC_CAST(PtrInfo*, mBuf.ObjectAt(i));
+        rv = pinfo->mParticipant->Unlink(pinfo->mPointer);
+        if (NS_FAILED(rv)) {
+            Fault("Failed unlink call while unlinking");
+#ifdef DEBUG_CC
+            mStats.mFailedUnlink++;
+#endif
+        }
+        else {
+#ifdef DEBUG_CC
+            ++mStats.mCollectedNode;
+#endif
         }
     }
 
-    for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
-        if (mRuntimes[i] &&
-            mBufs[i].GetSize() > 0) {
-            rv = mRuntimes[i]->Unlink(mBufs[i]);
-            if (NS_FAILED(rv)) {
-                Fault("Failed unlink call while unlinking");
-#ifdef DEBUG_CC
-                mStats.mFailedUnlink++;
-#endif
-            } else {
-#ifdef DEBUG_CC
-                mStats.mCollectedNode += mBufs[i].GetSize();
-#endif
-            }
-        }
+    for (i = 0; i < count; ++i) {
+        PtrInfo *pinfo = NS_STATIC_CAST(PtrInfo*, mBuf.ObjectAt(i));
+        rv = pinfo->mParticipant->Unroot(pinfo->mPointer);
+        if (NS_FAILED(rv))
+            Fault("Failed unroot call while unlinking");
     }
 
-    for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
-        if (mRuntimes[i] &&
-            mBufs[i].GetSize() > 0) {
-            rv = mRuntimes[i]->Unroot(mBufs[i]);
-            if (NS_FAILED(rv))
-                Fault("Failed unroot call while unlinking");
-        }
-    }
-
-    for (i = 0; i < nsIProgrammingLanguage::MAX+1; ++i)
-        mBufs[i].Empty();
+    mBuf.Empty();
 
 #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
     _CrtMemCheckpoint(&ms2);
     if (ms2.lTotalCount < ms1.lTotalCount)
         mStats.mFreedBytes += (ms1.lTotalCount - ms2.lTotalCount);
 #endif
 }
 
@@ -1631,19 +1602,16 @@ nsCycleCollector::nsCycleCollector() :
 
     memset(mRuntimes, 0, sizeof(mRuntimes));
     mRuntimes[nsIProgrammingLanguage::CPLUSPLUS] = &mXPCOMRuntime;
 }
 
 
 nsCycleCollector::~nsCycleCollector()
 {
-    for (PRUint32 i = 0; i < nsIProgrammingLanguage::MAX+1; ++i) {
-        mRuntimes[i] = NULL;
-    }
 }
 
 
 void 
 nsCycleCollector::RegisterRuntime(PRUint32 langID, 
                                   nsCycleCollectionLanguageRuntime *rt)
 {
     if (mParams.mDoNothing)
@@ -1765,46 +1733,63 @@ public:
         if (!sInitialized) {
             sSuppressionList = PR_GetEnv("XPCOM_CC_SUPPRESS");
             sInitialized = PR_TRUE;
         }
         if (sSuppressionList == nsnull) {
             mSuppressThisNode = PR_FALSE;
         } else {
             nsresult rv;
-            nsCOMPtr<nsCycleCollectionParticipant> cp = do_QueryInterface(s, &rv);
+            nsXPCOMCycleCollectionParticipant *cp;
+            rv = CallQueryInterface(s, &cp);
             if (NS_FAILED(rv)) {
                 Fault("checking suppression on wrong type of pointer", s);
                 return PR_TRUE;
             }
             cp->Traverse(s, *this);
         }
         return mSuppressThisNode;
     }
 
     void DescribeNode(size_t refCount, size_t objSz, const char *objName)
     {
         mSuppressThisNode = (PL_strstr(sSuppressionList, objName) != nsnull);
     }
 
     void NoteXPCOMChild(nsISupports *child) {}
     void NoteScriptChild(PRUint32 langID, void *child) {}
+    void NoteNativeChild(void *child,
+                         nsCycleCollectionParticipant *participant) {}
 };
 
 char *Suppressor::sSuppressionList = nsnull;
 PRBool Suppressor::sInitialized = PR_FALSE;
 
 static PRBool
 nsCycleCollector_shouldSuppress(nsISupports *s)
 {
     Suppressor supp;
     return supp.shouldSuppress(s);
 }
 #endif
 
+#ifdef DEBUG
+static PRBool
+nsCycleCollector_isScanSafe(nsISupports *s)
+{
+    if (!s)
+        return PR_FALSE;
+
+    nsXPCOMCycleCollectionParticipant *cp;
+    ToParticipant(s, &cp);
+
+    return cp != nsnull;
+}
+#endif
+
 void 
 nsCycleCollector::Suspect(nsISupports *n, PRBool current)
 {
     // Re-entering ::Suspect during collection used to be a fault, but
     // we are canonicalizing nsISupports pointers using QI, so we will
     // see some spurious refcount traffic here. 
 
     if (mScanInProgress)
@@ -1831,17 +1816,17 @@ nsCycleCollector::Suspect(nsISupports *n
     if (mParams.mLogPointers) {
         if (!mPtrLog)
             mPtrLog = fopen("pointer_log", "w");
         fprintf(mPtrLog, "S %p\n", NS_STATIC_CAST(void*, n));
     }
 #endif
 
     if (current)
-        mBufs[0].Push(n);
+        mBuf.Push(n);
     else
         mPurpleBuf.Put(n);
 }
 
 
 void 
 nsCycleCollector::Forget(nsISupports *n)
 {
@@ -1926,20 +1911,20 @@ nsCycleCollector::Collect(PRUint32 aTryC
     }
 
     while (aTryCollections > 0) {
         // This triggers a JS GC. Our caller assumes we always trigger at
         // least one JS GC -- they rely on this fact to avoid redundant JS
         // GC calls -- so it's essential that we actually execute this
         // step!
         //
-        // It is also essential to empty mBufs[0] here because starting up
+        // It is also essential to empty mBuf here because starting up
         // collection in language runtimes may force some "current" suspects
-        // into mBufs[0].
-        mBufs[0].Empty();
+        // into mBuf.
+        mBuf.Empty();
 
 #ifdef COLLECT_TIME_DEBUG
         now = PR_Now();
 #endif
         for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
             if (mRuntimes[i])
                 mRuntimes[i]->BeginCycleCollection();
         }
@@ -1958,17 +1943,17 @@ nsCycleCollector::Collect(PRUint32 aTryC
 
             CollectPurple();
 
 #ifdef COLLECT_TIME_DEBUG
             printf("cc: CollectPurple() took %lldms\n",
                    (PR_Now() - now) / PR_USEC_PER_MSEC);
 #endif
 
-            if (mBufs[0].GetSize() == 0) {
+            if (mBuf.GetSize() == 0) {
                 aTryCollections = 0;
             } else {
                 if (mCollectionInProgress)
                     Fault("re-entered collection");
 
                 mCollectionInProgress = PR_TRUE;
 
                 mScanInProgress = PR_TRUE;
@@ -2051,33 +2036,33 @@ nsCycleCollector::Shutdown()
     // the collector because the program is shutting down.
 
     mPurpleBuf.BumpGeneration();
     mParams.mScanDelay = 0;
     Collect(SHUTDOWN_COLLECTIONS(mParams));
 
 #ifdef DEBUG_CC
     CollectPurple();
-    if (mBufs[0].GetSize() != 0) {
+    if (mBuf.GetSize() != 0) {
         printf("Might have been able to release more cycles if the cycle collector would "
                "run once more at shutdown.\n");
     }
 
     ExplainLiveExpectedGarbage();
 #endif
     mParams.mDoNothing = PR_TRUE;
 }
 
 #ifdef DEBUG_CC
 
 PR_STATIC_CALLBACK(PLDHashOperator)
 AddExpectedGarbage(nsVoidPtrHashKey *p, void *arg)
 {
     nsCycleCollector *c = NS_STATIC_CAST(nsCycleCollector*, arg);
-    c->mBufs[0].Push(NS_CONST_CAST(void*, p->GetKey()));
+    c->mBuf.Push(NS_CONST_CAST(void*, p->GetKey()));
     return PL_DHASH_NEXT;
 }
 
 void
 nsCycleCollector::ExplainLiveExpectedGarbage()
 {
     if (mScanInProgress || mCollectionInProgress)
         Fault("can't explain expected garbage during collection itself");
@@ -2093,19 +2078,19 @@ nsCycleCollector::ExplainLiveExpectedGar
             mRuntimes[i]->BeginCycleCollection();
     }
 
     mCollectionInProgress = PR_TRUE;
     mScanInProgress = PR_TRUE;
 
     {
         GCGraph graph;
-        mBufs[0].Empty();
+        mBuf.Empty();
 
-        // Instead of filling mBufs[0] from the purple buffer, we fill it
+        // Instead of filling mBuf from the purple buffer, we fill it
         // from the list of nodes we were expected to collect.
         mExpectedGarbage.EnumerateEntries(&AddExpectedGarbage, this);
 
         MarkRoots(graph);
         ScanRoots(graph);
 
         mScanInProgress = PR_FALSE;
 
@@ -2238,36 +2223,8 @@ void
 nsCycleCollector_DEBUG_wasFreed(nsISupports *n)
 {
 #ifdef DEBUG_CC
     if (sCollector)
         sCollector->WasFreed(n);
 #endif
 }
 #endif
-
-PRBool
-nsCycleCollector_isScanSafe(nsISupports *s)
-{
-    if (!s)
-        return PR_FALSE;
-
-    nsCycleCollectionParticipant *cp;
-    ToParticipant(s, &cp);
-
-    return cp != nsnull;
-}
-
-static void
-ToParticipant(nsISupports *s, nsCycleCollectionParticipant **cp)
-{
-    // We use QI to move from an nsISupports to an
-    // nsCycleCollectionParticipant, which is a per-class singleton helper
-    // object that implements traversal and unlinking logic for the nsISupports
-    // in question.
-    CallQueryInterface(s, cp);
-#ifdef DEBUG_CC
-    if (cp)
-        ++sCollector->mStats.mSuccessfulQI;
-    else
-        ++sCollector->mStats.mFailedQI;
-#endif
-}
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -34,37 +34,28 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsCycleCollector_h__
 #define nsCycleCollector_h__
 
 class nsISupports;
-class nsDeque;
-struct nsCycleCollectionTraversalCallback;
+class nsCycleCollectionParticipant;
 
 // An nsCycleCollectionLanguageRuntime is a per-language object that
 // implements language-specific aspects of the cycle collection task.
 
 struct nsCycleCollectionLanguageRuntime
 {
     virtual nsresult BeginCycleCollection() = 0;
-
-    virtual nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb) = 0;
-
-    virtual nsresult Root(const nsDeque &nodes) = 0;
-    virtual nsresult Unlink(const nsDeque &nodes) = 0;
-    virtual nsresult Unroot(const nsDeque &nodes) = 0;
-
     virtual nsresult FinishCycleCollection() = 0;
+    virtual nsCycleCollectionParticipant *ToParticipant(void *p) = 0;
 };
 
-
-NS_COM PRBool nsCycleCollector_isScanSafe(nsISupports *n);
 NS_COM void nsCycleCollector_suspect(nsISupports *n);
 NS_COM void nsCycleCollector_suspectCurrent(nsISupports *n);
 NS_COM void nsCycleCollector_forget(nsISupports *n);
 nsresult nsCycleCollector_startup();
 NS_COM void nsCycleCollector_collect();
 void nsCycleCollector_shutdown();
 
 #ifdef DEBUG
--- a/xpcom/build/dlldeps.cpp
+++ b/xpcom/build/dlldeps.cpp
@@ -275,17 +275,17 @@ void XXXNeverCalled()
       nsStringBuffer::Alloc(0);
       nsStringBuffer::Realloc(nsnull, 0);
       nsStringBuffer::FromString(x);
       nsStringBuffer::FromString(y);
       b.ToString(0, x);
       b.ToString(0, y);
     }
 
-    nsCycleCollectionParticipant();
+    nsXPCOMCycleCollectionParticipant();
     nsCycleCollector_collect();
 
 #if !defined(WINCE) && !defined(XP_OS2)
     NS_NewWindowsRegKey(nsnull);
 #endif
 
     NS_NewThread(nsnull, nsnull);
     NS_GetCurrentThread(nsnull);
--- a/xpcom/ds/nsVariant.h
+++ b/xpcom/ds/nsVariant.h
@@ -38,16 +38,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* The long avoided variant support for xpcom. */
 
 #include "nsIVariant.h"
 #include "nsStringFwd.h"
 #include "xpt_struct.h"
 
+struct nsCycleCollectionTraversalCallback;
+
 /** 
  * Map the nsAUTF8String, nsUTF8String classes to the nsACString and
  * nsCString classes respectively for now.  These defines need to be removed
  * once Jag lands his nsUTF8String implementation.
  */
 #define nsAUTF8String nsACString
 #define nsUTF8String nsCString
 #define PromiseFlatUTF8String PromiseFlatCString