Bug 648801 (new DOM list bindings) - Support expandos on the new proxy-based DOM bindings. r=bz/jst/mrbkap.
authorPeter Van der Beken <peterv@propagandism.org>
Thu, 26 May 2011 21:58:35 +0200
changeset 78428 d64ee71954761887cceb784b141b529c49a315b4
parent 78427 93037e2151f3f3c7cfbf4f6eba441dda26fdeb21
child 78429 1169117ea7f1b124aae3b2b66607aeed0af087e8
push id21294
push userpvanderbeken@mozilla.com
push dateMon, 10 Oct 2011 08:58:09 +0000
treeherdermozilla-central@432f3a96bc2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, jst, mrbkap
bugs648801
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 648801 (new DOM list bindings) - Support expandos on the new proxy-based DOM bindings. r=bz/jst/mrbkap.
content/base/public/nsContentUtils.h
content/base/public/nsDOMEventTargetWrapperCache.h
content/base/src/nsContentList.cpp
content/base/src/nsContentList.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMAttribute.cpp
content/base/src/nsDOMDocumentType.cpp
content/base/src/nsDOMEventTargetWrapperCache.cpp
content/base/src/nsDocument.cpp
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGenericElement.h
content/base/src/nsNodeUtils.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsWrapperCache.h
dom/base/nsWrapperCacheInlines.h
js/src/xpconnect/src/dombindings.cpp
js/src/xpconnect/src/dombindings.h
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/qsgen.py
js/src/xpconnect/src/xpcconvert.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcpublic.h
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativejsops.cpp
js/src/xpconnect/wrappers/WrapperFactory.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1268,32 +1268,19 @@ public:
       aCache->SetPreservingWrapper(PR_TRUE);
 #ifdef DEBUG
       // Make sure the cycle collector will be able to traverse to the wrapper.
       CheckCCWrapperTraversal(aScriptObjectHolder, aCache);
 #endif
     }
   }
   static void ReleaseWrapper(nsISupports* aScriptObjectHolder,
-                             nsWrapperCache* aCache)
-  {
-    if (aCache->PreservingWrapper()) {
-      DropJSObjects(aScriptObjectHolder);
-      aCache->SetPreservingWrapper(PR_FALSE);
-    }
-  }
+                             nsWrapperCache* aCache);
   static void TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
-                           void *aClosure)
-  {
-    if (aCache->PreservingWrapper()) {
-      aCallback(nsIProgrammingLanguage::JAVASCRIPT,
-                aCache->GetWrapperPreserveColor(),
-                "Preserved wrapper", aClosure);
-    }
-  }
+                           void *aClosure);
 
   /**
    * Convert nsIContent::IME_STATUS_* to nsIWidget::IME_STATUS_*
    */
   static PRUint32 GetWidgetStatusFromIMEStatus(PRUint32 aState);
 
   /*
    * Notify when the first XUL menu is opened and when the all XUL menus are
--- a/content/base/public/nsDOMEventTargetWrapperCache.h
+++ b/content/base/public/nsDOMEventTargetWrapperCache.h
@@ -85,17 +85,17 @@ public:
 
     return static_cast<nsDOMEventTargetWrapperCache*>(target);
   }
 
   void Init(JSContext* aCx = nsnull);
 
 protected:
   nsDOMEventTargetWrapperCache() : nsDOMEventTargetHelper(), nsWrapperCache() {}
-  virtual ~nsDOMEventTargetWrapperCache();
+  virtual ~nsDOMEventTargetWrapperCache() {}
 };
 
 #define NS_DECL_EVENT_HANDLER(_event)                                         \
   protected:                                                                  \
     nsRefPtr<nsDOMEventListenerWrapper> mOn##_event##Listener;                \
   public:
 
 #define NS_DECL_AND_IMPL_EVENT_HANDLER(_event)                                \
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -73,20 +73,25 @@ using namespace mozilla::dom;
 
 nsBaseContentList::~nsBaseContentList()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBaseContentList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mElements)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mElements)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsBaseContentList)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 #define NS_CONTENT_LIST_INTERFACES(_class)                                    \
     NS_INTERFACE_TABLE_ENTRY(_class, nsINodeList)                             \
     NS_INTERFACE_TABLE_ENTRY(_class, nsIDOMNodeList)
 
 DOMCI_DATA(NodeList, nsBaseContentList)
 
 // QueryInterface implementation for nsBaseContentList
--- a/content/base/src/nsContentList.h
+++ b/content/base/src/nsContentList.h
@@ -94,17 +94,17 @@ public:
   // nsINodeList
   virtual nsIContent* GetNodeAt(PRUint32 aIndex);
   virtual PRInt32 IndexOf(nsIContent* aContent);
   
   PRUint32 Length() const { 
     return mElements.Count();
   }
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsBaseContentList, nsINodeList)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsBaseContentList)
 
   void AppendElement(nsIContent *aContent);
   void MaybeAppendElement(nsIContent* aContent)
   {
     if (aContent)
       AppendElement(aContent);
   }
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -198,16 +198,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif
 #include "nsDOMTouchEvent.h"
 #include "nsIScriptElement.h"
 #include "prdtoa.h"
 
 #include "mozilla/Preferences.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla;
 
 const char kLoadAsData[] = "loadAsData";
 
 static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
@@ -5798,8 +5799,40 @@ bool nsContentUtils::IsRequestFullScreen
   return !sTrustedFullScreenOnly || nsEventStateManager::IsHandlingUserInput();
 }
 
 bool
 nsContentUtils::IsFullScreenKeyInputRestricted()
 {
   return sFullScreenKeyInputRestricted;
 }
+
+// static
+void
+nsContentUtils::ReleaseWrapper(nsISupports* aScriptObjectHolder,
+                               nsWrapperCache* aCache)
+{
+  if (aCache->PreservingWrapper()) {
+    DropJSObjects(aScriptObjectHolder);
+    aCache->SetPreservingWrapper(false);
+  }
+
+  aCache->ClearWrapperIfProxy();
+}
+
+// static
+void
+nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
+                             void *aClosure)
+{
+  if (aCache->PreservingWrapper()) {
+    aCallback(nsIProgrammingLanguage::JAVASCRIPT,
+              aCache->GetWrapperPreserveColor(),
+              "Preserved wrapper", aClosure);
+  }
+  else {
+    JSObject *expando = aCache->GetExpandoObjectPreserveColor();
+    if (expando) {
+      aCallback(nsIProgrammingLanguage::JAVASCRIPT, expando, "Expando object",
+                aClosure);
+    }
+  }
+}
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -55,16 +55,17 @@
 #include "nsGkAtoms.h"
 #include "nsCOMArray.h"
 #include "nsNodeUtils.h"
 #include "nsEventListenerManager.h"
 #include "nsTextNode.h"
 #include "mozAutoDocUpdate.h"
 #include "nsMutationEvent.h"
 #include "nsPLDOMEvent.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 bool nsDOMAttribute::sInitialized;
 
 nsDOMAttribute::nsDOMAttribute(nsDOMAttributeMap *aAttrMap,
                                already_AddRefed<nsINodeInfo> aNodeInfo,
--- a/content/base/src/nsDOMDocumentType.cpp
+++ b/content/base/src/nsDOMDocumentType.cpp
@@ -46,16 +46,17 @@
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsDOMString.h"
 #include "nsNodeInfoManager.h"
 #include "nsIDocument.h"
 #include "nsIXPConnect.h"
 #include "nsIDOMDocument.h"
 #include "xpcpublic.h"
+#include "nsWrapperCacheInlines.h"
 
 nsresult
 NS_NewDOMDocumentType(nsIDOMDocumentType** aDocType,
                       nsNodeInfoManager *aNodeInfoManager,
                       nsIAtom *aName,
                       const nsAString& aPublicId,
                       const nsAString& aSystemId,
                       const nsAString& aInternalSubset)
--- a/content/base/src/nsDOMEventTargetWrapperCache.cpp
+++ b/content/base/src/nsDOMEventTargetWrapperCache.cpp
@@ -38,22 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsContentUtils.h"
 #include "nsDOMEventTargetWrapperCache.h"
 #include "nsIDocument.h"
 #include "nsIJSContextStack.h"
 #include "nsServiceManagerUtils.h"
 #include "nsDOMJSUtils.h"
-
-nsDOMEventTargetWrapperCache::~nsDOMEventTargetWrapperCache()
-{
-  nsISupports *supports = static_cast<nsIDOMEventTarget*>(this);
-  nsContentUtils::ReleaseWrapper(supports, this);
-}
+#include "nsWrapperCacheInlines.h"
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventTargetWrapperCache)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMEventTargetWrapperCache)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMEventTargetWrapperCache,
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -200,16 +200,17 @@
 #include "mozilla/dom/Link.h"
 #include "nsIHTMLDocument.h"
 #include "nsXULAppAPI.h"
 #include "nsDOMTouchEvent.h"
 
 #include "mozilla/Preferences.h"
 
 #include "imgILoader.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 
 #ifdef PR_LOGGING
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -61,16 +61,17 @@
 #include "nsNodeUtils.h"
 #include "nsBindingManager.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozAutoDocUpdate.h"
 #include "nsPLDOMEvent.h"
 
 #include "pldhash.h"
 #include "prprf.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla;
 
 nsGenericDOMDataNode::nsGenericDOMDataNode(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsIContent(aNodeInfo)
 {
   NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::TEXT_NODE ||
                     mNodeInfo->NodeType() == nsIDOMNode::CDATA_SECTION_NODE ||
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -142,30 +142,43 @@
 
 #include "mozAutoDocUpdate.h"
 
 #include "nsCSSParser.h"
 #include "prprf.h"
 
 #include "nsSVGFeatures.h"
 #include "nsDOMMemoryReporter.h"
+#include "nsWrapperCacheInlines.h"
 
 #include "xpcpublic.h"
 
 using namespace mozilla::dom;
 namespace css = mozilla::css;
 
 NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
 
 PRInt32 nsIContent::sTabFocusModel = eTabFocus_any;
 bool nsIContent::sTabFocusModelAppliesToXUL = false;
 PRUint32 nsMutationGuard::sMutationCount = 0;
 
 nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
 
+void
+nsWrapperCache::RemoveExpandoObject()
+{
+  JSObject *expando = GetExpandoObjectPreserveColor();
+  if (expando) {
+    JSCompartment *compartment = js::GetObjectCompartment(expando);
+    xpc::CompartmentPrivate *priv =
+      static_cast<xpc::CompartmentPrivate *>(js_GetCompartmentPrivate(compartment));
+    priv->RemoveDOMExpandoObject(expando);
+  }
+}
+
 //----------------------------------------------------------------------
 
 nsINode::nsSlots::~nsSlots()
 {
   if (mChildNodes) {
     mChildNodes->DropReference();
     NS_RELEASE(mChildNodes);
   }
@@ -1512,26 +1525,38 @@ nsIContent::GetBaseURI() const
     }
   }
 
   return base.forget();
 }
 
 //----------------------------------------------------------------------
 
-NS_IMPL_ADDREF(nsChildContentList)
-NS_IMPL_RELEASE(nsChildContentList)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsChildContentList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsChildContentList)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsChildContentList)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsChildContentList)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 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_ENTRIES_CYCLE_COLLECTION(nsChildContentList)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(NodeList)
 NS_INTERFACE_MAP_END
 
 JSObject*
 nsChildContentList::WrapObject(JSContext *cx, XPCWrappedNativeScope *scope)
 {
   return xpc::dom::NodeListBase::create(cx, scope, this);
 }
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -98,17 +98,18 @@ class nsChildContentList : public nsINod
 {
 public:
   nsChildContentList(nsINode* aNode)
     : mNode(aNode)
   {
     SetIsProxy();
   }
 
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsChildContentList)
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope);
 
   // nsIDOMNodeList interface
   NS_DECL_NSIDOMNODELIST
 
   // nsINodeList interface
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -59,17 +59,17 @@
 #endif
 #include "nsBindingManager.h"
 #include "nsGenericHTMLElement.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif // MOZ_MEDIA
 #include "nsImageLoadingContent.h"
 #include "jsgc.h"
-#include "xpcpublic.h"
+#include "nsWrapperCacheInlines.h"
 
 using namespace mozilla::dom;
 
 // This macro expects the ownerDocument of content_ to be in scope as
 // |nsIDocument* doc|
 // NOTE: AttributeChildRemoved doesn't use this macro but has a very similar use.
 // If you change how this macro behave please update AttributeChildRemoved.
 #define IMPL_MUTATION_NOTIFICATION(func_, content_, params_)      \
@@ -606,16 +606,19 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
         // If reparenting moves us to a new compartment, preserving causes
         // problems. In that case, we release ourselves and re-preserve after
         // reparenting so we're sure to have the right JS object preserved.
         // We use a JSObject stack copy of the wrapper to protect it from GC
         // under ReparentWrappedNativeIfFound.
         if (aNode->PreservingWrapper()) {
           preservedWrapper = wrapper;
           nsContentUtils::ReleaseWrapper(aNode, aNode);
+          NS_ASSERTION(aNode->GetWrapper(),
+                       "ReleaseWrapper cleared our wrapper, this code needs to "
+                       "be changed to deal with that!");
         }
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
         rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode,
                                                getter_AddRefs(oldWrapper));
 
         if (preservedWrapper) {
           nsContentUtils::PreserveWrapper(aNode, aNode);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -497,16 +497,18 @@
 #include "mozilla/dom/indexedDB/IDBIndex.h"
 #include "nsIIDBDatabaseException.h"
 
 #include "nsIDOMMediaQueryList.h"
 
 #include "nsDOMTouchEvent.h"
 #include "nsIDOMCustomEvent.h"
 
+#include "nsWrapperCacheInlines.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 static const char kDOMStringBundleURL[] =
   "chrome://global/locale/dom/dom.properties";
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -242,16 +242,17 @@
 
 #include "mozilla/dom/StructuredCloneTags.h"
 
 #include "nsRefreshDriver.h"
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
+#include "nsWrapperCacheInlines.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
 #endif
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
 using namespace mozilla;
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -10,17 +10,17 @@
  * 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.
+ *   Mozilla Foundation.
  * 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
@@ -68,69 +68,62 @@ public:
 
   nsWrapperCache() : mWrapperPtrBits(0)
   {
   }
   ~nsWrapperCache()
   {
     NS_ASSERTION(!PreservingWrapper(),
                  "Destroying cache with a preserved wrapper!");
+    RemoveExpandoObject();
   }
 
   /**
    * This getter clears the gray bit before handing out the JSObject which means
    * that the object is guaranteed to be kept alive past the next CC.
    *
    * Implemented in xpcpublic.h because we have to include some JS headers that
    * don't play nicely with the rest of the codebase. Include xpcpublic.h if you
    * need to call this method.
    */
-  inline JSObject* GetWrapper() const;
+  JSObject* GetWrapper() const;
 
   /**
    * This getter does not change the color of the JSObject meaning that the
    * object returned is not guaranteed to be kept alive past the next CC.
    *
    * This should only be called if you are certain that the return value won't
    * be passed into a JS API function and that it won't be stored without being
    * rooted (or otherwise signaling the stored value to the CC).
    */
-  JSObject* GetWrapperPreserveColor() const
-  {
-    return reinterpret_cast<JSObject*>(mWrapperPtrBits & ~kWrapperBitMask);
-  }
+  JSObject* GetWrapperPreserveColor() const;
+
+  JSObject* GetExpandoObjectPreserveColor() const;
 
-  void SetWrapper(JSObject* aWrapper)
-  {
-    NS_ASSERTION(!PreservingWrapper(), "Clearing a preserved wrapper!");
-    mWrapperPtrBits = reinterpret_cast<PtrBits>(aWrapper) |
-                      (mWrapperPtrBits & WRAPPER_IS_PROXY);
-  }
+  void SetWrapper(JSObject* aWrapper);
 
-  void ClearWrapper()
-  {
-    NS_ASSERTION(!PreservingWrapper(), "Clearing a preserved wrapper!");
-    mWrapperPtrBits = 0;
-  }
+  void ClearWrapper();
+  void ClearWrapperIfProxy();
 
   bool PreservingWrapper()
   {
     return (mWrapperPtrBits & WRAPPER_BIT_PRESERVED) != 0;
   }
 
   void SetIsProxy()
   {
     mWrapperPtrBits |= WRAPPER_IS_PROXY;
   }
 
-  bool IsProxy()
+  bool IsProxy() const
   {
     return (mWrapperPtrBits & WRAPPER_IS_PROXY) != 0;
   }
 
+
   /**
    * Wrap the object corresponding to this wrapper cache.  If non-null is
    * returned, the object has already been stored in the wrapper cache.
    */
   virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope) {
     return nsnull;
   }
 
@@ -140,16 +133,28 @@ private:
   {
     if(aPreserve) {
       mWrapperPtrBits |= WRAPPER_BIT_PRESERVED;
     }
     else {
       mWrapperPtrBits &= ~WRAPPER_BIT_PRESERVED;
     }
   }
+  JSObject *GetJSObjectFromBits() const
+  {
+    return reinterpret_cast<JSObject*>(mWrapperPtrBits & ~kWrapperBitMask);
+  }
+  void SetWrapperBits(void *aWrapper)
+  {
+    mWrapperPtrBits = reinterpret_cast<PtrBits>(aWrapper) |
+                      (mWrapperPtrBits & WRAPPER_IS_PROXY);
+  }
+  void RemoveExpandoObject();
+
+  static JSObject *GetExpandoFromSlot(JSObject *obj);
 
   enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
   enum { WRAPPER_IS_PROXY = 1 << 1 };
   enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_PROXY) };
 
   PtrBits mWrapperPtrBits;
 };
 
new file mode 100644
--- /dev/null
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** 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 Foundation.
+ * 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 nsWrapperCacheInline_h___
+#define nsWrapperCacheInline_h___
+
+#include "nsWrapperCache.h"
+#include "xpcprivate.h"
+
+inline JSObject*
+nsWrapperCache::GetWrapperPreserveColor() const
+{
+  JSObject *obj = GetJSObjectFromBits();
+  return !IsProxy() || !obj || js::IsProxy(obj) ? obj : nsnull;
+}
+
+inline JSObject*
+nsWrapperCache::GetWrapper() const
+{
+    JSObject* obj = GetWrapperPreserveColor();
+    xpc_UnmarkGrayObject(obj);
+    return obj;
+}
+
+inline void
+nsWrapperCache::SetWrapper(JSObject* aWrapper)
+{
+    NS_ASSERTION(!PreservingWrapper(), "Clearing a preserved wrapper!");
+    NS_ASSERTION(aWrapper, "Use ClearWrapper!");
+
+    JSObject *obj = GetJSObjectFromBits();
+    if (obj && xpc::dom::isExpandoObject(obj)) {
+        NS_ASSERTION(xpc::dom::instanceIsProxy(aWrapper),
+                     "We have an expando but this isn't a DOM proxy?");
+        js::SetProxyExtra(aWrapper, xpc::dom::JSPROXYSLOT_EXPANDO,
+                          js::ObjectValue(*obj));
+    }
+
+    SetWrapperBits(aWrapper);
+}
+
+inline JSObject*
+nsWrapperCache::GetExpandoObjectPreserveColor() const
+{
+    JSObject *obj = GetJSObjectFromBits();
+    if (!obj) {
+        return NULL;
+    }
+
+    if (!IsProxy()) {
+        // If we support non-proxy dom binding objects then this should be:
+        //return xpc::dom::isExpandoObject(obj) ? obj : js::GetSlot(obj, EXPANDO_SLOT);
+        return NULL;
+    }
+
+    // FIXME unmark gray?
+    if (xpc::dom::instanceIsProxy(obj)) {
+        return GetExpandoFromSlot(obj);
+    }
+
+    return xpc::dom::isExpandoObject(obj) ? obj : NULL;
+}
+
+inline JSObject*
+nsWrapperCache::GetExpandoFromSlot(JSObject *obj)
+{
+    NS_ASSERTION(xpc::dom::instanceIsProxy(obj),
+                 "Asking for an expando but this isn't a DOM proxy?");
+    const js::Value &v = js::GetProxyExtra(obj, xpc::dom::JSPROXYSLOT_EXPANDO);
+    return v.isUndefined() ? NULL : v.toObjectOrNull();
+}
+
+inline void
+nsWrapperCache::ClearWrapper()
+{
+    NS_ASSERTION(!PreservingWrapper(), "Clearing a preserved wrapper!");
+    JSObject *obj = GetJSObjectFromBits();
+    if (!obj) {
+        return;
+    }
+
+    JSObject *expando;
+    if (xpc::dom::instanceIsProxy(obj)) {
+        expando = GetExpandoFromSlot(obj);
+    }
+    else {
+        // If we support non-proxy dom binding objects then this should be:
+        //expando = js::GetSlot(obj, EXPANDO_SLOT);
+        expando = NULL;
+    }
+
+    SetWrapperBits(expando);
+}
+
+inline void
+nsWrapperCache::ClearWrapperIfProxy()
+{
+    if (!IsProxy()) {
+        return;
+    }
+
+    RemoveExpandoObject();
+
+    SetWrapperBits(NULL);
+}
+
+#endif /* nsWrapperCache_h___ */
--- a/js/src/xpconnect/src/dombindings.cpp
+++ b/js/src/xpconnect/src/dombindings.cpp
@@ -42,16 +42,18 @@
 #include "xpcquickstubs.h"
 #include "XPCWrapper.h"
 #include "WrapperFactory.h"
 
 #include "nsIDOMNode.h"
 
 #include "nsDOMClassInfo.h"
 #include "nsGlobalWindow.h"
+#include "jsiter.h"
+#include "nsWrapperCacheInlines.h"
 
 extern XPCNativeInterface* interfaces[];
 
 using namespace js;
 
 namespace xpc {
 namespace dom {
 
@@ -134,25 +136,25 @@ NodeList<T>::getNodeList(JSObject *obj)
     return static_cast<T *>(js::GetProxyPrivate(obj).toPrivate());
 }
 
 template<class T>
 uint32
 NodeList<T>::getProtoShape(JSObject *obj)
 {
     JS_ASSERT(js::IsProxy(obj) && js::GetProxyHandler(obj) == &NodeList<T>::instance);
-    return js::GetProxyExtra(obj, 0).toPrivateUint32();
+    return js::GetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE).toPrivateUint32();
 }
 
 template<class T>
 void
 NodeList<T>::setProtoShape(JSObject *obj, uint32 shape)
 {
     JS_ASSERT(js::IsProxy(obj) && js::GetProxyHandler(obj) == &NodeList<T>::instance);
-    js::SetProxyExtra(obj, 0, PrivateUint32Value(shape));
+    js::SetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE, PrivateUint32Value(shape));
 }
 
 template<class T>
 bool
 NodeList<T>::instanceIsNodeListObject(JSContext *cx, JSObject *obj, JSObject *callee)
 {
     if (XPCWrapper::IsSecurityWrapper(obj)) {
         if (callee && js::GetObjectGlobal(obj) == js::GetObjectGlobal(callee)) {
@@ -299,22 +301,39 @@ NodeList<T>::create(JSContext *cx, XPCWr
     NS_ADDREF(aNodeList);
     setProtoShape(obj, -1);
 
     aWrapperCache->SetWrapper(obj);
 
     return obj;
 }
 
+static JSObject *
+getExpandoObject(JSObject *obj)
+{
+    NS_ASSERTION(instanceIsProxy(obj), "expected a DOM proxy object");
+    Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
+    return v.isUndefined() ? NULL : v.toObjectOrNull();
+}
+
 template<class T>
 bool
 NodeList<T>::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, bool set,
                                       PropertyDescriptor *desc)
 {
-    // FIXME: expandos
+    JSObject *expando = getExpandoObject(proxy);
+    uintN flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED;
+    if (expando && !JS_GetPropertyDescriptorById(cx, expando, id, flags, desc))
+        return false;
+    if (desc->obj) {
+        // Pretend the property lives on the wrapper.
+        desc->obj = proxy;
+        return true;
+    }
+
     bool isNumber;
     int32 index = nsDOMClassInfo::GetArrayIndexFromId(cx, id, &isNumber);
     if (isNumber && index >= 0) {
         T *nodeList = getNodeList(proxy);
         nsIContent *result = nodeList->GetNodeAt(PRUint32(index));
         if (result) {
             jsval v;
             if (!WrapObject(cx, proxy, result, result, &v))
@@ -342,45 +361,99 @@ NodeList<T>::getPropertyDescriptor(JSCon
     if (desc->obj)
         return true;
     if (WrapperFactory::IsXrayWrapper(proxy))
         return resolveNativeName(cx, proxy, id, desc);
     return JS_GetPropertyDescriptorById(cx, js::GetObjectProto(proxy), id, JSRESOLVE_QUALIFIED,
                                         desc);
 }
 
+JSClass ExpandoClass = {
+    "DOM proxy binding expando object",
+    JSCLASS_HAS_PRIVATE,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub
+};
+
+template<class T>
+JSObject *
+NodeList<T>::ensureExpandoObject(JSContext *cx, JSObject *obj)
+{
+    NS_ASSERTION(instanceIsProxy(obj), "expected a DOM proxy object");
+    JSObject *expando = getExpandoObject(obj);
+    if (!expando) {
+        expando = JS_NewObjectWithGivenProto(cx, &ExpandoClass, nsnull,
+                                             js::GetObjectParent(obj));
+        if (!expando)
+            return NULL;
+
+        JSCompartment *compartment = js::GetObjectCompartment(obj);
+        xpc::CompartmentPrivate *priv =
+            static_cast<xpc::CompartmentPrivate *>(js_GetCompartmentPrivate(compartment));
+        if (!priv->RegisterDOMExpandoObject(expando))
+            return NULL;
+
+        js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
+        expando->setPrivate(js::GetProxyPrivate(obj).toPrivate());
+    }
+    return expando;
+}
+
 template<class T>
 bool
 NodeList<T>::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
                             PropertyDescriptor *desc)
 {
-    // FIXME: expandos
-    return true;
+    JSObject *expando = ensureExpandoObject(cx, proxy);
+    if (!expando)
+        return false;
+
+    return JS_DefinePropertyById(cx, expando, id, desc->value, desc->getter, desc->setter,
+                                 desc->attrs);
 }
 
 template<class T>
 bool
 NodeList<T>::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
-    // FIXME: expandos
+    JSObject *expando = getExpandoObject(proxy);
+    if (expando && !GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props))
+        return false;
+
     PRUint32 length;
     getNodeList(proxy)->GetLength(&length);
     JS_ASSERT(int32(length) >= 0);
     for (int32 i = 0; i < int32(length); ++i) {
         if (!props.append(INT_TO_JSID(i)))
             return false;
     }
     return true;
 }
 
 template<class T>
 bool
 NodeList<T>::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
-    // FIXME: expandos
+    JSBool b = true;
+
+    JSObject *expando;
+    if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = getExpandoObject(proxy))) {
+        jsval v;
+        if (!JS_DeletePropertyById2(cx, expando, id, &v) ||
+            !JS_ValueToBoolean(cx, v, &b)) {
+            return false;
+        }
+    }
+
+    *bp = !!b;
     return true;
 }
 
 template<class T>
 bool
 NodeList<T>::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     // FIXME: enumerate proto as well
@@ -394,42 +467,42 @@ NodeList<T>::fix(JSContext *cx, JSObject
     vp->setUndefined();
     return true;
 }
 
 template<class T>
 bool
 NodeList<T>::hasOwn(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
-    // FIXME: expandos
+    JSObject *expando = getExpandoObject(proxy);
+    if (expando) {
+        JSBool b = JS_TRUE;
+        JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
+        *bp = !!b;
+        if (!ok || *bp)
+            return ok;
+    }
+
     bool isNumber;
     int32 index = nsDOMClassInfo::GetArrayIndexFromId(cx, id, &isNumber);
     if (isNumber && index >= 0) {
         if (getNodeList(proxy)->GetNodeAt(PRUint32(index))) {
             *bp = true;
             return true;
         }
     }
     *bp = false;
     return true;
 }
 
 template<class T>
 bool
 NodeList<T>::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
-    if (!hasOwn(cx, proxy, id, bp))
-        return false;
-    if (*bp)
-        return true;
-    JSBool found;
-    if (!JS_HasPropertyById(cx, js::GetObjectProto(proxy), id, &found))
-        return false;
-    *bp = !!found;
-    return true;
+    return ProxyHandler::has(cx, proxy, id, bp);
 }
 
 template<class T>
 bool
 NodeList<T>::cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto)
 {
     JSPropertyDescriptor desc;
     if (!JS_GetPropertyDescriptorById(cx, proto, nsDOMClassInfo::sLength_id, JSRESOLVE_QUALIFIED, &desc))
@@ -501,17 +574,25 @@ NodeList<T>::resolveNativeName(JSContext
 
     return true;
 }
 
 template<class T>
 bool
 NodeList<T>::get(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, Value *vp)
 {
-    // FIXME: expandos
+    JSObject *expando = getExpandoObject(proxy);
+    if (expando) {
+        JSBool hasProp;
+        if (!JS_HasPropertyById(cx, expando, id, &hasProp))
+            return false;
+        if (hasProp)
+            return JS_GetPropertyById(cx, expando, id, vp);
+    }
+
     bool isNumber;
     int32 index = nsDOMClassInfo::GetArrayIndexFromId(cx, id, &isNumber);
     if (isNumber && index >= 0) {
         T *nodeList = getNodeList(proxy);
         nsIContent *result = nodeList->GetNodeAt(PRUint32(index));
         if (result)
             return WrapObject(cx, proxy, result, result, vp);
     }
@@ -538,28 +619,26 @@ NodeList<T>::get(JSContext *cx, JSObject
     }
 
     return JS_GetPropertyById(cx, proto, id, vp);
 }
 
 template<class T>
 bool
 NodeList<T>::set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
-              Value *vp)
+                 Value *vp)
 {
-    // FIXME: expandos
-    return true;
+    return ProxyHandler::set(cx, proxy, proxy, id, strict, vp);
 }
 
 template<class T>
 bool
 NodeList<T>::keys(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
-    // FIXME: expandos
-    return getOwnPropertyNames(cx, proxy, props);
+    return ProxyHandler::keys(cx, proxy, props);
 }
 
 template<class T>
 bool
 NodeList<T>::iterate(JSContext *cx, JSObject *proxy, uintN flags, Value *vp)
 {
     JS_ReportError(cx, "FIXME");
     return false;
@@ -587,17 +666,17 @@ NodeList<nsINodeList>::obj_toString(JSCo
     return JS_NewStringCopyZ(cx, "[object NodeList]");
 }
 
 template<class T>
 void
 NodeList<T>::finalize(JSContext *cx, JSObject *proxy)
 {
     T *nodeList = getNodeList(proxy);
-    nsWrapperCache* cache;
+    nsWrapperCache *cache;
     CallQueryInterface(nodeList, &cache);
     if (cache) {
         cache->ClearWrapper();
     }
     NS_RELEASE(nodeList);
 }
 
 }
--- a/js/src/xpconnect/src/dombindings.h
+++ b/js/src/xpconnect/src/dombindings.h
@@ -37,31 +37,24 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef dombindings_h
 #define dombindings_h
 
 #include "jsapi.h"
 #include "jsproxy.h"
+#include "xpcpublic.h"
 
 class nsINodeList;
 class nsIHTMLCollection;
 
 namespace xpc {
 namespace dom {
 
-extern int HandlerFamily;
-inline void* ProxyFamily() { return &HandlerFamily; }
-inline bool instanceIsDOMProxy(JSObject *obj)
-{
-    return js::IsProxy(obj) &&
-           js::GetProxyHandler(obj)->family() == ProxyFamily();
-}
-
 class NodeListBase : public js::ProxyHandler {
 public:
     NodeListBase() : js::ProxyHandler(ProxyFamily()) {}
 
     static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope,
                             nsINodeList *aNodeList);
     static JSObject *create(JSContext *cx, XPCWrappedNativeScope *scope,
                             nsIHTMLCollection *aHTMLCollection,
@@ -85,16 +78,18 @@ class NodeList : public NodeListBase {
 
     static Methods sProtoMethods[];
 
     static bool instanceIsNodeListObject(JSContext *cx, JSObject *obj, JSObject *callee);
     static JSObject *getPrototype(JSContext *cx, XPCWrappedNativeScope *scope);
 
     static T *getNodeList(JSObject *obj);
 
+    static JSObject *ensureExpandoObject(JSContext *cx, JSObject *obj);
+
     static uint32 getProtoShape(JSObject *obj);
     static void setProtoShape(JSObject *obj, uint32 shape);
 
     static JSBool length_getter(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
     static JSBool item(JSContext *cx, uintN argc, jsval *vp);
     static JSBool namedItem(JSContext *cx, uintN argc, jsval *vp);
 
     static bool cacheProtoShape(JSContext *cx, JSObject *proxy, JSObject *proto);
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -62,16 +62,17 @@
 #include "XrayWrapper.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 
 #include "jsdIDebuggerService.h"
 
 #include "xpcquickstubs.h"
 #include "dombindings.h"
+#include "nsWrapperCacheInlines.h"
 
 NS_IMPL_THREADSAFE_ISUPPORTS7(nsXPConnect,
                               nsIXPConnect,
                               nsISupportsWeakReference,
                               nsIThreadObserver,
                               nsIJSRuntimeService,
                               nsIJSContextStack,
                               nsIThreadJSContextStack,
@@ -903,17 +904,17 @@ nsXPConnect::Traverse(void *p, nsCycleCo
     // XXX This test does seem fragile, we should probably whitelist classes
     //     that do hold a strong reference, but that might not be possible.
     else if(clazz->flags & JSCLASS_HAS_PRIVATE &&
             clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
     {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)");
         cb.NoteXPCOMChild(static_cast<nsISupports*>(xpc_GetJSPrivate(obj)));
     }
-    else if(xpc::dom::instanceIsDOMProxy(obj))
+    else if(xpc::dom::instanceIsProxy(obj))
     {
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "js::GetProxyPrivate(obj)");
         nsISupports *identity =
             static_cast<nsISupports*>(js::GetProxyPrivate(obj).toPrivate());
         cb.NoteXPCOMChild(identity);
     }
 
     return NS_OK;
--- a/js/src/xpconnect/src/qsgen.py
+++ b/js/src/xpconnect/src/qsgen.py
@@ -1696,16 +1696,17 @@ stubTopTemplate = '''\
 #include "prtypes.h"
 #include "nsID.h"
 #include "%s"
 #include "nscore.h"
 #include "nsCOMPtr.h"
 #include "nsDependentString.h"
 #include "xpcprivate.h"  // for XPCCallContext
 #include "xpcquickstubs.h"
+#include "nsWrapperCacheInlines.h"
 #include "jsbuiltins.h"
 '''
 
 def writeStubFile(filename, headerFilename, conf, interfaces):
     print "Creating stub file", filename
     make_targets.append(filename)
 
     f = open(filename, 'w')
--- a/js/src/xpconnect/src/xpcconvert.cpp
+++ b/js/src/xpconnect/src/xpcconvert.cpp
@@ -49,16 +49,17 @@
 #include "XPCWrapper.h"
 #include "nsJSPrincipals.h"
 #include "nsWrapperCache.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 #include "nsJSUtils.h"
 
 #include "dombindings.h"
+#include "nsWrapperCacheInlines.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
 
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -255,16 +255,17 @@ ContextCallback(JSContext *cx, uintN ope
     }
     return JS_TRUE;
 }
 
 xpc::CompartmentPrivate::~CompartmentPrivate()
 {
     delete waiverWrapperMap;
     delete expandoMap;
+    delete domExpandoMap;
     MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
 }
 
 static JSBool
 CompartmentCallback(JSContext *cx, JSCompartment *compartment, uintN op)
 {
     JS_ASSERT(op == JSCOMPARTMENT_DESTROY);
 
@@ -423,22 +424,32 @@ TraceExpandos(XPCWrappedNative *wn, JSOb
 {
     if(wn->IsWrapperExpired())
         return PL_DHASH_REMOVE;
     JS_CALL_OBJECT_TRACER(static_cast<JSTracer *>(aClosure), expando, "expando object");
     return PL_DHASH_NEXT;
 }
 
 static PLDHashOperator
+TraceDOMExpandos(nsPtrHashKey<JSObject> *expando, void *aClosure)
+{
+    JS_CALL_OBJECT_TRACER(static_cast<JSTracer *>(aClosure), expando->GetKey(),
+                          "DOM expando object");
+    return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
 TraceCompartment(xpc::PtrAndPrincipalHashKey *aKey, JSCompartment *compartment, void *aClosure)
 {
     xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
         JS_GetCompartmentPrivate(static_cast<JSTracer *>(aClosure)->context, compartment);
     if (priv->expandoMap)
         priv->expandoMap->Enumerate(TraceExpandos, aClosure);
+    if (priv->domExpandoMap)
+        priv->domExpandoMap->EnumerateEntries(TraceDOMExpandos, aClosure);
     return PL_DHASH_NEXT;
 }
 
 void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc)
 {
     JSContext *iter = nsnull, *acx;
     while ((acx = JS_ContextIterator(GetJSRuntime(), &iter))) {
         JS_ASSERT(acx->hasRunOption(JSOPTION_UNROOTED_GLOBAL));
@@ -525,32 +536,42 @@ XPCJSRuntime::SuspectWrappedNative(JSCon
     // Only record objects that might be part of a cycle as roots, unless
     // the callback wants all traces (a debug feature).
     if(xpc_IsGrayGCThing(obj) || cb.WantAllTraces())
         cb.NoteRoot(nsIProgrammingLanguage::JAVASCRIPT, obj,
                     nsXPConnect::GetXPConnect());
 }
 
 static PLDHashOperator
-SuspectExpandos(XPCWrappedNative *wrapper, JSObject *&expando, void *arg)
+SuspectExpandos(XPCWrappedNative *wrapper, JSObject *expando, void *arg)
 {
     Closure* closure = static_cast<Closure*>(arg);
     XPCJSRuntime::SuspectWrappedNative(closure->cx, wrapper, *closure->cb);
 
     return PL_DHASH_NEXT;
 }
 
 static PLDHashOperator
+SuspectDOMExpandos(nsPtrHashKey<JSObject> *expando, void *arg)
+{
+    Closure *closure = static_cast<Closure*>(arg);
+    closure->cb->NoteXPCOMRoot(static_cast<nsISupports*>(expando->GetKey()->getPrivate()));
+    return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
 SuspectCompartment(xpc::PtrAndPrincipalHashKey *key, JSCompartment *compartment, void *arg)
 {
     Closure* closure = static_cast<Closure*>(arg);
     xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
         JS_GetCompartmentPrivate(closure->cx, compartment);
     if (priv->expandoMap)
-        priv->expandoMap->Enumerate(SuspectExpandos, arg);
+        priv->expandoMap->EnumerateRead(SuspectExpandos, arg);
+    if (priv->domExpandoMap)
+        priv->domExpandoMap->EnumerateEntries(SuspectDOMExpandos, arg);
     return PL_DHASH_NEXT;
 }
 
 void
 XPCJSRuntime::AddXPConnectRoots(JSContext* cx,
                                 nsCycleCollectionTraversalCallback &cb)
 {
     // For all JS objects that are held by native objects but aren't held
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -4425,42 +4425,45 @@ namespace xpc {
 struct CompartmentPrivate
 {
     CompartmentPrivate(PtrAndPrincipalHashKey *key, bool wantXrays, bool cycleCollectionEnabled)
         : key(key),
           ptr(nsnull),
           wantXrays(wantXrays),
           cycleCollectionEnabled(cycleCollectionEnabled),
           waiverWrapperMap(nsnull),
-          expandoMap(nsnull)
+          expandoMap(nsnull),
+          domExpandoMap(nsnull)
     {
         MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
     }
 
     CompartmentPrivate(nsISupports *ptr, bool wantXrays, bool cycleCollectionEnabled)
         : key(nsnull),
           ptr(ptr),
           wantXrays(wantXrays),
           cycleCollectionEnabled(cycleCollectionEnabled),
           waiverWrapperMap(nsnull),
-          expandoMap(nsnull)
+          expandoMap(nsnull),
+          domExpandoMap(nsnull)
     {
         MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
     }
 
     ~CompartmentPrivate();
 
     // NB: key and ptr are mutually exclusive.
     nsAutoPtr<PtrAndPrincipalHashKey> key;
     nsCOMPtr<nsISupports> ptr;
     bool wantXrays;
     bool cycleCollectionEnabled;
     JSObject2JSObjectMap *waiverWrapperMap;
     // NB: we don't want this map to hold a strong reference to the wrapper.
     nsDataHashtable<nsPtrHashKey<XPCWrappedNative>, JSObject *> *expandoMap;
+    nsTHashtable<nsPtrHashKey<JSObject> > *domExpandoMap;
     nsCString location;
 
     bool RegisterExpandoObject(XPCWrappedNative *wn, JSObject *expando) {
         if (!expandoMap) {
             expandoMap = new nsDataHashtable<nsPtrHashKey<XPCWrappedNative>, JSObject *>();
             if (!expandoMap->Init(8))
                 return false;
         }
@@ -4483,16 +4486,32 @@ struct CompartmentPrivate
      * This lookup clears the gray bit before handing out the JSObject which
      * means that the object is guaranteed to be kept alive past the next CC.
      */
     JSObject *LookupExpandoObject(XPCWrappedNative *wn) {
         JSObject *obj = LookupExpandoObjectPreserveColor(wn);
         xpc_UnmarkGrayObject(obj);
         return obj;
     }
+
+    bool RegisterDOMExpandoObject(JSObject *expando) {
+        if (!domExpandoMap) {
+            domExpandoMap = new nsTHashtable<nsPtrHashKey<JSObject> >();
+            if(!domExpandoMap->Init(8))
+            {
+                domExpandoMap = nsnull;
+                return false;
+            }
+        }
+        return domExpandoMap->PutEntry(expando);
+    }
+    void RemoveDOMExpandoObject(JSObject *expando) {
+        if(domExpandoMap)
+            domExpandoMap->RemoveEntry(expando);
+    }
 };
 
 inline bool
 CompartmentParticipatesInCycleCollection(JSContext *cx, JSCompartment *compartment)
 {
     CompartmentPrivate *priv =
         static_cast<CompartmentPrivate *>(JS_GetCompartmentPrivate(cx, compartment));
     NS_ASSERTION(priv, "This should never be null!");
--- a/js/src/xpconnect/src/xpcpublic.h
+++ b/js/src/xpconnect/src/xpcpublic.h
@@ -40,16 +40,17 @@
 #ifndef xpcpublic_h
 #define xpcpublic_h
 
 #include "jsapi.h"
 #include "jsclass.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jspubtd.h"
+#include "jsproxy.h"
 
 #include "nsISupports.h"
 #include "nsIPrincipal.h"
 #include "nsWrapperCache.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 
 class nsIPrincipal;
@@ -178,24 +179,16 @@ xpc_UnmarkGrayObjectRecursive(JSObject* 
 // be reached through it.
 inline void
 xpc_UnmarkGrayObject(JSObject *obj)
 {
     if(obj && xpc_IsGrayGCThing(obj))
         xpc_UnmarkGrayObjectRecursive(obj);
 }
 
-inline JSObject*
-nsWrapperCache::GetWrapper() const
-{
-  JSObject* obj = GetWrapperPreserveColor();
-  xpc_UnmarkGrayObject(obj);
-  return obj;
-}
-
 class nsIMemoryMultiReporterCallback;
 
 namespace mozilla {
 namespace xpconnect {
 namespace memory {
 
 struct CompartmentStats
 {
@@ -265,9 +258,33 @@ void
 ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix,
                      nsIMemoryMultiReporterCallback *callback,
                      nsISupports *closure);
 
 } // namespace memory
 } // namespace xpconnect
 } // namespace mozilla
 
+namespace xpc {
+namespace dom {
+
+extern int HandlerFamily;
+inline void* ProxyFamily() { return &HandlerFamily; }
+inline bool instanceIsProxy(JSObject *obj)
+{
+    return js::IsProxy(obj) &&
+           js::GetProxyHandler(obj)->family() == ProxyFamily();
+}
+extern JSClass ExpandoClass;
+inline bool isExpandoObject(JSObject *obj)
+{
+    return js::GetObjectJSClass(obj) == &ExpandoClass;
+}
+
+enum {
+    JSPROXYSLOT_PROTOSHAPE = 0,
+    JSPROXYSLOT_EXPANDO = 1
+};
+
+}
+}
+
 #endif
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -39,17 +39,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* Wrapper object for reflecting native xpcom objects into JavaScript. */
 
 #include "xpcprivate.h"
 #include "nsCRT.h"
 #include "XPCWrapper.h"
-#include "nsWrapperCache.h"
+#include "nsWrapperCacheInlines.h"
 #include "xpclog.h"
 #include "jstl.h"
 #include "nsINode.h"
 #include "xpcquickstubs.h"
 #include "jsproxy.h"
 #include "AccessCheck.h"
 #include "WrapperFactory.h"
 #include "dombindings.h"
--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
@@ -38,16 +38,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
+#include "nsWrapperCacheInlines.h"
 
 /***************************************************************************/
 
 // All of the exceptions thrown into JS from this file go through here.
 // That makes this a nice place to set a breakpoint.
 
 static JSBool Throw(uintN errNum, JSContext* cx)
 {
--- a/js/src/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/src/xpconnect/wrappers/WrapperFactory.cpp
@@ -251,17 +251,17 @@ GetWrappedNative(JSContext *cx, JSObject
 
 static bool
 CanXray(JSObject *obj, bool *proxy)
 {
     if (IS_WN_WRAPPER(obj) || js::GetObjectClass(obj)->ext.innerObject) {
         *proxy = false;
         return true;
     }
-    return (*proxy = dom::instanceIsDOMProxy(obj));
+    return (*proxy = dom::instanceIsProxy(obj));
 }
 
 JSObject *
 WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
                        uintN flags)
 {
     NS_ASSERTION(!IsWrapper(obj) ||
                  GetProxyHandler(obj) == &WaiveXrayWrapperWrapper ||