static JSBool XPC_SJOW_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp) { // Prevent setting __proto__ on an XPCSafeJSObjectWrapper if ((mode & JSACC_WATCH) == JSACC_PROTO && (mode & JSACC_WRITE)) { return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); } // Forward to the checkObjectAccess hook in the runtime, if any. JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx); if (callbacks && callbacks->checkObjectAccess && !callbacks->checkObjectAccess(cx, obj, id, mode, vp)) { return JS_FALSE; } JSObject *unsafeObj = GetUnsafeObject(cx, obj); if (!unsafeObj) { return JS_TRUE; } // Forward the unsafe object to the checkObjectAccess hook in the // runtime too, if any. if (callbacks && callbacks->checkObjectAccess && !callbacks->checkObjectAccess(cx, unsafeObj, id, mode, vp)) { return JS_FALSE; } JSClass *clazz = unsafeObj->getJSClass(); return !clazz->checkAccess || clazz->checkAccess(cx, unsafeObj, id, mode, vp); }
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, obj, 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 (!wrappedObj->getJSClass()->convert(cx, wrappedObj, type, vp)) { return JS_FALSE; } return NS_SUCCEEDED(rv) ? WrapSameOriginProp(cx, obj, vp) : RewrapIfNeeded(cx, obj, vp); }
static JSBool XPC_XOW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, 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, obj, 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 = iface->getJSClass(); *bp = JS_FALSE; if (!clasp->hasInstance) { return JS_TRUE; } // Prematurely unwrap the left hand side. jsval v = *valp; if (!JSVAL_IS_PRIMITIVE(*valp)) { 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); }
static JSBool XPC_SOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) { if (!AllowedToAct(cx, JSID_VOID)) { return JS_FALSE; } // 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. // XXX Can this happen? return JS_TRUE; } return wrappedObj->getJSClass()->convert(cx, wrappedObj, type, vp); }
static JSBool XPC_SOW_HasInstance(JSContext *cx, JSObject *obj, const jsval *valp, JSBool *bp) { if (!AllowedToAct(cx, JSID_VOID)) { return JS_FALSE; } JSObject *iface = GetWrappedObject(cx, obj); if (!iface) { *bp = JS_FALSE; return JS_TRUE; } JSClass *clasp = iface->getJSClass(); *bp = JS_FALSE; if (!clasp->hasInstance) { return JS_TRUE; } // Prematurely unwrap the left hand side. This isn't necessary, but could be // faster than waiting until XPCWrappedNative::GetWrappedNativeOfJSObject to // do it. jsval v = *valp; if (!JSVAL_IS_PRIMITIVE(v)) { JSObject *test = JSVAL_TO_OBJECT(v); // GetWrappedObject does a class check. test = GetWrappedObject(cx, test); if (test) { v = OBJECT_TO_JSVAL(test); } } return clasp->hasInstance(cx, iface, &v, bp); }
bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); uintN flags = 0; JS_CHECK_RECURSION(cx, return false); /* Only GC things have to be wrapped or copied. */ if (!vp->isMarkable()) return true; if (vp->isString()) { JSString *str = vp->toString(); /* If the string is already in this compartment, we are done. */ if (str->compartment() == this) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment); return true; } } /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ JSObject *global; if (cx->hasfp()) { global = cx->fp()->scopeChain().getGlobal(); } else { global = JS_ObjectToInnerObject(cx, cx->globalObject); if (!global) return false; } /* Unwrap incoming objects. */ if (vp->isObject()) { JSObject *obj = &vp->toObject(); /* If the object is already in this compartment, we are done. */ if (obj->compartment() == this) return true; /* Translate StopIteration singleton. */ if (obj->isStopIteration()) return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp); /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = UnwrapObject(&vp->toObject(), &flags); vp->setObject(*obj); if (obj->compartment() == this) return true; if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } vp->setObject(*obj); if (obj->compartment() == this) return true; } else { if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); vp->setObject(*obj); } #ifdef DEBUG { JSObject *outer = obj; OBJ_TO_OUTER_OBJECT(cx, outer); JS_ASSERT(outer && outer == obj); } #endif } /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) { *vp = p->value; if (vp->isObject()) { JSObject *obj = &vp->toObject(); JS_ASSERT(obj->isCrossCompartmentWrapper()); if (global->getJSClass() != &js_dummy_class && obj->getParent() != global) { do { obj->setParent(global); obj = obj->getProto(); } while (obj && obj->isCrossCompartmentWrapper()); } } return true; } if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); const jschar *chars = str->getChars(cx); if (!chars) return false; JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); return crossCompartmentWrappers.put(orig, *vp); } JSObject *obj = &vp->toObject(); /* * Recurse to wrap the prototype. Long prototype chains will run out of * stack, causing an error in CHECK_RECURSE. * * Wrapping the proto before creating the new wrapper and adding it to the * cache helps avoid leaving a bad entry in the cache on OOM. But note that * if we wrapped both proto and parent, we would get infinite recursion * here (since Object.prototype->parent->proto leads to Object.prototype * itself). */ JSObject *proto = obj->getProto(); if (!wrap(cx, &proto)) return false; /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied * to the object. */ JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags); if (!wrapper) return false; vp->setObject(*wrapper); if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false)) return false; if (!crossCompartmentWrappers.put(GetProxyPrivate(wrapper), *vp)) return false; wrapper->setParent(global); return true; }