Back out 0f5f58d5d410:8db4d719f5f8 (bug 973780) for debug WinXP mochitest-3 assertions
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 20 Feb 2014 20:46:50 -0800
changeset 170172 a917689cc392eefa3a2accd9853da14ebacbb437
parent 170171 1fbf700a1ba67d552b271bbeac13a24a7f61bbb5
child 170173 2d935f3e9587681e81bc32feabfee0e8044021fb
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
bugs973780
milestone30.0a1
backs out0f5f58d5d410c8d39da35cdcaa10a081a237e287
Back out 0f5f58d5d410:8db4d719f5f8 (bug 973780) for debug WinXP mochitest-3 assertions CLOSED TREE
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/DOMJSProxyHandler.h
dom/bindings/JSSlots.h
js/public/WeakMapPtr.h
js/src/jsapi-tests/testIntTypesABI.cpp
js/src/jsweakmap.h
js/src/moz.build
js/src/vm/WeakMapPtr.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1507,16 +1507,49 @@ AppendNamedPropertyIds(JSContext* cx, JS
         return false;
       }
     }
   }
 
   return true;
 }
 
+JSObject*
+GetXrayExpandoChain(JSObject* obj)
+{
+  const js::Class* clasp = js::GetObjectClass(obj);
+  JS::Value v;
+  if (IsNonProxyDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
+    v = js::GetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT);
+  } else if (clasp->isProxy()) {
+    MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
+    v = js::GetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO);
+  } else {
+    MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
+    v = js::GetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT);
+  }
+  return v.isUndefined() ? nullptr : &v.toObject();
+}
+
+void
+SetXrayExpandoChain(JSObject* obj, JSObject* chain)
+{
+  JS::Value v = chain ? JS::ObjectValue(*chain) : JSVAL_VOID;
+  const js::Class* clasp = js::GetObjectClass(obj);
+  if (IsNonProxyDOMClass(clasp) || IsDOMIfaceAndProtoClass(clasp)) {
+    js::SetReservedSlot(obj, DOM_XRAY_EXPANDO_SLOT, v);
+  } else if (clasp->isProxy()) {
+    MOZ_ASSERT(js::GetProxyHandler(obj)->family() == ProxyFamily());
+    js::SetProxyExtra(obj, JSPROXYSLOT_XRAY_EXPANDO, v);
+  } else {
+    MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
+    js::SetFunctionNativeReserved(obj, CONSTRUCTOR_XRAY_EXPANDO_SLOT, v);
+  }
+}
+
 bool
 DictionaryBase::ParseJSON(JSContext* aCx,
                           const nsAString& aJSON,
                           JS::MutableHandle<JS::Value> aVal)
 {
   if (aJSON.IsEmpty()) {
     return true;
   }
@@ -1719,16 +1752,17 @@ ReparentWrapper(JSContext* aCx, JS::Hand
         return NS_ERROR_FAILURE;
       }
     } else {
       propertyHolder = nullptr;
     }
 
     // Expandos from other compartments are attached to the target JS object.
     // Copy them over, and let the old ones die a natural death.
+    SetXrayExpandoChain(newobj, nullptr);
     if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
       return NS_ERROR_FAILURE;
     }
 
     // We've set up |newobj|, so we make it own the native by nulling
     // out the reserved slot of |obj|.
     //
     // NB: It's important to do this _after_ copying the properties to
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1950,22 +1950,24 @@ XrayEnumerateProperties(JSContext* cx, J
 
 extern NativePropertyHooks sWorkerNativePropertyHooks;
 
 // We use one constructor JSNative to represent all DOM interface objects (so
 // we can easily detect when we need to wrap them in an Xray wrapper). We store
 // the real JSNative in the mNative member of a JSNativeHolder in the
 // CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
 // specific interface object. We also store the NativeProperties in the
-// JSNativeHolder.
+// JSNativeHolder. The CONSTRUCTOR_XRAY_EXPANDO_SLOT is used to store the
+// expando chain of the Xray for the interface object.
 // Note that some interface objects are not yet a JSFunction but a normal
 // JSObject with a DOMJSClass, those do not use these slots.
 
 enum {
-  CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0
+  CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0,
+  CONSTRUCTOR_XRAY_EXPANDO_SLOT
 };
 
 bool
 Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 inline bool
 UseDOMXray(JSObject* obj)
 {
@@ -2000,16 +2002,21 @@ Take(nsAutoPtr<T>& smartPtr, T* ptr)
   smartPtr = ptr;
 }
 
 inline void
 MustInheritFromNonRefcountedDOMObject(NonRefcountedDOMObject*)
 {
 }
 
+// Set the chain of expando objects for various consumers of the given object.
+// For Paris Bindings only. See the relevant infrastructure in XrayWrapper.cpp.
+JSObject* GetXrayExpandoChain(JSObject *obj);
+void SetXrayExpandoChain(JSObject *obj, JSObject *chain);
+
 /**
  * This creates a JSString containing the value that the toString function for
  * obj should create according to the WebIDL specification, ignoring any
  * modifications by script. The value is prefixed with pre and postfixed with
  * post, unless this is called for an object that has a stringifier. It is
  * specifically for use by Xray code.
  *
  * wrapper is the Xray JS object.
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -16,17 +16,18 @@
 #define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT
 
 namespace mozilla {
 namespace dom {
 
 class DOMClass;
 
 enum {
-  JSPROXYSLOT_EXPANDO = 0
+  JSPROXYSLOT_EXPANDO = 0,
+  JSPROXYSLOT_XRAY_EXPANDO
 };
 
 template<typename T> struct Prefable;
 
 // This variable exists solely to provide a unique address for use as an identifier.
 extern const char HandlerFamily;
 inline const void* ProxyFamily() { return &HandlerFamily; }
 
--- a/dom/bindings/JSSlots.h
+++ b/dom/bindings/JSSlots.h
@@ -9,33 +9,42 @@
  */
 #ifndef mozilla_dom_DOMSlots_h
 #define mozilla_dom_DOMSlots_h
 
 // We use slot 0 for holding the raw object.  This is safe for both
 // globals and non-globals.
 #define DOM_OBJECT_SLOT 0
 
+// We use slot 1 for holding the expando object. This is not safe for globals
+// until bug 760095 is fixed, so that bug blocks converting Window to new
+// bindings.
+#define DOM_XRAY_EXPANDO_SLOT 1
+
 // We use slot 2 for holding either a JS::ObjectValue which points to the cached
 // SOW or JS::UndefinedValue if this class doesn't need SOWs. This is not safe
 // for globals until bug 760095 is fixed, so that bug blocks converting Window
 // to new bindings.
-#define DOM_OBJECT_SLOT_SOW 1
+#define DOM_OBJECT_SLOT_SOW 2
 
 // The total number of slots non-proxy DOM objects use by default.
 // Specific objects may have more for storing cached values.
-#define DOM_INSTANCE_RESERVED_SLOTS 2
+#define DOM_INSTANCE_RESERVED_SLOTS 3
 
 // NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
 // LSetDOMProperty. Those constants need to be changed accordingly if this value
 // changes.
 #define DOM_PROTO_INSTANCE_CLASS_SLOT 0
 
 // Interface objects store a number of reserved slots equal to
 // DOM_INTERFACE_SLOTS_BASE + number of named constructors.
-#define DOM_INTERFACE_SLOTS_BASE 0
+#define DOM_INTERFACE_SLOTS_BASE (DOM_XRAY_EXPANDO_SLOT + 1)
 
 // Interface prototype objects store a number of reserved slots equal to
 // DOM_INTERFACE_PROTO_SLOTS_BASE or DOM_INTERFACE_PROTO_SLOTS_BASE + 1 if a
 // slot for the unforgeable holder is needed.
-#define DOM_INTERFACE_PROTO_SLOTS_BASE (DOM_PROTO_INSTANCE_CLASS_SLOT + 1)
+#define DOM_INTERFACE_PROTO_SLOTS_BASE (DOM_XRAY_EXPANDO_SLOT + 1)
+
+static_assert(DOM_PROTO_INSTANCE_CLASS_SLOT != DOM_XRAY_EXPANDO_SLOT,
+              "Interface prototype object use both of these, so they must "
+              "not be the same slot.");
 
 #endif /* mozilla_dom_DOMSlots_h */
deleted file mode 100644
--- a/js/public/WeakMapPtr.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef js_WeakMapPtr_h
-#define js_WeakMapPtr_h
-
-#include "jspubtd.h"
-
-#include "js/TypeDecls.h"
-
-namespace JS {
-
-// A wrapper around the internal C++ representation of SpiderMonkey WeakMaps,
-// usable outside the engine.
-//
-// The supported template specializations are enumerated in WeakMapPtr.cpp. If
-// you want to use this class for a different key/value combination, add it to
-// the list and the compiler will generate the relevant machinery.
-template <typename K, typename V>
-class JS_PUBLIC_API(WeakMapPtr)
-{
-  public:
-    WeakMapPtr() : ptr(nullptr) {};
-    bool init(JSContext *cx);
-    bool initialized() { return ptr != nullptr; };
-    void destroy();
-    virtual ~WeakMapPtr() { MOZ_ASSERT(!initialized()); }
-    void trace(JSTracer *tracer);
-
-    V lookup(const K &key);
-    bool put(const K &key, const V &value);
-
-  private:
-    void *ptr;
-
-    // WeakMapPtr is neither copyable nor assignable.
-    WeakMapPtr(const WeakMapPtr &wmp) MOZ_DELETE;
-    WeakMapPtr &operator=(const WeakMapPtr &wmp) MOZ_DELETE;
-};
-
-} /* namespace JS */
-
-#endif  /* js_WeakMapPtr_h */
--- a/js/src/jsapi-tests/testIntTypesABI.cpp
+++ b/js/src/jsapi-tests/testIntTypesABI.cpp
@@ -32,17 +32,16 @@
 #include "js/RootingAPI.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/Tracer.h"
 #include "js/TypeDecls.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
-#include "js/WeakMapPtr.h"
 #include "jsapi-tests/tests.h"
 
 /*
  * Verify that our public (and intended to be public, versus being that way
  * because we haven't made them private yet) headers don't define
  * {u,}int{8,16,32,64} or JS{Ui,I}nt{8,16,32,64} types.  If any do, they will
  * assuredly conflict with a corresponding typedef below mapping to a *struct*.
  *
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -80,18 +80,17 @@ class WeakMapBase {
 
     // Remove entries whose keys are dead from all weak maps in a compartment marked as
     // live in this garbage collection.
     static void sweepCompartment(JSCompartment *c);
 
     // Trace all delayed weak map bindings. Used by the cycle collector.
     static void traceAllMappings(WeakMapTracer *tracer);
 
-    bool isInList() { return next != WeakMapNotInList; }
-    void check() { JS_ASSERT(!isInList()); }
+    void check() { JS_ASSERT(next == WeakMapNotInList); }
 
     // Remove everything from the weak map list for a compartment.
     static void resetCompartmentWeakMapList(JSCompartment *c);
 
     // Save the live weak map list for a compartment, appending the data to a vector.
     static bool saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector);
 
     // Restore live weak map lists for multiple compartments from a vector.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -83,17 +83,16 @@ EXPORTS.js += [
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
     '../public/Tracer.h',
     '../public/TypeDecls.h',
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
-    '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
     'assembler/jit/ExecutableAllocator.cpp',
     'builtin/Eval.cpp',
     'builtin/Intl.cpp',
     'builtin/MapObject.cpp',
     'builtin/Object.cpp',
@@ -185,17 +184,16 @@ UNIFIED_SOURCES += [
     'vm/Stack.cpp',
     'vm/String.cpp',
     'vm/StringBuffer.cpp',
     'vm/StructuredClone.cpp',
     'vm/ThreadPool.cpp',
     'vm/TypedArrayObject.cpp',
     'vm/Unicode.cpp',
     'vm/Value.cpp',
-    'vm/WeakMapPtr.cpp',
     'vm/Xdr.cpp',
     'yarr/PageBlock.cpp',
     'yarr/YarrCanonicalizeUCS2.cpp',
     'yarr/YarrInterpreter.cpp',
     'yarr/YarrPattern.cpp',
     'yarr/YarrSyntaxChecker.cpp',
 ]
 
deleted file mode 100644
--- a/js/src/vm/WeakMapPtr.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "js/WeakMapPtr.h"
-
-#include "jsweakmap.h"
-
-//
-// Machinery for the externally-linkable JS::WeakMapPtr, which wraps js::WeakMap
-// for a few public data types.
-//
-
-using namespace js;
-
-namespace {
-
-template<typename T>
-struct DataType
-{
-};
-
-template<>
-struct DataType<JSObject*>
-{
-    typedef EncapsulatedPtrObject Encapsulated;
-    static JSObject *NullValue() { return nullptr; }
-};
-
-template<>
-struct DataType<JS::Value>
-{
-    typedef EncapsulatedValue Encapsulated;
-    static JS::Value NullValue() { return JS::UndefinedValue(); }
-};
-
-template <typename K, typename V>
-struct Utils
-{
-    typedef typename DataType<K>::Encapsulated KeyType;
-    typedef typename DataType<V>::Encapsulated ValueType;
-    typedef WeakMap<KeyType, ValueType> Type;
-    typedef Type* PtrType;
-    static PtrType cast(void *ptr) { return static_cast<PtrType>(ptr); }
-};
-
-} /* namespace */
-
-template <typename K, typename V>
-void
-JS::WeakMapPtr<K, V>::destroy()
-{
-    MOZ_ASSERT(initialized());
-    auto map = Utils<K, V>::cast(ptr);
-    // If this destruction happens mid-GC, we might be in the compartment's list
-    // of known live weakmaps. If we are, remove ourselves before deleting.
-    if (map->isInList())
-        WeakMapBase::removeWeakMapFromList(map);
-    map->check();
-    js_delete(map);
-    ptr = nullptr;
-}
-
-template <typename K, typename V>
-bool
-JS::WeakMapPtr<K, V>::init(JSContext *cx)
-{
-    MOZ_ASSERT(!initialized());
-    typename Utils<K, V>::PtrType map = cx->runtime()->new_<typename Utils<K,V>::Type>(cx);
-    if (!map || !map->init())
-        return false;
-    ptr = map;
-    return true;
-}
-
-template <typename K, typename V>
-void
-JS::WeakMapPtr<K, V>::trace(JSTracer *trc)
-{
-    MOZ_ASSERT(initialized());
-    return Utils<K, V>::cast(ptr)->trace(trc);
-}
-
-template <typename K, typename V>
-V
-JS::WeakMapPtr<K, V>::lookup(const K &key)
-{
-    MOZ_ASSERT(initialized());
-    typename Utils<K, V>::Type::Ptr result = Utils<K, V>::cast(ptr)->lookup(key);
-    if (!result)
-        return DataType<V>::NullValue();
-    return result->value();
-}
-
-template <typename K, typename V>
-bool
-JS::WeakMapPtr<K, V>::put(const K &key, const V &value)
-{
-    MOZ_ASSERT(initialized());
-    return Utils<K, V>::cast(ptr)->put(key, value);
-}
-
-//
-// Supported specializations of JS::WeakMap:
-//
-
-template class JS::WeakMapPtr<JSObject*, JSObject*>;
-
-#ifdef DEBUG
-// Nobody's using this at the moment, but we want to make sure it compiles.
-template class JS::WeakMapPtr<JSObject*, JS::Value>;
-#endif
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -854,16 +854,20 @@ XPCWrappedNative::Init(HandleObject pare
     return FinishInit();
 }
 
 bool
 XPCWrappedNative::FinishInit()
 {
     AutoJSContext cx;
 
+    // For all WNs, we want to make sure that the expando chain slot starts out
+    // as null.
+    JS_SetReservedSlot(mFlatJSObject, WN_XRAYEXPANDOCHAIN_SLOT, JSVAL_NULL);
+
     // This reference will be released when mFlatJSObject is finalized.
     // Since this reference will push the refcount to 2 it will also root
     // mFlatJSObject;
     MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value");
     NS_ADDREF(this);
 
     if (mScriptableInfo && mScriptableInfo->GetFlags().WantCreate() &&
         NS_FAILED(mScriptableInfo->GetCallback()->Create(this, cx,
@@ -1177,16 +1181,17 @@ XPCWrappedNative::ReparentWrapperIfFound
                                                         aNewParent);
             if (!propertyHolder)
                 return NS_ERROR_OUT_OF_MEMORY;
             if (!JS_CopyPropertiesFrom(cx, propertyHolder, flat))
                 return NS_ERROR_FAILURE;
 
             // Expandos from other compartments are attached to the target JS object.
             // Copy them over, and let the old ones die a natural death.
+            SetWNExpandoChain(newobj, nullptr);
             if (!XrayUtils::CloneExpandoChain(cx, newobj, flat))
                 return NS_ERROR_FAILURE;
 
             // We've set up |newobj|, so we make it own the WN by nulling out
             // the private of |flat|.
             //
             // NB: It's important to do this _after_ copying the properties to
             // propertyHolder. Otherwise, an object with |foo.x === foo| will
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -297,19 +297,16 @@ XPCWrappedNativeScope::~XPCWrappedNative
     // with the scope but just in case.
     if (mComponents)
         mComponents->mScope = nullptr;
 
     // XXX we should assert that we are dead or that xpconnect has shutdown
     // XXX might not want to do this at xpconnect shutdown time???
     mComponents = nullptr;
 
-    if (mXrayExpandos.initialized())
-        mXrayExpandos.destroy();
-
     JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
     mXBLScope.finalize(rt);
     mGlobalJSObject.finalize(rt);
 }
 
 static PLDHashOperator
 WrappedNativeJSGCThingTracer(PLDHashTable *table, PLDHashEntryHdr *hdr,
                              uint32_t number, void *arg)
@@ -597,37 +594,16 @@ WNProtoRemover(PLDHashTable *table, PLDH
 
 void
 XPCWrappedNativeScope::RemoveWrappedNativeProtos()
 {
     mWrappedNativeProtoMap->Enumerate(WNProtoRemover,
                                       GetRuntime()->GetDetachedWrappedNativeProtoMap());
 }
 
-JSObject *
-XPCWrappedNativeScope::GetExpandoChain(JSObject *target)
-{
-    MOZ_ASSERT(GetObjectScope(target) == this);
-    if (!mXrayExpandos.initialized())
-        return nullptr;
-    return mXrayExpandos.lookup(target);
-}
-
-bool
-XPCWrappedNativeScope::SetExpandoChain(JSContext *cx, HandleObject target,
-                                       HandleObject chain)
-{
-    MOZ_ASSERT(GetObjectScope(target) == this);
-    MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
-    MOZ_ASSERT_IF(chain, GetObjectScope(chain) == this);
-    if (!mXrayExpandos.initialized() && !mXrayExpandos.init(cx))
-        return false;
-    return mXrayExpandos.put(target, chain);
-}
-
 /***************************************************************************/
 
 // static
 void
 XPCWrappedNativeScope::DebugDumpAllScopes(int16_t depth)
 {
 #ifdef DEBUG
     depth-- ;
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -85,17 +85,16 @@
 
 #include <math.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "xpcpublic.h"
 #include "js/Tracer.h"
-#include "js/WeakMapPtr.h"
 #include "pldhash.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsISupports.h"
 #include "nsIServiceManager.h"
@@ -208,32 +207,51 @@ extern const char XPC_XPCONNECT_CONTRACT
         result = (char*) nsMemory::Clone(src,                                 \
                                          sizeof(char)*(strlen(src)+1));       \
     else                                                                      \
         result = nullptr;                                                      \
     *dest = result;                                                           \
     return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY
 
 
-#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS )
+#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \
+                       JSCLASS_HAS_RESERVED_SLOTS(1))
 
 #define INVALID_OBJECT ((JSObject *)1)
 
+// NB: This slot isn't actually reserved for us on globals, because SpiderMonkey
+// uses the first N slots on globals internally. The fact that we use it for
+// wrapped global objects is totally broken. But due to a happy coincidence, the
+// JS engine never uses that slot. This still needs fixing though. See bug 760095.
+#define WN_XRAYEXPANDOCHAIN_SLOT 0
+
 // If IS_WN_CLASS for the JSClass of an object is true, the object is a
 // wrappednative wrapper, holding the XPCWrappedNative in its private slot.
 static inline bool IS_WN_CLASS(const js::Class* clazz)
 {
     return clazz->ext.isWrappedNative;
 }
 
 static inline bool IS_WN_REFLECTOR(JSObject *obj)
 {
     return IS_WN_CLASS(js::GetObjectClass(obj));
 }
 
+inline void SetWNExpandoChain(JSObject *obj, JSObject *chain)
+{
+    MOZ_ASSERT(IS_WN_REFLECTOR(obj));
+    JS_SetReservedSlot(obj, WN_XRAYEXPANDOCHAIN_SLOT, JS::ObjectOrNullValue(chain));
+}
+
+inline JSObject* GetWNExpandoChain(JSObject *obj)
+{
+    MOZ_ASSERT(IS_WN_REFLECTOR(obj));
+    return JS_GetReservedSlot(obj, WN_XRAYEXPANDOCHAIN_SLOT).toObjectOrNull();
+}
+
 /***************************************************************************
 ****************************************************************************
 *
 * Core runtime and context classes...
 *
 ****************************************************************************
 ***************************************************************************/
 
@@ -1014,37 +1032,29 @@ public:
     GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject;}
 
     nsIPrincipal*
     GetPrincipal() const {
         JSCompartment *c = js::GetObjectCompartment(mGlobalJSObject);
         return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
     }
 
-    JSObject*
-    GetExpandoChain(JSObject *target);
-
-    bool
-    SetExpandoChain(JSContext *cx, JS::HandleObject target, JS::HandleObject chain);
-
     void RemoveWrappedNativeProtos();
 
     static void
     SystemIsBeingShutDown();
 
     static void
     TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt);
 
     void TraceSelf(JSTracer *trc) {
         MOZ_ASSERT(mGlobalJSObject);
         mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
         if (mXBLScope)
             mXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
-        if (mXrayExpandos.initialized())
-            mXrayExpandos.trace(trc);
     }
 
     static void
     SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback &cb);
 
     static void
     StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt);
 
@@ -1154,18 +1164,16 @@ private:
     // EnsureXBLScope() decides whether it needs to be created or not.
     // This reference is wrapped into the compartment of mGlobalJSObject.
     JS::ObjectPtr                    mXBLScope;
 
     XPCContext*                      mContext;
 
     nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;
 
-    JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
-
     bool mIsXBLScope;
 
     // For remote XUL domains, we run all XBL in the content scope for compat
     // reasons (though we sometimes pref this off for automation). We separately
     // track the result of this decision (mAllowXBLScope), from the decision
     // of whether to actually _use_ an XBL scope (mUseXBLScope), which depends
     // on the type of global and whether the compartment is system principal
     // or not.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -164,23 +164,18 @@ public:
                                HandleObject consumer);
     JSObject* ensureExpandoObject(JSContext *cx, HandleObject wrapper,
                                   HandleObject target);
 
     JSObject* getHolder(JSObject *wrapper);
     JSObject* ensureHolder(JSContext *cx, HandleObject wrapper);
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0;
 
-    JSObject* getExpandoChain(JSObject *obj) {
-      return GetObjectScope(obj)->GetExpandoChain(obj);
-    }
-
-    bool setExpandoChain(JSContext *cx, HandleObject obj, HandleObject chain) {
-      return GetObjectScope(obj)->SetExpandoChain(cx, obj, chain);
-    }
+    virtual JSObject* getExpandoChain(JSObject *obj) = 0;
+    virtual void setExpandoChain(JSObject *obj, JSObject *chain) = 0;
     bool cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src);
 
 private:
     bool expandoObjectMatchesConsumer(JSContext *cx, HandleObject expandoObject,
                                       nsIPrincipal *consumerOrigin,
                                       HandleObject exclusiveGlobal);
     JSObject* getExpandoObjectInternal(JSContext *cx, HandleObject target,
                                        nsIPrincipal *origin,
@@ -221,16 +216,22 @@ public:
         return XPCWrappedNative::Get(getTargetObject(wrapper));
     }
 
     virtual void preserveWrapper(JSObject *target);
 
     typedef ResolvingId ResolvingIdImpl;
 
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper);
+    virtual JSObject* getExpandoChain(JSObject *obj) {
+        return GetWNExpandoChain(obj);
+    }
+    virtual void setExpandoChain(JSObject *obj, JSObject *chain) {
+        SetWNExpandoChain(obj, chain);
+    }
 
     static XPCWrappedNativeXrayTraits singleton;
 };
 
 class DOMXrayTraits : public XrayTraits
 {
 public:
     static const XrayType Type = XrayForDOMObject;
@@ -257,16 +258,23 @@ public:
     }
 
     typedef ResolvingIdDummy ResolvingIdImpl;
 
     virtual void preserveWrapper(JSObject *target);
 
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper);
 
+    virtual JSObject* getExpandoChain(JSObject *obj) {
+        return mozilla::dom::GetXrayExpandoChain(obj);
+    }
+    virtual void setExpandoChain(JSObject *obj, JSObject *chain) {
+        mozilla::dom::SetXrayExpandoChain(obj, chain);
+    }
+
     static DOMXrayTraits singleton;
 };
 
 XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton;
 DOMXrayTraits DOMXrayTraits::singleton;
 
 XrayTraits*
 GetXrayTraits(JSObject *obj)
@@ -427,17 +435,17 @@ XrayTraits::attachExpandoObject(JSContex
     // the wrapper. This keeps our expandos alive even if the Xray wrapper gets
     // collected.
     RootedObject chain(cx, getExpandoChain(target));
     if (!chain)
         preserveWrapper(target);
 
     // Insert it at the front of the chain.
     JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, OBJECT_TO_JSVAL(chain));
-    setExpandoChain(cx, target, expandoObject);
+    setExpandoChain(target, expandoObject);
 
     return expandoObject;
 }
 
 JSObject *
 XrayTraits::ensureExpandoObject(JSContext *cx, HandleObject wrapper,
                                 HandleObject target)
 {