Fix for bug 798264 (Split property tables). r=bz.
authorPeter Van der Beken <peterv@propagandism.org>
Tue, 09 Oct 2012 20:50:05 +0200
changeset 110356 d178bdc3a5572677547f829702eb30e6cf42e183
parent 110355 b4ecedf655f2f1b7bee66b738fcaf65d3a2725f7
child 110357 e3239845f9d2222b5e385247ba19eefecc5ad2ae
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersbz
bugs798264
milestone19.0a1
Fix for bug 798264 (Split property tables). r=bz.
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/tests/mochitest/chrome/file_bug799299.xul
dom/tests/mochitest/chrome/test_bug799299.xul
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3,16 +3,17 @@
 /* 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 <stdarg.h>
 
 #include "BindingUtils.h"
 
+#include "AccessCheck.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XPCQuickStubs.h"
 #include "nsIXPConnect.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -135,18 +136,18 @@ InterfaceObjectToString(JSContext* cx, u
 
   return xpc::NonVoidStringToJsval(cx, str, vp);
 }
 
 static JSObject*
 CreateInterfaceObject(JSContext* cx, JSObject* global, JSObject* receiver,
                       JSClass* constructorClass, JSNative constructorNative,
                       unsigned ctorNargs, JSObject* proto,
-                      Prefable<JSFunctionSpec>* staticMethods,
-                      Prefable<ConstantSpec>* constants,
+                      const NativeProperties* properties,
+                      const NativeProperties* chromeOnlyProperties,
                       const char* name)
 {
   JSObject* constructor;
   if (constructorClass) {
     JSObject* functionProto = JS_GetFunctionPrototype(cx, global);
     if (!functionProto) {
       return NULL;
     }
@@ -159,20 +160,16 @@ CreateInterfaceObject(JSContext* cx, JSO
       return NULL;
     }
     constructor = JS_GetFunctionObject(fun);
   }
   if (!constructor) {
     return NULL;
   }
 
-  if (staticMethods && !DefinePrefable(cx, constructor, staticMethods)) {
-    return NULL;
-  }
-
   if (constructorClass) {
     JSFunction* toString = js::DefineFunctionWithReserved(cx, constructor,
                                                           "toString",
                                                           InterfaceObjectToString,
                                                           0, 0);
     if (!toString) {
       return NULL;
     }
@@ -184,18 +181,38 @@ CreateInterfaceObject(JSContext* cx, JSO
     JSString *str = ::JS_InternString(cx, name);
     if (!str) {
       return NULL;
     }
     js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT,
                                   STRING_TO_JSVAL(str));
   }
 
-  if (constants && !DefinePrefable(cx, constructor, constants)) {
-    return NULL;
+  if (properties) {
+    if (properties->staticMethods &&
+        !DefinePrefable(cx, constructor, properties->staticMethods)) {
+      return nullptr;
+    }
+
+    if (properties->constants &&
+        !DefinePrefable(cx, constructor, properties->constants)) {
+      return nullptr;
+    }
+  }
+
+  if (chromeOnlyProperties) {
+    if (chromeOnlyProperties->staticMethods &&
+        !DefinePrefable(cx, constructor, chromeOnlyProperties->staticMethods)) {
+      return nullptr;
+    }
+
+    if (chromeOnlyProperties->constants &&
+        !DefinePrefable(cx, constructor, chromeOnlyProperties->constants)) {
+      return nullptr;
+    }
   }
 
   if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
     return NULL;
   }
 
   JSBool alreadyDefined;
   if (!JS_AlreadyHasOwnProperty(cx, receiver, name, &alreadyDefined)) {
@@ -210,81 +227,107 @@ CreateInterfaceObject(JSContext* cx, JSO
   }
 
   return constructor;
 }
 
 static JSObject*
 CreateInterfacePrototypeObject(JSContext* cx, JSObject* global,
                                JSObject* parentProto, JSClass* protoClass,
-                               Prefable<JSFunctionSpec>* methods,
-                               Prefable<JSPropertySpec>* properties,
-                               Prefable<ConstantSpec>* constants)
+                               const NativeProperties* properties,
+                               const NativeProperties* chromeOnlyProperties)
 {
   JSObject* ourProto = JS_NewObjectWithUniqueType(cx, protoClass, parentProto,
                                                   global);
   if (!ourProto) {
     return NULL;
   }
 
-  if (methods && !DefinePrefable(cx, ourProto, methods)) {
-    return NULL;
+  if (properties) {
+    if (properties->methods &&
+        !DefinePrefable(cx, ourProto, properties->methods)) {
+      return nullptr;
+    }
+
+    if (properties->attributes &&
+        !DefinePrefable(cx, ourProto, properties->attributes)) {
+      return nullptr;
+    }
+
+    if (properties->constants &&
+        !DefinePrefable(cx, ourProto, properties->constants)) {
+      return nullptr;
+    }
   }
 
-  if (properties && !DefinePrefable(cx, ourProto, properties)) {
-    return NULL;
-  }
+  if (chromeOnlyProperties) {
+    if (chromeOnlyProperties->methods &&
+        !DefinePrefable(cx, ourProto, chromeOnlyProperties->methods)) {
+      return nullptr;
+    }
 
-  if (constants && !DefinePrefable(cx, ourProto, constants)) {
-    return NULL;
+    if (chromeOnlyProperties->attributes &&
+        !DefinePrefable(cx, ourProto, chromeOnlyProperties->attributes)) {
+      return nullptr;
+    }
+
+    if (chromeOnlyProperties->constants &&
+        !DefinePrefable(cx, ourProto, chromeOnlyProperties->constants)) {
+      return nullptr;
+    }
   }
 
   return ourProto;
 }
 
 JSObject*
 CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject *receiver,
                        JSObject* protoProto, JSClass* protoClass,
                        JSClass* constructorClass, JSNative constructor,
                        unsigned ctorNargs, const DOMClass* domClass,
-                       Prefable<JSFunctionSpec>* methods,
-                       Prefable<JSPropertySpec>* properties,
-                       Prefable<ConstantSpec>* constants,
-                       Prefable<JSFunctionSpec>* staticMethods, const char* name)
+                       const NativeProperties* properties,
+                       const NativeProperties* chromeOnlyProperties,
+                       const char* name)
 {
   MOZ_ASSERT(protoClass || constructorClass || constructor,
              "Need at least one class or a constructor!");
-  MOZ_ASSERT(!(methods || properties) || protoClass,
+  MOZ_ASSERT(!((properties &&
+                (properties->methods || properties->attributes)) ||
+               (chromeOnlyProperties &&
+                (chromeOnlyProperties->methods ||
+                 chromeOnlyProperties->attributes))) || protoClass,
              "Methods or properties but no protoClass!");
-  MOZ_ASSERT(!staticMethods || constructorClass || constructor,
+  MOZ_ASSERT(!((properties && properties->staticMethods) ||
+               (chromeOnlyProperties && chromeOnlyProperties->staticMethods)) ||
+             constructorClass || constructor,
              "Static methods but no constructorClass or constructor!");
   MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
              "Must have name precisely when we have an interface object");
   MOZ_ASSERT(!constructorClass || !constructor);
 
   JSObject* proto;
   if (protoClass) {
     proto = CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
-                                           methods, properties, constants);
+                                           properties, chromeOnlyProperties);
     if (!proto) {
       return NULL;
     }
 
     js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
                         JS::PrivateValue(const_cast<DOMClass*>(domClass)));
   }
   else {
     proto = NULL;
   }
 
   JSObject* interface;
   if (constructorClass || constructor) {
     interface = CreateInterfaceObject(cx, global, receiver, constructorClass,
                                       constructor, ctorNargs, proto,
-                                      staticMethods, constants, name);
+                                      properties, chromeOnlyProperties, name);
     if (!interface) {
       return NULL;
     }
   }
 
   return protoClass ? proto : interface;
 }
 
@@ -415,185 +458,217 @@ QueryInterface(JSContext* cx, unsigned a
 }
 
 JSBool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
-bool
+static bool
 XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
                     JSPropertyDescriptor* desc,
-                    // And the things we need to determine the descriptor
-                    Prefable<JSFunctionSpec>* methods,
-                    jsid* methodIds,
-                    JSFunctionSpec* methodSpecs,
-                    size_t methodCount,
-                    Prefable<JSPropertySpec>* attributes,
-                    jsid* attributeIds,
-                    JSPropertySpec* attributeSpecs,
-                    size_t attributeCount,
-                    Prefable<ConstantSpec>* constants,
-                    jsid* constantIds,
-                    ConstantSpec* constantSpecs,
-                    size_t constantCount)
+                    const NativeProperties* nativeProperties)
 {
-  for (size_t prefIdx = 0; prefIdx < methodCount; ++prefIdx) {
-    MOZ_ASSERT(methods[prefIdx].specs);
-    if (methods[prefIdx].enabled) {
-      // Set i to be the index into our full list of ids/specs that we're
-      // looking at now.
-      size_t i = methods[prefIdx].specs - methodSpecs;
-      for ( ; methodIds[i] != JSID_VOID; ++i) {
-        if (id == methodIds[i]) {
-          JSFunction *fun = JS_NewFunctionById(cx, methodSpecs[i].call.op,
-                                               methodSpecs[i].nargs, 0,
-                                               wrapper, id);
-          if (!fun) {
-            return false;
+  if (nativeProperties->methods) {
+    Prefable<JSFunctionSpec>* method;
+    for (method = nativeProperties->methods; method->specs; ++method) {
+      if (method->enabled) {
+        // Set i to be the index into our full list of ids/specs that we're
+        // looking at now.
+        size_t i = method->specs - nativeProperties->methodsSpecs;
+        for ( ; nativeProperties->methodIds[i] != JSID_VOID; ++i) {
+          if (id == nativeProperties->methodIds[i]) {
+            JSFunctionSpec& methodSpec = nativeProperties->methodsSpecs[i];
+            JSFunction *fun = JS_NewFunctionById(cx, methodSpec.call.op,
+                                                 methodSpec.nargs, 0,
+                                                 wrapper, id);
+            if (!fun) {
+              return false;
+            }
+            SET_JITINFO(fun, methodSpec.call.info);
+            JSObject *funobj = JS_GetFunctionObject(fun);
+            desc->value.setObject(*funobj);
+            desc->attrs = methodSpec.flags;
+            desc->obj = wrapper;
+            desc->setter = nullptr;
+            desc->getter = nullptr;
+           return true;
           }
-          SET_JITINFO(fun, methodSpecs[i].call.info);
-          JSObject *funobj = JS_GetFunctionObject(fun);
-          desc->value.setObject(*funobj);
-          desc->attrs = methodSpecs[i].flags;
-          desc->obj = wrapper;
-          desc->setter = nullptr;
-          desc->getter = nullptr;
-          return true;
         }
       }
     }
   }
 
-  for (size_t prefIdx = 0; prefIdx < attributeCount; ++prefIdx) {
-    MOZ_ASSERT(attributes[prefIdx].specs);
-    if (attributes[prefIdx].enabled) {
-      // Set i to be the index into our full list of ids/specs that we're
-      // looking at now.
-      size_t i = attributes[prefIdx].specs - attributeSpecs;
-      for ( ; attributeIds[i] != JSID_VOID; ++i) {
-        if (id == attributeIds[i]) {
-          // Because of centralization, we need to make sure we fault in the
-          // JitInfos as well. At present, until the JSAPI changes, the easiest
-          // way to do this is wrap them up as functions ourselves.
-          desc->attrs = attributeSpecs[i].flags & ~JSPROP_NATIVE_ACCESSORS;
-          // They all have getters, so we can just make it.
-          JSObject *global = JS_GetGlobalForObject(cx, wrapper);
-          JSFunction *fun = JS_NewFunction(cx, (JSNative)attributeSpecs[i].getter.op,
-                                           0, 0, global, NULL);
-          if (!fun)
-            return false;
-          SET_JITINFO(fun, attributeSpecs[i].getter.info);
-          JSObject *funobj = JS_GetFunctionObject(fun);
-          desc->getter = js::CastAsJSPropertyOp(funobj);
-          desc->attrs |= JSPROP_GETTER;
-          if (attributeSpecs[i].setter.op) {
-            // We have a setter! Make it.
-            fun = JS_NewFunction(cx, (JSNative)attributeSpecs[i].setter.op,
-                                 1, 0, global, NULL);
+  if (nativeProperties->attributes) {
+    Prefable<JSPropertySpec>* attr;
+    for (attr = nativeProperties->attributes; attr->specs; ++attr) {
+      if (attr->enabled) {
+        // Set i to be the index into our full list of ids/specs that we're
+        // looking at now.
+        size_t i = attr->specs - nativeProperties->attributeSpecs;
+        for ( ; nativeProperties->attributeIds[i] != JSID_VOID; ++i) {
+          if (id == nativeProperties->attributeIds[i]) {
+            JSPropertySpec& attrSpec = nativeProperties->attributeSpecs[i];
+            // Because of centralization, we need to make sure we fault in the
+            // JitInfos as well. At present, until the JSAPI changes, the easiest
+            // way to do this is wrap them up as functions ourselves.
+            desc->attrs = attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS;
+            // They all have getters, so we can just make it.
+            JSObject *global = JS_GetGlobalForObject(cx, wrapper);
+            JSFunction *fun = JS_NewFunction(cx, (JSNative)attrSpec.getter.op,
+                                             0, 0, global, nullptr);
             if (!fun)
               return false;
-            SET_JITINFO(fun, attributeSpecs[i].setter.info);
-            funobj = JS_GetFunctionObject(fun);
-            desc->setter = js::CastAsJSStrictPropertyOp(funobj);
-            desc->attrs |= JSPROP_SETTER;
-          } else {
-            desc->setter = NULL;
+            SET_JITINFO(fun, attrSpec.getter.info);
+            JSObject *funobj = JS_GetFunctionObject(fun);
+            desc->getter = js::CastAsJSPropertyOp(funobj);
+            desc->attrs |= JSPROP_GETTER;
+            if (attrSpec.setter.op) {
+              // We have a setter! Make it.
+              fun = JS_NewFunction(cx, (JSNative)attrSpec.setter.op, 1, 0,
+                                   global, nullptr);
+              if (!fun)
+                return false;
+              SET_JITINFO(fun, attrSpec.setter.info);
+              funobj = JS_GetFunctionObject(fun);
+              desc->setter = js::CastAsJSStrictPropertyOp(funobj);
+              desc->attrs |= JSPROP_SETTER;
+            } else {
+              desc->setter = nullptr;
+            }
+            desc->obj = wrapper;
+            return true;
           }
-          desc->obj = wrapper;
-          return true;
         }
       }
     }
   }
 
-  for (size_t prefIdx = 0; prefIdx < constantCount; ++prefIdx) {
-    MOZ_ASSERT(constants[prefIdx].specs);
-    if (constants[prefIdx].enabled) {
-      // Set i to be the index into our full list of ids/specs that we're
-      // looking at now.
-      size_t i = constants[prefIdx].specs - constantSpecs;
-      for ( ; constantIds[i] != JSID_VOID; ++i) {
-        if (id == constantIds[i]) {
-          desc->attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
-          desc->obj = wrapper;
-          desc->value = constantSpecs[i].value;
-          return true;
+  if (nativeProperties->constants) {
+    Prefable<ConstantSpec>* constant;
+    for (constant = nativeProperties->constants; constant->specs; ++constant) {
+      if (constant->enabled) {
+        // Set i to be the index into our full list of ids/specs that we're
+        // looking at now.
+        size_t i = constant->specs - nativeProperties->constantSpecs;
+        for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
+          if (id == nativeProperties->constantIds[i]) {
+            desc->attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
+            desc->obj = wrapper;
+            desc->value = nativeProperties->constantSpecs[i].value;
+            return true;
+          }
         }
       }
     }
   }
 
   return true;
 }
 
 bool
+XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
+                    JSPropertyDescriptor* desc,
+                    const NativeProperties* nativeProperties,
+                    const NativeProperties* chromeOnlyNativeProperties)
+{
+  if (nativeProperties &&
+      !XrayResolveProperty(cx, wrapper, id, desc, nativeProperties)) {
+    return false;
+  }
+
+  if (!desc->obj &&
+      chromeOnlyNativeProperties &&
+      xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
+      !XrayResolveProperty(cx, wrapper, id, desc, chromeOnlyNativeProperties)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool
 XrayEnumerateProperties(JS::AutoIdVector& props,
-                        Prefable<JSFunctionSpec>* methods,
-                        jsid* methodIds,
-                        JSFunctionSpec* methodSpecs,
-                        size_t methodCount,
-                        Prefable<JSPropertySpec>* attributes,
-                        jsid* attributeIds,
-                        JSPropertySpec* attributeSpecs,
-                        size_t attributeCount,
-                        Prefable<ConstantSpec>* constants,
-                        jsid* constantIds,
-                        ConstantSpec* constantSpecs,
-                        size_t constantCount)
+                        const NativeProperties* nativeProperties)
 {
-  for (size_t prefIdx = 0; prefIdx < methodCount; ++prefIdx) {
-    MOZ_ASSERT(methods[prefIdx].specs);
-    if (methods[prefIdx].enabled) {
-      // Set i to be the index into our full list of ids/specs that we're
-      // looking at now.
-      size_t i = methods[prefIdx].specs - methodSpecs;
-      for ( ; methodIds[i] != JSID_VOID; ++i) {
-        if ((methodSpecs[i].flags & JSPROP_ENUMERATE) &&
-            !props.append(methodIds[i])) {
-          return false;
+  if (nativeProperties->methods) {
+    Prefable<JSFunctionSpec>* method;
+    for (method = nativeProperties->methods; method->specs; ++method) {
+      if (method->enabled) {
+        // Set i to be the index into our full list of ids/specs that we're
+        // looking at now.
+        size_t i = method->specs - nativeProperties->methodsSpecs;
+        for ( ; nativeProperties->methodIds[i] != JSID_VOID; ++i) {
+          if ((nativeProperties->methodsSpecs[i].flags & JSPROP_ENUMERATE) &&
+              !props.append(nativeProperties->methodIds[i])) {
+            return false;
+          }
         }
       }
     }
   }
 
-  for (size_t prefIdx = 0; prefIdx < attributeCount; ++prefIdx) {
-    MOZ_ASSERT(attributes[prefIdx].specs);
-    if (attributes[prefIdx].enabled) {
-      // Set i to be the index into our full list of ids/specs that we're
-      // looking at now.
-      size_t i = attributes[prefIdx].specs - attributeSpecs;
-      for ( ; attributeIds[i] != JSID_VOID; ++i) {
-        if ((attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
-            !props.append(attributeIds[i])) {
-          return false;
+  if (nativeProperties->attributes) {
+    Prefable<JSPropertySpec>* attr;
+    for (attr = nativeProperties->attributes; attr->specs; ++attr) {
+      if (attr->enabled) {
+        // Set i to be the index into our full list of ids/specs that we're
+        // looking at now.
+        size_t i = attr->specs - nativeProperties->attributeSpecs;
+        for ( ; nativeProperties->attributeIds[i] != JSID_VOID; ++i) {
+          if ((nativeProperties->attributeSpecs[i].flags & JSPROP_ENUMERATE) &&
+              !props.append(nativeProperties->attributeIds[i])) {
+            return false;
+          }
         }
       }
     }
   }
 
-  for (size_t prefIdx = 0; prefIdx < constantCount; ++prefIdx) {
-    MOZ_ASSERT(constants[prefIdx].specs);
-    if (constants[prefIdx].enabled) {
-      // Set i to be the index into our full list of ids/specs that we're
-      // looking at now.
-      size_t i = constants[prefIdx].specs - constantSpecs;
-      for ( ; constantIds[i] != JSID_VOID; ++i) {
-        if (!props.append(constantIds[i])) {
-          return false;
+  if (nativeProperties->constants) {
+    Prefable<ConstantSpec>* constant;
+    for (constant = nativeProperties->constants; constant->specs; ++constant) {
+      if (constant->enabled) {
+        // Set i to be the index into our full list of ids/specs that we're
+        // looking at now.
+        size_t i = constant->specs - nativeProperties->constantSpecs;
+        for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
+          if (!props.append(nativeProperties->constantIds[i])) {
+            return false;
+          }
         }
       }
     }
   }
 
   return true;
 }
 
 bool
+XrayEnumerateProperties(JSObject* wrapper,
+                        JS::AutoIdVector& props,
+                        const NativeProperties* nativeProperties,
+                        const NativeProperties* chromeOnlyNativeProperties)
+{
+  if (nativeProperties &&
+      !XrayEnumerateProperties(props, nativeProperties)) {
+    return false;
+  }
+
+  if (chromeOnlyNativeProperties &&
+      xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
+      !XrayEnumerateProperties(props, chromeOnlyNativeProperties)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool
 GetPropertyOnPrototype(JSContext* cx, JSObject* proxy, jsid id, bool* found,
                        JS::Value* vp)
 {
   JSObject* proto;
   if (!js::GetObjectProto(cx, proxy, &proto)) {
     return false;
   }
   if (!proto) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -339,16 +339,32 @@ struct Prefable {
   // A boolean indicating whether this set of specs is enabled
   bool enabled;
   // Array of specs, terminated in whatever way is customary for T.
   // Null to indicate a end-of-array for Prefable, when such an
   // indicator is needed.
   T* specs;
 };
 
+struct NativeProperties
+{
+  Prefable<JSFunctionSpec>* staticMethods;
+  jsid* staticMethodIds;
+  JSFunctionSpec* staticMethodsSpecs;
+  Prefable<JSFunctionSpec>* methods;
+  jsid* methodIds;
+  JSFunctionSpec* methodsSpecs;
+  Prefable<JSPropertySpec>* attributes;
+  jsid* attributeIds;
+  JSPropertySpec* attributeSpecs;
+  Prefable<ConstantSpec>* constants;
+  jsid* constantIds;
+  ConstantSpec* constantSpecs;
+};
+
 /*
  * Create a DOM interface object (if constructorClass is non-null) and/or a
  * DOM interface prototype object (if protoClass is non-null).
  *
  * global is used as the parent of the interface object and the interface
  *        prototype object
  * receiver is the object on which we need to define the interface object as a
  *          property
@@ -360,43 +376,41 @@ struct Prefable {
  *                  This is null if we should not create an interface object or
  *                  if it should be a function object.
  * constructor is the JSNative to use as a constructor.  If this is non-null, it
  *             should be used as a JSNative to back the interface object, which
  *             should be a Function.  If this is null, then we should create an
  *             object of constructorClass, unless that's also null, in which
  *             case we should not create an interface object at all.
  * ctorNargs is the length of the constructor function; 0 if no constructor
- * instanceClass is the JSClass of instance objects for this class.  This can
- *               be null if this is not a concrete proto.
- * methods and properties are to be defined on the interface prototype object;
- *                        these arguments are allowed to be null if there are no
- *                        methods or properties respectively.
- * constants are to be defined on the interface object and on the interface
- *           prototype object; allowed to be null if there are no constants.
- * staticMethods are to be defined on the interface object; allowed to be null
- *               if there are no static methods.
+ * domClass is the DOMClass of instance objects for this class.  This can be
+ *          null if this is not a concrete proto.
+ * properties contains the methods, attributes and constants to be defined on
+ *            objects in any compartment.
+ * chromeProperties contains the methods, attributes and constants to be defined
+ *                  on objects in chrome compartments. This must be null if the
+ *                  interface doesn't have any ChromeOnly properties or if the
+ *                  object is being created in non-chrome compartment.
  *
  * At least one of protoClass and constructorClass should be non-null.
  * If constructorClass is non-null, the resulting interface object will be
  * defined on the given global with property name |name|, which must also be
  * non-null.
  *
  * returns the interface prototype object if protoClass is non-null, else it
  * returns the interface object.
  */
 JSObject*
 CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* receiver,
                        JSObject* protoProto, JSClass* protoClass,
                        JSClass* constructorClass, JSNative constructor,
                        unsigned ctorNargs, const DOMClass* domClass,
-                       Prefable<JSFunctionSpec>* methods,
-                       Prefable<JSPropertySpec>* properties,
-                       Prefable<ConstantSpec>* constants,
-                       Prefable<JSFunctionSpec>* staticMethods, const char* name);
+                       const NativeProperties* properties,
+                       const NativeProperties* chromeProperties,
+                       const char* name);
 
 template <class T>
 inline bool
 WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp)
 {
   JSObject* obj = value->GetWrapper();
   if (obj && js::GetObjectCompartment(obj) == js::GetObjectCompartment(scope)) {
     *vp = JS::ObjectValue(*obj);
@@ -1173,44 +1187,24 @@ public:
       storage.addr()->~T();
     }
 };
 
 // Implementation of the bits that XrayWrapper needs
 bool
 XrayResolveProperty(JSContext* cx, JSObject* wrapper, jsid id,
                     JSPropertyDescriptor* desc,
-                    // And the things we need to determine the descriptor
-                    Prefable<JSFunctionSpec>* methods,
-                    jsid* methodIds,
-                    JSFunctionSpec* methodSpecs,
-                    size_t methodCount,
-                    Prefable<JSPropertySpec>* attributes,
-                    jsid* attributeIds,
-                    JSPropertySpec* attributeSpecs,
-                    size_t attributeCount,
-                    Prefable<ConstantSpec>* constants,
-                    jsid* constantIds,
-                    ConstantSpec* constantSpecs,
-                    size_t constantCount);
+                    const NativeProperties* nativeProperties,
+                    const NativeProperties* chromeOnlyNativeProperties);
 
 bool
-XrayEnumerateProperties(JS::AutoIdVector& props,
-                        Prefable<JSFunctionSpec>* methods,
-                        jsid* methodIds,
-                        JSFunctionSpec* methodSpecs,
-                        size_t methodCount,
-                        Prefable<JSPropertySpec>* attributes,
-                        jsid* attributeIds,
-                        JSPropertySpec* attributeSpecs,
-                        size_t attributeCount,
-                        Prefable<ConstantSpec>* constants,
-                        jsid* constantIds,
-                        ConstantSpec* constantSpecs,
-                        size_t constantCount);
+XrayEnumerateProperties(JSObject* wrapper,
+                        JS::AutoIdVector& props,
+                        const NativeProperties* nativeProperties,
+                        const NativeProperties* chromeOnlyNativeProperties);
 
 // Transfer reference in ptr to smartPtr.
 template<class T>
 inline void
 Take(nsRefPtr<T>& smartPtr, T* ptr)
 {
   smartPtr = dont_AddRef(ptr);
 }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -861,55 +861,52 @@ def isChromeOnly(m):
     return m.getExtendedAttribute("ChromeOnly")
 
 class PropertyDefiner:
     """
     A common superclass for defining things on prototype objects.
 
     Subclasses should implement generateArray to generate the actual arrays of
     things we're defining.  They should also set self.chrome to the list of
-    things exposed to chrome and self.regular to the list of things exposed to
-    web pages.  self.chrome must be a superset of self.regular but also include
-    all the ChromeOnly stuff.
+    things only exposed to chrome and self.regular to the list of things exposed
+    to both chrome and web pages.
     """
     def __init__(self, descriptor, name):
         self.descriptor = descriptor
         self.name = name
         # self.prefCacheData will store an array of (prefname, bool*)
         # pairs for our bool var caches.  generateArray will fill it
         # in as needed.
         self.prefCacheData = []
     def hasChromeOnly(self):
-        return len(self.chrome) > len(self.regular)
+        return len(self.chrome) > 0
     def hasNonChromeOnly(self):
         return len(self.regular) > 0
     def variableName(self, chrome):
-        if chrome and self.hasChromeOnly():
-            return "sChrome" + self.name
-        if self.hasNonChromeOnly():
-            return "s" + self.name
-        return "NULL"
-    def usedForXrays(self, chrome):
-        # We only need Xrays for methods, attributes and constants.  And we only
-        # need them for the non-chrome ones if we have no chromeonly things.
-        # Otherwise (we have chromeonly attributes) we need Xrays for the chrome
-        # methods/attributes/constants.  Finally, in workers there are no Xrays.
-        return ((self.name is "Methods" or self.name is "Attributes" or
-                 self.name is "Constants") and
-                chrome == self.hasChromeOnly() and
-                not self.descriptor.workers)
+        if chrome:
+            if self.hasChromeOnly():
+                return "sChrome" + self.name
+        else:
+            if self.hasNonChromeOnly():
+                return "s" + self.name
+        return "nullptr"
+    def usedForXrays(self):
+        # We only need Xrays for methods, attributes and constants, but in
+        # workers there are no Xrays.
+        return (self.name is "Methods" or self.name is "Attributes" or
+                self.name is "Constants") and not self.descriptor.workers
 
     def __str__(self):
         # We only need to generate id arrays for things that will end
         # up used via ResolveProperty or EnumerateProperties.
         str = self.generateArray(self.regular, self.variableName(False),
-                                 self.usedForXrays(False))
+                                 self.usedForXrays())
         if self.hasChromeOnly():
             str += self.generateArray(self.chrome, self.variableName(True),
-                                      self.usedForXrays(True))
+                                      self.usedForXrays())
         return str
 
     @staticmethod
     def getControllingPref(interfaceMember):
         prefName = interfaceMember.getExtendedAttribute("Pref")
         if prefName is None:
             return None
         # It's a list of strings
@@ -1013,31 +1010,25 @@ class MethodDefiner(PropertyDefiner):
         #       identifier. For now we check if the name starts with __
         methods = [m for m in descriptor.interface.members if
                    m.isMethod() and m.isStatic() == static and
                    not m.isIdentifierLess()]
         self.chrome = [{"name": m.identifier.name,
                         "length": methodLength(m),
                         "flags": "JSPROP_ENUMERATE",
                         "pref": PropertyDefiner.getControllingPref(m) }
-                       for m in methods]
+                       for m in methods if isChromeOnly(m)]
         self.regular = [{"name": m.identifier.name,
                          "length": methodLength(m),
                          "flags": "JSPROP_ENUMERATE",
                          "pref": PropertyDefiner.getControllingPref(m) }
                         for m in methods if not isChromeOnly(m)]
 
         # FIXME Check for an existing iterator on the interface first.
         if any(m.isGetter() and m.isIndexed() for m in methods):
-            self.chrome.append({"name": 'iterator',
-                                "methodInfo": False,
-                                "nativeName": "JS_ArrayIterator",
-                                "length": 0,
-                                "flags": "JSPROP_ENUMERATE",
-                                "pref": None })
             self.regular.append({"name": 'iterator',
                                  "methodInfo": False,
                                  "nativeName": "JS_ArrayIterator",
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
                                  "pref": None })
 
         if not descriptor.interface.parent and not static and descriptor.nativeOwnership == 'nsisupports':
@@ -1078,18 +1069,19 @@ class MethodDefiner(PropertyDefiner):
             '  JS_FS_END',
             'JSFunctionSpec',
             pref, specData, doIdArrays)
 
 class AttrDefiner(PropertyDefiner):
     def __init__(self, descriptor, name):
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
-        self.chrome = [m for m in descriptor.interface.members if m.isAttr()]
-        self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+        attributes = [m for m in descriptor.interface.members if m.isAttr()]
+        self.chrome = [m for m in attributes if isChromeOnly(m)]
+        self.regular = [m for m in attributes if not isChromeOnly(m)]
 
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def flags(attr):
             return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
 
@@ -1122,18 +1114,19 @@ class AttrDefiner(PropertyDefiner):
 
 class ConstDefiner(PropertyDefiner):
     """
     A class for definining constants on the interface object
     """
     def __init__(self, descriptor, name):
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
-        self.chrome = [m for m in descriptor.interface.members if m.isConst()]
-        self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+        constants = [m for m in descriptor.interface.members if m.isConst()]
+        self.chrome = [m for m in constants if isChromeOnly(m)]
+        self.regular = [m for m in constants if not isChromeOnly(m)]
 
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def specData(const):
             return (const.identifier.name,
                     convertConstIDLValueToJSVal(const.value))
@@ -1156,29 +1149,59 @@ class PropertyArrays():
     def arrayNames():
         return [ "staticMethods", "methods", "attrs", "consts" ]
 
     @staticmethod
     def xrayRelevantArrayNames():
         return [ "methods", "attrs", "consts" ]
 
     def hasChromeOnly(self):
-        return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(),
-                      self.arrayNames(), False)
-    def variableNames(self, chrome):
-        names = {}
-        for array in self.arrayNames():
-            names[array] = getattr(self, array).variableName(chrome)
-        return names
+        return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
+    def hasNonChromeOnly(self):
+        return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
     def __str__(self):
         define = ""
         for array in self.arrayNames():
             define += str(getattr(self, array))
         return define
 
+class CGNativeProperties(CGList):
+    def __init__(self, descriptor, properties):
+        def generateNativeProperties(name, chrome):
+            def check(p):
+                return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
+
+            nativeProps = []
+            for array in properties.arrayNames():
+                propertyArray = getattr(properties, array)
+                if check(propertyArray):
+                    if descriptor.workers:
+                        props = "%(name)s, nullptr, %(name)s_specs"
+                    else:
+                        if propertyArray.usedForXrays():
+                            ids = "%(name)s_ids"
+                        else:
+                            ids = "nullptr"
+                        props = "%(name)s, " + ids + ", %(name)s_specs"
+                    props = (props %
+                             { 'name': propertyArray.variableName(chrome) })
+                else:
+                    props = "nullptr, nullptr, nullptr"
+                nativeProps.append(CGGeneric(props))
+            return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
+                             pre="static const NativeProperties %s = {\n" % name,
+                             post="\n};")
+        
+        regular = generateNativeProperties("sNativeProperties", False)
+        chrome = generateNativeProperties("sChromeOnlyNativeProperties", True)
+        CGList.__init__(self, [regular, chrome], "\n\n")
+
+    def declare(self):
+        return ""
+
 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
     """
     Generate the CreateInterfaceObjects method for an interface descriptor.
 
     properties should be a PropertyArrays instance.
     """
     def __init__(self, descriptor, properties):
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
@@ -1204,17 +1227,17 @@ class CGCreateInterfaceObjectsMethod(CGA
         # There is no need to init any IDs in workers, because worker bindings
         # don't have Xrays.
         if not self.descriptor.workers:
             for var in self.properties.xrayRelevantArrayNames():
                 props = getattr(self.properties, var)
                 # We only have non-chrome ids to init if we have no chrome ids.
                 if props.hasChromeOnly():
                     idsToInit.append(props.variableName(True))
-                elif props.hasNonChromeOnly():
+                if props.hasNonChromeOnly():
                     idsToInit.append(props.variableName(False))
         if len(idsToInit) > 0:
             initIds = CGList(
                 [CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for
                  varname in idsToInit], ' ||\n')
             if len(idsToInit) > 1:
                 initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
             initIds = CGList(
@@ -1265,42 +1288,44 @@ class CGCreateInterfaceObjectsMethod(CGA
         if self.descriptor.concrete:
             if self.descriptor.proxy:
                 domClass = "&Class"
             else:
                 domClass = "&Class.mClass"
         else:
             domClass = "nullptr"
 
+        if self.properties.hasNonChromeOnly():
+            properties = "&sNativeProperties"
+        else:
+            properties = "nullptr"
+        if self.properties.hasChromeOnly():
+            if self.descriptor.workers:
+                accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
+            else:
+                accessCheck = "xpc::AccessCheck::isChrome(aGlobal)"
+            chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr"
+        else:
+            chromeProperties = "nullptr"
         call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,
                                    %s, %s, %s, %d,
                                    %s,
-                                   %%(methods)s, %%(attrs)s,
-                                   %%(consts)s, %%(staticMethods)s,
+                                   %s,
+                                   %s,
                                    %s);""" % (
             "&PrototypeClass" if needInterfacePrototypeObject else "NULL",
             "&InterfaceObjectClass" if needInterfaceObjectClass else "NULL",
             constructHook if needConstructor else "NULL",
             constructArgs,
             domClass,
+            properties,
+            chromeProperties,
             '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL")
-        if self.properties.hasChromeOnly():
-            if self.descriptor.workers:
-                accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
-            else:
-                accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
-            chrome = CGIfWrapper(CGGeneric(call % self.properties.variableNames(True)),
-                                 accessCheck)
-            chrome = CGWrapper(chrome, pre="\n\n")
-        else:
-            chrome = None
-
         functionBody = CGList(
-            [CGGeneric(getParentProto), initIds, prefCache, chrome,
-             CGGeneric(call % self.properties.variableNames(False))],
+            [CGGeneric(getParentProto), initIds, prefCache, CGGeneric(call)],
             "\n\n")
         return CGIndenter(functionBody).define()
 
 class CGGetPerInterfaceObject(CGAbstractMethod):
     """
     A method for getting a per-interface object (a prototype object or interface
     constructor object).
     """
@@ -4536,46 +4561,30 @@ class CGEnumerateOwnProperties(CGAbstrac
 """
 
 class CGXrayHelper(CGAbstractMethod):
     def __init__(self, descriptor, name, args, properties):
         CGAbstractMethod.__init__(self, descriptor, name, "bool", args)
         self.properties = properties
 
     def definition_body(self):
-        varNames = self.properties.variableNames(True)
-
-        methods = self.properties.methods
-        if methods.hasNonChromeOnly() or methods.hasChromeOnly():
-            methodArgs = """// %(methods)s has an end-of-list marker at the end that we ignore
-%(methods)s, %(methods)s_ids, %(methods)s_specs, ArrayLength(%(methods)s) - 1""" % varNames
+        prefixArgs = CGGeneric(self.getPrefixArgs())
+        if self.properties.hasNonChromeOnly():
+            regular = "&sNativeProperties"
         else:
-            methodArgs = "NULL, NULL, NULL, 0"
-        methodArgs = CGGeneric(methodArgs)
-
-        attrs = self.properties.attrs
-        if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
-            attrArgs = """// %(attrs)s has an end-of-list marker at the end that we ignore
-%(attrs)s, %(attrs)s_ids, %(attrs)s_specs, ArrayLength(%(attrs)s) - 1""" % varNames
+            regular = "nullptr"
+        regular = CGGeneric(regular)
+        if self.properties.hasChromeOnly():
+            chrome = "&sChromeOnlyNativeProperties"
         else:
-            attrArgs = "NULL, NULL, NULL, 0"
-        attrArgs = CGGeneric(attrArgs)
-
-        consts = self.properties.consts
-        if consts.hasNonChromeOnly() or consts.hasChromeOnly():
-            constArgs = """// %(consts)s has an end-of-list marker at the end that we ignore
-%(consts)s, %(consts)s_ids, %(consts)s_specs, ArrayLength(%(consts)s) - 1""" % varNames
-        else:
-            constArgs = "NULL, NULL, NULL, 0"
-        constArgs = CGGeneric(constArgs)
-
-        prefixArgs = CGGeneric(self.getPrefixArgs())
+            chrome = "nullptr"
+        chrome = CGGeneric(chrome)
 
         return CGIndenter(
-            CGWrapper(CGList([prefixArgs, methodArgs, attrArgs, constArgs], ",\n"),
+            CGWrapper(CGList([prefixArgs, regular, chrome], ",\n"),
                       pre=("return Xray%s(" % self.name),
                       post=");",
                       reindent=True)).define()
 
 class CGResolveProperty(CGXrayHelper):
     def __init__(self, descriptor, properties):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
                 Argument('jsid', 'id'), Argument('bool', 'set'),
@@ -4590,17 +4599,17 @@ class CGResolveProperty(CGXrayHelper):
 class CGEnumerateProperties(CGXrayHelper):
     def __init__(self, descriptor, properties):
         args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
                 Argument('JS::AutoIdVector&', 'props')]
         CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args,
                               properties)
 
     def getPrefixArgs(self):
-        return "props"
+        return "wrapper, props"
 
 class CGPrototypeTraitsClass(CGClass):
     def __init__(self, descriptor, indent=''):
         templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
         templateSpecialization = ['prototypes::id::' + descriptor.name]
         enums = [ClassEnum('', ['Depth'],
                            [descriptor.interface.inheritanceDepth()])]
         typedefs = [ClassTypedef('NativeType', descriptor.nativeType)]
@@ -5243,16 +5252,17 @@ class CGDescriptor(CGThing):
             cgThings.append(CGClassHasInstanceHook(descriptor))
             cgThings.append(CGInterfaceObjectJSClass(descriptor))
 
         if descriptor.interface.hasInterfacePrototypeObject():
             cgThings.append(CGPrototypeJSClass(descriptor))
 
         properties = PropertyArrays(descriptor)
         cgThings.append(CGGeneric(define=str(properties)))
+        cgThings.append(CGNativeProperties(descriptor, properties))
         cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
         if descriptor.interface.hasInterfacePrototypeObject():
             cgThings.append(CGGetProtoObjectMethod(descriptor))
         else:
             cgThings.append(CGGetConstructorObjectMethod(descriptor))
 
         # Set up our Xray callbacks as needed.  Note that we don't need to do
         # it in workers.