Bug 1146234 - Allow non-object values to be used as the this value when invoking IDL callback functions. r=peterv
authorCameron McCormack <cam@mcc.id.au>
Sat, 28 Mar 2015 11:44:29 +1100
changeset 266657 8183a5fa421db468925be3b4dedc53afd5c6d3dd
parent 266656 21aa17588defe40c117cd4a47ad8d91008a720f6
child 266658 d135a7b7864fdd9cf5043e1a70cca339aea5fa55
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1146234
milestone39.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 1146234 - Allow non-object values to be used as the this value when invoking IDL callback functions. r=peterv
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1629,56 +1629,78 @@ JSObject* GetJSObjectFromCallback(Callba
 
 MOZ_ALWAYS_INLINE
 JSObject* GetJSObjectFromCallback(void* noncallback)
 {
   return nullptr;
 }
 
 template<typename T>
-static inline JSObject*
-WrapCallThisObject(JSContext* cx, const T& p)
+static inline bool
+WrapCallThisValue(JSContext* cx, const T& p, JS::MutableHandle<JS::Value> rval)
 {
   // Callbacks are nsISupports, so WrapNativeParent will just happily wrap them
   // up as an nsISupports XPCWrappedNative... which is not at all what we want.
   // So we need to special-case them.
   JS::Rooted<JSObject*> obj(cx, GetJSObjectFromCallback(p));
   if (!obj) {
     // WrapNativeParent is a bit of a Swiss army knife that will
     // wrap anything for us.
     obj = WrapNativeParent(cx, p);
     if (!obj) {
-      return nullptr;
+      return false;
     }
   }
 
   // But all that won't necessarily put things in the compartment of cx.
   if (!JS_WrapObject(cx, &obj)) {
-    return nullptr;
+    return false;
   }
 
-  return obj;
+  rval.setObject(*obj);
+  return true;
 }
 
 /*
  * This specialized function simply wraps a JS::Rooted<> since
  * WrapNativeParent() is not applicable for JS objects.
  */
 template<>
-inline JSObject*
-WrapCallThisObject<JS::Rooted<JSObject*>>(JSContext* cx,
-                                          const JS::Rooted<JSObject*>& p)
+inline bool
+WrapCallThisValue<JS::Rooted<JSObject*>>(JSContext* cx,
+                                         const JS::Rooted<JSObject*>& p,
+                                         JS::MutableHandle<JS::Value> rval)
 {
   JS::Rooted<JSObject*> obj(cx, p);
 
   if (!JS_WrapObject(cx, &obj)) {
-    return nullptr;
+    return false;
   }
 
-  return obj;
+  rval.setObject(*obj);
+  return true;
+}
+
+/*
+ * This specialization is for wrapping any JS value.
+ */
+template<>
+inline bool
+WrapCallThisValue<JS::Rooted<JS::Value>>(JSContext* cx,
+                                         const JS::Rooted<JS::Value>& v,
+                                         JS::MutableHandle<JS::Value> rval)
+{
+  JS::Rooted<JS::Value> val(cx, v);
+
+  if (!JS_WrapValue(cx, &val)) {
+    return false;
+  }
+
+  rval.set(val);
+  return true;
 }
 
 // Helper for calling GetOrCreateDOMReflector with smart pointers
 // (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
 template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
 struct GetOrCreateDOMReflectorHelper
 {
   static inline bool GetOrCreate(JSContext* cx, const T& value,
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -13735,40 +13735,37 @@ class CGCallback(CGClass):
         # And the argument for communicating when exceptions should really be
         # rethrown.  In particular, even when aExceptionHandling is
         # eRethrowExceptions they won't get rethrown if aCompartment is provided
         # and its principal doesn't subsume either the callback or the
         # exception.
         args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
         # And now insert our template argument.
         argsWithoutThis = list(args)
-        args.insert(0, Argument("const T&",  "thisObjPtr"))
+        args.insert(0, Argument("const T&",  "thisVal"))
         errorReturn = method.getDefaultRetval()
 
         setupCall = fill(
             """
             CallSetup s(this, aRv, aExceptionHandling, aCompartment);
             if (!s.GetContext()) {
               aRv.Throw(NS_ERROR_UNEXPECTED);
               return${errorReturn};
             }
             """,
             errorReturn=errorReturn)
 
         bodyWithThis = fill(
             """
             $*{setupCall}
-            JS::Rooted<JSObject*> thisObjJS(s.GetContext(),
-              WrapCallThisObject(s.GetContext(), thisObjPtr));
-            if (!thisObjJS) {
+            JS::Rooted<JS::Value> thisValJS(s.GetContext());
+            if (!WrapCallThisValue(s.GetContext(), thisVal, &thisValJS)) {
               aRv.Throw(NS_ERROR_FAILURE);
               return${errorReturn};
             }
-            JS::Rooted<JS::Value> thisValJS(s.GetContext(),
-                                            JS::ObjectValue(*thisObjJS));
             return ${methodName}(${callArgs});
             """,
             setupCall=setupCall,
             errorReturn=errorReturn,
             methodName=method.name,
             callArgs=", ".join(argnamesWithThis))
         bodyWithoutThis = fill(
             """