// 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; }
static inline JSObject * FindSafeObject(JSObject *obj) { while (STOBJ_GET_CLASS(obj) != &sXPC_SJOW_JSClass.base) { obj = STOBJ_GET_PROTO(obj); if (!obj) { break; } } return obj; }
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); }
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; }