Make Xrays work correctly with WebIDL global objects.
authorPeter Van der Beken <peterv@propagandism.org>
Sat, 15 Feb 2014 22:12:34 +0100
changeset 181842 53b25b8cafe9b6ecc666dbeb0b93f976922bb39d
parent 181841 7ccef133912755ce2890b3b5f9ab651966ed96f8
child 181843 cdd53387bf1a4e03c800483430567808662243e6
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone32.0a1
Make Xrays work correctly with WebIDL global objects.
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
js/xpconnect/wrappers/XrayWrapper.cpp
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -909,19 +909,22 @@ ThrowingConstructor(JSContext* cx, unsig
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
 {
   return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
 }
 
 inline const NativePropertyHooks*
 GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
-                       DOMObjectType& type)
+                       DOMObjectType& type, bool& isGlobal)
 {
-  const DOMClass* domClass = GetDOMClass(obj);
+  const js::Class* clasp = js::GetObjectClass(obj);
+  isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0;
+
+  const DOMClass* domClass = GetDOMClass(clasp);
   if (domClass) {
     type = eInstance;
     return domClass->mNativeHooks;
   }
 
   if (JS_ObjectIsFunction(cx, obj)) {
     MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
     type = eInterface;
@@ -954,47 +957,62 @@ XrayResolveNativeProperty(JSContext* cx,
                           const NativePropertyHooks* nativePropertyHooks,
                           DOMObjectType type, JS::Handle<JSObject*> obj,
                           JS::Handle<jsid> id,
                           JS::MutableHandle<JSPropertyDescriptor> desc);
 
 bool
 XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                        JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
-                       JS::MutableHandle<JSPropertyDescriptor> desc)
+                       JS::MutableHandle<JSPropertyDescriptor> desc,
+                       bool& cacheOnHolder)
 {
   DOMObjectType type;
+  bool isGlobal;
   const NativePropertyHooks *nativePropertyHooks =
-    GetNativePropertyHooks(cx, obj, type);
+    GetNativePropertyHooks(cx, obj, type, isGlobal);
 
-  if (type != eInstance) {
+  if (type != eInstance || (isGlobal && GlobalPropertiesAreOwn())) {
     // For prototype objects and interface objects, just return their
-    // normal set of properties.
-    return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
-                                     obj, id, desc);
+    // 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)) {
+      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,
                                       nativeProperties.regular)) {
     return false;
   }
   if (desc.object()) {
+    cacheOnHolder = true;
     return true;
   }
   if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc,
                                       nativeProperties.chromeOnly)) {
     return false;
   }
   if (desc.object()) {
+    cacheOnHolder = true;
     return true;
   }
 
+  cacheOnHolder = false;
   return !nativePropertyHooks->mResolveOwnProperty ||
          nativePropertyHooks->mResolveOwnProperty(cx, wrapper, obj, id, desc);
 }
 
 static bool
 XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
                      JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                      const Prefable<const JSPropertySpec>* attributes, jsid* attributeIds,
@@ -1229,20 +1247,28 @@ XrayResolveNativeProperty(JSContext* cx,
 }
 
 bool
 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                           JS::Handle<JSObject*> obj,
                           JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc)
 {
   DOMObjectType type;
+  bool isGlobal;
   const NativePropertyHooks* nativePropertyHooks =
-    GetNativePropertyHooks(cx, obj, type);
+    GetNativePropertyHooks(cx, obj, type, isGlobal);
 
   if (type == eInstance) {
+    // Global objects return their interfaces' properties from
+    // XrayResolveOwnProperty, so skip those.
+    if ((isGlobal && GlobalPropertiesAreOwn()) &&
+        !(nativePropertyHooks = nativePropertyHooks->mProtoHooks)) {
+      return true;
+    }
+
     // Force the type to be eInterfacePrototype, since we need to walk the
     // prototype chain.
     type = eInterfacePrototype;
   }
 
   if (type == eInterfacePrototype) {
     do {
       if (!XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
@@ -1424,27 +1450,28 @@ XrayEnumerateNativeProperties(JSContext*
 }
 
 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);
+    GetNativePropertyHooks(cx, obj, type, isGlobal);
 
   if (type == eInstance) {
     if (nativePropertyHooks->mEnumerateOwnProperties &&
         !nativePropertyHooks->mEnumerateOwnProperties(cx, wrapper, obj,
                                                       props)) {
       return false;
     }
 
-    if (flags & JSITER_OWNONLY) {
+    if (!(isGlobal && GlobalPropertiesAreOwn()) && (flags & JSITER_OWNONLY)) {
       return true;
     }
 
     // Force the type to be eInterfacePrototype, since we need to walk the
     // prototype chain.
     type = eInterfacePrototype;
   }
 
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -173,23 +173,25 @@ UnwrapDOMObject(JSObject* obj)
   MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
              "Don't pass non-DOM objects to this function");
 
   JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
   return static_cast<T*>(val.toPrivate());
 }
 
 inline const DOMClass*
+GetDOMClass(const js::Class* clasp)
+{
+  return IsDOMClass(clasp) ? &DOMJSClass::FromJSClass(clasp)->mClass : nullptr;
+}
+
+inline const DOMClass*
 GetDOMClass(JSObject* obj)
 {
-  const js::Class* clasp = js::GetObjectClass(obj);
-  if (IsDOMClass(clasp)) {
-    return &DOMJSClass::FromJSClass(clasp)->mClass;
-  }
-  return nullptr;
+  return GetDOMClass(js::GetObjectClass(obj));
 }
 
 inline nsISupports*
 UnwrapDOMObjectToISupports(JSObject* aObject)
 {
   const DOMClass* clasp = GetDOMClass(aObject);
   if (!clasp || !clasp->mDOMObjectIsISupports) {
     return nullptr;
@@ -2083,17 +2085,18 @@ AddStringToIDVector(JSContext* cx, JS::A
  * wrapper is the Xray JS object.
  * obj is the target object of the Xray, a binding's instance object or a
  *     interface or interface prototype object.
  */
 bool
 XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                        JS::Handle<JSObject*> obj,
                        JS::Handle<jsid> id,
-                       JS::MutableHandle<JSPropertyDescriptor> desc);
+                       JS::MutableHandle<JSPropertyDescriptor> desc,
+                       bool& cacheOnHolder);
 
 /**
  * This resolves operations, attributes and constants of the interfaces for obj.
  *
  * wrapper is the Xray JS object.
  * obj is the target object of the Xray, a binding's instance object or a
  *     interface or interface prototype object.
  */
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1419,16 +1419,23 @@ DOMXrayTraits::resolveOwnProperty(JSCont
                                   HandleObject holder, HandleId id,
                                   MutableHandle<JSPropertyDescriptor> desc)
 {
     // Call the common code.
     bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc);
     if (!ok || desc.object())
         return ok;
 
+    if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
+        return false;
+    if (desc.object()) {
+        desc.object().set(wrapper);
+        return true;
+    }
+
     // Check for indexed access on a window.
     int32_t index = GetArrayIndexFromId(cx, id);
     if (IsArrayIndex(index)) {
         nsGlobalWindow* win = AsWindow(cx, wrapper);
         // Note: As() unwraps outer windows to get to the inner window.
         if (win) {
             bool unused;
             nsCOMPtr<nsIDOMWindow> subframe = win->IndexedGetter(index, unused);
@@ -1443,22 +1450,28 @@ DOMXrayTraits::resolveOwnProperty(JSCont
                 desc.value().setObject(*obj);
                 FillPropertyDescriptor(desc, wrapper, true);
                 return JS_WrapPropertyDescriptor(cx, desc);
             }
         }
     }
 
     RootedObject obj(cx, getTargetObject(wrapper));
-    if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc))
+    bool cacheOnHolder;
+    if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc, cacheOnHolder))
         return false;
 
     MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
 
-    return true;
+    if (!desc.object() || !cacheOnHolder)
+        return true;
+
+    return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.attributes(),
+                                 desc.getter(), desc.setter()) &&
+           JS_GetPropertyDescriptorById(cx, holder, id, desc);
 }
 
 bool
 DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                               MutableHandle<JSPropertyDescriptor> desc,
                               Handle<JSPropertyDescriptor> existingDesc, bool *defined)
 {
     // Check for an indexed property on a Window.  If that's happening, do