Bug 857439 Part 3: Make sure to delete owned interfaces if wrapping fails r=bz
authorDavid Zbarsky <dzbarsky@gmail.com>
Fri, 19 Apr 2013 04:49:21 -0400
changeset 129308 3a2610c6d7852df2a6ea1fb07cb4fea23643eb30
parent 129307 c39abce27c56adb46346d2666158bcb98542fc9d
child 129309 6e65c02b49e33bc37dd4df0eab7597add02dbff2
push idunknown
push userunknown
push dateunknown
reviewersbz
bugs857439
milestone23.0a1
Bug 857439 Part 3: Make sure to delete owned interfaces if wrapping fails r=bz
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -631,16 +631,51 @@ WrapNewBindingNonWrapperCachedObject(JSC
   }
 
   // We can end up here in all sorts of compartments, per above.  Make
   // sure to JS_WrapValue!
   *vp = JS::ObjectValue(*obj);
   return JS_WrapValue(cx, vp);
 }
 
+// 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
+template <class T>
+inline bool
+WrapNewBindingNonWrapperCachedOwnedObject(JSContext* cx, JSObject* scope,
+                                          nsAutoPtr<T>& value, JS::Value* vp)
+{
+  // We try to wrap in the compartment of the underlying object of "scope"
+  JSObject* obj;
+  {
+    // scope for the JSAutoCompartment so that we restore the compartment
+    // before we call JS_WrapValue.
+    Maybe<JSAutoCompartment> ac;
+    if (js::IsWrapper(scope)) {
+      scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
+      if (!scope)
+        return false;
+      ac.construct(cx, scope);
+    }
+
+    bool tookOwnership = false;
+    obj = value->WrapObject(cx, scope, &tookOwnership);
+    if (tookOwnership) {
+      value.forget();
+    }
+  }
+
+  // We can end up here in all sorts of compartments, per above.  Make
+  // sure to JS_WrapValue!
+  *vp = JS::ObjectValue(*obj);
+  return JS_WrapValue(cx, vp);
+}
+
 // Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
 template <template <typename> class SmartPtr, typename T>
 inline bool
 WrapNewBindingNonWrapperCachedObject(JSContext* cx, JSObject* scope,
                                      const SmartPtr<T>& value, JS::Value* vp)
 {
   return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), vp);
 }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1861,16 +1861,17 @@ def CreateBindingJSObject(descriptor, pa
     if descriptor.nativeOwnership in ['refcounted', 'nsisupports']:
         create += """  NS_ADDREF(aObject);
 """
     else:
         assert descriptor.nativeOwnership == 'owned'
         create += """  // Make sure the native objects inherit from NonRefcountedDOMObject so that we
   // log their ctor and dtor.
   MustInheritFromNonRefcountedDOMObject(aObject);
+  *aTookOwnership = true;
 """
     return create % parent
 
 def GetAccessCheck(descriptor, globalName):
     """
     globalName is the name of the global JSObject*
 
     returns a string
@@ -1999,16 +2000,18 @@ class CGWrapNonWrapperCacheMethod(CGAbst
 
     properties should be a PropertyArrays instance.
     """
     def __init__(self, descriptor, properties):
         # XXX can we wrap if we don't have an interface prototype object?
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
                 Argument(descriptor.nativeType + '*', 'aObject')]
+        if descriptor.nativeOwnership == 'owned':
+            args.append(Argument('bool*', 'aTookOwnership'))
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
         self.properties = properties
 
     def definition_body(self):
         return """%s
   JSObject* global = JS_GetGlobalForObject(aCx, aScope);
   JSObject* proto = GetProtoObject(aCx, global);
   if (!proto) {
@@ -3455,17 +3458,17 @@ def getWrapTemplateForType(type, descrip
         """
         if failureCode is None:
             failureCode = exceptionCode
         str = ("if (!%s) {\n" +
                CGIndenter(CGGeneric(failureCode)).define() + "\n" +
                "}\n" +
                successCode) % (wrapCall)
         return str
-    
+
     if type is None or type.isVoid():
         return (setValue("JSVAL_VOID"), True)
 
     if type.isArray():
         raise TypeError("Can't handle array return values yet")
 
     if type.isSequence():
         if type.nullable():
@@ -3537,21 +3540,25 @@ if (!returnArray) {
         else:
             wrappingCode = ""
 
         if descriptor.interface.isCallback():
             wrap = "WrapCallbackInterface(cx, ${obj}, %s, ${jsvalPtr})" % result
             failed = None
         elif not descriptor.interface.isExternal() and not descriptor.skipGen:
             if descriptor.wrapperCache:
+                assert descriptor.nativeOwnership != 'owned'
                 wrapMethod = "WrapNewBindingObject"
             else:
                 if not isCreator:
                     raise MethodNotCreatorError(descriptor.interface.identifier.name)
-                wrapMethod = "WrapNewBindingNonWrapperCachedObject"
+                if descriptor.nativeOwnership == 'owned':
+                    wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject"
+                else:
+                    wrapMethod = "WrapNewBindingNonWrapperCachedObject"
             wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
             if not descriptor.hasXPConnectImpls:
                 # Can only fail to wrap as a new-binding object
                 # if they already threw an exception.
                 failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
                           "%s" % exceptionCode)
             else:
                 if descriptor.notflattened:
@@ -3774,16 +3781,19 @@ def getRetvalDeclarationForType(returnTy
         if returnType.nullable():
             raise TypeError("We don't support nullable enum return values")
         return CGGeneric(returnType.inner.identifier.name), False
     if returnType.isGeckoInterface():
         result = CGGeneric(descriptorProvider.getDescriptor(
             returnType.unroll().inner.identifier.name).nativeType)
         if resultAlreadyAddRefed:
             result = CGTemplatedType("nsRefPtr", result)
+        elif descriptorProvider.getDescriptor(
+            returnType.unroll().inner.identifier.name).nativeOwnership == 'owned':
+            result = CGTemplatedType("nsAutoPtr", result)
         else:
             result = CGWrapper(result, post="*")
         return result, False
     if returnType.isCallback():
         name = returnType.unroll().identifier.name
         if descriptorProvider.workers:
             return CGGeneric("JSObject*"), False
         return CGGeneric("nsRefPtr<%s>" % name), False