Fix for bug 554432 (Allow non-scriptable types for QS arguments). r=mrbkap.
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 27 Jan 2010 10:53:51 +0100
changeset 40488 ff91de87a87887534179c2e09b650addad1dc520
parent 40487 5bbaec69228af648d92a588aef21e3a3374096e4
child 40489 c19b69a502ea175e344e4e5c7af76b8b77d5bfb9
push id12630
push userpvanderbeken@mozilla.com
push dateTue, 06 Apr 2010 13:53:25 +0000
treeherdermozilla-central@b974b6dca6ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs554432
milestone1.9.3a4pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Fix for bug 554432 (Allow non-scriptable types for QS arguments). r=mrbkap.
js/src/xpconnect/src/dom_quickstubs.qsconf
js/src/xpconnect/src/qsgen.py
js/src/xpconnect/src/xpcquickstubs.cpp
js/src/xpconnect/src/xpcquickstubs.h
--- a/js/src/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/src/xpconnect/src/dom_quickstubs.qsconf
@@ -549,46 +549,79 @@ CUSTOM_QS = {
 CUSTOM_QS_TN = {
     'skipgen': True,
     'traceable': True
 }
 
 customMethodCalls = {
     'nsIDOMNode_GetNextSibling': {
         'thisType': 'nsINode',
-        'code': '    nsINode* result = self->GetSibling(1);'
+        'code': '    nsINode *result = self->GetSibling(1);'
         },
     'nsIDOMNode_GetFirstChild': {
         'thisType': 'nsINode',
-        'code': '    nsINode* result = self->GetChildAt(0);'
+        'code': '    nsINode *result = self->GetChildAt(0);'
         },
     'nsIDOMNode_GetChildNodes': {
         'thisType': 'nsINode',
         'code': nsIDOMNode_GetChildNodes_customMethodCallCode,
         'canFail': True
         },
     'nsIDOMNode_GetPreviousSibling': {
         'thisType': 'nsINode',
-        'code': '    nsINode* result = self->GetSibling(-1);'
+        'code': '    nsINode *result = self->GetSibling(-1);'
         },
     'nsIDOMNode_GetLastChild': {
         'thisType': 'nsINode',
-        'code': '    nsINode* result = self->GetLastChild();'
+        'code': '    nsINode *result = self->GetLastChild();'
         },
     'nsIDOMNode_GetOwnerDocument': {
         'thisType': 'nsINode',
-        'code': '    nsIDocument* result = self->GetOwnerDocument();'
+        'code': '    nsIDocument *result = self->GetOwnerDocument();'
         },
     'nsIDOMNode_GetParentNode': {
         'thisType': 'nsINode',
-        'code': '    nsINode* result = self->GetNodeParent();'
+        'code': '    nsINode *result = self->GetNodeParent();'
+        },
+    'nsIDOMNode_InsertBefore': {
+        'thisType': 'nsINode',
+        'arg0Type': 'nsINode',
+        'arg1Type': 'nsINode',
+        'code': '    nsINode *result = self->InsertBefore(arg0, arg1, &rv);\n'
+                '    if(NS_FAILED(rv))\n'
+                '        result = nsnull;',
+        'canFail': True
+        },
+    'nsIDOMNode_ReplaceChild': {
+        'thisType': 'nsINode',
+        'arg0Type': 'nsINode',
+        'arg1Type': 'nsINode',
+        'code': '    nsINode *result = self->ReplaceChild(arg0, arg1, &rv);\n'
+                '    if(NS_FAILED(rv))\n'
+                '        result = nsnull;',
+        'canFail': True
+        },
+    'nsIDOMNode_RemoveChild': {
+        'thisType': 'nsINode',
+        'arg0Type': 'nsINode',
+        'code': '    rv = self->RemoveChild(arg0);\n'
+                '    nsINode *result = NS_SUCCEEDED(rv) ? arg0 : nsnull;',
+        'canFail': True
+        },
+    'nsIDOMNode_AppendChild': {
+        'thisType': 'nsINode',
+        'arg0Type': 'nsINode',
+        'code': '    nsINode *result = self->AppendChild(arg0, &rv);\n'
+                '    if(NS_FAILED(rv))\n'
+                '        result = nsnull;',
+        'canFail': True
         },
     'nsIDOMNodeList_Item': {
         'thisType': 'nsINodeList',
-        'code': '    nsINode* result = self->GetNodeAt(arg0);'
+        'code': '    nsINode *result = self->GetNodeAt(arg0);'
         },
     'nsIDOMHTMLDocument_Write': {
         'code': nsIDOMHTMLDocument_Write_customMethodCallCode % 'Write',
         'canFail': True
         },
     'nsIDOMHTMLDocument_Writeln': {
         'code': nsIDOMHTMLDocument_Write_customMethodCallCode % 'Writeln',
         'canFail': True
--- a/js/src/xpconnect/src/qsgen.py
+++ b/js/src/xpconnect/src/qsgen.py
@@ -860,20 +860,27 @@ def writeQuickStub(f, customMethodCalls,
                     "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
 
     # Convert in-parameters.
     rvdeclared = False
     if isMethod:
         if len(member.params) > 0:
             f.write("    jsval *argv = JS_ARGV(cx, vp);\n")
         for i, param in enumerate(member.params):
-            validateParam(member, param)
+            argName = 'arg%d' % i
+            argTypeKey = argName + 'Type'
+            if customMethodCall is None or not argTypeKey in customMethodCall:
+                validateParam(member, param)
+                realtype = param.realtype
+            else:
+                realtype = xpidl.Forward(name=customMethodCall[argTypeKey],
+                                         location='', doccomments='')
             # Emit code to convert this argument from jsval.
             rvdeclared = writeArgumentUnboxing(
-                f, i, 'arg%d' % i, param.realtype,
+                f, i, argName, realtype,
                 haveCcx=haveCcx,
                 optional=param.optional,
                 rvdeclared=rvdeclared,
                 nullBehavior=param.null,
                 undefinedBehavior=param.undefined)
     elif isSetter:
         rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype,
                                            haveCcx=False, optional=False,
@@ -1250,22 +1257,27 @@ def writeTraceableQuickStub(f, customMet
                 "&vp.array[0], nsnull)) {\n")
     writeFailure(f, getTraceInfoDefaultReturn(member.realtype), 2)
 
     argNames = []
 
     # Convert in-parameters.
     rvdeclared = False
     for i, param in enumerate(member.params):
-        validateParam(member, param)
-        type = unaliasType(param.realtype)
         argName = "arg%d" % i
+        argTypeKey = argName + 'Type'
+        if customMethodCall is None or not argTypeKey in customMethodCall:
+            validateParam(member, param)
+            realtype = unaliasType(param.realtype)
+        else:
+            realtype = xpidl.Forward(name=customMethodCall[argTypeKey],
+                                     location='', doccomments='')
         rvdeclared = writeTraceableArgumentConversion(f, member, i, argName,
-                                                      param.realtype,
-                                                      haveCcx, rvdeclared)
+                                                      realtype, haveCcx,
+                                                      rvdeclared)
         argNames.append(argName)
 
     if customMethodCall is not None:
         f.write("%s\n" % customMethodCall['code'])
     else:
         if not rvdeclared:
             f.write("    nsresult rv;\n")
             rvdeclared = True
--- a/js/src/xpconnect/src/xpcquickstubs.cpp
+++ b/js/src/xpconnect/src/xpcquickstubs.cpp
@@ -792,78 +792,88 @@ getNative(nsISupports *idobj,
 
     nsresult rv = idobj->QueryInterface(iid, ppThis);
     *pThisRef = static_cast<nsISupports*>(*ppThis);
     if(NS_SUCCEEDED(rv))
         *vp = OBJECT_TO_JSVAL(obj);
     return rv;
 }
 
-static nsresult
+inline nsresult
 getNativeFromWrapper(XPCWrappedNative *wrapper,
                      const nsIID &iid,
                      void **ppThis,
                      nsISupports **pThisRef,
                      jsval *vp)
 {
     return getNative(wrapper->GetIdentityObject(), wrapper->GetOffsets(),
                      wrapper->GetFlatJSObject(), iid, ppThis, pThisRef, vp);
 }
 
 
-JSBool
-xpc_qsUnwrapThisImpl(JSContext *cx,
-                     JSObject *obj,
-                     JSObject *callee,
-                     const nsIID &iid,
-                     void **ppThis,
-                     nsISupports **pThisRef,
-                     jsval *vp,
-                     XPCLazyCallContext *lccx)
+nsresult
+getWrapper(JSContext *cx,
+           JSObject *obj,
+           JSObject *callee,
+           XPCWrappedNative **wrapper,
+           JSObject **cur,
+           XPCWrappedNativeTearOff **tearoff)
 {
-    if(XPCWrapper::IsSecurityWrapper(obj))
+    if(XPCWrapper::IsSecurityWrapper(obj) &&
+       !(obj = XPCWrapper::Unwrap(cx, obj)))
     {
-        obj = XPCWrapper::Unwrap(cx, obj);
-        if(!obj)
-            return xpc_qsThrow(cx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
+        return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
     }
 
-    JSObject *cur = obj;
-    XPCWrappedNativeTearOff *tearoff = nsnull;
-    XPCWrappedNative *wrapper =
-        XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj, callee, &cur,
-                                                     &tearoff);
+    *cur = obj;
+    *tearoff = nsnull;
+
+    *wrapper =
+        XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj, callee, cur,
+                                                     tearoff);
+
+    return NS_OK;
+}
+
+nsresult
+castNative(JSContext *cx,
+           XPCWrappedNative *wrapper,
+           JSObject *cur,
+           XPCWrappedNativeTearOff *tearoff,
+           const nsIID &iid,
+           void **ppThis,
+           nsISupports **pThisRef,
+           jsval *vp,
+           XPCLazyCallContext *lccx)
+{
     if(wrapper)
     {
         nsresult rv = getNativeFromWrapper(wrapper, iid, ppThis, pThisRef, vp);
-        if(NS_SUCCEEDED(rv))
-        {
-            if(lccx)
-                lccx->SetWrapper(wrapper, tearoff);
-            
-            return JS_TRUE;
-        }
+
+        if(lccx && NS_SUCCEEDED(rv))
+            lccx->SetWrapper(wrapper, tearoff);
+
         if(rv != NS_ERROR_NO_INTERFACE)
-            return xpc_qsThrow(cx, rv);
+            return rv;
     }
     else if(cur)
     {
         nsISupports *native = static_cast<nsISupports*>(xpc_GetJSPrivate(cur));
         if(NS_SUCCEEDED(getNative(native, GetOffsetsFromSlimWrapper(cur),
                                   cur, iid, ppThis, pThisRef, vp)))
         {
             if(lccx)
                 lccx->SetWrapper(cur);
 
-            return JS_TRUE;
+            return NS_OK;
         }
     }
 
     *pThisRef = nsnull;
-    return xpc_qsThrow(cx, NS_ERROR_XPC_BAD_OP_ON_WN_PROTO);
+    return NS_ERROR_XPC_BAD_OP_ON_WN_PROTO;
 }
 
 JSBool
 xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx,
                             const nsIID &iid,
                             void **ppThis,
                             nsISupports **pThisRef,
                             jsval *vp)
@@ -875,84 +885,83 @@ xpc_qsUnwrapThisFromCcxImpl(XPCCallConte
     nsresult rv = getNative(native, GetOffsets(native, ccx.GetProto()),
                             ccx.GetFlattenedJSObject(), iid, ppThis, pThisRef,
                             vp);
     if(NS_FAILED(rv))
         return xpc_qsThrow(ccx.GetJSContext(), rv);
     return JS_TRUE;
 }
 
+JSObject*
+xpc_qsUnwrapObj(jsval v, nsISupports **ppArgRef, nsresult *rv)
+{
+    if(JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))
+    {
+        *ppArgRef = nsnull;
+        *rv = NS_OK;
+        return nsnull;
+    }
+
+    if(!JSVAL_IS_OBJECT(v))
+    {
+        *ppArgRef = nsnull;
+        *rv = ((JSVAL_IS_INT(v) && JSVAL_TO_INT(v) == 0)
+              ? NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL
+              : NS_ERROR_XPC_BAD_CONVERT_JS);
+        return nsnull;
+    }
+
+    *rv = NS_OK;
+    return JSVAL_TO_OBJECT(v);
+}
+
 nsresult
 xpc_qsUnwrapArgImpl(JSContext *cx,
                     jsval v,
                     const nsIID &iid,
                     void **ppArg,
                     nsISupports **ppArgRef,
                     jsval *vp)
 {
-    // From XPCConvert::JSData2Native
-    if(JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))
+    nsresult rv;
+    JSObject *src = xpc_qsUnwrapObj(v, ppArgRef, &rv);
+    if(!src)
     {
         *ppArg = nsnull;
-        *ppArgRef = nsnull;
-        return NS_OK;
+
+        return rv;
     }
 
-    if(!JSVAL_IS_OBJECT(v))
-    {
-        *ppArgRef = nsnull;
-        return ((JSVAL_IS_INT(v) && JSVAL_TO_INT(v) == 0)
-                ? NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL
-                : NS_ERROR_XPC_BAD_CONVERT_JS);
-    }
-    JSObject *src = JSVAL_TO_OBJECT(v);
-
-    JSObject *inner = nsnull;
-    if(XPCWrapper::IsSecurityWrapper(src))
-    {
-        inner = XPCWrapper::Unwrap(cx, src);
-        if(!inner)
-            return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
-    }
+    XPCWrappedNative *wrapper;
+    XPCWrappedNativeTearOff *tearoff;
+    JSObject *obj2;
+    rv = getWrapper(cx, src, nsnull, &wrapper, &obj2, &tearoff);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    // From XPCConvert::JSObject2NativeInterface
-    JSObject* obj2;
-    XPCWrappedNative* wrappedNative =
-        XPCWrappedNative::GetWrappedNativeOfJSObject(cx, inner ? inner : src,
-                                                     nsnull, &obj2);
-    nsISupports *iface;
-    if(wrappedNative)
+    if(wrapper || obj2)
     {
-        iface = wrappedNative->GetIdentityObject();
-        if(NS_FAILED(getNativeFromWrapper(wrappedNative, iid, ppArg, ppArgRef,
-                                          vp)))
-            return NS_ERROR_XPC_BAD_CONVERT_JS;
-        return NS_OK;
-    }
-    if(obj2)
-    {
-        iface = static_cast<nsISupports*>(xpc_GetJSPrivate(obj2));
-        if(NS_FAILED(getNative(iface, GetOffsetsFromSlimWrapper(obj2),
-                               obj2, iid, ppArg, ppArgRef, vp)))
+        if(NS_FAILED(castNative(cx, wrapper, obj2, tearoff, iid, ppArg,
+                                ppArgRef, vp, nsnull)))
             return NS_ERROR_XPC_BAD_CONVERT_JS;
         return NS_OK;
     }
     // else...
     // Slow path.
 
     // XXX E4X breaks the world. Don't try wrapping E4X objects!
     // This hack can be removed (or changed accordingly) when the
     // DOM <-> E4X bindings are complete, see bug 270553
     if(JS_TypeOfValue(cx, OBJECT_TO_JSVAL(src)) == JSTYPE_XML)
     {
         *ppArgRef = nsnull;
         return NS_ERROR_XPC_BAD_CONVERT_JS;
     }
 
     // Try to unwrap a slim wrapper.
+    nsISupports *iface;
     if(XPCConvert::GetISupportsFromJSObject(src, &iface))
     {
         if(!iface || NS_FAILED(iface->QueryInterface(iid, ppArg)))
         {
             *ppArgRef = nsnull;
             return NS_ERROR_XPC_BAD_CONVERT_JS;
         }
 
@@ -963,36 +972,35 @@ xpc_qsUnwrapArgImpl(JSContext *cx,
     // Create the ccx needed for quick stubs.
     XPCCallContext ccx(JS_CALLER, cx);
     if(!ccx.IsValid())
     {
         *ppArgRef = nsnull;
         return NS_ERROR_XPC_BAD_CONVERT_JS;
     }
 
-    nsXPCWrappedJS *wrapper;
-    nsresult rv =
-        nsXPCWrappedJS::GetNewOrUsed(ccx, src, iid, nsnull, &wrapper);
-    if(NS_FAILED(rv) || !wrapper)
+    nsRefPtr<nsXPCWrappedJS> wrappedJS;
+    rv = nsXPCWrappedJS::GetNewOrUsed(ccx, src, iid, nsnull,
+                                      getter_AddRefs(wrappedJS));
+    if(NS_FAILED(rv) || !wrappedJS)
     {
         *ppArgRef = nsnull;
         return rv;
     }
 
     // We need to go through the QueryInterface logic to make this return
     // the right thing for the various 'special' interfaces; e.g.
     // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
     // there is an outer to avoid nasty recursion.
-    rv = wrapper->QueryInterface(iid, ppArg);
+    rv = wrappedJS->QueryInterface(iid, ppArg);
     if(NS_SUCCEEDED(rv))
     {
         *ppArgRef = static_cast<nsISupports*>(*ppArg);
-        *vp = OBJECT_TO_JSVAL(wrapper->GetJSObject());
+        *vp = OBJECT_TO_JSVAL(wrappedJS->GetJSObject());
     }
-    NS_RELEASE(wrapper);
     return rv;
 }
 
 JSBool
 xpc_qsJsvalToCharStr(JSContext *cx, jsval v, jsval *pval, char **pstr)
 {
     JSString *str;
 
--- a/js/src/xpconnect/src/xpcquickstubs.h
+++ b/js/src/xpconnect/src/xpcquickstubs.h
@@ -343,25 +343,34 @@ xpc_qsJsvalToCharStr(JSContext *cx, jsva
 JSBool
 xpc_qsJsvalToWcharStr(JSContext *cx, jsval v, jsval *pval, PRUnichar **pstr);
 
 
 /** Convert an nsAString to jsval, returning JS_TRUE on success. */
 JSBool
 xpc_qsStringToJsval(JSContext *cx, const nsAString &str, jsval *rval);
 
-JSBool
-xpc_qsUnwrapThisImpl(JSContext *cx,
-                     JSObject *obj,
-                     JSObject *callee,
-                     const nsIID &iid,
-                     void **ppThis,
-                     nsISupports **ppThisRef,
-                     jsval *vp,
-                     XPCLazyCallContext *lccx);
+nsresult
+getWrapper(JSContext *cx,
+           JSObject *obj,
+           JSObject *callee,
+           XPCWrappedNative **wrapper,
+           JSObject **cur,
+           XPCWrappedNativeTearOff **tearoff);
+
+nsresult
+castNative(JSContext *cx,
+           XPCWrappedNative *wrapper,
+           JSObject *cur,
+           XPCWrappedNativeTearOff *tearoff,
+           const nsIID &iid,
+           void **ppThis,
+           nsISupports **ppThisRef,
+           jsval *vp,
+           XPCLazyCallContext *lccx);
 
 /**
  * Search @a obj and its prototype chain for an XPCOM object that implements
  * the interface T.
  *
  * If an object implementing T is found, store a reference to the wrapper
  * JSObject in @a *pThisVal, store a pointer to the T in @a *ppThis, and return
  * JS_TRUE. Otherwise, raise an exception on @a cx and return JS_FALSE.
@@ -379,24 +388,25 @@ inline JSBool
 xpc_qsUnwrapThis(JSContext *cx,
                  JSObject *obj,
                  JSObject *callee,
                  T **ppThis,
                  nsISupports **pThisRef,
                  jsval *pThisVal,
                  XPCLazyCallContext *lccx)
 {
-    return xpc_qsUnwrapThisImpl(cx,
-                                obj,
-                                callee,
-                                NS_GET_TEMPLATE_IID(T),
-                                reinterpret_cast<void **>(ppThis),
-                                pThisRef,
-                                pThisVal,
-                                lccx);
+    XPCWrappedNative *wrapper;
+    XPCWrappedNativeTearOff *tearoff;
+    nsresult rv = getWrapper(cx, obj, callee, &wrapper, &obj, &tearoff);
+    if(NS_SUCCEEDED(rv))
+        rv = castNative(cx, wrapper, obj, tearoff, NS_GET_TEMPLATE_IID(T),
+                        reinterpret_cast<void **>(ppThis), pThisRef, pThisVal,
+                        lccx);
+
+    return NS_SUCCEEDED(rv) || xpc_qsThrow(cx, rv);
 }
 
 JSBool
 xpc_qsUnwrapThisFromCcxImpl(XPCCallContext &ccx,
                             const nsIID &iid,
                             void **ppThis,
                             nsISupports **pThisRef,
                             jsval *vp);