From 742852d623f2c0c5777106fea0c9c125c7049ab6 Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Sat, 21 May 2005 06:24:08 +0000 Subject: [PATCH] Fix adblock and many other non-system chrome users of sytem-chrome xbl (294960, r+sr=bz). --- js/src/xpconnect/src/XPCNativeWrapper.cpp | 223 ++++++++++++++++++++-- 1 file changed, 206 insertions(+), 17 deletions(-) diff --git a/js/src/xpconnect/src/XPCNativeWrapper.cpp b/js/src/xpconnect/src/XPCNativeWrapper.cpp index 175c2fcc4de6..5fc7a2d0f8fd 100644 --- a/js/src/xpconnect/src/XPCNativeWrapper.cpp +++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp @@ -40,6 +40,9 @@ #include "xpcprivate.h" #include "XPCNativeWrapper.h" +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); + JS_STATIC_DLL_CALLBACK(JSBool) XPC_NW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); @@ -49,14 +52,30 @@ XPC_NW_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); JS_STATIC_DLL_CALLBACK(JSBool) XPC_NW_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp); +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_Enumerate(JSContext *cx, JSObject *obj); + JS_STATIC_DLL_CALLBACK(JSBool) XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp); +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp); + +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsval id, + JSAccessMode mode, jsval *vp); + +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); + JS_STATIC_DLL_CALLBACK(JSBool) XPC_NW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); + JS_STATIC_DLL_CALLBACK(uint32) XPC_NW_Mark(JSContext *cx, JSObject *obj, void *arg); @@ -68,19 +87,71 @@ XPC_NW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); JSExtendedClass XPCNativeWrapper::sXPC_NW_JSClass = { // JSClass (JSExtendedClass.base) initialization - { "XPCWrappedNative Class", + { "XPCNativeWrapper", JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_EXTENDED, - JS_PropertyStub, XPC_NW_DelProperty, XPC_NW_GetProperty, - XPC_NW_SetProperty, JS_EnumerateStub, (JSResolveOp)XPC_NW_NewResolve, - JS_ConvertStub, JS_FinalizeStub, nsnull, nsnull, nsnull, XPC_NW_Construct, - nsnull, nsnull, XPC_NW_Mark, nsnull + XPC_NW_AddProperty, XPC_NW_DelProperty, + XPC_NW_GetProperty, XPC_NW_SetProperty, + XPC_NW_Enumerate, (JSResolveOp)XPC_NW_NewResolve, + XPC_NW_Convert, JS_FinalizeStub, + nsnull, XPC_NW_CheckAccess, + XPC_NW_Call, XPC_NW_Construct, + nsnull, XPC_NW_HasInstance, + XPC_NW_Mark, nsnull }, // JSExtendedClass initialization XPC_NW_Equality }; +// If one of our class hooks is ever called from a non-system script, bypass +// the hook by calling the same hook on our wrapped native, with obj reset to +// the wrapped native's flat JSObject, so the hook and args macro parameters +// can be simply: +// +// getProperty, (cx, obj, id, vp) +// +// in the call from XPC_NW_GetProperty, for example. + +#define XPC_NW_CALL_HOOK(cx, obj, hook, args) \ + return JS_GET_CLASS(cx, obj)->hook args; + +#define XPC_NW_CAST_HOOK(cx, obj, type, hook, args) \ + return ((type) JS_GET_CLASS(cx, obj)->hook) args; + +static JSBool +ShouldBypassNativeWrapper(JSContext *cx, JSObject *obj) +{ + jsval deep = JS_FALSE; + + ::JS_GetReservedSlot(cx, obj, 0, &deep); + return deep == JSVAL_TRUE && + !(::JS_GetTopScriptFilenameFlags(cx, nsnull) & JSFILENAME_SYSTEM); +} + +#define XPC_NW_BYPASS_BASE(cx, obj, code) \ + JS_BEGIN_MACRO \ + if (ShouldBypassNativeWrapper(cx, obj)) { \ + XPCWrappedNative *wn_ = XPCNativeWrapper::GetWrappedNative(cx, obj); \ + if (!wn_) { \ + return JS_TRUE; \ + } \ + obj = wn_->GetFlatJSObject(); \ + code \ + } \ + JS_END_MACRO + +#define XPC_NW_BYPASS(cx, obj, hook, args) \ + XPC_NW_BYPASS_BASE(cx, obj, XPC_NW_CALL_HOOK(cx, obj, hook, args)) + +#define XPC_NW_BYPASS_CAST(cx, obj, type, hook, args) \ + XPC_NW_BYPASS_BASE(cx, obj, XPC_NW_CAST_HOOK(cx, obj, type, hook, args)) + +#define XPC_NW_BYPASS_TEST(cx, obj, hook, args) \ + XPC_NW_BYPASS_BASE(cx, obj, \ + JSClass *clasp_ = JS_GET_CLASS(cx, obj); \ + return !clasp_->hook || clasp_->hook args; \ + ) JS_STATIC_DLL_CALLBACK(JSBool) XPC_NW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, @@ -112,9 +183,19 @@ GetStringByIndex(JSContext *cx, uintN index) return ID_TO_VALUE(rt->GetStringID(index)); } +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + XPC_NW_BYPASS(cx, obj, addProperty, (cx, obj, id, vp)); + + return JS_TRUE; +} + JS_STATIC_DLL_CALLBACK(JSBool) XPC_NW_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { + XPC_NW_BYPASS(cx, obj, delProperty, (cx, obj, id, vp)); + return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); } @@ -127,8 +208,8 @@ RewrapIfDeepWrapper(JSContext *cx, JSObject *obj, jsval v, jsval *rval) } // Re-wrap non-primitive values if this is a deep wrapper, i.e. - // if (deep != JSVAL_FALSE). - if (deep != JSVAL_FALSE && !JSVAL_IS_PRIMITIVE(v)) { + // if (deep == JSVAL_TRUE). + if (deep == JSVAL_TRUE && !JSVAL_IS_PRIMITIVE(v)) { JSObject *rvalWrapper = ::JS_ConstructObjectWithArguments(cx, XPCNativeWrapper::GetJSClass(), nsnull, ::JS_GetGlobalObject(cx), 1, @@ -185,6 +266,25 @@ XPC_NW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, return JS_TRUE; } + // We can't use XPC_NW_BYPASS here, because we need to do a full + // OBJ_SET_PROPERTY or OBJ_GET_PROPERTY on the wrapped native's + // object, in order to trigger reflection done by the underlying + // OBJ_LOOKUP_PROPERTY done by SET and GET. + + if (ShouldBypassNativeWrapper(cx, obj)) { + XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(cx, obj); + jsid interned_id; + + if (!::JS_ValueToId(cx, id, &interned_id)) { + return JS_FALSE; + } + + JSObject *wn_obj = wn->GetFlatJSObject(); + return aIsSet + ? OBJ_SET_PROPERTY(cx, wn_obj, interned_id, vp) + : OBJ_GET_PROPERTY(cx, wn_obj, interned_id, vp); + } + // Be paranoid, don't let people use this as another object's // prototype or anything like that. if (!XPCNativeWrapper::IsNativeWrapper(cx, obj)) { @@ -250,7 +350,7 @@ XPC_NW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, JSProperty *prop; // Look up the property for the id being indexed in nativeObj. - if (!JS_ValueToId(cx, id, &index_id) || + if (!::JS_ValueToId(cx, id, &index_id) || !OBJ_LOOKUP_PROPERTY(cx, nativeObj, index_id, &pobj, &prop)) { return JS_FALSE; } @@ -287,6 +387,7 @@ XPC_NW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, // did we find a method/attribute by that name? XPCNativeMember* member = inner_cc.GetMember(); + NS_ASSERTION(member, "not doing IDispatch, how'd this happen?"); if (!member) { // No member, no property. @@ -397,6 +498,14 @@ XPC_NW_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return XPC_NW_GetOrSetProperty(cx, obj, id, vp, PR_TRUE); } +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_Enumerate(JSContext *cx, JSObject *obj) +{ + XPC_NW_BYPASS(cx, obj, enumerate, (cx, obj)); + + return JS_TRUE; +} + JS_STATIC_DLL_CALLBACK(JSBool) XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp) @@ -406,6 +515,41 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, return JS_TRUE; } + // We can't use XPC_NW_BYPASS here, because we need to do a full + // OBJ_LOOKUP_PROPERTY on the wrapped native's object, in order to + // trigger reflection along the wrapped native prototype chain. + // All we need to do is define the property in obj if it exists in + // the wrapped native's object. + + if (ShouldBypassNativeWrapper(cx, obj)) { + XPCWrappedNative *wn = XPCNativeWrapper::GetWrappedNative(cx, obj); + if (!wn) { + return JS_TRUE; + } + + jsid interned_id; + JSObject *pobj; + JSProperty *prop; + + if (!::JS_ValueToId(cx, id, &interned_id) || + !OBJ_LOOKUP_PROPERTY(cx, wn->GetFlatJSObject(), interned_id, + &pobj, &prop)) { + return JS_FALSE; + } + + if (prop) { + OBJ_DROP_PROPERTY(cx, pobj, prop); + + if (!OBJ_DEFINE_PROPERTY(cx, obj, interned_id, JSVAL_VOID, + nsnull, nsnull, 0, nsnull)) { + return JS_FALSE; + } + + *objp = obj; + } + return JS_TRUE; + } + // Be paranoid, don't let people use this as another object's // prototype or anything like that. if (!XPCNativeWrapper::IsNativeWrapper(cx, obj)) { @@ -426,7 +570,7 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, // the value in the get/set property hooks. if (!::JS_DefineElement(cx, obj, JSVAL_TO_INT(id), JSVAL_VOID, nsnull, - nsnull, JSPROP_PERMANENT)) { + nsnull, 0)) { return JS_FALSE; } @@ -465,6 +609,7 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, // did we find a method/attribute by that name? XPCNativeMember* member = inner_cc.GetMember(); + NS_ASSERTION(member, "not doing IDispatch, how'd this happen?"); if (!member) { // No member, nothing to resolve. @@ -537,6 +682,48 @@ XPC_NW_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, return JS_TRUE; } +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ + XPC_NW_BYPASS(cx, obj, convert, (cx, obj, type, vp)); + + return JS_TRUE; +} + +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_CheckAccess(JSContext *cx, JSObject *obj, jsval id, + JSAccessMode mode, jsval *vp) +{ + XPC_NW_BYPASS_TEST(cx, obj, checkAccess, (cx, obj, id, mode, vp)); + + return JS_TRUE; +} + +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + XPC_NW_BYPASS_TEST(cx, obj, call, (cx, obj, argc, argv, rval)); + + return JS_TRUE; +} + +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + XPC_NW_BYPASS_TEST(cx, obj, construct, (cx, obj, argc, argv, rval)); + + return JS_TRUE; +} + +JS_STATIC_DLL_CALLBACK(JSBool) +XPC_NW_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + XPC_NW_BYPASS_TEST(cx, obj, hasInstance, (cx, obj, v, bp)); + + return JS_TRUE; +} + static JSBool MirrorWrappedNativeParent(JSContext *cx, XPCWrappedNative *wrapper, JSObject **result) @@ -556,8 +743,8 @@ MirrorWrappedNativeParent(JSContext *cx, XPCWrappedNative *wrapper, } JS_STATIC_DLL_CALLBACK(JSBool) -XPC_NW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) +XPCNativeWrapperCtor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) { if (argc < 1) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); @@ -683,8 +870,8 @@ XPC_NW_Mark(JSContext *cx, JSObject *obj, void *arg) XPCNativeWrapper::GetWrappedNative(cx, obj); if (wrappedNative && wrappedNative->IsValid()) { - JS_MarkGCThing(cx, wrappedNative->GetFlatJSObject(), - "XPCNativeWrapper wrapped native", arg); + ::JS_MarkGCThing(cx, wrappedNative->GetFlatJSObject(), + "XPCNativeWrapper wrapped native", arg); } return 0; @@ -790,16 +977,18 @@ XPCNativeWrapper::AttachNewConstructorObject(XPCCallContext &ccx, { JSObject *class_obj = ::JS_InitClass(ccx, aGlobalObject, nsnull, &sXPC_NW_JSClass.base, - XPC_NW_Construct, 0, nsnull, sXPC_NW_JSClass_methods, + XPCNativeWrapperCtor, 0, nsnull, sXPC_NW_JSClass_methods, nsnull, nsnull); if (!class_obj) { NS_WARNING("can't initialize the XPCNativeWrapper class"); return NS_ERROR_OUT_OF_MEMORY; } - return ::JS_DefineObject(ccx, aGlobalObject, "XPCNativeWrapper", - &sXPC_NW_JSClass.base, 0, - JSPROP_READONLY | JSPROP_PERMANENT) != 0; + JSBool found; + return ::JS_SetPropertyAttributes(ccx, aGlobalObject, + sXPC_NW_JSClass.base.name, + JSPROP_READONLY | JSPROP_PERMANENT, + &found); } // static