Fix for bug 457022 (Cache DOM wrappers in the DOM object). r/sr=jst.
authorPeter Van der Beken <peterv@propagandism.org>
Thu, 13 Nov 2008 17:54:52 +0100
changeset 21618 326d60321ec739fae3be846be1f10a168cd798fb
parent 21617 8c92737298f60de7a5e5125b790364d3f598f1c1
child 21619 b02a1924b2310d177a5cdfa10bb3a033fe2f1344
push idunknown
push userunknown
push dateunknown
bugs457022
milestone1.9.1b2pre
Fix for bug 457022 (Cache DOM wrappers in the DOM object). r/sr=jst.
content/base/public/nsIContent.h
content/base/public/nsIDocument.h
content/base/public/nsINode.h
content/base/src/nsContentList.cpp
content/base/src/nsContentList.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMAttribute.cpp
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsNodeUtils.cpp
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/xbl/src/nsXBLProtoImpl.cpp
dom/public/Makefile.in
dom/public/nsWrapperCache.h
dom/src/base/nsDOMClassInfo.cpp
dom/src/base/nsDOMClassInfo.h
dom/src/base/nsGlobalWindow.cpp
dom/src/base/nsJSEnvironment.cpp
js/src/xpconnect/src/xpcconvert.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -897,39 +897,26 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIContent
 
 // Some cycle-collecting helper macros for nsIContent subclasses
 
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_LISTENERMANAGER \
   if (tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {           \
     nsContentUtils::TraverseListenerManager(tmp, cb);     \
   }
 
-#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER      \
-  {                                                              \
-    nsISupports *preservedWrapper = nsnull;                      \
-    if (tmp->GetOwnerDoc())                                      \
-      preservedWrapper = tmp->GetOwnerDoc()->GetReference(tmp);  \
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[preserved wrapper]");\
-    cb.NoteXPCOMChild(preservedWrapper);                         \
-  }
-
 #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_USERDATA \
   if (tmp->HasProperties()) {                      \
     nsNodeUtils::TraverseUserData(tmp, cb);        \
   }
 
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER \
   if (tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) {         \
     nsContentUtils::RemoveListenerManager(tmp);         \
     tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);          \
   }
 
-#define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
-  if (tmp->GetOwnerDoc())                                 \
-    tmp->GetOwnerDoc()->RemoveReference(tmp);
-
 #define NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA \
   if (tmp->HasProperties()) {                    \
     nsNodeUtils::UnlinkUserData(tmp);            \
   }
 
 
 #endif /* nsIContent_h___ */
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -92,18 +92,18 @@ class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 class nsFrameLoader;
 
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-{ 0x6304ae8e, 0x2634, 0x45ed, \
-  { 0x9e, 0x09, 0x83, 0x09, 0x5b, 0x46, 0x72, 0x8b } }
+{ 0x92b19d1c, 0x8f37, 0x4d4b, \
+  { 0xa3, 0x42, 0xb5, 0xc6, 0x8b, 0x54, 0xde, 0x6c } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -673,20 +673,16 @@ public:
   /**
    * Reset this document to aURI, aLoadGroup, and aPrincipal.  aURI must not be
    * null.  If aPrincipal is null, a codebase principal based on aURI will be
    * used.
    */
   virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup,
                           nsIPrincipal* aPrincipal) = 0;
 
-  virtual void AddReference(void *aKey, nsISupports *aReference) = 0;
-  virtual nsISupports *GetReference(void *aKey) = 0;
-  virtual void RemoveReference(void *aKey) = 0;
-
   /**
    * Set the container (docshell) for this document.
    */
   void SetContainer(nsISupports *aContainer)
   {
     mDocumentContainer = do_GetWeakReference(aContainer);
   }
 
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -39,16 +39,17 @@
 #define nsINode_h___
 
 #include "nsPIDOMEventTarget.h"
 #include "nsEvent.h"
 #include "nsPropertyTable.h"
 #include "nsTObserverArray.h"
 #include "nsINodeInfo.h"
 #include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
 
 class nsIContent;
 class nsIDocument;
 class nsIDOMEvent;
 class nsIPresShell;
 class nsPresContext;
 class nsEventChainVisitor;
 class nsEventChainPreVisitor;
@@ -145,26 +146,27 @@ inline nsINode* NODE_FROM(C& aContent, D
   if (aContent)
     return static_cast<nsINode*>(aContent);
   return static_cast<nsINode*>(aDocument);
 }
 
 
 // IID for the nsINode interface
 #define NS_INODE_IID \
-{ 0x2593b0d5, 0x9a06, 0x4d6b, \
-  { 0x9a, 0x10, 0xb1, 0x39, 0x9f, 0x1b, 0xa0, 0x8e } }
-
+{ 0x0f7b2557, 0xa09d, 0x468f, \
+  { 0x93, 0x92, 0xf1, 0xf1, 0xd1, 0xfa, 0x31, 0x78 } }
 
 /**
  * An internal interface that abstracts some DOMNode-related parts that both
  * nsIContent and nsIDocument share.  An instance of this interface has a list
  * of nsIContent children and provides access to them.
  */
-class nsINode : public nsPIDOMEventTarget {
+class nsINode : public nsPIDOMEventTarget,
+                public nsWrapperCache
+{
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
 
   friend class nsNodeUtils;
   friend class nsNodeWeakReference;
   friend class nsNodeSupportsWeakRefTearoff;
 
 #ifdef MOZILLA_INTERNAL_API
@@ -845,9 +847,17 @@ extern const nsIID kThisPtrOffsetsSID;
     NS_INTERFACE_TABLE_ENTRY(_class, _i7)                                     \
     NS_INTERFACE_TABLE_ENTRY(_class, _i8)                                     \
   NS_OFFSET_AND_INTERFACE_TABLE_END                                           \
   NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
 
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsINode, NS_INODE_IID)
 
+
+#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER \
+   tmp->TraverseWrapper(cb);
+
+#define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \
+  tmp->ReleaseWrapper();
+
+
 #endif /* nsINode_h___ */
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -353,16 +353,17 @@ nsContentList::~nsContentList()
     // Clean up mData
     (*mDestroyFunc)(mData);
   }
 }
 
 
 // QueryInterface implementation for nsContentList
 NS_INTERFACE_TABLE_HEAD(nsContentList)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsContentList)
     NS_CONTENT_LIST_INTERFACES(nsContentList)
     NS_INTERFACE_TABLE_ENTRY(nsContentList, nsIHTMLCollection)
     NS_INTERFACE_TABLE_ENTRY(nsContentList, nsIDOMHTMLCollection)
     NS_INTERFACE_TABLE_ENTRY(nsContentList, nsIMutationObserver)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(ContentList)
--- a/content/base/src/nsContentList.h
+++ b/content/base/src/nsContentList.h
@@ -49,16 +49,17 @@
 #include "nsString.h"
 #include "nsIHTMLCollection.h"
 #include "nsIDOMNodeList.h"
 #include "nsINodeList.h"
 #include "nsStubMutationObserver.h"
 #include "nsIAtom.h"
 #include "nsINameSpaceManager.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
 
 // Magic namespace id that means "match all namespaces".  This is
 // negative so it won't collide with actual namespace constants.
 #define kNameSpaceID_Wildcard PR_INT32_MIN
 
 // This is a callback function type that can be used to implement an
 // arbitrary matching algorithm.  aContent is the content that may
 // match the list, while aNamespaceID, aAtom, and aData are whatever
@@ -177,17 +178,18 @@ protected:
 
 /**
  * Class that implements a live NodeList that matches Elements in the
  * tree based on some criterion.
  */
 class nsContentList : public nsBaseContentList,
                       protected nsContentListKey,
                       public nsIHTMLCollection,
-                      public nsStubMutationObserver
+                      public nsStubMutationObserver,
+                      public nsWrapperCache
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   /**
    * @param aRootNode The node under which to limit our search.
    * @param aMatchAtom An atom whose meaning depends on aMatchNameSpaceId.
    *                   The special value "*" always matches whatever aMatchAtom
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1101,25 +1101,16 @@ nsContentUtils::doReparentContentWrapper
 
   nsresult rv;
 
   rv = sXPConnect->ReparentWrappedNativeIfFound(cx, aOldGlobal, aNewGlobal,
                                                 aNode,
                                                 getter_AddRefs(old_wrapper));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (aOldDocument) {
-    nsCOMPtr<nsISupports> old_ref = aOldDocument->GetReference(aNode);
-    if (old_ref) {
-      // Transfer the reference from aOldDocument to aNewDocument
-      aOldDocument->RemoveReference(aNode);
-      aNewDocument->AddReference(aNode, old_ref);
-    }
-  }
-
   // Whether or not aChild is already wrapped we must iterate through
   // its descendants since there's no guarantee that a descendant isn't
   // wrapped even if this child is not wrapped. That used to be true
   // when every DOM node's JSObject was parented at the DOM node's
   // parent's JSObject, but that's no longer the case.
 
   PRUint32 i, count = aNode->GetChildCount();
 
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -96,16 +96,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     NS_RELEASE(tmp->mChild);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER
   NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 // QueryInterface implementation for nsDOMAttribute
 NS_INTERFACE_TABLE_HEAD(nsDOMAttribute)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_NODE_INTERFACE_TABLE7(nsDOMAttribute, nsIDOMAttr, nsIAttribute, nsINode,
                            nsIDOMNode, nsIDOM3Node, nsIDOM3Attr,
                            nsPIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMAttribute)
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
                                  new nsNodeSupportsWeakRefTearoff(this))
   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Attr)
 NS_INTERFACE_MAP_END
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1562,23 +1562,22 @@ nsDocument::~nsDocument()
   }
 
   delete mHeaderData;
 
   if (mBoxObjectTable) {
     mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nsnull);
     delete mBoxObjectTable;
   }
-
-  delete mContentWrapperHash;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
 
 NS_INTERFACE_TABLE_HEAD(nsDocument)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_DOCUMENT_INTERFACE_TABLE_BEGIN(nsDocument)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOM3DocumentEvent)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentStyle)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNSDocumentStyle)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentRange)
     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
@@ -1748,19 +1747,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     tmp->mLinkMap.EnumerateEntries(LinkMapTraverser, &cb);
   }
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mVisitednessChangedURIs)
 
-  // Traverse any associated preserved wrapper.
-  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[preserved wrapper]");
-  cb.NoteXPCOMChild(tmp->GetReference(tmp));
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
 
   if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
     PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
@@ -1781,22 +1778,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     tmp->mChildren.RemoveChildAt(indx);
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedRootContent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDisplayDocument)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
 
-  // Drop the content hash.
-  delete tmp->mContentWrapperHash;
-  tmp->mContentWrapperHash = nsnull;
-
   tmp->mParentDocument = nsnull;
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+
   // nsDocument has a pretty complex destructor, so we're going to
   // assume that *most* cycles you actually want to break somewhere
   // else, and not unlink an awful lot here.
   //
   // In rare cases where you think an unlink will help here, add one
   // manually.
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
@@ -6234,51 +6229,16 @@ nsDocument::FlushPendingNotifications(mo
 
   nsPresShellIterator iter(this);
   nsCOMPtr<nsIPresShell> shell;
   while ((shell = iter.GetNextShell())) {
     shell->FlushPendingNotifications(aType);
   }
 }
 
-void
-nsDocument::AddReference(void *aKey, nsISupports *aReference)
-{
-  if (mScriptGlobalObject) {
-    if (!mContentWrapperHash) {
-      mContentWrapperHash = new nsInterfaceHashtable<nsVoidPtrHashKey, nsISupports>;
-      if (mContentWrapperHash) {
-        mContentWrapperHash->Init(10);
-      }
-    }
-    
-    if (mContentWrapperHash)
-      mContentWrapperHash->Put(aKey, aReference);
-  }
-}
-
-nsISupports*
-nsDocument::GetReference(void *aKey)
-{
-  // NB: This method is part of content cycle collection,
-  // and must *not* Addref its return value.
-    
-  if (mContentWrapperHash)
-    return mContentWrapperHash->GetWeak(aKey);
-  return nsnull;
-}
-
-void
-nsDocument::RemoveReference(void *aKey)
-{
-  if (mContentWrapperHash) {
-    mContentWrapperHash->Remove(aKey);
-  }
-}
-
 nsIScriptEventManager*
 nsDocument::GetScriptEventManager()
 {
   if (!mScriptEventManager) {
     mScriptEventManager = new nsScriptEventManager(this);
     // automatically AddRefs
   }
 
@@ -6883,20 +6843,17 @@ nsDocument::Destroy()
 
   // Shut down our external resource map.  We might not need this for
   // leak-fixing if we fix DocumentViewerImpl to do cycle-collection, but
   // tearing down all those frame trees right now is the right thing to do.
   mExternalResourceMap.Shutdown();
 
   // XXX We really should let cycle collection do this, but that currently still
   //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
-  //     When we start relying on cycle collection again we should remove the
-  //     check for mScriptGlobalObject in AddReference.
-  delete mContentWrapperHash;
-  mContentWrapperHash = nsnull;
+  ReleaseWrapper();
 }
 
 void
 nsDocument::RemovedFromDocShell()
 {
   if (mRemovedFromDocShell)
     return;
 
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -745,19 +745,16 @@ public:
                                 nsIStyleRule* aOldStyleRule,
                                 nsIStyleRule* aNewStyleRule);
   virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet,
                               nsIStyleRule* aStyleRule);
   virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet,
                                 nsIStyleRule* aStyleRule);
 
   virtual void FlushPendingNotifications(mozFlushType aType);
-  virtual void AddReference(void *aKey, nsISupports *aReference);
-  virtual nsISupports *GetReference(void *aKey);
-  virtual void RemoveReference(void *aKey);
   virtual nsIScriptEventManager* GetScriptEventManager();
   virtual void SetXMLDeclaration(const PRUnichar *aVersion,
                                  const PRUnichar *aEncoding,
                                  const PRInt32 aStandalone);
   virtual void GetXMLDeclaration(nsAString& aVersion,
                                  nsAString& aEncoding,
                                  nsAString& Standalone);
   virtual PRBool IsScriptEnabled();
@@ -1159,17 +1156,16 @@ protected:
                  "Shouldn't be called if table is already live!");
     ++mIdMissCount;
     return IdTableIsLive();
   }
 
   PRUint8 mIdMissCount;
 
   nsInterfaceHashtable<nsVoidPtrHashKey, nsPIBoxObject> *mBoxObjectTable;
-  nsInterfaceHashtable<nsVoidPtrHashKey, nsISupports> *mContentWrapperHash;
 
   // The channel that got passed to StartDocumentLoad(), if any
   nsCOMPtr<nsIChannel> mChannel;
   nsRefPtr<nsHTMLStyleSheet> mAttrStyleSheet;
   nsCOMPtr<nsIHTMLCSSStyleSheet> mStyleAttrStyleSheet;
   nsRefPtr<nsXMLEventsManager> mXMLEventsManager;
 
   nsCOMPtr<nsIScriptEventManager> mScriptEventManager;
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -100,17 +100,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_LISTENERMANAGER
   NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGenericDOMDataNode)
+NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsGenericDOMDataNode)
   NS_INTERFACE_MAP_ENTRY(nsIContent)
   NS_INTERFACE_MAP_ENTRY(nsINode)
   NS_INTERFACE_MAP_ENTRY(nsPIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventTarget,
                                  nsDOMEventRTTearoff::Create(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3EventTarget,
                                  nsDOMEventRTTearoff::Create(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMNSEventTarget,
@@ -853,16 +855,19 @@ nsGenericDOMDataNode::IsNodeOfType(PRUin
 void
 nsGenericDOMDataNode::SaveSubtreeState()
 {
 }
 
 void
 nsGenericDOMDataNode::DestroyContent()
 {
+  // XXX We really should let cycle collection do this, but that currently still
+  //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
+  ReleaseWrapper();
 }
 
 #ifdef DEBUG
 void
 nsGenericDOMDataNode::List(FILE* out, PRInt32 aIndent) const
 {
 }
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -457,16 +457,17 @@ nsIContent::FindFirstNonNativeAnonymous(
 }
 
 //----------------------------------------------------------------------
 
 NS_IMPL_ADDREF(nsChildContentList)
 NS_IMPL_RELEASE(nsChildContentList)
 
 NS_INTERFACE_TABLE_HEAD(nsChildContentList)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsChildContentList)
     NS_INTERFACE_TABLE_ENTRY(nsChildContentList, nsINodeList)
     NS_INTERFACE_TABLE_ENTRY(nsChildContentList, nsIDOMNodeList)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NodeList)
 NS_INTERFACE_MAP_END
 
@@ -3468,16 +3469,20 @@ void
 nsGenericElement::DestroyContent()
 {
   nsIDocument *document = GetOwnerDoc();
   if (document) {
     document->BindingManager()->ChangeDocumentFor(this, document, nsnull);
     document->ClearBoxObjectFor(this);
   }
 
+  // XXX We really should let cycle collection do this, but that currently still
+  //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
+  ReleaseWrapper();
+
   PRUint32 i, count = mAttrsAndChildren.ChildCount();
   for (i = 0; i < count; ++i) {
     // The child can remove itself from the parent in BindToTree.
     mAttrsAndChildren.ChildAt(i)->DestroyContent();
   }
 }
 
 void
@@ -4061,17 +4066,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
         cb.NoteXPCOMChild(slots->mControllers);
       cb.NoteXPCOMChild(
         static_cast<nsIDOMNodeList*>(slots->mChildrenList.get()));
     }
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGenericElement)
+NS_INTERFACE_MAP_BEGIN(nsGenericElement)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsGenericElement)
   NS_INTERFACE_MAP_ENTRY(nsIContent)
   NS_INTERFACE_MAP_ENTRY(nsINode)
   NS_INTERFACE_MAP_ENTRY(nsPIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMNSElement, new nsNSElementTearoff(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventTarget,
                                  nsDOMEventRTTearoff::Create(this))
   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3EventTarget,
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -84,17 +84,18 @@ struct nsRect;
 typedef unsigned long PtrBits;
 
 /**
  * Class that implements the nsIDOMNodeList interface (a list of children of
  * the content), by holding a reference to the content and delegating GetLength
  * and Item to its existing child list.
  * @see nsIDOMNodeList
  */
-class nsChildContentList : public nsINodeList
+class nsChildContentList : public nsINodeList,
+                           public nsWrapperCache
 {
 public:
   nsChildContentList(nsINode* aNode)
     : mNode(aNode)
   {
   }
 
   NS_DECL_ISUPPORTS
@@ -105,16 +106,37 @@ public:
   // nsINodeList interface
   virtual nsINode* GetNodeAt(PRUint32 aIndex);  
   
   void DropReference()
   {
     mNode = nsnull;
   }
 
+  nsISupports* GetParentObject()
+  {
+    return mNode;
+  }
+
+  static nsChildContentList* FromSupports(nsISupports* aSupports)
+  {
+    nsINodeList* list = static_cast<nsINodeList*>(aSupports);
+#ifdef DEBUG
+    {
+      nsCOMPtr<nsINodeList> list_qi = do_QueryInterface(aSupports);
+
+      // If this assertion fires the QI implementation for the object in
+      // question doesn't use the nsINodeList pointer as the nsISupports
+      // pointer. That must be fixed, or we'll crash...
+      NS_ASSERTION(list_qi == list, "Uh, fix QI!");
+    }
+#endif
+    return static_cast<nsChildContentList*>(list);
+  }
+
 private:
   // The node whose children make up the list (weak reference)
   nsINode* mNode;
 };
 
 /**
  * A tearoff class for nsGenericElement to implement additional interfaces
  */
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -559,36 +559,25 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
     else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
       // After cloning the document itself, we want to clone the children into
       // the cloned document (somewhat like cloning and importing them into the
       // cloned document).
       nodeInfoManager = clone->mNodeInfo->NodeInfoManager();
     }
   }
   else if (nodeInfoManager) {
-    nsCOMPtr<nsISupports> oldRef;
     nsIDocument* oldDoc = aNode->GetOwnerDoc();
-    if (oldDoc) {
-      if (aNode->IsNodeOfType(nsINode::eELEMENT)) {
-        oldDoc->ClearBoxObjectFor(static_cast<nsIContent*>(aNode));
-      }
-      oldRef = oldDoc->GetReference(aNode);
-      if (oldRef) {
-        oldDoc->RemoveReference(aNode);
-      }
+    if (oldDoc && aNode->IsNodeOfType(nsINode::eELEMENT)) {
+      oldDoc->ClearBoxObjectFor(static_cast<nsIContent*>(aNode));
     }
 
     aNode->mNodeInfo.swap(newNodeInfo);
 
     nsIDocument* newDoc = aNode->GetOwnerDoc();
     if (newDoc) {
-      if (oldRef) {
-        newDoc->AddReference(aNode, oldRef);
-      }
-
       nsPIDOMWindow* window = newDoc->GetInnerWindow();
       if (window) {
         nsCOMPtr<nsIEventListenerManager> elm;
         aNode->GetListenerManager(PR_FALSE, getter_AddRefs(elm));
         if (elm) {
           window->SetMutationListeners(elm->MutationListenerBits());
           if (elm->MayHavePaintEventListener()) {
             window->SetHasPaintEventListeners();
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -525,52 +525,42 @@ GetDocumentFromScriptContext(nsIScriptCo
   return doc;
 }
 
 /////////////////////////////////////////////
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXHREventTarget)
-  if (tmp->mOwner) {
-    nsCOMPtr<nsIDocument> doc =
-      do_QueryInterface(tmp->mOwner->GetExtantDocument());
-    if (doc) {
-      cb.NoteXPCOMChild(doc->GetReference(tmp));
-    }
-  }
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadStartListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnProgressListener)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXHREventTarget)
-  if (tmp->mOwner) {
-    nsCOMPtr<nsIDocument> doc =
-      do_QueryInterface(tmp->mOwner->GetExtantDocument());
-    if (doc) {
-      doc->RemoveReference(tmp);
-    }
-  }
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadStartListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnProgressListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXHREventTarget)
+NS_INTERFACE_MAP_BEGIN(nsXHREventTarget)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsXHREventTarget)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLHttpRequestEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsPIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsXHREventTarget,
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -68,16 +68,17 @@
 #include "nsHashKeys.h"
 #include "prclist.h"
 #include "prtime.h"
 #include "nsIEventListenerManager.h"
 #include "nsIDOMNSEvent.h"
 #include "nsITimer.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsDOMProgressEvent.h"
+#include "nsIScriptGlobalObject.h"
 
 class nsILoadGroup;
 
 class nsAccessControlLRUCache
 {
 public:
   struct TokenTime
   {
@@ -154,17 +155,18 @@ public:
 
   nsIDOMEventListener* GetInner() { return mListener; }
 protected:
   nsCOMPtr<nsIDOMEventListener> mListener;
 };
 
 class nsXHREventTarget : public nsIXMLHttpRequestEventTarget,
                          public nsPIDOMEventTarget,
-                         public nsIDOMNSEventTarget
+                         public nsIDOMNSEventTarget,
+                         public nsWrapperCache
 {
 public:
   nsXHREventTarget() : mLang(nsIProgrammingLanguage::JAVASCRIPT) {}
   virtual ~nsXHREventTarget() {}
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXHREventTarget,
                                            nsIXMLHttpRequestEventTarget)
   NS_DECL_NSIDOMNSEVENTTARGET
@@ -202,16 +204,46 @@ public:
       NS_ASSERTION(mOwner->IsInnerWindow(), "Should have inner window here!\n");
       nsPIDOMWindow* outer = mOwner->GetOuterWindow();
       if (!outer || outer->GetCurrentInnerWindow() != mOwner) {
         return NS_ERROR_FAILURE;
       }
     }
     return NS_OK;
   }
+
+  void GetParentObject(nsIScriptGlobalObject **aParentObject)
+  {
+    if (mOwner) {
+      CallQueryInterface(mOwner, aParentObject);
+    }
+    else {
+      *aParentObject = nsnull;
+    }
+  }
+
+  static nsXHREventTarget* FromSupports(nsISupports* aSupports)
+  {
+    nsIXMLHttpRequestEventTarget* target =
+      static_cast<nsIXMLHttpRequestEventTarget*>(aSupports);
+#ifdef DEBUG
+    {
+      nsCOMPtr<nsIXMLHttpRequestEventTarget> target_qi =
+        do_QueryInterface(aSupports);
+
+      // If this assertion fires the QI implementation for the object in
+      // question doesn't use the nsIXMLHttpRequestEventTarget pointer as the
+      // nsISupports pointer. That must be fixed, or we'll crash...
+      NS_ASSERTION(target_qi == target, "Uh, fix QI!");
+    }
+#endif
+
+    return static_cast<nsXHREventTarget*>(target);
+  }
+
 protected:
   nsRefPtr<nsDOMEventListenerWrapper> mOnLoadListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnAbortListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnLoadStartListener;
   nsRefPtr<nsDOMEventListenerWrapper> mOnProgressListener;
   nsCOMPtr<nsIEventListenerManager> mListenerManager;
   PRUint32 mLang;
--- a/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/content/xbl/src/nsXBLProtoImpl.cpp
@@ -144,23 +144,17 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLP
   // concrete base class.  We need to alter the object so that our concrete class is interposed
   // between the object and its base class.  We become the new base class of the object, and the
   // object's old base class becomes the new class' base class.
   rv = aBinding->InitClass(mClassName, jscontext, global, object,
                            aTargetClassObject);
   if (NS_FAILED(rv))
     return rv;
 
-  // Root ourselves in the document.
-  nsIDocument* doc = aBoundElement->GetOwnerDoc();
-  if (doc) {
-    nsCOMPtr<nsIXPConnectWrappedNative> nativeWrapper(do_QueryInterface(wrapper));
-    if (nativeWrapper)
-      doc->AddReference(aBoundElement, nativeWrapper);
-  }
+  aBoundElement->PreserveWrapper();
 
   wrapper.swap(*aScriptObjectHolder);
   
   return rv;
 }
 
 nsresult
 nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding)
--- a/dom/public/Makefile.in
+++ b/dom/public/Makefile.in
@@ -66,11 +66,12 @@ EXPORTS=nsIScriptContext.h		\
 	nsDOMError.h			\
 	nsIJSEventListener.h		\
 	nsIDOMClassInfo.h		\
 	nsDOMClassInfoID.h		\
 	nsIBaseDOMException.h		\
 	nsDOMString.h			\
 	nsDOMJSUtils.h			\
 	nsDOMScriptObjectHolder.h	\
+	nsWrapperCache.h	\
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/public/nsWrapperCache.h
@@ -0,0 +1,146 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Gecko DOM code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Peter Van der Beken <peterv@propagandism.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * 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 ***** */
+
+#ifndef nsWrapperCache_h___
+#define nsWrapperCache_h___
+
+#include "nsCycleCollectionParticipant.h"
+
+typedef unsigned long PtrBits;
+
+#define NS_WRAPPERCACHE_IID \
+{ 0x3a51ca81, 0xddab, 0x422c, \
+  { 0x95, 0x3a, 0x13, 0x06, 0x28, 0x0e, 0xee, 0x14 } }
+
+/**
+ * Class to store the XPCWrappedNative for an object. This can only be used
+ * with objects that only have one XPCWrappedNative at a time (usually ensured
+ * by setting an explicit parent in the PreCreate hook for the class). This
+ * object can be gotten by calling QueryInterface, note that this breaks XPCOM
+ * rules a bit (this object doesn't derive from nsISupports).
+ */
+class nsWrapperCache
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
+
+  nsWrapperCache() : mWrapperPtrBits(0)
+  {
+  }
+  ~nsWrapperCache()
+  {
+    if (PreservingWrapper()) {
+      GetWrapper()->Release();
+    }
+  }
+
+  /**
+   * This method returns an nsIXPConnectWrappedNative, but we want to avoid
+   * including nsIXPConnect, because we don't want to make everyone require
+   * JS and XPConnect.
+   */
+  nsISupports* GetWrapper()
+  {
+    return reinterpret_cast<nsISupports*>(mWrapperPtrBits & ~kWrapperBitMask);
+  }
+
+  /**
+   * This method takes an nsIXPConnectWrappedNative, but we want to avoid
+   * including nsIXPConnect, because we don't want to make everyone require
+   * JS and XPConnect.
+   */
+  void SetWrapper(nsISupports* aWrapper)
+  {
+    NS_ASSERTION(!mWrapperPtrBits, "Already have a wrapper!");
+    mWrapperPtrBits = reinterpret_cast<PtrBits>(aWrapper);
+  }
+
+  void ClearWrapper()
+  {
+    if (PreservingWrapper()) {
+      GetWrapper()->Release();
+    }
+    mWrapperPtrBits = 0;
+  }
+
+  void PreserveWrapper()
+  {
+    NS_ASSERTION(mWrapperPtrBits, "No wrapper to preserve?");
+    if (!PreservingWrapper()) {
+      NS_ADDREF(reinterpret_cast<nsISupports*>(mWrapperPtrBits));
+      mWrapperPtrBits |= WRAPPER_BIT_PRESERVED;
+    }
+  }
+
+  void ReleaseWrapper()
+  {
+    if (PreservingWrapper()) {
+      nsISupports* wrapper = GetWrapper();
+      mWrapperPtrBits = reinterpret_cast<PtrBits>(wrapper);
+      NS_RELEASE(wrapper);
+    }
+  }
+
+  void TraverseWrapper(nsCycleCollectionTraversalCallback &cb)
+  {
+    if (PreservingWrapper()) {
+      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mWrapper");
+      cb.NoteXPCOMChild(GetWrapper());
+    }
+  }
+
+private:
+  PRBool PreservingWrapper()
+  {
+    return mWrapperPtrBits & WRAPPER_BIT_PRESERVED;
+  }
+
+  enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
+  enum { kWrapperBitMask = 0x1 };
+
+  PtrBits mWrapperPtrBits;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
+
+#define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY                                   \
+  if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
+    *aInstancePtr = static_cast<nsWrapperCache*>(this);                       \
+    return NS_OK;                                                             \
+  }
+
+#endif /* nsWrapperCache_h___ */
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -166,16 +166,17 @@
 #include "nsIDOMHTMLOptionElement.h"
 #include "nsIDOMNSHTMLOptionElement.h"
 #include "nsIDOMHTMLOptionsCollection.h"
 #include "nsIDOMNSHTMLOptionCollectn.h"
 #include "nsIDOMHTMLOptionsCollection.h"
 
 // ContentList includes
 #include "nsContentList.h"
+#include "nsGenericElement.h"
 
 // Event related includes
 #include "nsIEventListenerManager.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMNSEventTarget.h"
 
 // CSS related includes
 #include "nsIDOMStyleSheet.h"
@@ -207,17 +208,17 @@
 #include "nsIDOMXPathEvaluator.h"
 #include "nsIXSLTProcessor.h"
 #include "nsIXSLTProcessorObsolete.h"
 #include "nsIXSLTProcessorPrivate.h"
 
 #include "nsIDOMLSProgressEvent.h"
 #include "nsIDOMParser.h"
 #include "nsIDOMSerializer.h"
-#include "nsIXMLHttpRequest.h"
+#include "nsXMLHttpRequest.h"
 
 // includes needed for the prototype chain interfaces
 #include "nsIDOMNavigator.h"
 #include "nsIDOMBarProp.h"
 #include "nsIDOMScreen.h"
 #include "nsIDOMDocumentType.h"
 #include "nsIDOMDOMImplementation.h"
 #include "nsIDOMDocumentFragment.h"
@@ -487,17 +488,18 @@ static const char kDOMStringBundleURL[] 
   nsIXPCScriptable::WANT_OUTER_OBJECT |                                       \
   nsIXPCScriptable::WANT_INNER_OBJECT |                                       \
   nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
 
 #define NODE_SCRIPTABLE_FLAGS                                                 \
  ((DOM_DEFAULT_SCRIPTABLE_FLAGS |                                             \
    nsIXPCScriptable::WANT_GETPROPERTY |                                       \
    nsIXPCScriptable::WANT_ADDPROPERTY |                                       \
-   nsIXPCScriptable::WANT_SETPROPERTY) &                                      \
+   nsIXPCScriptable::WANT_SETPROPERTY |                                       \
+   nsIXPCScriptable::WANT_FINALIZE) &                                         \
   ~nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY)
 
 // We need to let JavaScript QI elements to interfaces that are not in
 // the classinfo since XBL can be used to dynamically implement new
 // unknown interfaces on elements, accessibility relies on this being
 // possible.
 
 #define ELEMENT_SCRIPTABLE_FLAGS                                              \
@@ -511,25 +513,32 @@ static const char kDOMStringBundleURL[] 
    nsIXPCScriptable::WANT_SETPROPERTY |                                       \
    nsIXPCScriptable::WANT_CALL)
 
 #define DOCUMENT_SCRIPTABLE_FLAGS                                             \
   (NODE_SCRIPTABLE_FLAGS |                                                    \
    nsIXPCScriptable::WANT_ADDPROPERTY |                                       \
    nsIXPCScriptable::WANT_DELPROPERTY |                                       \
    nsIXPCScriptable::WANT_GETPROPERTY |                                       \
-   nsIXPCScriptable::WANT_ENUMERATE   |                                       \
-   nsIXPCScriptable::WANT_POSTCREATE  |                                       \
-   nsIXPCScriptable::WANT_FINALIZE)
+   nsIXPCScriptable::WANT_ENUMERATE)
 
 #define ARRAY_SCRIPTABLE_FLAGS                                                \
   (DOM_DEFAULT_SCRIPTABLE_FLAGS       |                                       \
    nsIXPCScriptable::WANT_GETPROPERTY |                                       \
    nsIXPCScriptable::WANT_ENUMERATE)
 
+#define NODELIST_SCRIPTABLE_FLAGS                                             \
+  (ARRAY_SCRIPTABLE_FLAGS             |                                       \
+   nsIXPCScriptable::WANT_FINALIZE)
+
+#define EVENTTARGET_SCRIPTABLE_FLAGS                                          \
+  (DOM_DEFAULT_SCRIPTABLE_FLAGS       |                                       \
+   nsIXPCScriptable::WANT_ADDPROPERTY |                                       \
+   nsIXPCScriptable::WANT_FINALIZE)
+
 #define DOMCLASSINFO_STANDARD_FLAGS                                           \
   (nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::DOM_OBJECT)
 
 
 #ifdef NS_DEBUG
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
     eDOMClassInfo_##_class##_id,
 #else
@@ -630,17 +639,17 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(Text, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(Comment, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CDATASection, nsNodeSH, NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(ProcessingInstruction, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(Notation, nsNodeSH, NODE_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(NodeList, nsNodeListSH, ARRAY_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(NodeList, nsNodeListSH, NODELIST_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(NamedNodeMap, nsNamedNodeMapSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
   // Misc Core related classes
 
   // StyleSheet classes
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(DocumentStyleSheetList, StyleSheetList,
                                      nsStyleSheetListSH,
@@ -881,19 +890,17 @@ static nsDOMClassInfoData sClassInfoData
 
   NS_DEFINE_CLASSINFO_DATA(RangeException, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CSSValueList, nsCSSValueListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(ContentList, HTMLCollection,
-                                     nsContentListSH,
-                                     ARRAY_SCRIPTABLE_FLAGS |
-                                     nsIXPCScriptable::WANT_PRECREATE)
+                                     nsContentListSH, NODELIST_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XMLStylesheetProcessingInstruction, nsNodeSH,
                            NODE_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(ImageDocument, nsHTMLDocumentSH,
                            DOCUMENT_SCRIPTABLE_FLAGS)
 
 #ifdef MOZ_XUL
@@ -1204,18 +1211,17 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(DOMParser, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(XMLSerializer, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XMLHttpProgressEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(XMLHttpRequest, nsEventTargetSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                           nsIXPCScriptable::WANT_ADDPROPERTY)
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(ClientRect, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(ClientRectList, nsClientRectListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
 #ifdef MOZ_SVG
   NS_DEFINE_CLASSINFO_DATA(SVGForeignObjectElement, nsElementSH,
@@ -1276,18 +1282,17 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(HTMLAudioElement, nsHTMLElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(ProgressEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XMLHttpRequestUpload, nsEventTargetSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                           nsIXPCScriptable::WANT_ADDPROPERTY)
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
 
   // DOM Traversal NodeIterator class  
   NS_DEFINE_CLASSINFO_DATA(NodeIterator, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // data transfer for drag and drop
   NS_DEFINE_CLASSINFO_DATA(DataTransfer, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -4203,38 +4208,24 @@ nsDOMClassInfo::GetClassInfoInstance(nsD
     NS_ADDREF(aData->mCachedClassInfo);
     aData->mCachedClassInfo = MARK_EXTERNAL(aData->mCachedClassInfo);
   }
 
   return GET_CLEAN_CI_PTR(aData->mCachedClassInfo);
 }
 
 // static
-nsresult
+void
 nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper)
 {
-   nsISupports *native = aWrapper->Native();
-   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(native));
-   
-   nsCOMPtr<nsIDocument> doc;
-   if (node) {
-     nsCOMPtr<nsIDOMDocument> domdoc;
-     node->GetOwnerDocument(getter_AddRefs(domdoc));
-     doc = do_QueryInterface(domdoc);
-  }
-
-   if (!doc) {
-     doc = do_QueryInterface(native);
-   }
-
-   if (doc) {
-     nsCOMPtr<nsINode> n(do_QueryInterface(node));
-     doc->AddReference(n, aWrapper);
-   }
-   return NS_OK;
+  nsWrapperCache* cache = nsnull;
+  CallQueryInterface(aWrapper->Native(), &cache);
+  if (cache) {
+    cache->PreserveWrapper();
+  }
 }
 
 
 // static
 void
 nsDOMClassInfo::ShutDown()
 {
   if (sClassInfoData[0].u.mConstructorFptr) {
@@ -6968,16 +6959,26 @@ nsNodeSH::PreCreate(nsISupports *nativeO
                            getter_AddRefs(holder));
 
   *parentObj = JSVAL_TO_OBJECT(v);
 
   return rv;
 }
 
 NS_IMETHODIMP
+nsNodeSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                     JSObject *obj)
+{
+  nsINode* node = static_cast<nsINode*>(wrapper->Native());
+  node->SetWrapper(wrapper);
+
+  return nsEventReceiverSH::PostCreate(wrapper, cx, obj);
+}
+
+NS_IMETHODIMP
 nsNodeSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                       JSObject *obj, jsval id, jsval *vp, PRBool *_retval)
 {
   nsDOMClassInfo::PreserveNodeWrapper(wrapper);
   return nsEventReceiverSH::AddProperty(wrapper, cx, obj, id, vp, _retval);
 }
 
 NS_IMETHODIMP
@@ -6985,16 +6986,23 @@ nsNodeSH::NewResolve(nsIXPConnectWrapped
                      JSObject *obj, jsval id, PRUint32 flags,
                      JSObject **objp, PRBool *_retval)
 {
   if ((id == sBaseURIObject_id || id == sNodePrincipal_id) &&
       IsPrivilegedScript()) {
     return DefineVoidProp(cx, obj, id, objp);
   }
 
+  if (id == sOnload_id || id == sOnerror_id) {
+    // Make sure that this node can't go away while waiting for a
+    // network load that could fire an event handler.
+    nsINode* node = static_cast<nsINode*>(wrapper->Native());
+    node->PreserveWrapper();
+  }
+
   return nsEventReceiverSH::NewResolve(wrapper, cx, obj, id, flags, objp,
                                        _retval);
 }
 
 NS_IMETHODIMP
 nsNodeSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                       JSObject *obj, jsval id, jsval *vp, PRBool *_retval)
 {
@@ -7053,16 +7061,26 @@ nsNodeSH::SetProperty(nsIXPConnectWrappe
 NS_IMETHODIMP
 nsNodeSH::GetFlags(PRUint32 *aFlags)
 {
   *aFlags = DOMCLASSINFO_STANDARD_FLAGS | nsIClassInfo::CONTENT_NODE;
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNodeSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                   JSObject *obj)
+{
+  nsINode* node = static_cast<nsINode*>(wrapper->Native());
+  node->ClearWrapper();
+
+  return NS_OK;
+}
+ 
 // EventReceiver helper
 
 // static
 PRBool
 nsEventReceiverSH::ReallyIsEventName(jsval id, jschar aFirstChar)
 {
   // I wonder if this is faster than using a hash...
 
@@ -7303,22 +7321,16 @@ nsEventReceiverSH::DefineAddEventListene
   return fnc ? NS_OK : NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
 nsEventReceiverSH::NewResolve(nsIXPConnectWrappedNative *wrapper,
                               JSContext *cx, JSObject *obj, jsval id,
                               PRUint32 flags, JSObject **objp, PRBool *_retval)
 {
-  if (id == sOnload_id || id == sOnerror_id) {    
-    // Make sure that this node can't go away while waiting for a
-    // network load that could fire an event handler.
-    nsDOMClassInfo::PreserveNodeWrapper(wrapper);
-  }
-
   if (!JSVAL_IS_STRING(id)) {
     return NS_OK;
   }
 
   if (flags & JSRESOLVE_ASSIGNING) {
     if (!IsEventName(id)) {
       // Bail out.  We don't care about this assignment.
       return NS_OK;
@@ -7394,16 +7406,40 @@ nsEventReceiverSH::AddProperty(nsIXPConn
                                jsval *vp, PRBool *_retval)
 {
   return nsEventReceiverSH::SetProperty(wrapper, cx, obj, id, vp, _retval);
 }
 
 // EventTarget helper
 
 NS_IMETHODIMP
+nsEventTargetSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
+                           JSObject *globalObj, JSObject **parentObj)
+{
+  nsXHREventTarget *target = nsXHREventTarget::FromSupports(nativeObj);
+
+  nsCOMPtr<nsIScriptGlobalObject> native_parent;
+  target->GetParentObject(getter_AddRefs(native_parent));
+
+  *parentObj = native_parent ? native_parent->GetGlobalJSObject() : globalObj;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventTargetSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                            JSObject *obj)
+{
+  nsXHREventTarget *target = nsXHREventTarget::FromSupports(wrapper->Native());
+  target->SetWrapper(wrapper);
+
+  return nsDOMGenericSH::PostCreate(wrapper, cx, obj);
+}
+
+NS_IMETHODIMP
 nsEventTargetSH::NewResolve(nsIXPConnectWrappedNative *wrapper,
                             JSContext *cx, JSObject *obj, jsval id,
                             PRUint32 flags, JSObject **objp, PRBool *_retval)
 {
   if ((flags & JSRESOLVE_ASSIGNING) || !JSVAL_IS_STRING(id)) {
     return NS_OK;
   }
   if (id == sAddEventListener_id) {
@@ -7415,36 +7451,34 @@ nsEventTargetSH::NewResolve(nsIXPConnect
 
 NS_IMETHODIMP
 nsEventTargetSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                              JSObject *obj, jsval id, jsval *vp, PRBool *_retval)
 {
   if (id == sAddEventListener_id) {
     return NS_OK;
   }
-  nsISupports* native = wrapper->Native();
-  nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(native);
-  if (target) {
-    nsCOMPtr<nsIScriptContext> scriptContext;
-    target->GetContextForEventHandlers(getter_AddRefs(scriptContext));
-    if (scriptContext) {
-      nsCOMPtr<nsPIDOMWindow> window =
-        do_QueryInterface(scriptContext->GetGlobalObject());
-      if (window) {
-        nsCOMPtr<nsIDocument> doc =
-          do_QueryInterface(window->GetExtantDocument());
-        if (doc) {
-          doc->AddReference(native, wrapper);
-        }
-      }
-    }
-  }
+
+  nsXHREventTarget *target = nsXHREventTarget::FromSupports(wrapper->Native());
+  target->PreserveWrapper();
+
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsEventTargetSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                          JSObject *obj)
+{
+  nsXHREventTarget *target = nsXHREventTarget::FromSupports(wrapper->Native());
+  target->ClearWrapper();
+
+  return NS_OK;
+}
+
+
 // Element helper
 
 NS_IMETHODIMP
 nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj)
 {
   nsresult rv = nsNodeSH::PostCreate(wrapper, cx, obj);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -7708,16 +7742,59 @@ nsArraySH::GetProperty(nsIXPConnectWrapp
 
   return rv;
 }
 
 
 // NodeList scriptable helper
 
 nsresult
+nsNodeListSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
+                        JSObject *globalObj, JSObject **parentObj)
+{
+  nsWrapperCache* cache = nsnull;
+  CallQueryInterface(nativeObj, &cache);
+  if (!cache) {
+    *parentObj = globalObj;
+    return NS_OK;
+  }
+
+  // nsChildContentList is the only class that uses nsNodeListSH and has a
+  // cached wrapper.
+  nsChildContentList *list = nsChildContentList::FromSupports(nativeObj);
+  nsISupports *native_parent = list->GetParentObject();
+  if (!native_parent) {
+    return NS_ERROR_FAILURE;
+  }
+
+  jsval v;
+  nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+  nsresult rv = WrapNative(cx, globalObj, native_parent,
+                           NS_GET_IID(nsISupports), &v,
+                           getter_AddRefs(holder));
+
+  *parentObj = JSVAL_TO_OBJECT(v);
+
+  return rv;
+}
+
+NS_IMETHODIMP
+nsNodeListSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                         JSObject *obj)
+{
+  nsWrapperCache* cache = nsnull;
+  CallQueryInterface(wrapper->Native(), &cache);
+  if (cache) {
+    cache->SetWrapper(wrapper);
+  }
+
+  return nsArraySH::PostCreate(wrapper, cx, obj);
+}
+
+nsresult
 nsNodeListSH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, PRUint32 *length)
 {
   nsINodeList* list = static_cast<nsINodeList*>(wrapper->Native());
 #ifdef DEBUG
   {
     nsCOMPtr<nsINodeList> list_qi = do_QueryWrappedNative(wrapper);
 
@@ -7745,16 +7822,29 @@ nsNodeListSH::GetItemAt(nsISupports *aNa
     // pointer. That must be fixed, or we'll crash...
     NS_ASSERTION(list_qi == list, "Uh, fix QI!");
   }
 #endif
 
   return list->GetNodeAt(aIndex);
 }
 
+NS_IMETHODIMP
+nsNodeListSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                       JSObject *obj)
+{
+  nsWrapperCache* cache = nsnull;
+  CallQueryInterface(wrapper->Native(), &cache);
+  if (cache) {
+    cache->ClearWrapper();
+  }
+
+  return NS_OK;
+}
+
 
 // StringList scriptable helper
 
 nsresult
 nsStringListSH::GetStringAt(nsISupports *aNative, PRInt32 aIndex,
                             nsAString& aResult)
 {
   nsCOMPtr<nsIDOMDOMStringList> list(do_QueryInterface(aNative));
@@ -7882,31 +7972,40 @@ nsHTMLCollectionSH::GetNamedItem(nsISupp
 nsresult
 nsContentListSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
                            JSObject *globalObj, JSObject **parentObj)
 {
   nsContentList *contentList = nsContentList::FromSupports(nativeObj);
   nsISupports *native_parent = contentList->GetParentObject();
 
   if (!native_parent) {
-    *parentObj = globalObj;
-    return NS_OK;
+    return NS_ERROR_FAILURE;
   }
 
   jsval v;
   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
   nsresult rv = WrapNative(cx, globalObj, native_parent,
                            NS_GET_IID(nsISupports), &v,
                            getter_AddRefs(holder));
 
   *parentObj = JSVAL_TO_OBJECT(v);
 
   return rv;
 }
 
+NS_IMETHODIMP
+nsContentListSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                            JSObject *obj)
+{
+  nsContentList *list = nsContentList::FromSupports(wrapper->Native());
+  list->SetWrapper(wrapper);
+
+  return nsNamedArraySH::PostCreate(wrapper, cx, obj);
+}
+
 nsresult
 nsContentListSH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                            JSObject *obj, PRUint32 *length)
 {
   nsContentList *list = nsContentList::FromSupports(wrapper->Native());
 
   return list->GetLength(length);
 }
@@ -7924,16 +8023,25 @@ nsISupports*
 nsContentListSH::GetNamedItem(nsISupports *aNative, const nsAString& aName,
                               nsresult *aResult)
 {
   nsContentList *list = nsContentList::FromSupports(aNative);
 
   return list->GetNamedItem(aName, aResult);
 }
 
+NS_IMETHODIMP
+nsContentListSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                          JSObject *obj)
+{
+  nsContentList *list = nsContentList::FromSupports(wrapper->Native());
+  list->ClearWrapper();
+
+  return NS_OK;
+}
 
 // Document helper for document.location and document.on*
 
 NS_IMETHODIMP
 nsDocumentSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                           JSObject *obj, jsval id, jsval *vp,
                           PRBool *_retval)
 {
@@ -8148,17 +8256,17 @@ nsDocumentSH::Finalize(nsIXPConnectWrapp
 {
   nsCOMPtr<nsIDocument> doc = do_QueryWrappedNative(wrapper);
   if (!doc) {
     return NS_ERROR_UNEXPECTED;
   }
 
   doc->SetJSObject(nsnull);
 
-  return NS_OK;
+  return nsNodeSH::Finalize(wrapper, cx, obj);
 }
 
 // HTMLDocument helper
 
 // static
 nsresult
 nsHTMLDocumentSH::ResolveImpl(JSContext *cx,
                               nsIXPConnectWrappedNative *wrapper, jsval id,
--- a/dom/src/base/nsDOMClassInfo.h
+++ b/dom/src/base/nsDOMClassInfo.h
@@ -168,17 +168,17 @@ public:
                       "Must know what the XPCNativeWrapper class is!");
     }
 #endif
 
     return sXPCNativeWrapperClass &&
       ::JS_GET_CLASS(cx, obj) == sXPCNativeWrapperClass;
   }
 
-  static nsresult PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
+  static void PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
 
 protected:
   friend nsIClassInfo* NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID);
 
   const nsDOMClassInfoData* mData;
 
   static nsresult Init();
   static nsresult RegisterClassName(PRInt32 aDOMClassInfoID);
@@ -396,21 +396,27 @@ protected:
   nsEventTargetSH(nsDOMClassInfoData* aData) : nsDOMGenericSH(aData)
   {
   }
 
   virtual ~nsEventTargetSH()
   {
   }
 public:
+  NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
+                       JSObject *globalObj, JSObject **parentObj);
+  NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                        JSObject *obj);
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsval id, PRUint32 flags,
                         JSObject **objp, PRBool *_retval);
   NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
+  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                      JSObject *obj);
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsEventTargetSH(aData);
   }
 };
 
 // Window scriptable helper
@@ -552,26 +558,30 @@ protected:
   // we're defining on, |id| is the name of the prop.  This must be a string
   // jsval.  |objp| is the out param if we define successfully.
   nsresult DefineVoidProp(JSContext* cx, JSObject* obj, jsval id,
                           JSObject** objp);
 
 public:
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj);
+  NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                        JSObject *obj);
   NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsval id, PRUint32 flags,
                         JSObject **objp, PRBool *_retval);
   NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
   NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
   NS_IMETHOD GetFlags(PRUint32 *aFlags);
+  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                      JSObject *obj);
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsNodeSH(aData);
   }
 };
 
 
@@ -664,16 +674,23 @@ private:
 class nsNodeListSH : public nsArraySH
 {
 protected:
   nsNodeListSH(nsDOMClassInfoData* aData) : nsArraySH(aData)
   {
   }
 
 public:
+  NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
+                       JSObject *globalObj, JSObject **parentObj);
+  NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                        JSObject *obj);
+  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                      JSObject *obj);
+
   virtual nsresult GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                              JSObject *obj, PRUint32 *length);
   virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex,
                                  nsresult *aResult);
  
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsNodeListSH(aData);
@@ -775,16 +792,20 @@ class nsContentListSH : public nsNamedAr
 protected:
   nsContentListSH(nsDOMClassInfoData* aData) : nsNamedArraySH(aData)
   {
   }
 
 public:
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj);
+  NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                        JSObject *obj);
+  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                      JSObject *obj);
 
   virtual nsresult GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                              JSObject *obj, PRUint32 *length);
   virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex,
                                  nsresult *aResult);
   virtual nsISupports* GetNamedItem(nsISupports *aNative,
                                     const nsAString& aName,
                                     nsresult *aResult);
--- a/dom/src/base/nsGlobalWindow.cpp
+++ b/dom/src/base/nsGlobalWindow.cpp
@@ -1011,23 +1011,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOpenerScriptPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocumentPrincipal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDoc)
 
-  // Traverse any associated preserved wrappers.
-  {
-    if (tmp->mDoc) {
-      cb.NoteXPCOMChild(tmp->mDoc->GetReference(tmp));
-    }
-  }
-
   // Traverse stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
 
   // Traverse mDummyJavaPluginOwner
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDummyJavaPluginOwner)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -1050,22 +1043,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOpenerScriptPrincipal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSessionStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mApplicationCache)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocumentPrincipal)
 
-  // Unlink any associated preserved wrapper.
-  if (tmp->mDoc) {
-    tmp->mDoc->RemoveReference(tmp);
-    NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDoc)
-  }
-
   // Unlink stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
 
   // Unlink mDummyJavaPluginOwner
   if (tmp->mDummyJavaPluginOwner) {
     tmp->mDummyJavaPluginOwner->Destroy();
     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDummyJavaPluginOwner)
--- a/dom/src/base/nsJSEnvironment.cpp
+++ b/dom/src/base/nsJSEnvironment.cpp
@@ -3372,17 +3372,18 @@ nsJSContext::ScriptExecuted()
   ScriptEvaluated(!::JS_IsRunning(mContext));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJSContext::PreserveWrapper(nsIXPConnectWrappedNative *aWrapper)
 {
-  return nsDOMClassInfo::PreserveNodeWrapper(aWrapper);
+  nsDOMClassInfo::PreserveNodeWrapper(aWrapper);
+  return NS_OK;
 }
 
 //static
 void
 nsJSContext::CC()
 {
   ++sCCollectCount;
 #ifdef DEBUG_smaug
--- a/js/src/xpconnect/src/xpcconvert.cpp
+++ b/js/src/xpconnect/src/xpcconvert.cpp
@@ -43,16 +43,17 @@
 /* Data conversion between native and JavaScript types. */
 
 #include "xpcprivate.h"
 #include "nsString.h"
 #include "XPCNativeWrapper.h"
 #include "nsIAtom.h"
 #include "XPCWrapper.h"
 #include "nsJSPrincipals.h"
+#include "nsWrapperCache.h"
 
 //#define STRICT_CHECK_OF_UNICODE
 #ifdef STRICT_CHECK_OF_UNICODE
 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80))
 #else // STRICT_CHECK_OF_UNICODE
 #define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00))
 #endif // STRICT_CHECK_OF_UNICODE
 
@@ -1086,25 +1087,40 @@ XPCConvert::NativeInterface2JSObject(XPC
         if(!xpcscope)
             return JS_FALSE;
 
         AutoMarkingNativeInterfacePtr iface(ccx);
         iface = XPCNativeInterface::GetNewOrUsed(ccx, iid);
         if(!iface)
             return JS_FALSE;
 
+        nsresult rv;
         XPCWrappedNative* wrapper;
-        nsresult rv = XPCWrappedNative::GetNewOrUsed(ccx, src, xpcscope,
-                                                     iface, isGlobal,
-                                                     &wrapper);
+        nsWrapperCache* cache = nsnull;
+        CallQueryInterface(src, &cache);
+        if(cache &&
+           (wrapper = static_cast<XPCWrappedNative*>(cache->GetWrapper())))
+        {
+            NS_ADDREF(wrapper);
+            wrapper->FindTearOff(ccx, iface, JS_FALSE, &rv);
+            if(NS_FAILED(rv))
+                NS_RELEASE(wrapper);
+        }
+        else
+        {
+            rv = XPCWrappedNative::GetNewOrUsed(ccx, src, xpcscope, iface,
+                                                isGlobal, &wrapper);
+        }
+
         if(pErr)
             *pErr = rv;
         if(NS_SUCCEEDED(rv) && wrapper)
         {
             uint32 flags = 0;
+            JSObject *flat = wrapper->GetFlatJSObject();
             if (allowNativeWrapper && wrapper->GetScope() != xpcscope)
             {
                 // Cross scope access detected. Check if chrome code
                 // is accessing non-chrome objects, and if so, wrap
                 // the XPCWrappedNative with an XPCNativeWrapper to
                 // prevent user-defined properties from shadowing DOM
                 // properties from chrome code.
 
@@ -1152,17 +1168,16 @@ XPCConvert::NativeInterface2JSObject(XPC
                         callee = nsnull;
                     }
                 }
                 // else don't create XPCNativeWrappers, since we have
                 // no idea what's calling what here.
 
                 flags = script ? JS_GetScriptFilenameFlags(script) : 0;
                 NS_ASSERTION(flags != JSFILENAME_NULL, "null script filename");
-                JSObject *flat = wrapper->GetFlatJSObject();
 
                 if(!JS_IsSystemObject(ccx, flat))
                 {
                     if(flags & JSFILENAME_PROTECTED)
                     {
 #ifdef DEBUG_XPCNativeWrapper
                         {
                             char *s = wrapper->ToString(ccx);
@@ -1260,17 +1275,16 @@ XPCConvert::NativeInterface2JSObject(XPC
 
                     NS_ADDREF(objHolder);
                     NS_RELEASE(wrapper);
                     *dest = objHolder;
                     return JS_TRUE;
                 }
             }
 
-            JSObject *flat = wrapper->GetFlatJSObject();
             const char *name = STOBJ_GET_CLASS(flat)->name;
             if(allowNativeWrapper &&
                !(flags & JSFILENAME_SYSTEM) &&
                !JS_IsSystemObject(ccx, flat) &&
                XPC_XOW_ClassNeedsXOW(name))
             {
                 jsval v = OBJECT_TO_JSVAL(flat);
                 XPCJSObjectHolder *objHolder = nsnull;
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -40,16 +40,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* Wrapper object for reflecting native xpcom objects into JavaScript. */
 
 #include "xpcprivate.h"
 #include "nsCRT.h"
 #include "XPCNativeWrapper.h"
 #include "XPCWrapper.h"
+#include "nsWrapperCache.h"
 
 /***************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
 
 NS_IMETHODIMP
 NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::RootAndUnlinkJSObjects(void *p)
 {
@@ -584,44 +585,60 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
 nsresult
 XPCWrappedNative::GetUsedOnly(XPCCallContext& ccx,
                               nsISupports* Object,
                               XPCWrappedNativeScope* Scope,
                               XPCNativeInterface* Interface,
                               XPCWrappedNative** resultWrapper)
 {
     NS_ASSERTION(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object");
-    nsCOMPtr<nsISupports> identity;
-#ifdef XPC_IDISPATCH_SUPPORT
-    // XXX See GetNewOrUsed for more info on this
-    if(Interface->GetIID()->Equals(NSID_IDISPATCH))
-        identity = Object;
-    else
-#endif
-        identity = do_QueryInterface(Object);
-
-    if(!identity)
-    {
-        NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
-        return NS_ERROR_FAILURE;
-    }
 
     XPCWrappedNative* wrapper;
-    Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
-
-    {   // scoped lock
-        XPCAutoLock lock(Scope->GetRuntime()->GetMapLock());
-        wrapper = map->Find(identity);
+    nsWrapperCache* cache = nsnull;
+    CallQueryInterface(Object, &cache);
+    if(cache)
+    {
+        wrapper = static_cast<XPCWrappedNative*>(cache->GetWrapper());
         if(!wrapper)
         {
             *resultWrapper = nsnull;
             return NS_OK;
         }
         NS_ADDREF(wrapper);
     }
+    else
+    {
+        nsCOMPtr<nsISupports> identity;
+#ifdef XPC_IDISPATCH_SUPPORT
+        // XXX See GetNewOrUsed for more info on this
+        if(Interface->GetIID()->Equals(NSID_IDISPATCH))
+            identity = Object;
+        else
+#endif
+            identity = do_QueryInterface(Object);
+
+        if(!identity)
+        {
+            NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
+            return NS_ERROR_FAILURE;
+        }
+
+        Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
+
+        {   // scoped lock
+            XPCAutoLock lock(Scope->GetRuntime()->GetMapLock());
+            wrapper = map->Find(identity);
+            if(!wrapper)
+            {
+                *resultWrapper = nsnull;
+                return NS_OK;
+            }
+            NS_ADDREF(wrapper);
+        }
+    }
 
     nsresult rv;
     if(!wrapper->FindTearOff(ccx, Interface, JS_FALSE, &rv))
     {
         NS_RELEASE(wrapper);
         NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
         return rv;
     }