Fix for bug 785188 (Make Xrays work with newest DOM list bindings). r=mrbkap.
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 06 Jun 2012 21:52:26 +0200
changeset 105298 8ba35e8d4457e22ac453627d30e144a59110973e
parent 105297 1bed30d952cc688c1c5d7575c4bf6574aedfe570
child 105299 d5f32f0e1c05005f5e2a5bfeb6cd85353add3823
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersmrbkap
bugs785188
milestone17.0a1
Fix for bug 785188 (Make Xrays work with newest DOM list bindings). r=mrbkap.
dom/bindings/Codegen.py
dom/bindings/DOMJSClass.h
dom/workers/Worker.cpp
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -67,22 +67,27 @@ class CGNativePropertyHooks(CGThing):
         self.descriptor = descriptor
     def declare(self):
         if self.descriptor.workers:
             return ""
         return "extern const NativePropertyHooks NativeHooks;\n"
     def define(self):
         if self.descriptor.workers:
             return ""
+        if self.descriptor.concrete and self.descriptor.proxy:
+            resolveOwnProperty = "ResolveOwnProperty"
+            enumerateOwnProperties = "EnumerateOwnProperties"
+        else:
+            enumerateOwnProperties = resolveOwnProperty = "NULL"
         parent = self.descriptor.interface.parent
         parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
                        if parent else 'NULL')
         return """
-const NativePropertyHooks NativeHooks = { ResolveProperty, EnumerateProperties, %s };
-""" % parentHooks
+const NativePropertyHooks NativeHooks = { %s, ResolveProperty, %s, EnumerateProperties, %s };
+""" % (resolveOwnProperty, enumerateOwnProperties, parentHooks)
 
 def DOMClass(descriptor):
         protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
         # Pad out the list to the right length with _ID_Count so we
         # guarantee that all the lists are the same length.  _ID_Count
         # is never the ID of any prototype, so it's safe to use as
         # padding.
         protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
@@ -4197,16 +4202,47 @@ class CGClass(CGThing):
         result = ''
         itemCount = 0
         for (memberList, separator) in order:
             (memberString, itemCount) = defineMembers(self, memberList,
                                                       itemCount, separator)
             result = result + memberString
         return result
 
+class CGResolveOwnProperty(CGAbstractMethod):
+    def __init__(self, descriptor):
+        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+                Argument('jsid', 'id'), Argument('bool', 'set'),
+                Argument('JSPropertyDescriptor*', 'desc')]
+        CGAbstractMethod.__init__(self, descriptor, "ResolveOwnProperty", "bool", args)
+    def definition_body(self):
+        return """  JSObject* obj = wrapper;
+  if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+    obj = js::UnwrapObject(obj);
+  }
+  // We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
+  // properties. If that changes we'll need to filter here.
+  return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, set, desc);
+"""
+
+class CGEnumerateOwnProperties(CGAbstractMethod):
+    def __init__(self, descriptor):
+        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+                Argument('JS::AutoIdVector&', 'props')]
+        CGAbstractMethod.__init__(self, descriptor, "EnumerateOwnProperties", "bool", args)
+    def definition_body(self):
+        return """  JSObject* obj = wrapper;
+  if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+    obj = js::UnwrapObject(obj);
+  }
+  // We rely on getOwnPropertyNames not shadowing prototype properties by named
+  // properties. If that changes we'll need to filter here.
+  return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
+"""
+
 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)
 
@@ -4251,17 +4287,18 @@ class CGResolveProperty(CGXrayHelper):
                               properties)
 
     def getPrefixArgs(self):
         return "cx, wrapper, id, desc"
 
 
 class CGEnumerateProperties(CGXrayHelper):
     def __init__(self, descriptor, properties):
-        args = [Argument('JS::AutoIdVector&', 'props')]
+        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+                Argument('JS::AutoIdVector&', 'props')]
         CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args,
                               properties)
 
     def getPrefixArgs(self):
         return "props"
 
 class CGPrototypeTraitsClass(CGClass):
     def __init__(self, descriptor, indent=''):
@@ -4473,16 +4510,19 @@ class CGDOMJSProxyHandler_getOwnProperty
                                 "}\n\n")
 
         namedGetter = self.descriptor.operations['NamedGetter']
         if namedGetter:
             readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
             fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
             templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
                               'obj': 'proxy', 'successCode': fillDescriptor}
+            # Once we start supporting OverrideBuiltins we need to make
+            # ResolveOwnProperty or EnumerateOwnProperties filter out named
+            # properties that shadow prototype properties.
             namedGet = ("\n" +
                         "if (!set && JSID_IS_STRING(id) && !HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
                         "  JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
                         "  FakeDependentString name;\n"
                         "  if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
                         "                              eStringify, eStringify, name)) {\n" +
                         "    return false;\n" +
                         "  }\n" +
@@ -4882,16 +4922,19 @@ class CGDescriptor(CGThing):
             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.
         if (descriptor.interface.hasInterfacePrototypeObject() and
             not descriptor.workers):
+            if descriptor.concrete and descriptor.proxy:
+                cgThings.append(CGResolveOwnProperty(descriptor))
+                cgThings.append(CGEnumerateOwnProperties(descriptor))
             cgThings.append(CGResolveProperty(descriptor, properties))
             cgThings.append(CGEnumerateProperties(descriptor, properties))
 
         if descriptor.interface.hasInterfaceObject():
             cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
 
         if descriptor.interface.hasInterfacePrototypeObject():
             cgThings.append(CGNativePropertyHooks(descriptor))
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -30,21 +30,24 @@
 
 namespace mozilla {
 namespace dom {
 
 typedef bool
 (* ResolveProperty)(JSContext* cx, JSObject* wrapper, jsid id, bool set,
                     JSPropertyDescriptor* desc);
 typedef bool
-(* EnumerateProperties)(JS::AutoIdVector& props);
+(* EnumerateProperties)(JSContext* cx, JSObject* wrapper,
+                        JS::AutoIdVector& props);
 
 struct NativePropertyHooks
 {
+  ResolveProperty mResolveOwnProperty;
   ResolveProperty mResolveProperty;
+  EnumerateProperties mEnumerateOwnProperties;
   EnumerateProperties mEnumerateProperties;
 
   const NativePropertyHooks *mProtoHooks;
 };
 
 struct DOMClass
 {
   // A list of interfaces that this object implements, in order of decreasing
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -28,22 +28,25 @@ using mozilla::ErrorResult;
 // These are temporary until these classes are moved to be codegenerated.
 bool
 WorkerResolveProperty(JSContext* cx, JSObject* wrapper, jsid id, bool set,
                       JSPropertyDescriptor* desc)
 {
   return true;
 }
 bool
-WorkerEnumerateProperties(JS::AutoIdVector& props)
+WorkerEnumerateProperties(JSContext* cx, JSObject* wrapper,
+                          JS::AutoIdVector& props)
 {
   return true;
 }
 NativePropertyHooks mozilla::dom::workers::sNativePropertyHooks =
-  { WorkerResolveProperty, WorkerEnumerateProperties, NULL };
+  { WorkerResolveProperty, WorkerResolveProperty,
+    WorkerEnumerateProperties, WorkerEnumerateProperties,
+    NULL };
 
 
 namespace {
 
 class Worker
 {
   static DOMJSClass sClass;
   static JSPropertySpec sProperties[];
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -285,23 +285,23 @@ enum XrayType {
     XrayForDOMProxyObject,
     XrayForWrappedNative,
     NotXray
 };
 
 static XrayType
 GetXrayType(JSObject *obj)
 {
-    js::Class* clasp = js::GetObjectClass(obj);
-    if (mozilla::dom::IsDOMClass(Jsvalify(clasp))) {
+    if (mozilla::dom::IsDOMObject(obj))
         return XrayForDOMObject;
-    }
-    if (mozilla::dom::oldproxybindings::instanceIsProxy(obj)) {
+
+    if (mozilla::dom::oldproxybindings::instanceIsProxy(obj))
         return XrayForDOMProxyObject;
-    }
+
+    js::Class* clasp = js::GetObjectClass(obj);
     if (IS_WRAPPER_CLASS(clasp) || clasp->ext.innerObject) {
         NS_ASSERTION(clasp->ext.innerObject || IS_WN_WRAPPER_OBJECT(obj),
                      "We forgot to Morph a slim wrapper!");
         return XrayForWrappedNative;
     }
     return NotXray;
 }
 
@@ -363,20 +363,20 @@ WrapperFactory::Rewrap(JSContext *cx, JS
             (wn = GetWrappedNative(cx, obj)) &&
             wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) {
             typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
             usingXray = true;
             if (IsLocationObject(obj))
                 wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
             else
                 wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton;
+        } else if (mozilla::dom::IsDOMObject(obj)) {
+            wrapper = &FilteringWrapper<XrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (mozilla::dom::oldproxybindings::instanceIsProxy(obj)) {
             wrapper = &FilteringWrapper<XrayProxy, CrossOriginAccessiblePropertiesOnly>::singleton;
-        } else if (mozilla::dom::IsDOMClass(JS_GetClass(obj))) {
-            wrapper = &FilteringWrapper<XrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (IsComponentsObject(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ComponentsObjectPolicy>::singleton;
         } else {
             wrapper = &ChromeObjectWrapper::singleton;
 
             // If the prototype of the chrome object being wrapped is a prototype
             // for a standard class, use the one from the content compartment so
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1031,34 +1031,46 @@ ProxyXrayTraits::createHolderObject(JSCo
     return obj;
 }
 
 bool
 DOMXrayTraits::resolveNativeProperty(JSContext *cx, JSObject *wrapper, JSObject *holder, jsid id,
                                      bool set, JSPropertyDescriptor *desc)
 {
     JSObject *obj = getInnerObject(wrapper);
-    const NativePropertyHooks *nativeHooks =
-        DOMJSClass::FromJSClass(JS_GetClass(obj))->mClass.mNativeHooks;
+    const NativePropertyHooks *nativeHooks = GetDOMClass(obj)->mNativeHooks;
 
     do {
-        if (nativeHooks->mResolveProperty(cx, wrapper, id, set, desc) &&
-            desc->obj) {
+        if (!nativeHooks->mResolveProperty(cx, wrapper, id, set, desc))
+            return false;
+
+        if (desc->obj) {
             NS_ASSERTION(desc->obj == wrapper, "What did we resolve this on?");
             return true;
         }
     } while ((nativeHooks = nativeHooks->mProtoHooks));
 
     return true;
 }
 
 bool
 DOMXrayTraits::resolveOwnProperty(JSContext *cx, js::Wrapper &jsWrapper, JSObject *wrapper,
                                   JSObject *holder, jsid id, bool set, JSPropertyDescriptor *desc)
 {
+    JSObject *obj = getInnerObject(wrapper);
+    const NativePropertyHooks *nativeHooks = GetDOMClass(obj)->mNativeHooks;
+
+    if (nativeHooks->mResolveOwnProperty) {
+        if (!nativeHooks->mResolveOwnProperty(cx, wrapper, id, set, desc))
+            return false;
+
+        NS_ASSERTION(!desc->obj || desc->obj == wrapper,
+                     "What did we resolve this on?");
+    }
+
     return true;
 }
 
 bool
 DOMXrayTraits::defineProperty(JSContext *cx, JSObject *wrapper, jsid id, PropertyDescriptor *desc)
 {
     JSObject *holder = getHolderObject(cx, wrapper);
     if (!holder)
@@ -1077,26 +1089,29 @@ DOMXrayTraits::delete_(JSContext *cx, JS
 
     return true;
 }
 
 bool
 DOMXrayTraits::enumerateNames(JSContext *cx, JSObject *wrapper, unsigned flags,
                               JS::AutoIdVector &props)
 {
+    JSObject *obj = getInnerObject(wrapper);
+    const NativePropertyHooks *nativeHooks = GetDOMClass(obj)->mNativeHooks;
+
+    if (nativeHooks->mEnumerateOwnProperties &&
+        !nativeHooks->mEnumerateOwnProperties(cx, wrapper, props))
+        return false;
+
     if (flags & (JSITER_OWNONLY | JSITER_HIDDEN))
         // Probably need to return expandos on the Xray here!
         return true;
 
-    JSObject *obj = getInnerObject(wrapper);
-    const NativePropertyHooks *nativeHooks =
-        DOMJSClass::FromJSClass(JS_GetClass(obj))->mClass.mNativeHooks;
-
     do {
-        if (!nativeHooks->mEnumerateProperties(props)) {
+        if (!nativeHooks->mEnumerateProperties(cx, wrapper, props)) {
             return false;
         }
     } while ((nativeHooks = nativeHooks->mProtoHooks));
 
     return true;
 }
 
 JSObject*