Bug 787070 - Expandos on the xray of DOM prototypes should have effect on xrays of DOM nodes, add a named properties object type to DOMObjectType and how to get the parent prototype object in DOMIfaceAndProtoJSClass. r=bz, r=bholley.
authorPeter Van der Beken <peterv@propagandism.org>
Tue, 09 Sep 2014 15:19:10 +0200
changeset 207615 ee4fe6bc73be
parent 207614 3ef7fe194800
child 207616 194b0d93d32a
push id49735
push userpvanderbeken@mozilla.com
push dateMon, 29 Sep 2014 07:58:28 +0000
treeherdermozilla-inbound@090b62fdfd21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, bholley
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, add a named properties object type to DOMObjectType and how to get the parent prototype object in DOMIfaceAndProtoJSClass. r=bz, r=bholley.
content/base/src/nsDocument.cpp
dom/base/WindowNamedPropertiesHandler.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/DOMJSClass.h
dom/xbl/nsXBLProtoImpl.cpp
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5895,17 +5895,17 @@ nsDocument::RegisterElement(JSContext* a
     rv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
   JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
 
   JSAutoCompartment ac(aCx, global);
 
   JS::Handle<JSObject*> htmlProto(
-    HTMLElementBinding::GetProtoObject(aCx, global));
+    HTMLElementBinding::GetProtoObjectHandle(aCx, global));
   if (!htmlProto) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   int32_t namespaceID = kNameSpaceID_XHTML;
   JS::Rooted<JSObject*> protoObject(aCx);
   if (!aOptions.mPrototype) {
@@ -5941,17 +5941,17 @@ nsDocument::RegisterElement(JSContext* a
 
     // Check if non-configurable
     if (desc.isPermanent()) {
       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
 
     JS::Handle<JSObject*> svgProto(
-      SVGElementBinding::GetProtoObject(aCx, global));
+      SVGElementBinding::GetProtoObjectHandle(aCx, global));
     if (!svgProto) {
       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
       return;
     }
 
     JS::Rooted<JSObject*> protoProto(aCx, protoObject);
 
     // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=78: */
 /* 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 "WindowNamedPropertiesHandler.h"
+#include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsDOMClassInfo.h"
 #include "nsGlobalWindow.h"
 #include "nsHTMLDocument.h"
 #include "nsJSUtils.h"
 #include "xpcprivate.h"
 
 namespace mozilla {
@@ -214,21 +215,22 @@ const NativePropertyHooks sWindowNamedPr
   constructors::id::_ID_Count,
   nullptr
 } };
 
 static const DOMIfaceAndProtoJSClass WindowNamedPropertiesClass = {
   PROXY_CLASS_DEF("WindowProperties",
                   DOM_INTERFACE_PROTO_SLOTS_BASE, /* extra slots */
                   0),
-  eInterfacePrototype,
+  eNamedPropertiesObject,
   sWindowNamedPropertiesNativePropertyHooks,
   "[object WindowProperties]",
   prototypes::id::_ID_Count,
   0,
+  EventTargetBinding::GetProtoObject
 };
 
 // static
 JSObject*
 WindowNamedPropertiesHandler::Create(JSContext* aCx,
                                      JS::Handle<JSObject*> aProto)
 {
   // Note: since the scope polluter proxy lives on the window's prototype
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1249,66 +1249,89 @@ XrayResolveOwnProperty(JSContext* cx, JS
                        bool& cacheOnHolder)
 {
   cacheOnHolder = false;
 
   DOMObjectType type;
   bool isGlobal;
   const NativePropertyHooks *nativePropertyHooks =
     GetNativePropertyHooks(cx, obj, type, isGlobal);
+  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) {
+    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,
+                                        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;
     }
 
-    // For non-global non-instance Xrays there are no other properties, so
-    // return here for them whether we resolved the property or not.
-    if (!isGlobal || desc.object()) {
-      cacheOnHolder = true;
-      return true;
-    }
-  }
-
-  {
-    // Check for unforgeable properties before doing mResolveOwnProperty weirdness
-    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,
-                                        nativeProperties.chromeOnly)) {
-      return false;
-    }
     if (desc.object()) {
       return true;
     }
   }
 
-  return !nativePropertyHooks->mResolveOwnProperty ||
-         nativePropertyHooks->mResolveOwnProperty(cx, wrapper, obj, id, desc);
+  if (type == eInstance) {
+    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;
+      }
+    }
+  }
+
+  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) {
     if (IdEquals(id, "prototype")) {
       return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
              ResolvePrototypeOrConstructor(cx, wrapper, obj,
                                            nativePropertyHooks->mPrototypeID,
                                            JSPROP_PERMANENT | JSPROP_READONLY,
                                            desc, cacheOnHolder);
     }
@@ -1365,16 +1388,20 @@ XrayResolveNativeProperty(JSContext* cx,
 {
   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
@@ -1454,16 +1481,18 @@ XrayEnumerateAttributesOrMethods(JSConte
 
 bool
 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
                         JS::Handle<JSObject*> obj,
                         unsigned flags, JS::AutoIdVector& props,
                         DOMObjectType type, bool isGlobal,
                         const NativeProperties* nativeProperties)
 {
+  MOZ_ASSERT(type != eNamedPropertiesObject);
+
   if (type == eInstance) {
     ENUMERATE_IF_DEFINED(unforgeableMethod);
     ENUMERATE_IF_DEFINED(unforgeableAttribute);
   } else if (type == eInterface) {
     ENUMERATE_IF_DEFINED(staticMethod);
     ENUMERATE_IF_DEFINED(staticAttribute);
   } else {
     MOZ_ASSERT(type == eInterfacePrototype);
@@ -1494,16 +1523,18 @@ XrayEnumerateProperties(JSContext* cx, J
 
 bool
 XrayEnumerateNativeProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
                               const NativePropertyHooks* nativePropertyHooks,
                               DOMObjectType type, bool isGlobal,
                               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 &&
       nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
@@ -1535,21 +1566,26 @@ 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);
+  EnumerateOwnProperties enumerateOwnProperties =
+    nativePropertyHooks->mEnumerateOwnProperties;
+
+  if (type == eNamedPropertiesObject) {
+    return enumerateOwnProperties(cx, wrapper, obj, props);
+  }
 
   if (type == eInstance) {
-    if (nativePropertyHooks->mEnumerateOwnProperties &&
-        !nativePropertyHooks->mEnumerateOwnProperties(cx, wrapper, obj,
-                                                      props)) {
+    if (enumerateOwnProperties &&
+        !enumerateOwnProperties(cx, wrapper, obj, props)) {
       return false;
     }
 
     // Handle Unforgeable properties.
     if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
                                        isGlobal, obj, flags, props)) {
       return false;
     }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2365,16 +2365,53 @@ XrayDefineProperty(JSContext* cx, JS::Ha
  *     interface or interface prototype object.
  * flags are JSITER_* flags.
  */
 bool
 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
                         JS::Handle<JSObject*> obj,
                         unsigned flags, JS::AutoIdVector& props);
 
+/**
+ * Returns the prototype to use for an Xray for a DOM object, wrapped in cx's
+ * compartment. This always returns the prototype that would be used for a DOM
+ * object if we ignore any changes that might have been done to the prototype
+ * chain by JS, the XBL code or plugins.
+ *
+ * cx should be in the Xray's compartment.
+ * obj is the target object of the Xray, a binding's instance object or an
+ *     interface or interface prototype object.
+ */
+inline bool
+XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
+                   JS::MutableHandle<JSObject*> protop)
+{
+  JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
+  {
+    JSAutoCompartment ac(cx, global);
+    const DOMJSClass* domClass = GetDOMClass(obj);
+    if (domClass) {
+      ProtoHandleGetter protoGetter = domClass->mGetProto;
+      if (protoGetter) {
+        protop.set(protoGetter(cx, global));
+      } else {
+        protop.set(JS_GetObjectPrototype(cx, global));
+      }
+    } else {
+      const js::Class* clasp = js::GetObjectClass(obj);
+      MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
+      ProtoGetter protoGetter =
+        DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mGetParentProto;
+      protop.set(protoGetter(cx, global));
+    }
+  }
+
+  return JS_WrapObject(cx, protop);
+}
+
 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.
@@ -2834,17 +2871,17 @@ struct CreateGlobalOptions<nsGlobalWindo
   static MOZ_CONSTEXPR_VAR bool ForceInitStandardClassesToFalse = false;
   static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
   static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 };
 
 nsresult
 RegisterDOMNames();
 
-template <class T, ProtoGetter GetProto>
+template <class T, ProtoHandleGetter GetProto>
 bool
 CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
              const JSClass* aClass, JS::CompartmentOptions& aOptions,
              JSPrincipals* aPrincipal, bool aInitStandardClasses,
              JS::MutableHandle<JSObject*> aGlobal)
 {
   aOptions.setTrace(CreateGlobalOptions<T>::TraceGlobal);
 
@@ -3020,12 +3057,19 @@ StrongOrRawPtr(T* aPtr)
 {
   return aPtr;
 }
 
 template<class T, template<typename> class SmartPtr, class S>
 inline void
 StrongOrRawPtr(SmartPtr<S>&& aPtr) MOZ_DELETE;
 
+inline
+JSObject*
+GetErrorPrototype(JSContext* aCx, JS::Handle<JSObject*> aForObj)
+{
+  return JS_GetErrorPrototype(aCx);
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_BindingUtils_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -335,17 +335,17 @@ def DOMClass(descriptor):
     protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
 
     return fill(
         """
           { ${protoChain} },
           IsBaseOf<nsISupports, ${nativeType} >::value,
           ${hooks},
           GetParentObject<${nativeType}>::Get,
-          GetProtoObject,
+          GetProtoObjectHandle,
           GetCCParticipant<${nativeType}>::Get()
         """,
         protoChain=', '.join(protoList),
         nativeType=descriptor.nativeType,
         hooks=NativePropertyHooks(descriptor))
 
 
 class CGDOMJSClass(CGThing):
@@ -597,31 +597,62 @@ def CallOnUnforgeableHolder(descriptor, 
         }
         """,
         pre=pre,
         holderDecl=holderDecl,
         name=descriptor.name,
         code=code)
 
 
+def InterfacePrototypeObjectProtoGetter(descriptor):
+    """
+    Returns a tuple with two elements:
+
+        1) The name of the function to call to get the prototype to use for the
+           interface prototype object as a JSObject*.
+
+        2) The name of the function to call to get the prototype to use for the
+           interface prototype object as a JS::Handle<JSObject*> or None if no
+           such function exists.
+    """
+    parentProtoName = descriptor.parentPrototypeName
+    if descriptor.hasNamedPropertiesObject:
+        protoGetter = "GetNamedPropertiesObject"
+        protoHandleGetter = None
+    elif parentProtoName is None:
+        if descriptor.interface.getExtendedAttribute("ArrayClass"):
+            protoGetter = "JS_GetArrayPrototype"
+        elif descriptor.interface.getExtendedAttribute("ExceptionClass"):
+            protoGetter = "GetErrorPrototype"
+        else:
+            protoGetter = "JS_GetObjectPrototype"
+        protoHandleGetter = None
+    else:
+        prefix = toBindingNamespace(parentProtoName)
+        protoGetter = prefix + "::GetProtoObject"
+        protoHandleGetter = prefix + "::GetProtoObjectHandle"
+
+    return (protoGetter, protoHandleGetter)
+
 class CGPrototypeJSClass(CGThing):
     def __init__(self, descriptor, properties):
         CGThing.__init__(self)
         self.descriptor = descriptor
         self.properties = properties
 
     def declare(self):
         # We're purely for internal consumption
         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)
         return fill(
             """
             static const DOMIfaceAndProtoJSClass PrototypeClass = {
               {
                 "${name}Prototype",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 JS_PropertyStub,       /* addProperty */
                 JS_DeletePropertyStub, /* delProperty */
@@ -638,29 +669,56 @@ class CGPrototypeJSClass(CGThing):
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
                 JS_NULL_OBJECT_OPS
               },
               eInterfacePrototype,
               ${hooks},
               "[object ${name}Prototype]",
               ${prototypeID},
-              ${depth}
+              ${depth},
+              ${protoGetter}
             };
             """,
             name=self.descriptor.interface.identifier.name,
             slotCount=slotCount,
             hooks=NativePropertyHooks(self.descriptor),
             prototypeID=prototypeID,
-            depth=depth)
+            depth=depth,
+            protoGetter=protoGetter)
 
 
 def NeedsGeneratedHasInstance(descriptor):
     return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
 
+def InterfaceObjectProtoGetter(descriptor):
+    """
+    Returns a tuple with two elements:
+
+        1) The name of the function to call to get the prototype to use for the
+           interface object as a JSObject*.
+
+        2) The name of the function to call to get the prototype to use for the
+           interface prototype as a JS::Handle<JSObject*> or None if no such
+           function exists.
+    """
+    parentWithInterfaceObject = descriptor.interface.parent
+    while (parentWithInterfaceObject and
+           not parentWithInterfaceObject.hasInterfaceObject()):
+        parentWithInterfaceObject = parentWithInterfaceObject.parent
+    if parentWithInterfaceObject:
+        parentIfaceName = parentWithInterfaceObject.identifier.name
+        parentDesc = descriptor.getDescriptor(parentIfaceName)
+        prefix = toBindingNamespace(parentDesc.name)
+        protoGetter = prefix + "::GetConstructorObject"
+        protoHandleGetter = prefix + "::GetConstructorObjectHandle"
+    else:
+        protoGetter = "JS_GetFunctionPrototype"
+        protoHandleGetter = None
+    return (protoGetter, protoHandleGetter)
 
 class CGInterfaceObjectJSClass(CGThing):
     def __init__(self, descriptor, properties):
         CGThing.__init__(self)
         self.descriptor = descriptor
         self.properties = properties
 
     def declare(self):
@@ -678,16 +736,18 @@ class CGInterfaceObjectJSClass(CGThing):
             hasinstance = "InterfaceHasInstance"
         else:
             hasinstance = "nullptr"
         prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
         slotCount = "DOM_INTERFACE_SLOTS_BASE"
         if len(self.descriptor.interface.namedConstructors) > 0:
             slotCount += (" + %i /* slots for the named constructors */" %
                           len(self.descriptor.interface.namedConstructors))
+        (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor)
+
         return fill(
             """
             static const DOMIfaceAndProtoJSClass InterfaceObjectClass = {
               {
                 "Function",
                 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
                 JS_PropertyStub,       /* addProperty */
                 JS_DeletePropertyStub, /* delProperty */
@@ -704,26 +764,28 @@ class CGInterfaceObjectJSClass(CGThing):
                 JS_NULL_CLASS_SPEC,
                 JS_NULL_CLASS_EXT,
                 JS_NULL_OBJECT_OPS
               },
               eInterface,
               ${hooks},
               "function ${name}() {\\n    [native code]\\n}",
               ${prototypeID},
-              ${depth}
+              ${depth},
+              ${protoGetter}
             };
             """,
             slotCount=slotCount,
             ctorname=ctorname,
             hasInstance=hasinstance,
             hooks=NativePropertyHooks(self.descriptor),
             name=self.descriptor.interface.identifier.name,
             prototypeID=prototypeID,
-            depth=depth)
+            depth=depth,
+            protoGetter=protoGetter)
 
 
 class CGList(CGThing):
     """
     Generate code for a list of GCThings.  Just concatenates them together, with
     an optional joiner string.  "\n" is a common joiner.
     """
     def __init__(self, children, joiner=""):
@@ -2574,48 +2636,33 @@ class CGCreateInterfaceObjectsMethod(CGA
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'aGlobal'),
                 Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
                 Argument('bool', 'aDefineOnGlobal')]
         CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
         self.properties = properties
 
     def definition_body(self):
-        parentProtoName = self.descriptor.parentPrototypeName
-        if self.descriptor.hasNamedPropertiesObject:
-            parentProtoType = "Handle"
-            getParentProto = "GetNamedPropertiesObject(aCx, aGlobal)"
-        elif parentProtoName is None:
+        (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(self.descriptor)
+        if protoHandleGetter is None:
             parentProtoType = "Rooted"
-            if self.descriptor.interface.getExtendedAttribute("ArrayClass"):
-                getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)"
-            elif self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
-                getParentProto = "aCx, JS_GetErrorPrototype(aCx)"
-            else:
-                getParentProto = "aCx, JS_GetObjectPrototype(aCx, aGlobal)"
+            getParentProto = "aCx, " + protoGetter
         else:
             parentProtoType = "Handle"
-            getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" %
-                              toBindingNamespace(parentProtoName))
-
-        parentWithInterfaceObject = self.descriptor.interface.parent
-        while (parentWithInterfaceObject and
-               not parentWithInterfaceObject.hasInterfaceObject()):
-            parentWithInterfaceObject = parentWithInterfaceObject.parent
-        if parentWithInterfaceObject:
-            parentIfaceName = parentWithInterfaceObject.identifier.name
-            parentDesc = self.descriptor.getDescriptor(parentIfaceName)
-            if parentDesc.workers:
-                parentIfaceName += "_workers"
-            getConstructorProto = ("%s::GetConstructorObject(aCx, aGlobal)" %
-                                   toBindingNamespace(parentIfaceName))
+            getParentProto = protoHandleGetter
+        getParentProto = getParentProto + "(aCx, aGlobal)"
+
+        (protoGetter, protoHandleGetter) = InterfaceObjectProtoGetter(self.descriptor)
+        if protoHandleGetter is None:
+            getConstructorProto = "aCx, " + protoGetter
+            constructorProtoType = "Rooted"
+        else:
+            getConstructorProto = protoHandleGetter
             constructorProtoType = "Handle"
-        else:
-            getConstructorProto = "aCx, JS_GetFunctionPrototype(aCx, aGlobal)"
-            constructorProtoType = "Rooted"
+        getConstructorProto += "(aCx, aGlobal)"
 
         needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
         needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
 
         # if we don't need to create anything, why are we generating this?
         assert needInterfaceObject or needInterfacePrototypeObject
 
         idsToInit = []
@@ -2810,90 +2857,123 @@ class CGGetPerInterfaceObject(CGAbstract
              * traced by TraceProtoAndIfaceCache() and its contents are never
              * changed after they have been set.
              */
             return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceCache.EntrySlotMustExist(${id}).address());
             """,
             id=self.id)
 
 
-class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
+class CGGetProtoObjectHandleMethod(CGGetPerInterfaceObject):
     """
     A method for getting the interface prototype object.
     """
     def __init__(self, descriptor):
-        CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
+        CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObjectHandle",
                                          "prototypes::")
 
     def definition_body(self):
         return dedent("""
             /* Get the interface prototype object for this class.  This will create the
                object as needed. */
             bool aDefineOnGlobal = true;
 
             """) + CGGetPerInterfaceObject.definition_body(self)
 
 
-class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
+class CGGetProtoObjectMethod(CGAbstractMethod):
+    """
+    A method for getting the interface prototype object.
+    """
+    def __init__(self, descriptor):
+        CGAbstractMethod.__init__(
+            self, descriptor, "GetProtoObject", "JSObject*", [Argument('JSContext*', 'aCx'),
+                Argument('JS::Handle<JSObject*>', 'aGlobal')])
+
+    def definition_body(self):
+        return "return GetProtoObjectHandle(aCx, aGlobal);\n"
+
+class CGGetConstructorObjectHandleMethod(CGGetPerInterfaceObject):
     """
     A method for getting the interface constructor object.
     """
     def __init__(self, descriptor):
         CGGetPerInterfaceObject.__init__(
-            self, descriptor, "GetConstructorObject",
+            self, descriptor, "GetConstructorObjectHandle",
             "constructors::",
             extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
 
     def definition_body(self):
         return dedent("""
             /* Get the interface object for this class.  This will create the object as
                needed. */
 
             """) + CGGetPerInterfaceObject.definition_body(self)
 
+class CGGetConstructorObjectMethod(CGAbstractMethod):
+    """
+    A method for getting the interface constructor object.
+    """
+    def __init__(self, descriptor):
+        CGAbstractMethod.__init__(
+            self, descriptor, "GetConstructorObject", "JSObject*", [Argument('JSContext*', 'aCx'),
+                Argument('JS::Handle<JSObject*>', 'aGlobal')])
+
+    def definition_body(self):
+        return "return GetConstructorObjectHandle(aCx, aGlobal);\n"
 
 class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'aGlobal')]
         CGAbstractStaticMethod.__init__(self, descriptor,
                                         'GetNamedPropertiesObject',
-                                        'JS::Handle<JSObject*>', args)
+                                        'JSObject*', args)
 
     def definition_body(self):
         parentProtoName = self.descriptor.parentPrototypeName
         if parentProtoName is None:
             getParentProto = ""
             parentProto = "nullptr"
         else:
             getParentProto = fill(
                 """
-                JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObject(aCx, aGlobal));
+                JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx, aGlobal));
                 if (!parentProto) {
-                  return js::NullPtr();
+                  return nullptr;
                 }
                 """,
                 parent=toBindingNamespace(parentProtoName))
             parentProto = "parentProto"
         return fill(
             """
             /* Make sure our global is sane.  Hopefully we can remove this sometime */
             if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
-              return JS::NullPtr();
+              return nullptr;
             }
 
             /* Check to see whether the named properties object has already been created */
             ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
 
             JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
             if (!namedPropertiesObject) {
               $*{getParentProto}
               namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
-            }
-            return JS::Handle<JSObject*>::fromMarkedLocation(namedPropertiesObject.address());
+              DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
+                DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(namedPropertiesObject));
+              MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
+                         "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
+              MOZ_ASSERT(clasp->mNativeHooks,
+                         "The named properties object for ${nativeType} should have NativePropertyHooks.");
+              MOZ_ASSERT(clasp->mNativeHooks->mResolveOwnProperty,
+                         "Don't know how to resolve the properties of the named properties object for ${nativeType}.");
+              MOZ_ASSERT(clasp->mNativeHooks->mEnumerateOwnProperties,
+                         "Don't know how to enumerate the properties of the named properties object for ${nativeType}.");
+            }
+            return namedPropertiesObject.get();
             """,
             getParentProto=getParentProto,
             ifaceName=self.descriptor.name,
             parentProto=parentProto,
             nativeType=self.descriptor.nativeType)
 
 
 class CGDefineDOMInterfaceMethod(CGAbstractMethod):
@@ -2916,30 +2996,30 @@ class CGDefineDOMInterfaceMethod(CGAbstr
     def define(self):
         if self.descriptor.workers:
             return ''
         return CGAbstractMethod.define(self)
 
     def definition_body(self):
         if len(self.descriptor.interface.namedConstructors) > 0:
             getConstructor = dedent("""
-                JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);
+                JSObject* interfaceObject = GetConstructorObjectHandle(aCx, aGlobal, aDefineOnGlobal);
                 if (!interfaceObject) {
                   return nullptr;
                 }
                 for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&InterfaceObjectClass.mBase); ++slot) {
                   JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject();
                   if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) {
                     return constructor;
                   }
                 }
                 return interfaceObject;
                 """)
         else:
-            getConstructor = "return GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);\n"
+            getConstructor = "return GetConstructorObjectHandle(aCx, aGlobal, aDefineOnGlobal);\n"
         return getConstructor
 
 
 class CGConstructorEnabled(CGAbstractMethod):
     """
     A method for testing whether we should be exposing this interface
     object or navigator property.  This can perform various tests
     depending on what conditions are specified on the interface.
@@ -3210,17 +3290,17 @@ class CGWrapWithCacheMethod(CGAbstractMe
               JSObject* obj = aCache->GetWrapper();
               if (obj) {
                 return obj;
               }
             }
 
             JSAutoCompartment ac(aCx, parent);
             JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, parent));
-            JS::Handle<JSObject*> proto = GetProtoObject(aCx, global);
+            JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx, global);
             if (!proto) {
               return nullptr;
             }
 
             $*{parent}
 
             $*{unforgeable}
 
@@ -3266,17 +3346,17 @@ class CGWrapNonWrapperCacheMethod(CGAbst
         self.properties = properties
 
     def definition_body(self):
         return fill(
             """
             $*{assertions}
 
             JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
-            JS::Handle<JSObject*> proto = GetProtoObject(aCx, global);
+            JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx, global);
             if (!proto) {
               return nullptr;
             }
 
             $*{global_}
 
             $*{unforgeable}
 
@@ -3328,17 +3408,17 @@ class CGWrapGlobalMethod(CGAbstractMetho
 
         return fill(
             """
             $*{assertions}
             MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                        "nsISupports must be on our primary inheritance chain");
 
             JS::Rooted<JSObject*> obj(aCx);
-            CreateGlobal<${nativeType}, GetProtoObject>(aCx,
+            CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx,
                                              aObject,
                                              aCache,
                                              Class.ToJSClass(),
                                              aOptions,
                                              aPrincipal,
                                              aInitStandardClasses,
                                              &obj);
             if (!obj) {
@@ -11130,18 +11210,21 @@ class CGDescriptor(CGThing):
 
         # CGCreateInterfaceObjectsMethod needs to come after our
         # CGDOMJSClass, if any.
         cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
 
         # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
         # to come after CGCreateInterfaceObjectsMethod.
         if descriptor.interface.hasInterfacePrototypeObject():
-            cgThings.append(CGGetProtoObjectMethod(descriptor))
+            cgThings.append(CGGetProtoObjectHandleMethod(descriptor))
+            if descriptor.interface.hasChildInterfaces():
+                cgThings.append(CGGetProtoObjectMethod(descriptor))
         if descriptor.interface.hasInterfaceObject():
+            cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
             cgThings.append(CGGetConstructorObjectMethod(descriptor))
 
         # See whether we need we need to generate an IsPermitted method
         if crossOriginGetters or crossOriginSetters or crossOriginMethods:
             cgThings.append(CGIsPermittedMethod(descriptor,
                                                 crossOriginGetters,
                                                 crossOriginSetters,
                                                 crossOriginMethods))
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -158,28 +158,32 @@ struct NativePropertyHooks
 
   // The NativePropertyHooks instance for the parent interface.
   const NativePropertyHooks* mProtoHooks;
 };
 
 enum DOMObjectType {
   eInstance,
   eInterface,
-  eInterfacePrototype
+  eInterfacePrototype,
+  eNamedPropertiesObject
 };
 
 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
  * global traces its array of WebIDL prototypes and constructors.
  */
-typedef JS::Handle<JSObject*> (*ProtoGetter)(JSContext* aCx,
-                                             JS::Handle<JSObject*> aGlobal);
+typedef JS::Handle<JSObject*> (*ProtoHandleGetter)(JSContext* aCx,
+                                                   JS::Handle<JSObject*> aGlobal);
 
 // Special JSClass for reflected DOM objects.
 struct DOMJSClass
 {
   // It would be nice to just inherit from JSClass, but that precludes pure
   // compile-time initialization of the form |DOMJSClass = {...};|, since C++
   // only allows brace initialization for aggregate/POD types.
   const js::Class mBase;
@@ -192,17 +196,17 @@ struct DOMJSClass
   // the proxy private if we use a proxy object.
   // Sometimes it's an nsISupports and sometimes it's not; this class tells
   // us which it is.
   const bool mDOMObjectIsISupports;
 
   const NativePropertyHooks* mNativeHooks;
 
   ParentGetter mGetParent;
-  ProtoGetter mGetProto;
+  ProtoHandleGetter mGetProto;
 
   // This stores the CC participant for the native, null if this class is for a
   // worker or for a native inheriting from nsISupports (we can get the CC
   // participant by QI'ing in that case).
   nsCycleCollectionParticipant* mParticipant;
 
   static const DOMJSClass* FromJSClass(const JSClass* base) {
     MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
@@ -220,28 +224,30 @@ 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 or eInterfacePrototype
+  // Either eInterface, eInterfacePrototype or eNamedPropertiesObject
   DOMObjectType mType;
 
   const NativePropertyHooks* mNativeHooks;
 
   // The value to return for toString() on this interface or interface prototype
   // object.
   const char* mToString;
 
   const prototypes::ID mPrototypeID;
   const uint32_t mDepth;
 
+  ProtoGetter mGetParentProto;
+
   static const DOMIfaceAndProtoJSClass* FromJSClass(const JSClass* base) {
     MOZ_ASSERT(base->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS);
     return reinterpret_cast<const DOMIfaceAndProtoJSClass*>(base);
   }
   static const DOMIfaceAndProtoJSClass* FromJSClass(const js::Class* base) {
     return FromJSClass(Jsvalify(base));
   }
 
--- a/dom/xbl/nsXBLProtoImpl.cpp
+++ b/dom/xbl/nsXBLProtoImpl.cpp
@@ -205,17 +205,17 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLP
   AutoJSContext cx;
   JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
   JS::Rooted<JS::Value> v(cx);
 
   JSAutoCompartment ac(cx, global);
   // Make sure the interface object is created before the prototype object
   // so that XULElement is hidden from content. See bug 909340.
   bool defineOnGlobal = dom::XULElementBinding::ConstructorEnabled(cx, global);
-  dom::XULElementBinding::GetConstructorObject(cx, global, defineOnGlobal);
+  dom::XULElementBinding::GetConstructorObjectHandle(cx, global, defineOnGlobal);
 
   rv = nsContentUtils::WrapNative(cx, aBoundElement, &v,
                                   /* aAllowWrapping = */ false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JS::Rooted<JSObject*> value(cx, &v.toObject());
   JSAutoCompartment ac2(cx, value);