From 8fc20a0ce4da7757f4ded0e33a6eaf6828946906 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 21 May 2009 01:17:51 -0700 Subject: [PATCH] Bug 480205 - Implement a wrapper for exposing chrome objects to content (aka COWs). r+sr=jst sr=bzbarsky on some parts. --- browser/components/feeds/src/FeedWriter.js | 13 +- dom/base/nsDOMClassInfo.cpp | 188 ++--- dom/base/nsDOMClassInfo.h | 7 +- js/src/xpconnect/idl/nsIXPConnect.idl | 15 +- js/src/xpconnect/src/Makefile.in | 1 + .../xpconnect/src/XPCChromeObjectWrapper.cpp | 674 ++++++++++++++++++ js/src/xpconnect/src/XPCWrapper.h | 2 +- js/src/xpconnect/src/nsXPConnect.cpp | 17 +- js/src/xpconnect/src/xpcconvert.cpp | 15 +- js/src/xpconnect/src/xpcjsruntime.cpp | 3 +- js/src/xpconnect/src/xpcprivate.h | 21 +- js/src/xpconnect/src/xpcwrappednative.cpp | 23 +- 12 files changed, 857 insertions(+), 122 deletions(-) create mode 100644 js/src/xpconnect/src/XPCChromeObjectWrapper.cpp diff --git a/browser/components/feeds/src/FeedWriter.js b/browser/components/feeds/src/FeedWriter.js index 769e7696a7c0..362f748b9d5e 100644 --- a/browser/components/feeds/src/FeedWriter.js +++ b/browser/components/feeds/src/FeedWriter.js @@ -845,8 +845,6 @@ FeedWriter.prototype = { // nsIDomEventListener handleEvent: function(event) { - // see comments in init() - event = new XPCNativeWrapper(event); if (event.target.ownerDocument != this._document) { LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!"); return; @@ -1152,10 +1150,7 @@ FeedWriter.prototype = { // nsIFeedWriter init: function FW_init(aWindow) { - // Explicitly wrap |window| in an XPCNativeWrapper to make sure - // it's a real native object! This will throw an exception if we - // get a non-native object. - var window = new XPCNativeWrapper(aWindow); + var window = aWindow; this._feedURI = this._getOriginalURI(window); if (!this._feedURI) return; @@ -1332,9 +1327,6 @@ FeedWriter.prototype = { // nsIObserver observe: function FW_observe(subject, topic, data) { - // see init() - subject = new XPCNativeWrapper(subject); - if (!this._window) { // this._window is null unless this.init was called with a trusted // window object. @@ -1401,9 +1393,6 @@ FeedWriter.prototype = { // nsINavHistoryService onPageChanged: function FW_onPageChanged(aURI, aWhat, aValue) { - // see init() - aURI = new XPCNativeWrapper(aURI); - if (aWhat == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) { // Go through the readers menu and look for the corresponding // reader menu-item for the page if any. diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 3c99fb5da6e2..ad02271e2e25 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1638,7 +1638,7 @@ nsDOMClassInfo::DefineStaticJSVals(JSContext *cx) // static nsresult nsDOMClassInfo::WrapNative(JSContext *cx, JSObject *scope, nsISupports *native, - const nsIID* aIID, jsval *vp, + const nsIID* aIID, PRBool aAllowWrapping, jsval *vp, nsIXPConnectJSObjectHolder **aHolder) { if (!native) { @@ -1652,7 +1652,42 @@ nsDOMClassInfo::WrapNative(JSContext *cx, JSObject *scope, nsISupports *native, NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED); return sXPConnect->WrapNativeToJSVal(cx, ::JS_GetGlobalForObject(cx, scope), - native, aIID, vp, aHolder); + native, aIID, aAllowWrapping, vp, + aHolder); +} + +static nsresult +CreateExceptionFromResult(JSContext *cx, nsresult aResult) +{ + nsCOMPtr xs = + do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID); + if (!xs) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr xm; + nsresult rv = xs->GetCurrentExceptionManager(getter_AddRefs(xm)); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr exception; + rv = xm->GetExceptionFromProvider(aResult, 0, getter_AddRefs(exception)); + if (NS_FAILED(rv) || !exception) { + return NS_ERROR_FAILURE; + } + + jsval jv; + nsCOMPtr holder; + rv = nsDOMClassInfo::WrapNative(cx, ::JS_GetGlobalObject(cx), exception, + &NS_GET_IID(nsIException), PR_FALSE, &jv, + getter_AddRefs(holder)); + if (NS_FAILED(rv) || JSVAL_IS_NULL(jv)) { + return NS_ERROR_FAILURE; + } + + JS_SetPendingException(cx, jv); + return NS_OK; } // static @@ -1661,36 +1696,9 @@ nsDOMClassInfo::ThrowJSException(JSContext *cx, nsresult aResult) { JSAutoRequest ar(cx); - do { - nsCOMPtr xs = - do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID); - if (!xs) { - break; - } - - nsCOMPtr xm; - nsresult rv = xs->GetCurrentExceptionManager(getter_AddRefs(xm)); - if (NS_FAILED(rv)) { - break; - } - - nsCOMPtr exception; - rv = xm->GetExceptionFromProvider(aResult, 0, getter_AddRefs(exception)); - if (NS_FAILED(rv) || !exception) { - break; - } - - jsval jv; - nsCOMPtr holder; - rv = WrapNative(cx, ::JS_GetGlobalObject(cx), exception, - &NS_GET_IID(nsIException), &jv, getter_AddRefs(holder)); - if (NS_FAILED(rv) || JSVAL_IS_NULL(jv)) { - break; - } - JS_SetPendingException(cx, jv); - + if (NS_SUCCEEDED(CreateExceptionFromResult(cx, aResult))) { return NS_OK; - } while (0); + } // XXX This probably wants to be localized, but that can fail in ways that // are hard to report correctly. @@ -4559,7 +4567,8 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj, if (result) { jsval v; nsCOMPtr holder; - nsresult rv = WrapNative(cx, obj, result, &v, getter_AddRefs(holder)); + nsresult rv = WrapNative(cx, obj, result, PR_TRUE, &v, + getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, JS_FALSE); if (!::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(jsstr), @@ -4753,7 +4762,7 @@ nsWindowSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, nsCOMPtr holder; rv = WrapNative(cx, frameWin->GetGlobalJSObject(), frame, - &NS_GET_IID(nsIDOMWindow), vp, + &NS_GET_IID(nsIDOMWindow), PR_TRUE, vp, getter_AddRefs(holder)); } @@ -4870,8 +4879,8 @@ nsWindowSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && location, rv); nsCOMPtr holder; - rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), vp, - getter_AddRefs(holder)); + rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, + vp, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); rv = location->SetHref(nsDependentJSString(val)); @@ -5118,8 +5127,8 @@ BaseStubConstructor(nsIWeakReference* aWeakOwner, } nsCOMPtr holder; - rv = nsDOMGenericSH::WrapNative(cx, obj, native, rval, - getter_AddRefs(holder)); + rv = nsDOMClassInfo::WrapNative(cx, obj, native, PR_TRUE, + rval, getter_AddRefs(holder)); return rv; } @@ -5573,8 +5582,8 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx, jsval v; rv = nsDOMClassInfo::WrapNative(cx, obj, constructor, - &NS_GET_IID(nsIDOMDOMConstructor), &v, - getter_AddRefs(holder)); + &NS_GET_IID(nsIDOMDOMConstructor), + PR_FALSE, &v, getter_AddRefs(holder)); sDoSecurityCheckInAddProperty = doSecurityCheckInAddProperty; @@ -5832,8 +5841,8 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, nsCOMPtr holder; jsval v; - rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor), &v, - getter_AddRefs(holder)); + rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor), + PR_FALSE, &v, getter_AddRefs(holder)); sDoSecurityCheckInAddProperty = doSecurityCheckInAddProperty; @@ -5875,7 +5884,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, jsval val; nsCOMPtr holder; rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor), - &val, getter_AddRefs(holder)); + PR_FALSE, &val, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); rv = constructor->Install(cx, obj, val); @@ -5922,7 +5931,8 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx, scope = aWin->GetGlobalJSObject(); } - rv = WrapNative(cx, scope, native, &prop_val, getter_AddRefs(holder)); + rv = WrapNative(cx, scope, native, PR_TRUE, &prop_val, + getter_AddRefs(holder)); } NS_ENSURE_SUCCESS(rv, rv); @@ -6213,10 +6223,19 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, jsval v; nsCOMPtr holder; rv = WrapNative(cx, wrapperObj, child_win, - &NS_GET_IID(nsIDOMWindowInternal), &v, + &NS_GET_IID(nsIDOMWindowInternal), PR_TRUE, &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); +#ifdef DEBUG + if (!win->IsChromeWindow()) { + NS_ASSERTION(JSVAL_IS_OBJECT(v) && + !strcmp(STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name, + "XPCCrossOriginWrapper"), + "Didn't wrap a window!"); + } +#endif + // Script is accessing a child frame and this access can // potentially come from a context from a different domain. // ::JS_DefineUCProperty() will call @@ -6229,12 +6248,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // property is 'ok' in this case, even if the call comes from // a different context. - if (!win->IsChromeWindow()) { - rv = sXPConnect->GetXOWForObject(cx, win->GetGlobalJSObject(), - JSVAL_TO_OBJECT(v), &v); - NS_ENSURE_SUCCESS(rv, rv); - } - JSAutoRequest ar(cx); PRBool ok = ::JS_DefineUCProperty(cx, obj, chars, @@ -6335,18 +6348,22 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, jsval v; nsCOMPtr holder; - rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), &v, - getter_AddRefs(holder)); + rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, + &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); +#ifdef DEBUG + if (!win->IsChromeWindow()) { + NS_ASSERTION(JSVAL_IS_OBJECT(v) && + !strcmp(STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name, + "XPCCrossOriginWrapper"), + "Didn't wrap a location object!"); + } +#endif + PRBool doSecurityCheckInAddProperty = sDoSecurityCheckInAddProperty; sDoSecurityCheckInAddProperty = PR_FALSE; - if (!win->IsChromeWindow()) { - rv = sXPConnect->GetXOWForObject(cx, scope, JSVAL_TO_OBJECT(v), &v); - NS_ENSURE_SUCCESS(rv, rv); - } - JSAutoRequest ar(cx); JSBool ok = ::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str), @@ -6392,8 +6409,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, jsval v; nsCOMPtr holder; - rv = WrapNative(cx, obj, navigator, &NS_GET_IID(nsIDOMNavigator), &v, - getter_AddRefs(holder)); + rv = WrapNative(cx, obj, navigator, &NS_GET_IID(nsIDOMNavigator), PR_TRUE, + &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); JSAutoRequest ar(cx); @@ -6416,8 +6433,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, jsval v; nsCOMPtr holder; - rv = WrapNative(cx, obj, document, &NS_GET_IID(nsIDOMDocument), &v, - getter_AddRefs(holder)); + rv = WrapNative(cx, obj, document, &NS_GET_IID(nsIDOMDocument), PR_FALSE, + &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); // The PostCreate hook for the document will handle defining the @@ -6999,7 +7016,7 @@ nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, jsval v; nsCOMPtr holder; - nsresult rv = WrapNative(cx, globalObj, native_parent, &v, + nsresult rv = WrapNative(cx, globalObj, native_parent, PR_FALSE, &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); @@ -7058,7 +7075,7 @@ nsNodeSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } nsCOMPtr holder; - nsresult rv = WrapNative(cx, obj, uri, &NS_GET_IID(nsIURI), vp, + nsresult rv = WrapNative(cx, obj, uri, &NS_GET_IID(nsIURI), PR_TRUE, vp, getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } @@ -7069,7 +7086,7 @@ nsNodeSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, nsCOMPtr holder; nsresult rv = WrapNative(cx, obj, node->NodePrincipal(), - &NS_GET_IID(nsIPrincipal), vp, + &NS_GET_IID(nsIPrincipal), PR_TRUE, vp, getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } @@ -7748,7 +7765,7 @@ nsArraySH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); if (array_item) { - rv = WrapNative(cx, obj, array_item, vp); + rv = WrapNative(cx, obj, array_item, PR_TRUE, vp); NS_ENSURE_SUCCESS(rv, rv); rv = NS_SUCCESS_I_DID_SOMETHING; @@ -7782,7 +7799,7 @@ nsNodeListSH::PreCreate(nsISupports *nativeObj, JSContext *cx, jsval v; nsCOMPtr holder; - nsresult rv = WrapNative(cx, globalObj, native_parent, &v, + nsresult rv = WrapNative(cx, globalObj, native_parent, PR_FALSE, &v, getter_AddRefs(holder)); *parentObj = JSVAL_TO_OBJECT(v); @@ -7856,7 +7873,7 @@ nsNamedArraySH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); if (item) { - rv = WrapNative(cx, obj, item, vp); + rv = WrapNative(cx, obj, item, PR_TRUE, vp); NS_ENSURE_SUCCESS(rv, rv); rv = NS_SUCCESS_I_DID_SOMETHING; @@ -7968,7 +7985,7 @@ nsContentListSH::PreCreate(nsISupports *nativeObj, JSContext *cx, jsval v; nsCOMPtr holder; - nsresult rv = WrapNative(cx, globalObj, native_parent, &v, + nsresult rv = WrapNative(cx, globalObj, native_parent, PR_FALSE, &v, getter_AddRefs(holder)); *parentObj = JSVAL_TO_OBJECT(v); @@ -8064,8 +8081,8 @@ nsDocumentSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, jsval v; nsCOMPtr holder; - rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), &v, - getter_AddRefs(holder)); + rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, + &v, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); PRBool doSecurityCheckInAddProperty = sDoSecurityCheckInAddProperty; @@ -8108,7 +8125,7 @@ nsDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_TRUE(uri, NS_ERROR_NOT_AVAILABLE); nsCOMPtr holder; - nsresult rv = WrapNative(cx, obj, uri, &NS_GET_IID(nsIURI), vp, + nsresult rv = WrapNative(cx, obj, uri, &NS_GET_IID(nsIURI), PR_TRUE, vp, getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; @@ -8140,8 +8157,8 @@ nsDocumentSH::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr holder; - rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), vp, - getter_AddRefs(holder)); + rv = WrapNative(cx, obj, location, &NS_GET_IID(nsIDOMLocation), PR_TRUE, + vp, getter_AddRefs(holder)); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } } @@ -8194,7 +8211,7 @@ nsDocumentSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, jsval winVal; nsCOMPtr holder; - rv = WrapNative(cx, obj, win, &NS_GET_IID(nsIDOMWindow), &winVal, + rv = WrapNative(cx, obj, win, &NS_GET_IID(nsIDOMWindow), PR_FALSE, &winVal, getter_AddRefs(holder)); NS_ENSURE_SUCCESS(rv, rv); @@ -8300,7 +8317,7 @@ nsHTMLDocumentSH::DocumentOpen(JSContext *cx, JSObject *obj, uintN argc, } nsCOMPtr holder; - rv = WrapNative(cx, obj, retval, &NS_GET_IID(nsIDOMDocument), rval, + rv = WrapNative(cx, obj, retval, &NS_GET_IID(nsIDOMDocument), PR_FALSE, rval, getter_AddRefs(holder)); NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to wrap native!"); @@ -8375,7 +8392,7 @@ nsHTMLDocumentSH::GetDocumentAllNodeList(JSContext *cx, JSObject *obj, rv |= domdoc->GetElementsByTagName(NS_LITERAL_STRING("*"), nodeList); nsCOMPtr holder; - rv |= nsDOMClassInfo::WrapNative(cx, obj, *nodeList, &collection, + rv |= nsDOMClassInfo::WrapNative(cx, obj, *nodeList, PR_FALSE, &collection, getter_AddRefs(holder)); // ... and store it in our reserved slot. @@ -8472,7 +8489,7 @@ nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JSObject *obj, } if (result) { - rv = WrapNative(cx, obj, result, vp); + rv = WrapNative(cx, obj, result, PR_TRUE, vp); if (NS_FAILED(rv)) { nsDOMClassInfo::ThrowJSException(cx, rv); @@ -8744,7 +8761,7 @@ nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj, if (tags) { jsval v; nsCOMPtr holder; - nsresult rv = nsDOMClassInfo::WrapNative(cx, obj, tags, &v, + nsresult rv = nsDOMClassInfo::WrapNative(cx, obj, tags, PR_TRUE, &v, getter_AddRefs(holder)); if (NS_FAILED(rv)) { nsDOMClassInfo::ThrowJSException(cx, rv); @@ -8903,7 +8920,7 @@ nsHTMLDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper, NS_ENSURE_SUCCESS(rv, rv); if (result) { - rv = WrapNative(cx, obj, result, vp); + rv = WrapNative(cx, obj, result, PR_TRUE, vp); if (NS_SUCCEEDED(rv)) { rv = NS_SUCCESS_I_DID_SOMETHING; } @@ -9043,7 +9060,7 @@ nsHTMLFormElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper, if (result) { // Wrap result, result can be either an element or a list of // elements - nsresult rv = WrapNative(cx, obj, result, vp); + nsresult rv = WrapNative(cx, obj, result, PR_TRUE, vp); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } } @@ -9055,7 +9072,7 @@ nsHTMLFormElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper, form->GetElementAt(n, getter_AddRefs(control)); if (control) { - nsresult rv = WrapNative(cx, obj, control, vp); + nsresult rv = WrapNative(cx, obj, control, PR_TRUE, vp); return NS_FAILED(rv) ? rv : NS_SUCCESS_I_DID_SOMETHING; } } @@ -9162,7 +9179,7 @@ nsHTMLSelectElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper, options->Item(n, getter_AddRefs(node)); - rv = WrapNative(cx, obj, node, &NS_GET_IID(nsIDOMNode), vp); + rv = WrapNative(cx, obj, node, &NS_GET_IID(nsIDOMNode), PR_TRUE, vp); if (NS_SUCCEEDED(rv)) { rv = NS_SUCCESS_I_DID_SOMETHING; } @@ -9810,12 +9827,11 @@ nsHTMLPluginObjElementSH::NewResolve(nsIXPConnectWrappedNative *wrapper, } nsCOMPtr holder; - rv = sXPConnect->WrapNative(cx, obj, pi, *iid, getter_AddRefs(holder)); + jsval v; + rv = WrapNative(cx, obj, pi, iid, PR_TRUE, &v, getter_AddRefs(holder)); if (NS_SUCCEEDED(rv)) { - JSObject* ifaceObj; - - rv = holder->GetJSObject(&ifaceObj); + JSObject* ifaceObj = JSVAL_TO_OBJECT(v); if (NS_SUCCEEDED(rv)) { nsMemory::Free(iid); diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index f39effad1ed6..433a512cb554 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -131,19 +131,20 @@ public: static nsresult WrapNative(JSContext *cx, JSObject *scope, nsISupports *native, const nsIID* aIID, - jsval *vp, + PRBool aAllowWrapping, jsval *vp, // If non-null aHolder will keep the jsval alive // while there's a ref to it nsIXPConnectJSObjectHolder** aHolder = nsnull); // Same as the WrapNative above, but use this one if aIID is nsISupports' IID. static nsresult WrapNative(JSContext *cx, JSObject *scope, - nsISupports *native, jsval *vp, + nsISupports *native, PRBool aAllowWrapping, + jsval *vp, // If non-null aHolder will keep the jsval alive // while there's a ref to it nsIXPConnectJSObjectHolder** aHolder = nsnull) { - return WrapNative(cx, scope, native, nsnull, vp, aHolder); + return WrapNative(cx, scope, native, nsnull, aAllowWrapping, vp, aHolder); } static nsresult ThrowJSException(JSContext *cx, nsresult aResult); diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index d98a76216a5e..bffad47b5bca 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -405,7 +405,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } } %} -[uuid(b2ddc328-194b-45d6-95c6-52e487438096)] +[uuid(b76828b8-3ac5-469e-946d-3401c6a2104d)] interface nsIXPConnect : nsISupports { %{ C++ @@ -476,12 +476,15 @@ interface nsIXPConnect : nsISupports * &NS_GET_IID(nsISupports) but when passing in null certain shortcuts * can be taken because we know without comparing IIDs that the caller is * asking for an nsISupports wrapper. + * If aAllowWrapper, then the returned value will be wrapped in the proper + * type of security wrapper on top of the XPCWrappedNative (if needed). */ void wrapNativeToJSVal(in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDPtr aIID, + in boolean aAllowWrapper, out JSVal aVal, out nsIXPConnectJSObjectHolder aHolder); @@ -705,6 +708,16 @@ interface nsIXPConnect : nsISupports in JSObjectPtr aParent, in JSObjectPtr aWrappedObj); + /** + * Wrap a jsval in a chrome object wrapper. + * @param aJSContext A context to use to create objects. + * @param aParent The parent to create the wrapper with. + * @param aWrappedObj The object to wrap. + */ + [noscript] JSVal getCOWForObject(in JSContextPtr aJSContext, + in JSObjectPtr aParent, + in JSObjectPtr aWrappedObj); + /** * Tells updateXOWs to clear the scope of all of the XOWs it finds. */ diff --git a/js/src/xpconnect/src/Makefile.in b/js/src/xpconnect/src/Makefile.in index 60bfda0d6f24..b6a4a5d83f40 100644 --- a/js/src/xpconnect/src/Makefile.in +++ b/js/src/xpconnect/src/Makefile.in @@ -109,6 +109,7 @@ CPPSRCS = \ xpcJSWeakReference.cpp \ XPCSafeJSObjectWrapper.cpp \ XPCCrossOriginWrapper.cpp \ + XPCChromeObjectWrapper.cpp \ XPCSystemOnlyWrapper.cpp \ XPCWrapper.cpp \ xpcquickstubs.cpp \ diff --git a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp new file mode 100644 index 000000000000..92d01ad59e47 --- /dev/null +++ b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp @@ -0,0 +1,674 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=78 sts=2: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Blake Kaplan (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "xpcprivate.h" +#include "nsDOMError.h" +#include "jsdbgapi.h" +#include "jscntxt.h" // For JSAutoTempValueRooter. +#include "jsobj.h" +#include "XPCNativeWrapper.h" +#include "XPCWrapper.h" + +// This file implements a wrapper around trusted objects that allows them to +// be safely injected into untrusted code. + +static JSBool +XPC_COW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static JSBool +XPC_COW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static JSBool +XPC_COW_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static JSBool +XPC_COW_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + +static JSBool +XPC_COW_Enumerate(JSContext *cx, JSObject *obj); + +static JSBool +XPC_COW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp); + +static JSBool +XPC_COW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); + +static JSBool +XPC_COW_CheckAccess(JSContext *cx, JSObject *obj, jsval id, JSAccessMode mode, + jsval *vp); + +static JSBool +XPC_COW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + +static JSObject * +XPC_COW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly); + +static JSObject * +XPC_COW_WrappedObject(JSContext *cx, JSObject *obj); + +JSExtendedClass sXPC_COW_JSClass = { + // JSClass (JSExtendedClass.base) initialization + { "ChromeObjectWrapper", + JSCLASS_NEW_RESOLVE | JSCLASS_IS_EXTENDED | + JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots), + XPC_COW_AddProperty, XPC_COW_DelProperty, + XPC_COW_GetProperty, XPC_COW_SetProperty, + XPC_COW_Enumerate, (JSResolveOp)XPC_COW_NewResolve, + XPC_COW_Convert, JS_FinalizeStub, + nsnull, XPC_COW_CheckAccess, + nsnull, nsnull, + nsnull, nsnull, + nsnull, nsnull + }, + + // JSExtendedClass initialization + XPC_COW_Equality, + nsnull, // outerObject + nsnull, // innerObject + XPC_COW_Iterator, + XPC_COW_WrappedObject, + JSCLASS_NO_RESERVED_MEMBERS +}; + +static JSBool +XPC_COW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval); + +// Throws an exception on context |cx|. +static inline JSBool +ThrowException(nsresult rv, JSContext *cx) +{ + return XPCWrapper::ThrowException(rv, cx); +} + +// Like GetWrappedObject, but works on other types of wrappers, too. +// See also js_GetWrappedObject in jsobj.h. +// TODO Move to XPCWrapper? +static inline JSObject * +GetWrappedJSObject(JSContext *cx, JSObject *obj) +{ + JSClass *clasp = STOBJ_GET_CLASS(obj); + if (!(clasp->flags & JSCLASS_IS_EXTENDED)) { + return obj; + } + + JSExtendedClass *xclasp = (JSExtendedClass *)clasp; + if (!xclasp->wrappedObject) { + if (XPCNativeWrapper::IsNativeWrapper(obj)) { + XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); + return wn ? wn->GetFlatJSObject() : nsnull; + } + + return obj; + } + + return xclasp->wrappedObject(cx, obj); +} + +// Get the (possibly non-existant) COW off of an object +// TODO Move to XPCWrapper and share with other wrappers. +static inline +JSObject * +GetWrapper(JSObject *obj) +{ + while (STOBJ_GET_CLASS(obj) != &sXPC_COW_JSClass.base) { + obj = STOBJ_GET_PROTO(obj); + if (!obj) { + break; + } + } + + return obj; +} + +// TODO Templatize, move to XPCWrapper and share with other wrappers! +static inline +JSObject * +GetWrappedObject(JSContext *cx, JSObject *wrapper) +{ + if (STOBJ_GET_CLASS(wrapper) != &sXPC_COW_JSClass.base) { + return nsnull; + } + + jsval v; + if (!JS_GetReservedSlot(cx, wrapper, XPCWrapper::sWrappedObjSlot, &v)) { + JS_ClearPendingException(cx); + return nsnull; + } + + if (!JSVAL_IS_OBJECT(v)) { + return nsnull; + } + + return JSVAL_TO_OBJECT(v); +} + +// Forward declaration for the function wrapper. +JSBool +XPC_COW_RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp); +JSBool +XPC_COW_RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp); + +// This function wrapper calls a function from untrusted content into chrome. + +static JSBool +XPC_COW_FunctionWrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + JSObject *wrappedObj; + + // Allow 'this' to be either a COW, in which case we unwrap it or something + // that isn't a COW. We disallow invalid COWs that have no wrapped object. + + wrappedObj = GetWrapper(obj); + if (wrappedObj) { + wrappedObj = GetWrappedObject(cx, wrappedObj); + if (!wrappedObj) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + } else { + wrappedObj = obj; + } + + JSObject *funObj = JSVAL_TO_OBJECT(argv[-2]); + jsval funToCall; + if (!JS_GetReservedSlot(cx, funObj, XPCWrapper::eWrappedFunctionSlot, + &funToCall)) { + return JS_FALSE; + } + + JSFunction *fun = JS_ValueToFunction(cx, funToCall); + if (!fun) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + + for (uintN i = 0; i < argc; ++i) { + if (!XPC_COW_RewrapForChrome(cx, obj, &argv[i])) { + return JS_FALSE; + } + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + JSNative native = JS_GetFunctionNative(cx, fun); + NS_ASSERTION(native, "How'd we get here with a scripted function?"); + + // A trick! Calling the native directly doesn't push the native onto the + // JS stack, so interested onlookers will only see us, meaning that they + // will compute *our* subject principal. + + argv[-2] = funToCall; + argv[-1] = OBJECT_TO_JSVAL(wrappedObj); + if (!native(cx, wrappedObj, argc, argv, rval)) { + return JS_FALSE; + } + + return XPC_COW_RewrapForContent(cx, obj, rval); +} + +JSBool +XPC_COW_WrapFunction(JSContext *cx, JSObject *outerObj, JSObject *funobj, + jsval *rval) +{ + jsval funobjVal = OBJECT_TO_JSVAL(funobj); + JSFunction *wrappedFun = + reinterpret_cast(xpc_GetJSPrivate(funobj)); + JSNative native = JS_GetFunctionNative(cx, wrappedFun); + if (!native || native == XPC_COW_FunctionWrapper) { + *rval = funobjVal; + return JS_TRUE; + } + + JSFunction *funWrapper = + JS_NewFunction(cx, XPC_COW_FunctionWrapper, + JS_GetFunctionArity(wrappedFun), 0, + JS_GetGlobalForObject(cx, outerObj), + JS_GetFunctionName(wrappedFun)); + if (!funWrapper) { + return JS_FALSE; + } + + JSObject *funWrapperObj = JS_GetFunctionObject(funWrapper); + *rval = OBJECT_TO_JSVAL(funWrapperObj); + + return JS_SetReservedSlot(cx, funWrapperObj, + XPCWrapper::eWrappedFunctionSlot, + funobjVal); +} + +JSBool +XPC_COW_RewrapForChrome(JSContext *cx, JSObject *wrapperObj, jsval *vp) +{ + jsval v = *vp; + if (JSVAL_IS_PRIMITIVE(v)) { + return JS_TRUE; + } + + // We're rewrapping for chrome, so this is safe. + JSObject *obj = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(v)); + if (!obj) { + *vp = JSVAL_NULL; + return JS_TRUE; + } + + XPCWrappedNative *wn; + if (IS_WRAPPER_CLASS(STOBJ_GET_CLASS(obj)) && + (wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj))) { + // Return an explicit XPCNativeWrapper in case "chrome" code happens to be + // XBL code cloned into an untrusted context. + return XPCNativeWrapperCtor(cx, obj, 1, vp, vp); + } + + return XPC_SJOW_Construct(cx, obj, 1, vp, vp); +} + +JSBool +XPC_COW_RewrapForContent(JSContext *cx, JSObject *wrapperObj, jsval *vp) +{ + jsval v = *vp; + if (JSVAL_IS_PRIMITIVE(v)) { + return JS_TRUE; + } + + JSObject *obj = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(v)); + if (!obj) { + *vp = JSVAL_NULL; + return JS_TRUE; + } + + if (JS_ObjectIsFunction(cx, obj)) { + return XPC_COW_WrapFunction(cx, wrapperObj, obj, vp); + } + + return XPC_COW_WrapObject(cx, JS_GetScopeChain(cx), OBJECT_TO_JSVAL(obj), + vp); +} + +static JSBool +XPC_COW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + obj = GetWrapper(obj); + jsval resolving; + if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, &resolving)) { + return JS_FALSE; + } + + if (HAS_FLAGS(resolving, FLAG_RESOLVING)) { + // Allow us to define a property on ourselves. + return JS_TRUE; + } + + // Someone's adding a property to us. We need to protect ourselves from + // getters and setters. + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + + jsid interned_id; + JSPropertyDescriptor desc; + + if (!JS_ValueToId(cx, id, &interned_id) || + !XPCWrapper::GetPropertyAttrs(cx, obj, interned_id, JSRESOLVE_QUALIFIED, + JS_TRUE, &desc)) { + return JS_FALSE; + } + + NS_ASSERTION(desc.obj == obj, "The JS engine lies!"); + + if (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) { + // Only chrome is allowed to add getters or setters to our object. + if (!AllowedToAct(cx, id)) { + return JS_FALSE; + } + } + + return XPC_COW_RewrapForChrome(cx, obj, vp) && + JS_DefinePropertyById(cx, wrappedObj, interned_id, *vp, + desc.getter, desc.setter, desc.attrs); +} + +static JSBool +XPC_COW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + // Deleting a property is safe. + return XPCWrapper::DelProperty(cx, wrappedObj, id, vp); +} + +static JSBool +XPC_COW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, + JSBool isSet) +{ + if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { + return JS_TRUE; + } + + obj = GetWrapper(obj); + if (!obj) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + AUTO_MARK_JSVAL(ccx, vp); + + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + + if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_PROTO) || + id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_PARENT)) { + // No getting or setting __proto__ or __parent__ on my object. + return ThrowException(NS_ERROR_INVALID_ARG, cx); // XXX better error message + } + + if (!XPC_COW_RewrapForChrome(cx, obj, vp)) { + return JS_FALSE; + } + + jsid interned_id; + if (!JS_ValueToId(cx, id, &interned_id)) { + return JS_FALSE; + } + + JSBool ok = isSet + ? JS_SetPropertyById(cx, wrappedObj, interned_id, vp) + : JS_GetPropertyById(cx, wrappedObj, interned_id, vp); + if (!ok) { + return JS_FALSE; + } + + return XPC_COW_RewrapForContent(cx, obj, vp); +} + +static JSBool +XPC_COW_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return XPC_COW_GetOrSetProperty(cx, obj, id, vp, JS_FALSE); +} + +static JSBool +XPC_COW_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return XPC_COW_GetOrSetProperty(cx, obj, id, vp, JS_TRUE); +} + +static JSBool +XPC_COW_Enumerate(JSContext *cx, JSObject *obj) +{ + obj = GetWrapper(obj); + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + // Nothing to enumerate. + return JS_TRUE; + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + return XPCWrapper::Enumerate(cx, obj, wrappedObj); +} + +static JSBool +XPC_COW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, + JSObject **objp) +{ + obj = GetWrapper(obj); + + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + // No wrappedObj means that this is probably the prototype. + *objp = nsnull; + return JS_TRUE; + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { + jsval oldSlotVal; + if (!::JS_GetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, &oldSlotVal) || + !::JS_SetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, + INT_TO_JSVAL(JSVAL_TO_INT(oldSlotVal) | + FLAG_RESOLVING))) { + return JS_FALSE; + } + + JSBool ok = JS_DefineFunction(cx, obj, "toString", + XPC_COW_toString, 0, 0) != nsnull; + + if (ok && (ok = ::JS_SetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, + oldSlotVal))) { + *objp = obj; + } + + return ok; + } + + return XPCWrapper::NewResolve(cx, obj, JS_TRUE, wrappedObj, id, flags, objp); +} + +static JSBool +XPC_COW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + // Don't do any work to convert to object. + if (type == JSTYPE_OBJECT) { + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + // Converting the prototype to something. + + if (type == JSTYPE_STRING || type == JSTYPE_VOID) { + return XPC_COW_toString(cx, obj, 0, nsnull, vp); + } + + *vp = OBJECT_TO_JSVAL(obj); + return JS_TRUE; + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + if (!STOBJ_GET_CLASS(wrappedObj)->convert(cx, wrappedObj, type, vp)) { + return JS_FALSE; + } + + return XPC_COW_RewrapForContent(cx, obj, vp); +} + +static JSBool +XPC_COW_CheckAccess(JSContext *cx, JSObject *obj, jsval prop, JSAccessMode mode, + jsval *vp) +{ + // Simply forward checkAccess to our wrapped object. It's already expecting + // untrusted things to ask it about accesses. + + uintN junk; + jsid id; + return JS_ValueToId(cx, prop, &id) && + JS_CheckAccess(cx, GetWrappedObject(cx, obj), id, mode, vp, &junk); +} + +static JSBool +XPC_COW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + // Convert both sides to XPCWrappedNative and see if they match. + if (JSVAL_IS_PRIMITIVE(v)) { + *bp = JS_FALSE; + return JS_TRUE; + } + + JSObject *test = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(v)); + + obj = GetWrappedObject(cx, obj); + if (!obj) { + return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); + } + XPCWrappedNative *other = + XPCWrappedNative::GetWrappedNativeOfJSObject(cx, test); + if (!other) { + *bp = JS_FALSE; + return JS_TRUE; + } + + XPCWrappedNative *me = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj); + obj = me->GetFlatJSObject(); + test = other->GetFlatJSObject(); + return ((JSExtendedClass *)STOBJ_GET_CLASS(obj))-> + equality(cx, obj, OBJECT_TO_JSVAL(test), bp); +} + +static JSObject * +XPC_COW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly) +{ + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + ThrowException(NS_ERROR_INVALID_ARG, cx); + return nsnull; + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + ThrowException(NS_ERROR_FAILURE, cx); + return nsnull; + } + + JSObject *wrapperIter = JS_NewObject(cx, &sXPC_COW_JSClass.base, nsnull, + JS_GetGlobalForObject(cx, obj)); + if (!wrapperIter) { + return nsnull; + } + + JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter)); + + // Initialize our COW. + jsval v = OBJECT_TO_JSVAL(wrappedObj); + if (!JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sWrappedObjSlot, v) || + !JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sFlagsSlot, + JSVAL_ZERO)) { + return nsnull; + } + + return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, wrappedObj, + keysonly); +} + +static JSObject * +XPC_COW_WrappedObject(JSContext *cx, JSObject *obj) +{ + return GetWrappedObject(cx, obj); +} + +static JSBool +XPC_COW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + obj = GetWrapper(obj); + if (!obj) { + return ThrowException(NS_ERROR_UNEXPECTED, cx); + } + + JSObject *wrappedObj = GetWrappedObject(cx, obj); + if (!wrappedObj) { + // Someone's calling toString on our prototype. + NS_NAMED_LITERAL_CSTRING(protoString, "[object XPCCrossOriginWrapper]"); + JSString *str = + JS_NewStringCopyN(cx, protoString.get(), protoString.Length()); + if (!str) { + return JS_FALSE; + } + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; + } + + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + return ThrowException(NS_ERROR_FAILURE, cx); + } + + XPCWrappedNative *wn = + XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); + return XPCWrapper::NativeToString(cx, wn, argc, argv, rval, JS_FALSE); +} + +JSBool +XPC_COW_WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp) +{ + JSObject *wrapperObj = + JS_NewObjectWithGivenProto(cx, &sXPC_COW_JSClass.base, NULL, parent); + if (!wrapperObj) { + return JS_FALSE; + } + + *vp = OBJECT_TO_JSVAL(wrapperObj); + if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot, v) || + !JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sFlagsSlot, + JSVAL_ZERO)) { + return JS_FALSE; + } + + return JS_TRUE; +} diff --git a/js/src/xpconnect/src/XPCWrapper.h b/js/src/xpconnect/src/XPCWrapper.h index 64895693ba72..17e7246b2ce9 100644 --- a/js/src/xpconnect/src/XPCWrapper.h +++ b/js/src/xpconnect/src/XPCWrapper.h @@ -111,6 +111,7 @@ XPC_XOW_ClassNeedsXOW(const char *name) return JS_FALSE; } +extern JSExtendedClass sXPC_COW_JSClass; extern JSExtendedClass sXPC_SJOW_JSClass; extern JSExtendedClass sXPC_SOW_JSClass; extern JSExtendedClass sXPC_XOW_JSClass; @@ -377,7 +378,6 @@ public: uintN argc, jsval *argv, jsval *rval, JSBool isNativeWrapper); -private: /** * Looks up a property on obj. If it exists, then the parameters are filled * in with useful values. diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 9e1ee8b0820d..80952567f269 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -1200,7 +1200,8 @@ nsXPConnect::WrapNative(JSContext * aJSContext, NS_ASSERTION(aHolder, "bad param"); jsval v; - return WrapNativeToJSVal(aJSContext, aScope, aCOMObj, &aIID, &v, aHolder); + return WrapNativeToJSVal(aJSContext, aScope, aCOMObj, &aIID, PR_FALSE, + &v, aHolder); } /* void wrapNativeToJSVal (in JSContextPtr aJSContext, in JSObjectPtr aScope, in nsISupports aCOMObj, in nsIIDPtr aIID, out JSVal aVal, out nsIXPConnectJSObjectHolder aHolder); */ @@ -1209,6 +1210,7 @@ nsXPConnect::WrapNativeToJSVal(JSContext * aJSContext, JSObject * aScope, nsISupports *aCOMObj, const nsIID * aIID, + PRBool aAllowWrapping, jsval *aVal, nsIXPConnectJSObjectHolder **aHolder) { @@ -1225,7 +1227,7 @@ nsXPConnect::WrapNativeToJSVal(JSContext * aJSContext, nsresult rv; if(!XPCConvert::NativeInterface2JSObject(ccx, aVal, aHolder, aCOMObj, aIID, - nsnull, nsnull, aScope, PR_FALSE, + nsnull, nsnull, aScope, aAllowWrapping, OBJ_IS_NOT_GLOBAL, &rv)) return rv; @@ -1949,6 +1951,17 @@ nsXPConnect::GetXOWForObject(JSContext * aJSContext, ? NS_OK : NS_ERROR_FAILURE; } +NS_IMETHODIMP +nsXPConnect::GetCOWForObject(JSContext * aJSContext, + JSObject * aParent, + JSObject * aWrappedObj, + jsval * rval) +{ + *rval = OBJECT_TO_JSVAL(aWrappedObj); + return XPC_COW_WrapObject(aJSContext, aParent, *rval, rval) + ? NS_OK : NS_ERROR_FAILURE; +} + static inline PRBool PerformOp(JSContext *cx, PRUint32 aWay, JSObject *obj) { diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 738a61276c59..38df100a3f2a 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -1320,14 +1320,15 @@ XPCConvert::NativeInterface2JSObject(XPCCallContext& ccx, CreateHolderIfNeeded(ccx, JSVAL_TO_OBJECT(v), d, dest); } - if(allowNativeWrapper && wrapper->NeedsChromeWrapper()) + *d = v; + if(allowNativeWrapper) { - if(!XPC_SOW_WrapObject(ccx, xpcscope->GetGlobalJSObject(), v, d)) - return JS_FALSE; - } - else - { - *d = v; + if(wrapper->NeedsChromeWrapper()) + if(!XPC_SOW_WrapObject(ccx, xpcscope->GetGlobalJSObject(), v, d)) + return JS_FALSE; + if(wrapper->IsDoubleWrapper()) + if(!XPC_COW_WrapObject(ccx, xpcscope->GetGlobalJSObject(), v, d)) + return JS_FALSE; } if(dest) *dest = strongWrapper.forget().get(); diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 7872ce782147..abecd4b82d66 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -61,7 +61,8 @@ const char* XPCJSRuntime::mStrings[] = { "createInstance", // IDX_CREATE_INSTANCE "item", // IDX_ITEM "__proto__", // IDX_PROTO - "__iterator__" // IDX_ITERATOR + "__iterator__", // IDX_ITERATOR + "__parent__" // IDX_PARENT #ifdef XPC_IDISPATCH_SUPPORT , "GeckoActiveXObject" // IDX_ACTIVEX_OBJECT , "COMObject" // IDX_COMOBJECT diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index d0060f33846c..3ff73b505f3a 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -688,6 +688,7 @@ public: IDX_ITEM , IDX_PROTO , IDX_ITERATOR , + IDX_PARENT , #ifdef XPC_IDISPATCH_SUPPORT IDX_ACTIVEX_OBJECT , IDX_COM_OBJECT , @@ -2381,16 +2382,20 @@ public: JSBool HasExternalReference() const {return mRefCnt > 1;} - JSBool NeedsChromeWrapper() { return !!(mWrapper & 1); } - void SetNeedsChromeWrapper() { mWrapper |= 1; } + JSBool NeedsChromeWrapper() { return !!(mWrapperWord & CHROME_ONLY); } + void SetNeedsChromeWrapper() { mWrapperWord |= CHROME_ONLY; } + JSBool IsDoubleWrapper() { return !!(mWrapperWord & DOUBLE_WRAPPER); } + void SetIsDoubleWrapper() { mWrapperWord |= DOUBLE_WRAPPER; } + JSObject* GetWrapper() { - return (JSObject *)(mWrapper & ~1); + return (JSObject *) JSVAL_CLRTAG(mWrapperWord); } void SetWrapper(JSObject *obj) { - JSBool reset = NeedsChromeWrapper(); - mWrapper = PRWord(obj) | reset; + JSBool needsChrome = NeedsChromeWrapper(); + JSBool doubleWrapper = IsDoubleWrapper(); + mWrapperWord = PRWord(obj) | doubleWrapper | needsChrome; } void NoteTearoffs(nsCycleCollectionTraversalCallback& cb); @@ -2426,6 +2431,8 @@ protected: virtual ~XPCWrappedNative(); private: + enum { CHROME_ONLY = JS_BIT(0), DOUBLE_WRAPPER = JS_BIT(1) }; + void TraceOtherWrapper(JSTracer* trc); JSBool Init(XPCCallContext& ccx, JSObject* parent, JSBool isGlobal, const XPCNativeScriptableCreateInfo* sci); @@ -2457,7 +2464,7 @@ private: JSObject* mFlatJSObject; XPCNativeScriptableInfo* mScriptableInfo; XPCWrappedNativeTearOffChunk mFirstChunk; - PRWord mWrapper; + PRWord mWrapperWord; #ifdef XPC_CHECK_WRAPPER_THREADSAFETY public: @@ -4088,6 +4095,8 @@ XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp, JSBool XPC_SOW_WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp); +JSBool +XPC_COW_WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp); #ifdef XPC_IDISPATCH_SUPPORT // IDispatch specific classes diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 7c185910520b..d25b5f9cbb8c 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -420,6 +420,7 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, XPCMarkableJSVal newParentVal_markable(&newParentVal); AutoMarkingJSVal newParentVal_automarker(ccx, &newParentVal_markable); JSBool chromeOnly = JS_FALSE; + JSBool crossDoubleWrapped = JS_FALSE; if(sciWrapper.GetFlags().WantPreCreate()) { @@ -477,6 +478,21 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, return NS_OK; } } + else + { + if(nsXPCWrappedJSClass::IsWrappedJS(Object)) + { + nsCOMPtr wrappedjs(do_QueryInterface(Object)); + JSObject *obj; + wrappedjs->GetJSObject(&obj); + if((STOBJ_IS_SYSTEM(obj) || + STOBJ_IS_SYSTEM(JS_GetGlobalForObject(ccx, obj))) && + !STOBJ_IS_SYSTEM(Scope->GetGlobalJSObject())) + { + crossDoubleWrapped = JS_TRUE; + } + } + } AutoMarkingWrappedNativeProtoPtr proto(ccx); @@ -540,7 +556,8 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx, if(chromeOnly) wrapper->SetNeedsChromeWrapper(); - + if(crossDoubleWrapped) + wrapper->SetIsDoubleWrapper(); #if DEBUG_xpc_leaks { @@ -705,7 +722,7 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed aIdentity, mSet(aProto->GetSet()), mFlatJSObject((JSObject*)JSVAL_ONE), // non-null to pass IsValid() test mScriptableInfo(nsnull), - mWrapper(nsnull) + mWrapperWord(0) { mIdentity = aIdentity.get(); @@ -724,7 +741,7 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed aIdentity, mSet(aSet), mFlatJSObject((JSObject*)JSVAL_ONE), // non-null to pass IsValid() test mScriptableInfo(nsnull), - mWrapper(nsnull) + mWrapperWord(0) { mIdentity = aIdentity.get();