// Because of the drastically different ways that same- and cross-origin XOWs // work, we have to call JS_ClearScope when a XOW changes from being same- // origin to cross-origin. Normally, there are defined places in Gecko where // this happens and they notify us. However, UniversalXPConnect causes the // same transition without any notifications. We could try to detect when this // happens, but doing so would require calling JS_ClearScope from random // hooks, which is bad. // // The compromise is the UXPCObject. When resolving a property on a XOW as // same-origin because of UniversalXPConnect, we actually resolve it on the // UXPCObject (which is just a XOW for the same object). This causes the JS // engine to do all of its work on another object, not polluting the main // object. However, if the get results in calling a setter, the engine still // uses the regular object as 'this', ensuring that the UXPCObject doesn't // leak to script. static JSObject * GetUXPCObject(JSContext *cx, JSObject *obj) { NS_ASSERTION(STOBJ_GET_CLASS(obj) == &sXPC_XOW_JSClass.base, "wrong object"); jsval v; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, &v)) { return nsnull; } if (HAS_FLAGS(v, FLAG_IS_UXPC_OBJECT)) { return obj; } if (!JS_GetReservedSlot(cx, obj, sUXPCObjectSlot, &v)) { return nsnull; } if (JSVAL_IS_OBJECT(v)) { return JSVAL_TO_OBJECT(v); } JSObject *uxpco = JS_NewObjectWithGivenProto(cx, &sXPC_XOW_JSClass.base, nsnull, STOBJ_GET_PARENT(obj)); if (!uxpco) { return nsnull; } JSAutoTempValueRooter tvr(cx, uxpco); jsval wrappedObj, parentScope; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sWrappedObjSlot, &wrappedObj) || !JS_GetReservedSlot(cx, obj, XPC_XOW_ScopeSlot, &parentScope)) { return nsnull; } if (!JS_SetReservedSlot(cx, uxpco, XPCWrapper::sWrappedObjSlot, wrappedObj) || !JS_SetReservedSlot(cx, uxpco, XPCWrapper::sFlagsSlot, INT_TO_JSVAL(FLAG_IS_UXPC_OBJECT)) || !JS_SetReservedSlot(cx, uxpco, XPC_XOW_ScopeSlot, parentScope)) { return nsnull; } if (!JS_SetReservedSlot(cx, obj, sUXPCObjectSlot, OBJECT_TO_JSVAL(uxpco))) { return nsnull; } return uxpco; }
static JSBool XPC_XOW_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_XOW_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); } // Note: JSTYPE_VOID and JSTYPE_STRING are equivalent. nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull); if (NS_FAILED(rv) && (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED || (type != JSTYPE_STRING && type != JSTYPE_VOID))) { // Ensure that we report some kind of error. if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { ThrowException(rv, cx); } return JS_FALSE; } if (!STOBJ_GET_CLASS(wrappedObj)->convert(cx, wrappedObj, type, vp)) { return JS_FALSE; } return NS_SUCCEEDED(rv) ? WrapSameOriginProp(cx, obj, vp) : XPC_XOW_RewrapIfNeeded(cx, obj, vp); }
static JSBool CloseGenerator(JSContext *cx, JSObject *obj) { JSGenerator *gen; JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass); gen = (JSGenerator *) JS_GetPrivate(cx, obj); if (!gen) { /* Generator prototype object. */ return JS_TRUE; } if (gen->state == JSGEN_CLOSED) return JS_TRUE; return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JSVAL_VOID); }
static JSBool XPC_XOW_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); } nsresult rv = CanAccessWrapper(cx, wrappedObj, nsnull); if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); } rv = ssm->CheckPropertyAccess(cx, wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING), nsIXPCSecurityManager::ACCESS_GET_PROPERTY); } if (NS_FAILED(rv)) { return JS_FALSE; } XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); return XPCWrapper::NativeToString(cx, wn, argc, argv, rval, JS_FALSE); }
static JSBool DefineGetterOrSetter(JSContext *cx, uintN argc, JSBool wantGetter, jsval *vp) { uintN attrs; JSBool found; JSPropertyOp getter, setter; JSObject *obj2; jsval v; jsid interned_id; XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; JSFastNative forward = wantGetter ? js_obj_defineGetter : js_obj_defineSetter; jsval id = (argc >= 1) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID; if(!JSVAL_IS_STRING(id)) return forward(cx, argc, vp); JSString *str = JSVAL_TO_STRING(id); const char *name = JS_GetStringBytes(str); if(!JS_ValueToId(cx, id, &interned_id) || !JS_LookupPropertyWithFlagsById(cx, obj, interned_id, JSRESOLVE_QUALIFIED, &obj2, &v) || (obj2 && !JS_GetPropertyAttrsGetterAndSetterById(cx, obj2, interned_id, &attrs, &found, &getter, &setter))) return JS_FALSE; // The property didn't exist, already has a getter or setter, or is not // our property, then just forward now. if(!obj2 || (attrs & (JSPROP_GETTER | JSPROP_SETTER)) || !(getter || setter) || !IS_PROTO_CLASS(STOBJ_GET_CLASS(obj2))) return forward(cx, argc, vp); // Reify the getter and setter... if(!ReifyPropertyOps(cx, obj, id, interned_id, name, getter, setter, nsnull, nsnull)) return JS_FALSE; return forward(cx, argc, vp); }
// 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); }
static JSBool InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) { jsval state; JSBool ok; JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); /* Initialize iterobj in case of enumerate hook failure. */ STOBJ_SET_PARENT(iterobj, obj); STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL); STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_FLAGS, INT_TO_JSVAL(flags)); if (!js_RegisterCloseableIterator(cx, iterobj)) return JS_FALSE; if (!obj) return JS_TRUE; ok = #if JS_HAS_XML_SUPPORT ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj)) ? ((JSXMLObjectOps *) obj->map->ops)-> enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL) : #endif OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL); if (!ok) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (flags & JSITER_ENUMERATE) { /* * The enumerating iterator needs the original object to suppress * enumeration of deleted or shadowed prototype properties. Since the * enumerator never escapes to scripts, we use the prototype slot to * store the original object. */ JS_ASSERT(obj != iterobj); STOBJ_SET_PROTO(iterobj, obj); } return JS_TRUE; }
JSBool xpc_qsXPCOMObjectToJsval(XPCLazyCallContext &lccx, nsISupports *p, nsWrapperCache *cache, const nsIID *iid, XPCNativeInterface **iface, jsval *rval) { // From the T_INTERFACE case in XPCConvert::NativeData2JS. // This is one of the slowest things quick stubs do. JSContext *cx = lccx.GetJSContext(); if(!iface) return xpc_qsThrow(cx, NS_ERROR_XPC_BAD_CONVERT_NATIVE); // XXX The OBJ_IS_NOT_GLOBAL here is not really right. In // fact, this code is depending on the fact that the // global object will not have been collected, and // therefore this NativeInterface2JSObject will not end up // creating a new XPCNativeScriptableShared. nsresult rv; if(!XPCConvert::NativeInterface2JSObject(lccx, rval, nsnull, p, iid, iface, cache, lccx.GetCurrentJSObject(), PR_TRUE, OBJ_IS_NOT_GLOBAL, &rv)) { // I can't tell if NativeInterface2JSObject throws JS exceptions // or not. This is a sloppy stab at the right semantics; the // method really ought to be fixed to behave consistently. if(!JS_IsExceptionPending(cx)) xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); return JS_FALSE; } #ifdef DEBUG JSObject* jsobj = JSVAL_TO_OBJECT(*rval); if(jsobj && !STOBJ_GET_PARENT(jsobj)) NS_ASSERTION(STOBJ_GET_CLASS(jsobj)->flags & JSCLASS_IS_GLOBAL, "Why did we recreate this wrapper?"); #endif return JS_TRUE; }
static XPCWrappedNativeScope* GetScopeOfObject(JSObject* obj) { nsISupports* supports; JSClass* clazz = STOBJ_GET_CLASS(obj); if(!IS_WRAPPER_CLASS(clazz) || !(supports = (nsISupports*) xpc_GetJSPrivate(obj))) { #ifdef DEBUG { if(!(~clazz->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) && (supports = (nsISupports*) xpc_GetJSPrivate(obj)) && !XPCNativeWrapper::IsNativeWrapperClass(clazz)) { nsCOMPtr<nsIXPConnectWrappedNative> iface = do_QueryInterface(supports); NS_ASSERTION(!iface, "Uh, how'd this happen?"); } } #endif return nsnull; } #ifdef DEBUG { nsCOMPtr<nsIXPConnectWrappedNative> iface = do_QueryInterface(supports); NS_ASSERTION(iface, "Uh, how'd this happen?"); } #endif // obj is one of our nsXPConnectWrappedNative objects. return ((XPCWrappedNative*)supports)->GetScope(); }
static JSBool XPC_XOW_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) { JSObject *iface = GetWrappedObject(cx, obj); XPCCallContext ccx(JS_CALLER, cx); if (!ccx.IsValid()) { return ThrowException(NS_ERROR_FAILURE, cx); } nsresult rv = CanAccessWrapper(cx, iface, nsnull); if (NS_FAILED(rv)) { if (rv == NS_ERROR_DOM_PROP_ACCESS_DENIED) { // Don't do this test across origins. return ThrowException(rv, cx); } return JS_FALSE; } JSClass *clasp = STOBJ_GET_CLASS(iface); *bp = JS_FALSE; if (!clasp->hasInstance) { return JS_TRUE; } // Prematurely unwrap the left hand side. if (!JSVAL_IS_PRIMITIVE(v)) { JSObject *test = JSVAL_TO_OBJECT(v); // GetWrappedObject does an instanceof check. test = GetWrappedObject(cx, test); if (test) { v = OBJECT_TO_JSVAL(test); } } return clasp->hasInstance(cx, iface, v, bp); }
JSBool XPC_XOW_RewrapIfNeeded(JSContext *cx, JSObject *outerObj, jsval *vp) { // Don't need to wrap primitive values. if (JSVAL_IS_PRIMITIVE(*vp)) { return JS_TRUE; } JSObject *obj = JSVAL_TO_OBJECT(*vp); if (JS_ObjectIsFunction(cx, obj)) { return XPC_XOW_WrapFunction(cx, outerObj, obj, vp); } XPCWrappedNative *wn = nsnull; if (STOBJ_GET_CLASS(obj) == &sXPC_XOW_JSClass.base && STOBJ_GET_PARENT(outerObj) != STOBJ_GET_PARENT(obj)) { *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, obj)); } else if (!(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, obj))) { return JS_TRUE; } return XPC_XOW_WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp, wn); }
JSBool XPC_SJOW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { if (argc < 1) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); } // |obj| almost always has the wrong proto and parent so we have to create // our own object anyway. Set |obj| to null so we don't use it by accident. obj = nsnull; if (JSVAL_IS_PRIMITIVE(argv[0])) { JSStackFrame *fp = nsnull; if (JS_FrameIterator(cx, &fp) && JS_IsConstructorFrame(cx, fp)) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } *rval = argv[0]; return JS_TRUE; } JSObject *objToWrap = JSVAL_TO_OBJECT(argv[0]); // Prevent script created Script objects from ever being wrapped // with XPCSafeJSObjectWrapper, and never let the eval function // object be directly wrapped. if (STOBJ_GET_CLASS(objToWrap) == &js_ScriptClass || (::JS_ObjectIsFunction(cx, objToWrap) && ::JS_GetFunctionNative(cx, ::JS_ValueToFunction(cx, argv[0])) == XPCWrapper::sEvalNative)) { return ThrowException(NS_ERROR_INVALID_ARG, cx); } SLIM_LOG_WILL_MORPH(cx, objToWrap); if(IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) { return ThrowException(NS_ERROR_FAILURE, cx); } // Check that the caller can access the unsafe object. if (!CanCallerAccess(cx, objToWrap)) { // CanCallerAccess() already threw for us. return JS_FALSE; } JSObject *unsafeObj = GetUnsafeObject(objToWrap); if (unsafeObj) { // We're asked to wrap an already wrapped object. Re-wrap the // object wrapped by the given wrapper. objToWrap = unsafeObj; } // Don't use the object the JS engine created for us, it is in most // cases incorectly parented and has a proto from the wrong scope. JSObject *wrapperObj = ::JS_NewObjectWithGivenProto(cx, &sXPC_SJOW_JSClass.base, nsnull, objToWrap); if (!wrapperObj) { // JS_NewObjectWithGivenProto already threw. return JS_FALSE; } if (!::JS_SetReservedSlot(cx, wrapperObj, XPC_SJOW_SLOT_IS_RESOLVING, JSVAL_ZERO)) { return JS_FALSE; } *rval = OBJECT_TO_JSVAL(wrapperObj); return JS_TRUE; }
void XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal) { // We allow for calling this more than once. This feature is used by // nsXPConnect::InitClassesWithNewWrappedGlobal. mGlobalJSObject = aGlobal; #ifndef XPCONNECT_STANDALONE mScriptObjectPrincipal = nsnull; // Now init our script object principal, if the new global has one const JSClass* jsClass = STOBJ_GET_CLASS(aGlobal); if(!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS))) { // Our global has an nsISupports native pointer. Let's // see whether it's what we want. nsISupports* priv = (nsISupports*)xpc_GetJSPrivate(aGlobal); nsCOMPtr<nsIXPConnectWrappedNative> native = do_QueryInterface(priv); if(native) { mScriptObjectPrincipal = do_QueryWrappedNative(native); } if(!mScriptObjectPrincipal) { mScriptObjectPrincipal = do_QueryInterface(priv); } } #endif // Lookup 'globalObject.Object.prototype' for our wrapper's proto { AutoJSErrorAndExceptionEater eater(ccx); // scoped error eater jsval val; jsid idObj = mRuntime->GetStringID(XPCJSRuntime::IDX_OBJECT); jsid idFun = mRuntime->GetStringID(XPCJSRuntime::IDX_FUNCTION); jsid idProto = mRuntime->GetStringID(XPCJSRuntime::IDX_PROTOTYPE); if(OBJ_GET_PROPERTY(ccx, aGlobal, idObj, &val) && !JSVAL_IS_PRIMITIVE(val) && OBJ_GET_PROPERTY(ccx, JSVAL_TO_OBJECT(val), idProto, &val) && !JSVAL_IS_PRIMITIVE(val)) { mPrototypeJSObject = JSVAL_TO_OBJECT(val); } else { NS_ERROR("Can't get globalObject.Object.prototype"); } if(OBJ_GET_PROPERTY(ccx, aGlobal, idFun, &val) && !JSVAL_IS_PRIMITIVE(val) && OBJ_GET_PROPERTY(ccx, JSVAL_TO_OBJECT(val), idProto, &val) && !JSVAL_IS_PRIMITIVE(val)) { mPrototypeJSFunction = JSVAL_TO_OBJECT(val); } else { NS_ERROR("Can't get globalObject.Function.prototype"); } } // Clear the no helper wrapper prototype object so that a new one // gets created if needed. mPrototypeNoHelper = nsnull; }
static JSBool CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) { JSObject *obj, *origobj; jsval state; JSBool foreach; jsid id; JSObject *obj2; JSBool cond; JSClass *clasp; JSExtendedClass *xclasp; JSProperty *prop; JSString *str; JS_ASSERT(flags & JSITER_ENUMERATE); JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); obj = STOBJ_GET_PARENT(iterobj); origobj = STOBJ_GET_PROTO(iterobj); state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) goto stop; foreach = (flags & JSITER_FOREACH) != 0; #if JS_HAS_XML_SUPPORT /* * Treat an XML object specially only when it starts the prototype chain. * Otherwise we need to do the usual deleted and shadowed property checks. */ if (obj == origobj && OBJECT_IS_XML(cx, obj)) { if (foreach) { JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, &id, rval)) { return JS_FALSE; } } else { if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; } STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) goto stop; } else #endif { restart: if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) { #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { /* * We just finished enumerating an XML obj that is present on * the prototype chain of a non-XML origobj. Stop further * prototype chain searches because XML objects don't * enumerate prototypes. */ JS_ASSERT(origobj != obj); JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); } else #endif { obj = OBJ_GET_PROTO(cx, obj); if (obj) { STOBJ_SET_PARENT(iterobj, obj); if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (!JSVAL_IS_NULL(state)) goto restart; } } goto stop; } /* Skip properties not in obj when looking from origobj. */ if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) return JS_FALSE; if (!prop) goto restart; OBJ_DROP_PROPERTY(cx, obj2, prop); /* * If the id was found in a prototype object or an unrelated object * (specifically, not in an inner object for obj), skip it. This step * means that all OBJ_LOOKUP_PROPERTY implementations must return an * object further along on the prototype chain, or else possibly an * object returned by the JSExtendedClass.outerObject optional hook. */ if (obj != obj2) { cond = JS_FALSE; clasp = OBJ_GET_CLASS(cx, obj2); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; cond = xclasp->outerObject && xclasp->outerObject(cx, obj2) == obj; } if (!cond) goto restart; } if (foreach) { /* Get property querying the original object. */ if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) return JS_FALSE; } } if (foreach) { if (flags & JSITER_KEYVALUE) { if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } } else { /* Make rval a string for uniformity and compatibility. */ str = js_ValueToString(cx, ID_TO_VALUE(id)); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); } return JS_TRUE; stop: JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; }
static JSBool LookupGetterOrSetter(JSContext *cx, JSBool wantGetter, uintN argc, jsval *vp) { uintN attrs; JSBool found; JSPropertyOp getter, setter; JSObject *obj2; jsid interned_id; jsval v; XPC_QS_ASSERT_CONTEXT_OK(cx); if(argc == 0) { JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; jsval idval = JS_ARGV(cx, vp)[0]; const char *name = JSVAL_IS_STRING(idval) ? JS_GetStringBytes(JSVAL_TO_STRING(idval)) : nsnull; if(!JS_ValueToId(cx, idval, &interned_id) || !JS_LookupPropertyWithFlagsById(cx, obj, interned_id, JSRESOLVE_QUALIFIED, &obj2, &v) || (obj2 && !JS_GetPropertyAttrsGetterAndSetterById(cx, obj2, interned_id, &attrs, &found, &getter, &setter))) return JS_FALSE; // No property at all means no getters or setters possible. if(!obj2 || !found) { JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } // Inline obj_lookup[GS]etter here. if(wantGetter) { if(attrs & JSPROP_GETTER) { JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, getter))); return JS_TRUE; } } else { if(attrs & JSPROP_SETTER) { JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, setter))); return JS_TRUE; } } // Since XPConnect doesn't use JSPropertyOps in any other contexts, // ensuring that we have an XPConnect prototype object ensures that // we are only going to expose quickstubbed properties to script. // Also be careful not to overwrite existing properties! if(!name || !IS_PROTO_CLASS(STOBJ_GET_CLASS(obj2)) || (attrs & (JSPROP_GETTER | JSPROP_SETTER)) || !(getter || setter)) { JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; } JSObject *getterobj, *setterobj; if(!ReifyPropertyOps(cx, obj, idval, interned_id, name, getter, setter, &getterobj, &setterobj)) return JS_FALSE; JSObject *wantedobj = wantGetter ? getterobj : setterobj; v = wantedobj ? OBJECT_TO_JSVAL(wantedobj) : JSVAL_VOID; JS_SET_RVAL(cx, vp, v); return JS_TRUE; }
static JSBool XPC_XOW_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); } JSBool privilegeEnabled; nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled); if (NS_FAILED(rv)) { if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { return JS_FALSE; } // We're dealing with a cross-origin lookup. Ensure that we're allowed to // resolve this property and resolve it if so. Otherwise, we deny access // and throw a security error. Note that this code does not actually check // to see if the property exists, that's dealt with below. XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); NS_ASSERTION(wn, "How did we wrap a non-WrappedNative?"); if (!IsValFrame(wrappedObj, id, wn)) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); } PRUint32 action = (flags & JSRESOLVE_ASSIGNING) ? XPCWrapper::sSecMgrSetProp : XPCWrapper::sSecMgrGetProp; rv = ssm->CheckPropertyAccess(cx, wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, id, action); if (NS_FAILED(rv)) { // The security manager threw an exception for us. return JS_FALSE; } } // We're out! We're allowed to resolve this property. return XPCWrapper::ResolveNativeProperty(cx, obj, wrappedObj, wn, id, flags, objp, JS_FALSE); } if (privilegeEnabled && !(obj = GetUXPCObject(cx, obj))) { return JS_FALSE; } 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_XOW_toString, 0, 0) != nsnull; JS_SetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, oldSlotVal); if (ok) { *objp = obj; } return ok; } return XPCWrapper::NewResolve(cx, obj, JS_TRUE, wrappedObj, id, flags, objp); }
static JSBool XPC_XOW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, JSBool isSet) { if (id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_TO_STRING)) { return JS_TRUE; } // Don't do anything if we already resolved to a wrapped function in // NewResolve. In practice, this means that this is a wrapped eval // function. jsval v = *vp; if (!JSVAL_IS_PRIMITIVE(v) && JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v)) && JS_GetFunctionNative(cx, JS_ValueToFunction(cx, v)) == XPC_XOW_FunctionWrapper) { return JS_TRUE; } JSObject *origObj = obj; 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); } JSBool privilegeEnabled; nsresult rv = CanAccessWrapper(cx, wrappedObj, &privilegeEnabled); if (NS_FAILED(rv)) { if (rv != NS_ERROR_DOM_PROP_ACCESS_DENIED) { return JS_FALSE; } // This is a request to get a property across origins. We need to // determine if this property is allAccess. If it is, then we need to // actually get the property. If not, we simply need to throw an // exception. XPCWrappedNative *wn = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, wrappedObj); NS_ASSERTION(wn, "How did we wrap a non-WrappedNative?"); if (!IsValFrame(wrappedObj, id, wn)) { nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager(); if (!ssm) { return ThrowException(NS_ERROR_NOT_INITIALIZED, cx); } rv = ssm->CheckPropertyAccess(cx, wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, id, isSet ? XPCWrapper::sSecMgrSetProp : XPCWrapper::sSecMgrGetProp); if (NS_FAILED(rv)) { // The security manager threw an exception for us. return JS_FALSE; } } return XPCWrapper::GetOrSetNativeProperty(cx, obj, wn, id, vp, isSet, JS_FALSE); } JSObject *proto = nsnull; // Initialize this to quiet GCC. JSBool checkProto = (isSet && id == GetRTStringByIndex(cx, XPCJSRuntime::IDX_PROTO)); if (checkProto) { proto = STOBJ_GET_PROTO(wrappedObj); } // Same origin, pass this request along as though nothing interesting // happened. jsid asId; if (!JS_ValueToId(cx, id, &asId)) { return JS_FALSE; } JSBool ok = isSet ? JS_SetPropertyById(cx, wrappedObj, asId, vp) : JS_GetPropertyById(cx, wrappedObj, asId, vp); if (!ok) { return JS_FALSE; } if (checkProto) { JSObject *newProto = STOBJ_GET_PROTO(wrappedObj); // If code is trying to set obj.__proto__ and we're on obj's // prototype chain, then the JS_GetPropertyById above will do the // wrong thing if wrappedObj still delegates to Object.prototype. // However, it's hard to figure out if wrappedObj still does // delegate to Object.prototype so check to see if proto changed as a // result of setting __proto__. if (origObj != obj) { // Undo the damage. if (!JS_SetPrototype(cx, wrappedObj, proto) || !JS_SetPrototype(cx, origObj, newProto)) { return JS_FALSE; } } else if (newProto) { // __proto__ setting is a bad hack, people shouldn't do it. In // this case we're setting the direct prototype of a XOW object, // in the interests of sanity only allow it to be set to null in // this case. JS_SetPrototype(cx, wrappedObj, proto); JS_ReportError(cx, "invalid __proto__ value (can only be set to null)"); return JS_FALSE; } } return WrapSameOriginProp(cx, obj, vp); }
JSBool XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp, XPCWrappedNative* wn) { NS_ASSERTION(XPCPerThreadData::IsMainThread(cx), "Can't do this off the main thread!"); // Our argument should be a wrapped native object, but the caller may have // passed it in as an optimization. JSObject *wrappedObj; if (!JSVAL_IS_OBJECT(*vp) || !(wrappedObj = JSVAL_TO_OBJECT(*vp)) || STOBJ_GET_CLASS(wrappedObj) == &sXPC_XOW_JSClass.base) { return JS_TRUE; } if (!wn && !(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wrappedObj))) { return JS_TRUE; } XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); // The parent must be the inner global object for its scope. parent = JS_GetGlobalForObject(cx, parent); JSClass *clasp = STOBJ_GET_CLASS(parent); if (clasp->flags & JSCLASS_IS_EXTENDED) { JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp); if (xclasp->innerObject) { parent = xclasp->innerObject(cx, parent); if (!parent) { return JS_FALSE; } } } XPCWrappedNativeScope *parentScope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent, nsnull, rt); #ifdef DEBUG_mrbkap_off printf("Wrapping object at %p (%s) [%p]\n", (void *)wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, (void *)parentScope); #endif JSObject *outerObj = nsnull; WrappedNative2WrapperMap *map = parentScope->GetWrapperMap(); outerObj = map->Find(wrappedObj); if (outerObj) { NS_ASSERTION(STOBJ_GET_CLASS(outerObj) == &sXPC_XOW_JSClass.base, "What crazy object are we getting here?"); #ifdef DEBUG_mrbkap_off printf("But found a wrapper in the map %p!\n", (void *)outerObj); #endif *vp = OBJECT_TO_JSVAL(outerObj); return JS_TRUE; } outerObj = JS_NewObjectWithGivenProto(cx, &sXPC_XOW_JSClass.base, nsnull, parent); if (!outerObj) { return JS_FALSE; } if (!JS_SetReservedSlot(cx, outerObj, XPCWrapper::sWrappedObjSlot, *vp) || !JS_SetReservedSlot(cx, outerObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO) || !JS_SetReservedSlot(cx, outerObj, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(parentScope))) { return JS_FALSE; } *vp = OBJECT_TO_JSVAL(outerObj); map->Add(wn->GetScope()->GetWrapperMap(), wrappedObj, outerObj); return JS_TRUE; }