Bug 787070 - Expandos on the xray of DOM prototypes should have effect on xrays of DOM nodes, make Xrays walk the prototype chain when resolving DOM properties. r=bholley.
authorPeter Van der Beken <peterv@propagandism.org>
Mon, 15 Sep 2014 16:51:40 +0200
changeset 207658 73f22a0e55cb
parent 207657 194b0d93d32a
child 207659 7efcdcec493b
push id27564
push userryanvm@gmail.com
push dateMon, 29 Sep 2014 18:57:04 +0000
treeherdermozilla-central@ce9a0b34225e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs787070
milestone35.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 787070 - Expandos on the xray of DOM prototypes should have effect on xrays of DOM nodes, make Xrays walk the prototype chain when resolving DOM properties. r=bholley. * * * Bug 787070 - Expandos on the xray of DOM prototypes should have effect on xrays of DOM nodes, remove obsolete code. r=bholley.
dom/base/WindowNamedPropertiesHandler.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/DOMJSClass.h
dom/tests/mochitest/chrome/test_sandbox_bindings.xul
js/xpconnect/wrappers/XrayWrapper.cpp
js/xpconnect/wrappers/XrayWrapper.h
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -245,17 +245,17 @@ const NativePropertyHooks sWindowNamedPr
   prototypes::id::_ID_Count,
   constructors::id::_ID_Count,
   nullptr
 } };
 
 static const DOMIfaceAndProtoJSClass WindowNamedPropertiesClass = {
   PROXY_CLASS_DEF("WindowProperties",
                   DOM_INTERFACE_PROTO_SLOTS_BASE, /* extra slots */
-                  0),
+                  JSCLASS_IS_DOMIFACEANDPROTOJSCLASS),
   eNamedPropertiesObject,
   sWindowNamedPropertiesNativePropertyHooks,
   "[object WindowProperties]",
   prototypes::id::_ID_Count,
   0,
   EventTargetBinding::GetProtoObject
 };
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -31,16 +31,17 @@
 #include "XPCQuickStubs.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "prprf.h"
 
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/DOMError.h"
 #include "mozilla/dom/DOMErrorBinding.h"
+#include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "WorkerPrivate.h"
 #include "nsDOMClassInfo.h"
@@ -907,24 +908,24 @@ ThrowingConstructor(JSContext* cx, unsig
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
 {
   return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
 }
 
 inline const NativePropertyHooks*
 GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
-                       DOMObjectType& type, bool& isGlobal)
+                       DOMObjectType& type)
 {
   const js::Class* clasp = js::GetObjectClass(obj);
-  isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0;
 
   const DOMJSClass* domClass = GetDOMClass(clasp);
   if (domClass) {
-    type = eInstance;
+    bool isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0;
+    type = isGlobal ? eGlobalInstance : eInstance;
     return domClass->mNativeHooks;
   }
 
   if (JS_ObjectIsFunction(cx, obj)) {
     MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
     type = eInterface;
     const JS::Value& v =
       js::GetFunctionNativeReserved(obj,
@@ -937,24 +938,16 @@ GetNativePropertyHooks(JSContext *cx, JS
   MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
   const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
     DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
   type = ifaceAndProtoJSClass->mType;
   return ifaceAndProtoJSClass->mNativeHooks;
 }
 
 static bool
-XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
-                          const NativePropertyHooks* nativePropertyHooks,
-                          DOMObjectType type, JS::Handle<JSObject*> obj,
-                          JS::Handle<jsid> id,
-                          JS::MutableHandle<JSPropertyDescriptor> desc,
-                          bool& cacheOnHolder);
-
-static bool
 XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
                      JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                      const Prefable<const JSPropertySpec>* attributes, jsid* attributeIds,
                      const JSPropertySpec* attributeSpecs, JS::MutableHandle<JSPropertyDescriptor> desc,
                      bool& cacheOnHolder)
 {
   for (; attributes->specs; ++attributes) {
     if (attributes->isEnabled(cx, obj)) {
@@ -1049,17 +1042,17 @@ XrayResolveMethod(JSContext* cx, JS::Han
     }
   }
   return true;
 }
 
 // Try to resolve a property as an unforgeable property from the given
 // NativeProperties, if it's there.  nativeProperties is allowed to be null (in
 // which case we of course won't resolve anything).
-/* static */ bool
+static bool
 XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                                JS::MutableHandle<JSPropertyDescriptor> desc,
                                bool& cacheOnHolder,
                                const NativeProperties* nativeProperties)
 {
   if (!nativeProperties) {
     return true;
@@ -1215,17 +1208,17 @@ DEBUG_CheckXBLCallable(JSContext *cx, JS
     // contain wrappers to functions defined in the XBL scope. But if the node
     // has been adopted into another compartment, those prototypes will now point
     // to a different XBL scope (which is ok).
     MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj),
                   xpc::IsContentXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
     MOZ_ASSERT(JS::IsCallable(obj));
 }
 
-void
+static void
 DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc)
 {
     if (!desc->obj)
         return;
     if (!desc->value.isUndefined()) {
         MOZ_ASSERT(desc->value.isObject());
         DEBUG_CheckXBLCallable(cx, &desc->value.toObject());
     }
@@ -1237,102 +1230,105 @@ DEBUG_CheckXBLLookup(JSContext *cx, JSPr
         MOZ_ASSERT(desc->attrs & JSPROP_SETTER);
         DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->setter));
     }
 }
 #else
 #define DEBUG_CheckXBLLookup(a, b) {}
 #endif
 
-bool
+/* static */ bool
 XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                        JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                        JS::MutableHandle<JSPropertyDescriptor> desc,
                        bool& cacheOnHolder)
 {
   cacheOnHolder = false;
 
   DOMObjectType type;
-  bool isGlobal;
   const NativePropertyHooks *nativePropertyHooks =
-    GetNativePropertyHooks(cx, obj, type, isGlobal);
+    GetNativePropertyHooks(cx, obj, type);
+  const NativePropertiesHolder& nativeProperties =
+    nativePropertyHooks->mNativeProperties;
   ResolveOwnProperty resolveOwnProperty =
     nativePropertyHooks->mResolveOwnProperty;
 
   if (type == eNamedPropertiesObject) {
     // None of these should be cached on the holder, since they're dynamic.
     return resolveOwnProperty(cx, wrapper, obj, id, desc);
   }
 
   // Check for unforgeable properties first.
-  if (type == eInstance) {
+  if (IsInstance(type)) {
     const NativePropertiesHolder& nativeProperties =
       nativePropertyHooks->mNativeProperties;
     if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
                                         nativeProperties.regular)) {
       return false;
     }
 
-    if (desc.object()) {
-      return true;
-    }
-
-    if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
+    if (!desc.object() && xpc::AccessCheck::isChrome(wrapper) &&
+        !XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
                                         nativeProperties.chromeOnly)) {
       return false;
     }
 
     if (desc.object()) {
       return true;
     }
   }
 
-  if (type != eInstance || (isGlobal && GlobalPropertiesAreOwn())) {
-    // For prototype objects and interface objects, just return their
-    // normal set of properties. For global objects the WebIDL properties live
-    // on the instance objects, so resolve those here too.
-    if (!XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
-                                   obj, id, desc, cacheOnHolder)) {
-      return false;
-    }
-
-    if (desc.object()) {
-      return true;
-    }
-  }
-
-  if (type == eInstance) {
+  if (IsInstance(type)) {
     if (resolveOwnProperty) {
       if (!resolveOwnProperty(cx, wrapper, obj, id, desc)) {
         return false;
       }
 
       if (desc.object()) {
         // None of these should be cached on the holder, since they're dynamic.
-        cacheOnHolder = false;
+        return true;
+      }
+    }
+
+    // If we're a special scope for in-content XBL, our script expects to see
+    // the bound XBL methods and attributes when accessing content. However,
+    // these members are implemented in content via custom-spliced prototypes,
+    // and thus aren't visible through Xray wrappers unless we handle them
+    // explicitly. So we check if we're running in such a scope, and if so,
+    // whether the wrappee is a bound element. If it is, we do a lookup via
+    // specialized XBL machinery.
+    //
+    // While we have to do some sketchy walking through content land, we should
+    // be protected by read-only/non-configurable properties, and any functions
+    // we end up with should _always_ be living in our own scope (the XBL scope).
+    // Make sure to assert that.
+    Element* element;
+    if (xpc::ObjectScope(wrapper)->IsContentXBLScope() &&
+        NS_SUCCEEDED(UNWRAP_OBJECT(Element, obj, element))) {
+      if (!nsContentUtils::LookupBindingMember(cx, element, id, desc)) {
+        return false;
+      }
+
+      DEBUG_CheckXBLLookup(cx, desc.address());
+
+      if (desc.object()) {
+        // XBL properties shouldn't be cached on the holder, as they might be
+        // shadowed by own properties returned from mResolveOwnProperty.
+        desc.object().set(wrapper);
 
         return true;
       }
     }
-  }
 
-  return true;
-}
-
-/* static */ bool
-XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
-                          const NativePropertyHooks* nativePropertyHooks,
-                          DOMObjectType type, JS::Handle<JSObject*> obj,
-                          JS::Handle<jsid> id,
-                          JS::MutableHandle<JSPropertyDescriptor> desc,
-                          bool& cacheOnHolder)
-{
-  MOZ_ASSERT(type != eNamedPropertiesObject);
-
-  if (type == eInterface) {
+    // For non-global instance Xrays there are no other properties, so return
+    // here for them.
+    if (type != eGlobalInstance || !GlobalPropertiesAreOwn()) {
+      return true;
+    }
+  } else if (type == eInterface) {
     if (IdEquals(id, "prototype")) {
       return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
              ResolvePrototypeOrConstructor(cx, wrapper, obj,
                                            nativePropertyHooks->mPrototypeID,
                                            JSPROP_PERMANENT | JSPROP_READONLY,
                                            desc, cacheOnHolder);
     }
 
@@ -1346,28 +1342,33 @@ XrayResolveNativeProperty(JSContext* cx,
 
       cacheOnHolder = true;
 
       FillPropertyDescriptor(desc, wrapper, 0,
                              JS::ObjectValue(*JS_GetFunctionObject(toString)));
 
       return JS_WrapPropertyDescriptor(cx, desc);
     }
-  }
+  } else {
+    MOZ_ASSERT(IsInterfacePrototype(type));
 
-  if (type == eInterfacePrototype && IdEquals(id, "constructor")) {
-    return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count ||
-           ResolvePrototypeOrConstructor(cx, wrapper, obj,
-                                         nativePropertyHooks->mConstructorID,
-                                         0, desc, cacheOnHolder);
+    if (IdEquals(id, "constructor")) {
+      return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count ||
+             ResolvePrototypeOrConstructor(cx, wrapper, obj,
+                                           nativePropertyHooks->mConstructorID,
+                                           0, desc, cacheOnHolder);
+    }
+
+    // The properties for globals live on the instance, so return here as there
+    // are no properties on their interface prototype object.
+    if (type == eGlobalInterfacePrototype && GlobalPropertiesAreOwn()) {
+      return true;
+    }
   }
 
-  const NativePropertiesHolder& nativeProperties =
-    nativePropertyHooks->mNativeProperties;
-
   if (nativeProperties.regular &&
       !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
                            nativeProperties.regular)) {
     return false;
   }
 
   if (!desc.object() &&
       nativeProperties.chromeOnly &&
@@ -1376,66 +1377,16 @@ XrayResolveNativeProperty(JSContext* cx,
                            nativeProperties.chromeOnly)) {
     return false;
   }
 
   return true;
 }
 
 bool
-XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
-                          JS::Handle<JSObject*> obj,
-                          JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc,
-                          bool& cacheOnHolder)
-{
-  cacheOnHolder = false;
-
-  DOMObjectType type;
-  bool isGlobal;
-  const NativePropertyHooks* nativePropertyHooks =
-    GetNativePropertyHooks(cx, obj, type, isGlobal);
-
-  if (type == eNamedPropertiesObject) {
-    return true;
-  }
-
-  if (type == eInstance) {
-    // Global objects return their interfaces' properties from
-    // XrayResolveOwnProperty, so skip those.
-    if (isGlobal && GlobalPropertiesAreOwn()) {
-      nativePropertyHooks = nativePropertyHooks->mProtoHooks;
-    }
-
-    // Force the type to be eInterfacePrototype, since we need to walk the
-    // prototype chain.
-    type = eInterfacePrototype;
-  }
-
-  if (type == eInterfacePrototype) {
-    while (nativePropertyHooks) {
-      if (!XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
-                                     obj, id, desc, cacheOnHolder)) {
-        return false;
-      }
-
-      if (desc.object()) {
-        return true;
-      }
-
-      nativePropertyHooks = nativePropertyHooks->mProtoHooks;
-    }
-
-    return true;
-  }
-
-  return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type, obj,
-                                   id, desc, cacheOnHolder);
-}
-
-bool
 XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                    JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                    JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined)
 {
   if (!js::IsProxy(obj))
       return true;
 
   const DOMProxyHandler* handler = GetDOMProxyHandler(obj);
@@ -1478,29 +1429,33 @@ XrayEnumerateAttributesOrMethods(JSConte
   }                                                                           \
 }
 
 
 bool
 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
                         JS::Handle<JSObject*> obj,
                         unsigned flags, JS::AutoIdVector& props,
-                        DOMObjectType type, bool isGlobal,
+                        DOMObjectType type,
                         const NativeProperties* nativeProperties)
 {
   MOZ_ASSERT(type != eNamedPropertiesObject);
 
-  if (type == eInstance) {
+  if (IsInstance(type)) {
     ENUMERATE_IF_DEFINED(unforgeableMethod);
     ENUMERATE_IF_DEFINED(unforgeableAttribute);
+    if (type == eGlobalInstance && GlobalPropertiesAreOwn()) {
+      ENUMERATE_IF_DEFINED(method);
+      ENUMERATE_IF_DEFINED(attribute);
+    }
   } else if (type == eInterface) {
     ENUMERATE_IF_DEFINED(staticMethod);
     ENUMERATE_IF_DEFINED(staticAttribute);
-  } else {
-    MOZ_ASSERT(type == eInterfacePrototype);
+  } else if (type != eGlobalInterfacePrototype || !GlobalPropertiesAreOwn()) {
+    MOZ_ASSERT(IsInterfacePrototype(type));
     ENUMERATE_IF_DEFINED(method);
     ENUMERATE_IF_DEFINED(attribute);
   }
 
   if (nativeProperties->constants) {
     const Prefable<const ConstantSpec>* constant;
     for (constant = nativeProperties->constants; constant->specs; ++constant) {
       if (constant->isEnabled(cx, obj)) {
@@ -1519,110 +1474,81 @@ XrayEnumerateProperties(JSContext* cx, J
   return true;
 }
 
 #undef ENUMERATE_IF_DEFINED
 
 bool
 XrayEnumerateNativeProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
                               const NativePropertyHooks* nativePropertyHooks,
-                              DOMObjectType type, bool isGlobal,
+                              DOMObjectType type,
                               JS::Handle<JSObject*> obj, unsigned flags,
                               JS::AutoIdVector& props)
 {
   MOZ_ASSERT(type != eNamedPropertiesObject);
 
   if (type == eInterface &&
       nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count &&
       !AddStringToIDVector(cx, props, "prototype")) {
     return false;
   }
 
-  if (type == eInterfacePrototype &&
+  if (IsInterfacePrototype(type) &&
       nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
       (flags & JSITER_HIDDEN) &&
       !AddStringToIDVector(cx, props, "constructor")) {
     return false;
   }
 
   const NativePropertiesHolder& nativeProperties =
     nativePropertyHooks->mNativeProperties;
 
   if (nativeProperties.regular &&
-      !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type, isGlobal,
+      !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type,
                                nativeProperties.regular)) {
     return false;
   }
 
   if (nativeProperties.chromeOnly &&
       xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
-      !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type, isGlobal,
+      !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type,
                                nativeProperties.chromeOnly)) {
     return false;
   }
 
   return true;
 }
 
 bool
 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
                         JS::Handle<JSObject*> obj,
                         unsigned flags, JS::AutoIdVector& props)
 {
   DOMObjectType type;
-  bool isGlobal;
   const NativePropertyHooks* nativePropertyHooks =
-    GetNativePropertyHooks(cx, obj, type, isGlobal);
+    GetNativePropertyHooks(cx, obj, type);
   EnumerateOwnProperties enumerateOwnProperties =
     nativePropertyHooks->mEnumerateOwnProperties;
 
   if (type == eNamedPropertiesObject) {
     return enumerateOwnProperties(cx, wrapper, obj, props);
   }
 
-  if (type == eInstance) {
+  if (IsInstance(type)) {
+    // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1071189
+    //       Should do something about XBL properties too.
     if (enumerateOwnProperties &&
         !enumerateOwnProperties(cx, wrapper, obj, props)) {
       return false;
     }
-
-    // Handle Unforgeable properties.
-    if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
-                                       isGlobal, obj, flags, props)) {
-      return false;
-    }
-
-    // This will incorrectly return properties from EventTarget.prototype as own
-    // property names for Window.
-    if (!(isGlobal && GlobalPropertiesAreOwn()) && (flags & JSITER_OWNONLY)) {
-      return true;
-    }
-
-    // Force the type to be eInterfacePrototype, since we need to walk the
-    // prototype chain.
-    type = eInterfacePrototype;
   }
 
-  if (type == eInterfacePrototype) {
-    do {
-      if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
-                                         isGlobal, obj, flags, props)) {
-        return false;
-      }
-
-      if (flags & JSITER_OWNONLY) {
-        return true;
-      }
-    } while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks));
-
-    return true;
-  }
-
-  return XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
-                                       isGlobal, obj, flags, props);
+  return (type == eGlobalInterfacePrototype && GlobalPropertiesAreOwn()) ||
+         XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
+                                       obj, flags, props);
 }
 
 NativePropertyHooks sWorkerNativePropertyHooks = {
   nullptr,
   nullptr,
   {
     nullptr,
     nullptr
@@ -1735,95 +1661,16 @@ DictionaryBase::AppendJSONToString(const
                                    uint32_t aDataLength,
                                    void* aString)
 {
   nsAString* string = static_cast<nsAString*>(aString);
   string->Append(aJSONData, aDataLength);
   return true;
 }
 
-static JSString*
-ConcatJSString(JSContext* cx, const char* pre, JS::Handle<JSString*> str, const char* post)
-{
-  if (!str) {
-    return nullptr;
-  }
-
-  JS::Rooted<JSString*> preString(cx, JS_NewStringCopyN(cx, pre, strlen(pre)));
-  JS::Rooted<JSString*> postString(cx, JS_NewStringCopyN(cx, post, strlen(post)));
-  if (!preString || !postString) {
-    return nullptr;
-  }
-
-  preString = JS_ConcatStrings(cx, preString, str);
-  if (!preString) {
-    return nullptr;
-  }
-
-  return JS_ConcatStrings(cx, preString, postString);
-}
-
-bool
-NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
-               JS::Handle<JSObject*> obj,
-               JS::MutableHandle<JS::Value> v)
-{
-  JS::Rooted<JSPropertyDescriptor> toStringDesc(cx);
-  toStringDesc.object().set(nullptr);
-  toStringDesc.setAttributes(0);
-  toStringDesc.setGetter(nullptr);
-  toStringDesc.setSetter(nullptr);
-  toStringDesc.value().set(JS::UndefinedValue());
-  JS::Rooted<jsid> id(cx,
-    nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
-  bool unused;
-  if (!XrayResolveNativeProperty(cx, wrapper, obj, id, &toStringDesc, unused)) {
-    return false;
-  }
-
-  JS::Rooted<JSString*> str(cx);
-  {
-    JSAutoCompartment ac(cx, obj);
-    if (toStringDesc.object()) {
-      JS::Rooted<JS::Value> toString(cx, toStringDesc.value());
-      if (!JS_WrapValue(cx, &toString)) {
-        return false;
-      }
-      MOZ_ASSERT(JS::IsCallable(&toString.toObject()));
-      JS::Rooted<JS::Value> toStringResult(cx);
-      if (JS_CallFunctionValue(cx, obj, toString, JS::HandleValueArray::empty(),
-                               &toStringResult)) {
-        str = toStringResult.toString();
-      } else {
-        str = nullptr;
-      }
-    } else {
-      const js::Class* clasp = js::GetObjectClass(obj);
-      if (IsDOMClass(clasp)) {
-        str = JS_NewStringCopyZ(cx, clasp->name);
-        str = ConcatJSString(cx, "[object ", str, "]");
-      } else if (IsDOMIfaceAndProtoClass(clasp)) {
-        const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
-          DOMIfaceAndProtoJSClass::FromJSClass(clasp);
-        str = JS_NewStringCopyZ(cx, ifaceAndProtoJSClass->mToString);
-      } else {
-        MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
-        JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj));
-        str = JS_DecompileFunction(cx, fun, 0);
-      }
-    }
-  }
-
-  if (!str) {
-    return false;
-  }
-
-  v.setString(str);
-  return JS_WrapValue(cx, v);
-}
 
 // Dynamically ensure that two objects don't end up with the same reserved slot.
 class MOZ_STACK_CLASS AutoCloneDOMObjectSlotGuard
 {
 public:
   AutoCloneDOMObjectSlotGuard(JSContext* aCx, JSObject* aOld, JSObject* aNew)
     : mOldReflector(aCx, aOld), mNewReflector(aCx, aNew)
   {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2312,43 +2312,30 @@ AddStringToIDVector(JSContext* cx, JS::A
 {
   return vector.growBy(1) &&
          InternJSString(cx, *(vector[vector.length() - 1]).address(), name);
 }
 
 // Implementation of the bits that XrayWrapper needs
 
 /**
- * This resolves indexed or named properties of obj.
+ * This resolves operations, attributes and constants of the interfaces for obj.
  *
  * wrapper is the Xray JS object.
  * obj is the target object of the Xray, a binding's instance object or a
  *     interface or interface prototype object.
  */
 bool
 XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                        JS::Handle<JSObject*> obj,
                        JS::Handle<jsid> id,
                        JS::MutableHandle<JSPropertyDescriptor> desc,
                        bool& cacheOnHolder);
 
 /**
- * This resolves operations, attributes and constants of the interfaces for obj.
- *
- * wrapper is the Xray JS object.
- * obj is the target object of the Xray, a binding's instance object or a
- *     interface or interface prototype object.
- */
-bool
-XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
-                          JS::Handle<JSObject*> obj,
-                          JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc,
-                          bool& cacheOnHolder);
-
-/**
  * Define a property on obj through an Xray wrapper.
  *
  * wrapper is the Xray JS object.
  * obj is the target object of the Xray, a binding's instance object or a
  *     interface or interface prototype object.
  * defined will be set to true if a property was set as a result of this call.
  */
 bool
@@ -2459,33 +2446,16 @@ Take(nsAutoPtr<T>& smartPtr, T* ptr)
   smartPtr = ptr;
 }
 
 inline void
 MustInheritFromNonRefcountedDOMObject(NonRefcountedDOMObject*)
 {
 }
 
-/**
- * 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.
- * obj is the target object of the Xray, a binding's instance object or a
- *     interface or interface prototype object.
- * v contains the JSString for the value if the function returns true.
- */
-bool
-NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
-               JS::Handle<JSObject*> obj,
-               JS::MutableHandle<JS::Value> v);
-
 HAS_MEMBER(JSBindingFinalized)
 
 template<class T, bool hasCallback=HasJSBindingFinalizedMember<T>::Value>
 struct JSBindingFinalized
 {
   static void Finalized(T* self)
   {
   }
@@ -2990,22 +2960,16 @@ GlobalPropertiesAreOwn()
 {
   return true;
 }
 
 #ifdef DEBUG
 void
 AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
                                JS::Handle<JS::Value> aValue);
-
-extern void
-DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc);
-#else
-#define DEBUG_CheckXBLLookup(a, b) {}
-
 #endif
 
 // Returns true if aObj's global has any of the permissions named in aPermissions
 // set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated.
 bool
 CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
 
 //Returns true if page is being prerendered.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -643,16 +643,17 @@ class CGPrototypeJSClass(CGThing):
         return ""
 
     def define(self):
         prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
         slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
         if UseHolderForUnforgeable(self.descriptor):
             slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
         (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
+        type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype"
         return fill(
             """
             static const DOMIfaceAndProtoJSClass PrototypeClass = {
               {
                 "${name}Prototype",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 JS_PropertyStub,       /* addProperty */
                 JS_DeletePropertyStub, /* delProperty */
@@ -665,26 +666,27 @@ class CGPrototypeJSClass(CGThing):
                 nullptr,               /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 nullptr,               /* trace */
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
                 JS_NULL_OBJECT_OPS
               },
-              eInterfacePrototype,
+              ${type},
               ${hooks},
               "[object ${name}Prototype]",
               ${prototypeID},
               ${depth},
               ${protoGetter}
             };
             """,
             name=self.descriptor.interface.identifier.name,
             slotCount=slotCount,
+            type=type,
             hooks=NativePropertyHooks(self.descriptor),
             prototypeID=prototypeID,
             depth=depth,
             protoGetter=protoGetter)
 
 
 def NeedsGeneratedHasInstance(descriptor):
     return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -151,27 +151,44 @@ struct NativePropertyHooks
   // prototypes::id::_ID_Count.
   prototypes::ID mPrototypeID;
 
   // This will be set to the ID of the interface object for the interface, if it
   // has one. If it doesn't have one it will be set to
   // constructors::id::_ID_Count.
   constructors::ID mConstructorID;
 
-  // The NativePropertyHooks instance for the parent interface.
+  // The NativePropertyHooks instance for the parent interface (for
+  // ShimInterfaceInfo).
   const NativePropertyHooks* mProtoHooks;
 };
 
 enum DOMObjectType {
   eInstance,
+  eGlobalInstance,
   eInterface,
   eInterfacePrototype,
+  eGlobalInterfacePrototype,
   eNamedPropertiesObject
 };
 
+inline
+bool
+IsInstance(DOMObjectType type)
+{
+  return type == eInstance || type == eGlobalInstance;
+}
+
+inline
+bool
+IsInterfacePrototype(DOMObjectType type)
+{
+  return type == eInterfacePrototype || type == eGlobalInterfacePrototype;
+}
+
 typedef JSObject* (*ParentGetter)(JSContext* aCx, JS::Handle<JSObject*> aObj);
 
 typedef JSObject* (*ProtoGetter)(JSContext* aCx,
                                  JS::Handle<JSObject*> aGlobal);
 /**
  * Returns a handle to the relevent WebIDL prototype object for the given global
  * (which may be a handle to null on out of memory).  Once allocated, the
  * prototype object is guaranteed to exist as long as the global does, since the
@@ -224,17 +241,18 @@ struct DOMJSClass
 struct DOMIfaceAndProtoJSClass
 {
   // It would be nice to just inherit from js::Class, but that precludes pure
   // compile-time initialization of the form
   // |DOMJSInterfaceAndPrototypeClass = {...};|, since C++ only allows brace
   // initialization for aggregate/POD types.
   const js::Class mBase;
 
-  // Either eInterface, eInterfacePrototype or eNamedPropertiesObject
+  // Either eInterface, eInterfacePrototype, eGlobalInterfacePrototype or
+  // eNamedPropertiesObject.
   DOMObjectType mType;
 
   const NativePropertyHooks* mNativeHooks;
 
   // The value to return for toString() on this interface or interface prototype
   // object.
   const char* mToString;
 
--- a/dom/tests/mochitest/chrome/test_sandbox_bindings.xul
+++ b/dom/tests/mochitest/chrome/test_sandbox_bindings.xul
@@ -69,36 +69,25 @@ https://bugzilla.mozilla.org/show_bug.cg
         ok(false, "'Image.prototype' shouldn't throw in a sandbox");
       }
       try {
         var xhr = Components.utils.evalInSandbox("XMLHttpRequest", sandbox);
         xhr.prototype = "notok";
       } finally {
         isnot(xhr.prototype, "notok", "'XMLHttpRequest.prototype' should be readonly");
       }
-      try {
-        var xhr = Components.utils.evalInSandbox("XMLHttpRequest", sandbox);
-        delete xhr.prototype;
-      } catch (e) {
-        ok(true, "'XMLHttpRequest.prototype' should be permanent");
-      }
+      var constructorWritable = false;
       try {
         var xhr = Components.utils.evalInSandbox("XMLHttpRequest.prototype", sandbox);
         xhr.constructor = "ok";
         is(xhr.constructor, "ok", "'XMLHttpRequest.prototype.constructor' should be writeable");
       } catch (e) {
         ok(false, "'XMLHttpRequest.prototype.constructor' should be writeable");
       }
       try {
-        var xhr = Components.utils.evalInSandbox("XMLHttpRequest.prototype", sandbox);
-        delete xhr.constructor;
-      } catch (e) {
-        is(xhr.constructor, undefined, "'XMLHttpRequest.prototype.constructor' should be permanent");
-      }
-      try {
         var xhr = Components.utils.evalInSandbox("XMLHttpRequest", sandbox);
         is(xhr, XMLHttpRequest + "", "'XMLHttpRequest' in a sandbox should return the XMLHttpRequest interface object");
         ok(isXrayWrapper(xhr.prototype), "Getting the prototype property on an Xray wrapper of an interface object should return an Xray wrapper");
         isnot(Object.getOwnPropertyDescriptor(xhr, "UNSENT"), undefined,
               "We should claim to have an UNSENT constant");
         isnot(Object.keys(xhr).indexOf("OPENED"), -1,
               "We should claim to have an OPENED constant");
         isnot(Object.getOwnPropertyNames(xhr).indexOf("DONE"), -1,
@@ -110,16 +99,22 @@ https://bugzilla.mozilla.org/show_bug.cg
       }
       try {
         var xhr = Components.utils.evalInSandbox("new XMLHttpRequest()", sandbox);
         is("" + xhr, new XMLHttpRequest() + "", "'new XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
       } catch (e) {
         ok(false, "'new XMLHttpRequest()' shouldn't throw in a sandbox (1)");
       }
       try {
+        var xhr = Components.utils.evalInSandbox("XMLHttpRequest.toString = function () { return 'Failed'; }; XMLHttpRequest;", sandbox);
+        is(xhr.toString(), XMLHttpRequest + "", "XMLHttpRequest.toString in the sandbox should not override the native toString behaviour");
+      } catch (e) {
+        ok(false, "'XMLHttpRequest' shouldn't throw in a sandbox");
+      }
+      try {
         var xhr = Components.utils.evalInSandbox("XMLHttpRequest.prototype.toString = function () { return 'Failed'; }; new XMLHttpRequest();", sandbox);
         is(xhr.toString(), new XMLHttpRequest() + "", "XMLHttpRequest.prototype.toString in the sandbox should not override the native toString behaviour");
       } catch (e) {
         ok(false, "'new XMLHttpRequest()' shouldn't throw in a sandbox (2)");
       }
 
       try {
         // have to run this test before document.defaultView.XMLHttpRequest
@@ -133,16 +128,26 @@ https://bugzilla.mozilla.org/show_bug.cg
            "'dispatchEvent' property should be enumerable on XMLHttpRequest.prototype");
         props = Object.getOwnPropertyNames(proto);
         is(props.indexOf("dispatchEvent"), -1,
            "'dispatchEvent' is not an own property on XMLHttpRequest.prototype; it's on EventTarget.prototype")
       } catch (e) {
         ok(false, "XMLHttpRequest.prototype manipulation via an Xray shouldn't throw" + e);
       }
       try {
+        Components.utils.evalInSandbox("XMLHttpRequest.prototype.a = 'expando a'", sandbox);
+        Components.utils.evalInSandbox("XMLHttpRequest.prototype.b = 'expando b'", sandbox);
+        Components.utils.evalInSandbox("XMLHttpRequest.prototype", sandbox).b = 'xrayexpando';
+        var xhr = Components.utils.evalInSandbox("new XMLHttpRequest()", sandbox);
+        is(xhr.a, undefined, "'XMLHttpRequest()' in a sandbox should not have expandos from inside the sandbox");
+        is(xhr.b, "xrayexpando", "'new XMLHttpRequest()' in a sandbox should have Xray expandos");
+      } catch (e) {
+        ok(false, "'new XMLHttpRequest()' shouldn't throw in a sandbox");
+      }
+      try {
         Components.utils.evalInSandbox("document.defaultView.XMLHttpRequest = function() {};", sandbox);
         var win = Components.utils.evalInSandbox("document.defaultView", sandbox);
         var xhr = new win.XMLHttpRequest();
         is("" + xhr, new XMLHttpRequest() + "", "'new XMLHttpRequest()' in a sandbox should create an XMLHttpRequest object");
       } catch (e) {
         ok(false, "'new XMLHttpRequest()' shouldn't throw in a sandbox");
       }
       try {
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -3,21 +3,20 @@
 /* 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 "XrayWrapper.h"
 #include "AccessCheck.h"
 #include "WrapperFactory.h"
 
-#include "nsIContent.h"
 #include "nsIControllers.h"
+#include "nsDependentString.h"
 #include "nsIScriptError.h"
 #include "mozilla/dom/Element.h"
-#include "nsContentUtils.h"
 
 #include "XPCWrapper.h"
 #include "xpcprivate.h"
 
 #include "jsapi.h"
 #include "jsprf.h"
 #include "nsJSUtils.h"
 #include "nsPrintfCString.h"
@@ -1048,19 +1047,16 @@ AsWindow(JSContext *cx, JSObject *wrappe
 }
 
 static bool
 IsWindow(JSContext *cx, JSObject *wrapper)
 {
     return !!AsWindow(cx, wrapper);
 }
 
-static nsQueryInterface
-do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper);
-
 void
 XPCWrappedNativeXrayTraits::preserveWrapper(JSObject *target)
 {
     XPCWrappedNative *wn = XPCWrappedNative::Get(target);
     nsRefPtr<nsXPCClassInfo> ci;
     CallQueryInterface(wn->Native(), getter_AddRefs(ci));
     if (ci)
         ci->PreserveWrapper(wn->Native());
@@ -1505,43 +1501,16 @@ DOMXrayTraits::resolveOwnProperty(JSCont
         return true;
 
     return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.attributes(),
                                  desc.getter(), desc.setter()) &&
            JS_GetPropertyDescriptorById(cx, holder, id, desc);
 }
 
 bool
-DOMXrayTraits::resolveNativeProperty(JSContext *cx, HandleObject wrapper,
-                                     HandleObject holder, HandleId id,
-                                     MutableHandle<JSPropertyDescriptor> desc)
-{
-    bool unused;
-    RootedObject obj(cx, getTargetObject(wrapper));
-    if (!XrayResolveNativeProperty(cx, wrapper, obj, id, desc, unused))
-        return false;
-
-    if (!desc.object() &&
-        id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING))
-    {
-
-        JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, wrapper, "toString");
-        if (!toString)
-            return false;
-
-        FillPropertyDescriptor(desc, wrapper, 0,
-                               ObjectValue(*JS_GetFunctionObject(toString)));
-    }
-
-    MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
-
-    return true;
-}
-
-bool
 DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                               MutableHandle<JSPropertyDescriptor> desc,
                               Handle<JSPropertyDescriptor> existingDesc, bool *defined)
 {
     // Check for an indexed property on a Window.  If that's happening, do
     // nothing but claim we defined it so it won't get added as an expando.
     if (IsWindow(cx, wrapper)) {
         int32_t index = GetArrayIndexFromId(cx, id);
@@ -1631,16 +1600,24 @@ DOMXrayTraits::construct(JSContext *cx, 
         if (!baseInstance.construct(cx, wrapper, args))
             return false;
     }
     if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval()))
         return false;
     return true;
 }
 
+bool
+DOMXrayTraits::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
+                              JS::HandleObject target,
+                              JS::MutableHandleObject protop)
+{
+    return mozilla::dom::XrayGetNativeProto(cx, target, protop);
+}
+
 void
 DOMXrayTraits::preserveWrapper(JSObject *target)
 {
     nsISupports *identity = mozilla::dom::UnwrapDOMObjectToISupports(target);
     if (!identity)
         return;
     nsWrapperCache* cache = nullptr;
     CallQueryInterface(identity, &cache);
@@ -1724,29 +1701,17 @@ XrayToString(JSContext *cx, unsigned arg
         wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper);
     }
     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
         JS_ReportError(cx, "XrayToString called on an incompatible object");
         return false;
     }
 
     RootedObject obj(cx, XrayTraits::getTargetObject(wrapper));
-    XrayType type = GetXrayType(obj);
-    if (type == XrayForDOMObject) {
-        {
-            JSAutoCompartment ac(cx, obj);
-            JSString *str = JS_BasicObjectToString(cx, obj);
-            if (!str)
-                return false;
-            args.rval().setString(str);
-        }
-        return JS_WrapValue(cx, args.rval());
-    }
-
-    if (type != XrayForWrappedNative) {
+    if (GetXrayType(obj) != XrayForWrappedNative) {
         JS_ReportError(cx, "XrayToString called on an incompatible object");
         return false;
     }
 
     static const char start[] = "[object XrayWrapper ";
     static const char end[] = "]";
     nsAutoString result;
     result.AppendASCII(start);
@@ -1863,39 +1828,16 @@ XrayWrapper<Base, Traits>::getPropertyDe
             if (MOZ_UNLIKELY(!childObj))
                 return xpc::Throw(cx, NS_ERROR_FAILURE);
             FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj),
                                    /* readOnly = */ true);
             return JS_WrapPropertyDescriptor(cx, desc);
         }
     }
 
-    // If we're a special scope for in-content XBL, our script expects to see
-    // the bound XBL methods and attributes when accessing content. However,
-    // these members are implemented in content via custom-spliced prototypes,
-    // and thus aren't visible through Xray wrappers unless we handle them
-    // explicitly. So we check if we're running in such a scope, and if so,
-    // whether the wrappee is a bound element. If it is, we do a lookup via
-    // specialized XBL machinery.
-    //
-    // While we have to do some sketchy walking through content land, we should
-    // be protected by read-only/non-configurable properties, and any functions
-    // we end up with should _always_ be living in an XBL scope (usually ours,
-    // but could be another if the node has been adopted).
-    //
-    // Make sure to assert this.
-    nsCOMPtr<nsIContent> content;
-    if (!desc.object() && ObjectScope(wrapper)->IsContentXBLScope() &&
-        (content = do_QueryInterfaceNative(cx, wrapper)))
-    {
-        if (!nsContentUtils::LookupBindingMember(cx, content, id, desc))
-            return false;
-        DEBUG_CheckXBLLookup(cx, desc.address());
-    }
-
     // If we still have nothing, we're done.
     if (!desc.object())
         return true;
 
     if (!JS_DefinePropertyById(cx, holder, id, desc.value(), desc.attributes(),
                                desc.getter(), desc.setter()) ||
         !JS_GetPropertyDescriptorById(cx, holder, id, desc))
     {
@@ -2114,16 +2056,17 @@ XrayWrapper<Base, Traits>::get(JSContext
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::set(JSContext *cx, HandleObject wrapper,
                                HandleObject receiver, HandleId id,
                                bool strict, MutableHandleValue vp) const
 {
+    MOZ_ASSERT(!Traits::HasPrototype);
     // Delegate to Traits.
     // NB: None of the functions we call are prepared for the receiver not
     // being the wrapper, so ignore the receiver here.
     return Traits::set(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, strict, vp);
 }
 
 template <typename Base, typename Traits>
 bool
@@ -2285,30 +2228,9 @@ template class PermissiveXrayJS;
 template<>
 const PermissiveXrayOpaque PermissiveXrayOpaque::singleton(0);
 template class PermissiveXrayOpaque;
 
 template<>
 const SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0);
 template class SCSecurityXrayXPCWN;
 
-static nsQueryInterface
-do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper)
-{
-    nsISupports* nativeSupports = nullptr;
-    if (IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper)) {
-        RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
-        XrayType type = GetXrayType(target);
-        if (type == XrayForDOMObject) {
-            nativeSupports = UnwrapDOMObjectToISupports(target);
-        } else if (type == XrayForWrappedNative) {
-            XPCWrappedNative *wn = XPCWrappedNative::Get(target);
-            nativeSupports = wn->Native();
-        }
-    } else {
-        nsIXPConnect *xpc = nsXPConnect::XPConnect();
-        nativeSupports = xpc->GetNativeOfWrapper(cx, wrapper);
-    }
-
-    return nsQueryInterface(nativeSupports);
 }
-
-}
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -149,39 +149,55 @@ public:
     static const JSClass HolderClass;
     static XPCWrappedNativeXrayTraits singleton;
 };
 
 class DOMXrayTraits : public XrayTraits
 {
 public:
     enum {
-        HasPrototype = 0
+        HasPrototype = 1
     };
 
     static const XrayType Type = XrayForDOMObject;
 
     virtual bool resolveNativeProperty(JSContext *cx, JS::HandleObject wrapper,
                                        JS::HandleObject holder, JS::HandleId id,
-                                       JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
+                                       JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE
+    {
+        // Xrays for DOM binding objects have a prototype chain that consists of
+        // Xrays for the prototypes of the DOM binding object (ignoring changes
+        // in the prototype chain made by script, plugins or XBL). All properties for
+        // these Xrays are really own properties, either of the instance object or
+        // of the prototypes.
+        // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1072482
+        //       This should really be:
+        // MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
+        //       but we can't do that yet because XrayUtils::HasNativeProperty calls this.
+        return true;
+    }
     virtual bool resolveOwnProperty(JSContext *cx, const js::Wrapper &jsWrapper, JS::HandleObject wrapper,
                                     JS::HandleObject holder, JS::HandleId id,
                                     JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
     bool defineProperty(JSContext *cx, JS::HandleObject wrapper, JS::HandleId id,
                         JS::MutableHandle<JSPropertyDescriptor> desc,
                         JS::Handle<JSPropertyDescriptor> existingDesc, bool *defined);
     static bool set(JSContext *cx, JS::HandleObject wrapper, JS::HandleObject receiver, JS::HandleId id,
                     bool strict, JS::MutableHandleValue vp);
     virtual bool enumerateNames(JSContext *cx, JS::HandleObject wrapper, unsigned flags,
                                 JS::AutoIdVector &props);
     static bool call(JSContext *cx, JS::HandleObject wrapper,
                      const JS::CallArgs &args, const js::Wrapper& baseInstance);
     static bool construct(JSContext *cx, JS::HandleObject wrapper,
                           const JS::CallArgs &args, const js::Wrapper& baseInstance);
 
+    static bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
+                               JS::HandleObject target,
+                               JS::MutableHandleObject protop);
+
     virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE;
 
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
 
     static DOMXrayTraits singleton;
 };
 
 class JSXrayTraits : public XrayTraits