From d957e12a1246e53e5a7ae3a2c3df484941e5e8ca Mon Sep 17 00:00:00 2001 From: "jband%netscape.com" Date: Wed, 2 Aug 2000 03:57:59 +0000 Subject: [PATCH] interim fix for bug 25180. Build double wrappers around JS components used by JS code. Includes secured scheme for getting at underlying JSObject for those who really need to do this. a=warren r=rginda --- js/src/xpconnect/idl/nsIXPConnect.idl | 79 ++++++++++++++++ js/src/xpconnect/src/xpcconvert.cpp | 17 +++- js/src/xpconnect/src/xpcjsruntime.cpp | 1 + js/src/xpconnect/src/xpcprivate.h | 1 + .../xpconnect/src/xpcwrappednativejsops.cpp | 91 ++++++++++++++++++- js/src/xpconnect/tests/idl/xpctest.idl | 5 + 6 files changed, 187 insertions(+), 7 deletions(-) diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index a204fef3eda1..dc248139240c 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -206,6 +206,85 @@ interface nsIXPCNativeCallContext : nsISupports /***************************************************************************/ +/** + * This is a sort of a placeholder interface. It is not intended to be + * implemented. It exists to give the nsIXPCSecurityManager an iid on + * which to gate a specific activity in XPConnect. + * + * That activity is... + * + * When JavaScript code uses a component that is itself implemeted in + * JavaScript then XPConnect will build a wrapper rather than directly + * expose the JSObject of the component. This allows components implemented + * in JavaScript to 'look' just like any other xpcom component (from the + * perspective of the JavaScript caller). This insulates the component from + * the caller and hides any properties or methods that are not part of the + * interface as declared in xpidl. Usually this is a good thing. + * + * However, in some cases it is useful to allow the JS caller access to the + * JS component's underlying implementation. In order to facilitate this + * XPConnect supports the 'wrappedJSObject' property. The caller code can do: + * + * // 'foo' is some xpcom component (that might be implemented in JS). + * try { + * var bar = foo.wrappedJSObject; + * if(bar) { + * // bar is the underlying JSObject. Do stuff with it here. + * } + * } catch(e) { + * // security exception? + * } + * + * Recall that 'foo' above is an XPConnect wrapper, not the underlying JS + * object. The property get "foo.wrappedJSObject" will only succeed if three + * conditions are met: + * + * 1) 'foo' really is an XPConnect wrapper around a JSObject. + * 2) The underlying JSObject actually implements a "wrappedJSObject" + * property that returns a JSObject. This is called by XPConnect. This + * restriction allows wrapped objects to only allow access to the underlying + * JSObject if they choose to do so. Ususally this just means that 'foo' + * would have a property tht looks like: + * this.wrappedJSObject = this. + * 3) The implemementation of nsIXPCSecurityManager (if installed) allows + * a property get on the interface below. Although the JSObject need not + * implement 'nsIXPCWrappedJSObjectGetter', XPConnect will ask the + * security manager if it is OK for the caller to access the only method + * in nsIXPCWrappedJSObjectGetter before allowing the activity. This fits + * in with the security manager paradigm and makes control over accessing + * the property on this interface the control factor for getting the + * underlying wrapped JSObject of a JS component from JS code. + * + * Notes: + * + * a) If 'foo' above were the underlying JSObject and not a wrapper at all, + * then this all just works and XPConnect is not part of the picture at all. + * b) One might ask why 'foo' should not just implement an interface through + * which callers might get at the underlying object. There are three reasons: + * i) XPConnect would still have to do magic since JSObject is not a + * scriptable type. + * ii) JS Components might use aggregation (like C++ objects) and have + * different JSObjects for different interfaces 'within' an aggregate + * object. But, using an additional interface only allows returning one + * underlying JSObject. However, this allows for the possibility that + * each of the aggregte JSObjects could return something different. + * Note that one might do: this.wrappedJSObject = someOtherObject; + * iii) Avoiding the explicit interface makes it easier for both the caller + * and the component. + * + * Anyway, some future implementation of nsIXPCSecurityManager might want + * do special processing on 'nsIXPCSecurityManager::CanGetProperty' when + * the interface id is that of nsIXPCWrappedJSObjectGetter. + */ + +[scriptable, uuid(254bb2e0-6439-11d4-8fe0-0010a4e73d9a)] +interface nsIXPCWrappedJSObjectGetter : nsISupports +{ + readonly attribute nsISupports neverCalled; +}; + +/***************************************************************************/ + %{ C++ // For use with the service manager // {CB6593E0-F9B2-11d2-BDD6-000064657374} diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index 59eb7f60bb5e..959d231059a3 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -722,6 +722,17 @@ XPCConvert::NativeInterface2JSObject(JSContext* cx, if(pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; +// #define this if we want to 'double wrap' of JSObjects. +// This is for the case where we have a JSObject wrapped for native use +// which needs to be converted to a JSObject. Originally, we were unwrapping +// and just exposing the underlying JSObject. This causes anomolies when +// JSComponents are accessed from other JS code - they don't act like +// other xpconnect wrapped components. Eventually we want to build a new +// kind of wrapper especially for JS <-> JS. For now we are building a wrapper +// around a wrapper. This is not optimal, but good enough for now. +#define XPC_DO_DOUBLE_WRAP 1 + +#ifndef XPC_DO_DOUBLE_WRAP // is this a wrapped JS object? if(nsXPCWrappedJSClass::IsWrappedJS(src)) { @@ -734,6 +745,8 @@ XPCConvert::NativeInterface2JSObject(JSContext* cx, (void**) dest)); } else +#endif /* XPC_DO_DOUBLE_WRAP */ + { #ifndef XPCONNECT_STANDALONE // is this a DOM wrapped native object? @@ -767,8 +780,8 @@ XPCConvert::NativeInterface2JSObject(JSContext* cx, *pErr = NS_ERROR_XPC_CANT_GET_JSOBJECT_OF_DOM_OBJECT; } else +#endif /* XPCONNECT_STANDALONE */ { -#endif // not a DOM object. Just try to build a wrapper nsXPCWrappedNativeScope* xpcscope; XPCContext* xpcc; @@ -786,9 +799,7 @@ XPCConvert::NativeInterface2JSObject(JSContext* cx, return JS_TRUE; } } -#ifndef XPCONNECT_STANDALONE } -#endif } return JS_FALSE; } diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index b9a32ba040ba..f223d2d7010c 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -47,6 +47,7 @@ const char* XPCJSRuntime::mStrings[] = { "value", // IDX_VALUE "QueryInterface", // IDX_QUERY_INTERFACE "Components", // IDX_COMPONENTS + "wrappedJSObject" // IDX_WRAPPED_JSOBJECT }; /***************************************************************************/ diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 17cf9ede075f..4d8e893e98a2 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -227,6 +227,7 @@ public: IDX_VALUE , IDX_QUERY_INTERFACE , IDX_COMPONENTS , + IDX_WRAPPED_JSOBJECT , IDX_TOTAL_COUNT // just a count of the above }; diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index 6652821ff792..fcfd55723cda 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -87,6 +87,32 @@ static void ThrowException(uintN errNum, JSContext* cx, /***************************************************************************/ +static JSObject* +GetDoubleWrappedJSObject(JSContext* cx, nsXPCWrappedNative* wrapper, jsid id) +{ + JSObject* obj = nsnull; + + if(wrapper && wrapper->GetNative()) + { + nsCOMPtr + underware = do_QueryInterface(wrapper->GetNative()); + if(underware) + { + JSObject* mainObj = nsnull; + if(NS_SUCCEEDED(underware->GetJSObject(&mainObj)) && mainObj) + { + jsval val; + if(OBJ_GET_PROPERTY(cx, mainObj, id, &val) && + !JSVAL_IS_PRIMITIVE(val)) + obj = JSVAL_TO_OBJECT(val); + } + } + } + return obj; +} + +/***************************************************************************/ + JS_STATIC_DLL_CALLBACK(JSBool) WrappedNative_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { @@ -243,6 +269,50 @@ WrappedNative_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) else // attribute return clazz->GetAttributeAsJSVal(cx, wrapper, desc, vp); } + else if(wrapper->GetNative() && + id == clazz->GetRuntime()-> + GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) + { + JSObject* realObject = GetDoubleWrappedJSObject(cx, wrapper, id); + if(realObject) + { + // It is a double wrapped object. Figure out if the caller + // is allowed to see it. + + XPCContext* xpcc = nsXPConnect::GetContext(cx); + if(xpcc) + { + nsIXPCSecurityManager* sm; + + sm = xpcc->GetAppropriateSecurityManager( + nsIXPCSecurityManager::HOOK_GET_PROPERTY); + if(sm) + { + nsCOMPtr iimgr = + dont_AddRef(nsXPConnect::GetInterfaceInfoManager()); + if(iimgr) + { + const nsIID& iid = + NS_GET_IID(nsIXPCWrappedJSObjectGetter); + nsCOMPtr info; + if(NS_SUCCEEDED(iimgr->GetInfoForIID(&iid, + getter_AddRefs(info)))) + { + if(NS_OK != sm->CanGetProperty(cx, iid, + wrapper->GetNative(), + info, 3, id)) + { + // The SecurityManager should have set an exception. + return JS_FALSE; + } + } + } + } + *vp = OBJECT_TO_JSVAL(realObject); + return JS_TRUE; + } + } + } else { nsIXPCScriptable* ds; @@ -264,13 +334,14 @@ WrappedNative_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) JSObject* proto = JS_GetPrototype(cx, obj); if(proto) return OBJ_GET_PROPERTY(cx, proto, id, vp); - - // XXX silently fail when property not found or call fails? - *vp = JSVAL_VOID; - return JS_TRUE; } + + // XXX silently fail when property not found or call fails? + *vp = JSVAL_VOID; + return JS_TRUE; } + JS_STATIC_DLL_CALLBACK(JSBool) WrappedNative_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) { @@ -361,6 +432,18 @@ WrappedNative_LookupProperty(JSContext *cx, JSObject *obj, jsid id, *propp = XPC_BUILT_IN_PROPERTY; return JS_TRUE; } + else if(wrapper->GetNative() && + id == clazz->GetRuntime()-> + GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) + { + JSObject* realObject = GetDoubleWrappedJSObject(cx, wrapper, id); + if(realObject) + { + *objp = realObject; + *propp = XPC_BUILT_IN_PROPERTY; + return JS_TRUE; + } + } else if(nsnull != (ds = wrapper->GetDynamicScriptable()) && nsnull != (as = wrapper->GetArbitraryScriptable())) { diff --git a/js/src/xpconnect/tests/idl/xpctest.idl b/js/src/xpconnect/tests/idl/xpctest.idl index 6dbdcc0749c5..a427a5a4afe9 100644 --- a/js/src/xpconnect/tests/idl/xpctest.idl +++ b/js/src/xpconnect/tests/idl/xpctest.idl @@ -215,3 +215,8 @@ interface nsIXPCTestArray : nsISupports { }; +[scriptable, uuid(13082da0-643c-11d4-8fe0-0010a4e73d9a)] +interface nsIWrappedJSObjectTest : nsISupports +{ + void interfaceMember(); +};