Bug 1466221. Implement ToJSValue variants for non-refcounted (so owned) DOM objects. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Sat, 09 Jun 2018 01:03:15 -0400
changeset 422058 292092eabd6fffbec0e7c82bac6d4c03e30edf23
parent 422057 317eee299a24c5adc2e87dc27128e82eab145599
child 422059 5e0625eb0387a71b26de6a26ef31971d0d28cf3e
push id34114
push userbtara@mozilla.com
push dateSat, 09 Jun 2018 15:31:58 +0000
treeherdermozilla-central@e02a5155d815 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1466221
milestone62.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 1466221. Implement ToJSValue variants for non-refcounted (so owned) DOM objects. r=peterv
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/ToJSValue.h
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1152,17 +1152,18 @@ GetOrCreateDOMReflectorNoWrap(JSContext*
   return DoGetOrCreateDOMReflector<T, eDontWrapIntoContextCompartment>(cx,
                                                                        value,
                                                                        nullptr,
                                                                        rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
 // non-wrapper-cached object using WebIDL bindings.  "value" must implement a
-// WrapObject() method taking a JSContext and a scope.
+// WrapObject() method taking a JSContext and a prototype (possibly null) and
+// returning the resulting object via a MutableHandle<JSObject*> outparam.
 template <class T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx,
                                      JS::Handle<JSObject*> scopeArg,
                                      T* value,
                                      JS::MutableHandle<JS::Value> rval,
                                      JS::Handle<JSObject*> givenProto = nullptr)
 {
@@ -1197,19 +1198,21 @@ WrapNewBindingNonWrapperCachedObject(JSC
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   rval.set(JS::ObjectValue(*obj));
   return MaybeWrapObjectValue(cx, rval);
 }
 
 // Create a JSObject wrapping "value", for cases when "value" is a
-// non-wrapper-cached owned object using WebIDL bindings.  "value" must implement a
-// WrapObject() method taking a JSContext, a scope, and a boolean outparam that
-// is true if the JSObject took ownership
+// non-wrapper-cached owned object using WebIDL bindings.  "value" must
+// implement a WrapObject() method taking a taking a JSContext and a prototype
+// (possibly null) and returning two pieces of information: the resulting object
+// via a MutableHandle<JSObject*> outparam and a boolean return value that is
+// true if the JSObject took ownership
 template <class T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx,
                                      JS::Handle<JSObject*> scopeArg,
                                      nsAutoPtr<T>& value,
                                      JS::MutableHandle<JS::Value> rval,
                                      JS::Handle<JSObject*> givenProto = nullptr)
 {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3751,16 +3751,18 @@ class CGWrapWithCacheMethod(CGAbstractMe
             """
             aCache->ReleaseWrapper(aObject);
             aCache->ClearWrapper();
             return false;
             """)
 
         return fill(
             """
+            static_assert(!IsBaseOf<NonRefcountedDOMObject, ${nativeType}>::value,
+                          "Shouldn't have wrappercached things that are not refcounted.");
             $*{assertInheritance}
             MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
             MOZ_ASSERT(!aCache->GetWrapper(),
                        "You should probably not be using Wrap() directly; use "
                        "GetOrCreateDOMReflector instead");
 
             MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                        "nsISupports must be on our primary inheritance chain");
@@ -3802,16 +3804,17 @@ class CGWrapWithCacheMethod(CGAbstractMe
             // somewhat common) to have a non-null aGivenProto which is the
             // same as canonicalProto.
             if (proto != canonicalProto) {
               PreserveWrapper(aObject);
             }
 
             return true;
             """,
+            nativeType=self.descriptor.nativeType,
             assertInheritance=AssertInheritanceChain(self.descriptor),
             declareProto=DeclareProto(),
             createObject=CreateBindingJSObject(self.descriptor, self.properties),
             unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor,
                                                             failureCode),
             slots=InitMemberSlots(self.descriptor, failureCode),
             setImmutablePrototype=SetImmutablePrototype(self.descriptor,
                                                         failureCode))
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -4,22 +4,25 @@
  * 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/. */
 
 #ifndef mozilla_dom_ToJSValue_h
 #define mozilla_dom_ToJSValue_h
 
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/NonRefcountedDOMObject.h"
 #include "mozilla/dom/TypedArray.h"
 #include "jsapi.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
+#include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 
 // If ToJSValue returns false, it must set an exception on the
 // JSContext.
@@ -147,16 +150,84 @@ ToJSValue(JSContext* aCx,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
 
   return GetOrCreateDOMReflector(aCx, aArgument, aValue);
 }
 
+// Accept non-refcounted DOM objects that do not inherit from
+// nsWrapperCache.  Refcounted ones would be too much of a footgun:
+// you could convert them to JS twice and get two different objects.
+namespace binding_detail {
+template<class T>
+MOZ_MUST_USE
+typename EnableIf<IsBaseOf<NonRefcountedDOMObject, T>::value, bool>::Type
+ToJSValueFromPointerHelper(JSContext* aCx,
+                           T* aArgument,
+                           JS::MutableHandle<JS::Value> aValue)
+{
+  // Make sure we're called in a compartment
+  MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+  // This is a cut-down version of
+  // WrapNewBindingNonWrapperCachedObject that doesn't need to deal
+  // with nearly as many cases.
+  if (!aArgument) {
+    aValue.setNull();
+    return true;
+  }
+
+  JS::Rooted<JSObject*> obj(aCx);
+  if (!aArgument->WrapObject(aCx, nullptr, &obj)) {
+    return false;
+  }
+
+  aValue.setObject(*obj);
+  return true;
+}
+} // namespace binding_detail
+
+// We can take a non-refcounted non-wrapper-cached DOM object that lives in an
+// nsAutoPtr.
+template<class T>
+MOZ_MUST_USE
+typename EnableIf<IsBaseOf<NonRefcountedDOMObject, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+          nsAutoPtr<T>&& aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  if (!binding_detail::ToJSValueFromPointerHelper(aCx, aArgument.get(), aValue)) {
+    return false;
+  }
+
+  // JS object took ownership
+  aArgument.forget();
+  return true;
+}
+
+// We can take a non-refcounted non-wrapper-cached DOM object that lives in a
+// UniquePtr.
+template<class T>
+MOZ_MUST_USE
+typename EnableIf<IsBaseOf<NonRefcountedDOMObject, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+          UniquePtr<T>&& aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  if (!binding_detail::ToJSValueFromPointerHelper(aCx, aArgument.get(), aValue)) {
+    return false;
+  }
+
+  // JS object took ownership
+  Unused << aArgument.release();
+  return true;
+}
+
 // Accept typed arrays built from appropriate nsTArray values
 template<typename T>
 MOZ_MUST_USE
 typename EnableIf<IsBaseOf<AllTypedArraysBase, T>::value, bool>::Type
 ToJSValue(JSContext* aCx,
           const TypedArrayCreator<T>& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {