void ObjectWrapper::Key::set(JSContext* cx, JS::HandleObject o, JS::HandleValue value) { switch (_type) { case Type::Field: if (JS_SetProperty(cx, o, _field, value)) return; break; case Type::Index: if (JS_SetElement(cx, o, _idx, value)) return; break; case Type::Id: { JS::RootedId id(cx, _id); if (JS_SetPropertyById(cx, o, id, value)) return; break; } case Type::InternedString: { InternedStringId id(cx, _internedString); if (JS_SetPropertyById(cx, o, id, value)) return; break; } } throwCurrentJSException(cx, ErrorCodes::InternalError, "Failed to set value on a JSObject"); }
static JSBool XPC_COW_GetOrSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp, JSBool isSet) { 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); } jsid interned_id; if (!JS_ValueToId(cx, id, &interned_id)) { return JS_FALSE; } if (interned_id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PROTO) || interned_id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_PARENT) || interned_id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS)) { // No getting or setting __proto__ or __parent__ on my object. return ThrowException(NS_ERROR_INVALID_ARG, cx); // XXX better error message } JSBool canTouch; if (!CanTouchProperty(cx, obj, interned_id, isSet, &canTouch)) { return JS_FALSE; } if (!canTouch) { return ThrowException(NS_ERROR_XPC_SECURITY_MANAGER_VETO, cx); } if (!XPC_COW_RewrapForChrome(cx, obj, vp)) { return JS_FALSE; } JSBool ok = isSet ? JS_SetPropertyById(cx, wrappedObj, interned_id, vp) : JS_GetPropertyById(cx, wrappedObj, interned_id, vp); if (!ok) { return JS_FALSE; } return XPC_COW_RewrapForContent(cx, obj, vp); }
jsval to_js_object(ErlNifEnv* env, JSContext* cx, ERL_NIF_TERM list) { JSObject* ret; jsval kval; jsval vval; jsid idp; ERL_NIF_TERM head; ERL_NIF_TERM tail; const ERL_NIF_TERM* pair; int arity; ret = JS_NewObject(cx, NULL, NULL, NULL); if(ret == NULL) return JSVAL_VOID; if(enif_is_empty_list(env, list)) { return OBJECT_TO_JSVAL(ret); } if(!enif_get_list_cell(env, list, &head, &tail)) { return JSVAL_VOID; } do { if(!enif_get_tuple(env, head, &arity, &pair)) { return JSVAL_VOID; } if(arity != 2) { return JSVAL_VOID; } kval = to_js_key(env, cx, pair[0]); if(kval == JSVAL_VOID) return JSVAL_VOID; if(!JS_ValueToId(cx, kval, &idp)) return JSVAL_VOID; vval = to_js(env, cx, pair[1]); if(vval == JSVAL_VOID) return JSVAL_VOID; if(!JS_SetPropertyById(cx, ret, idp, &vval)) { return JSVAL_VOID; } } while(enif_get_list_cell(env, tail, &head, &tail)); return OBJECT_TO_JSVAL(ret); }
// Clone a new value (and root it and add it to the mapping) jsval Clone(jsval val) { if (JSVAL_IS_DOUBLE(val)) { jsval rval; CLONE_REQUIRE(JS_NewNumberValue(cxTo, JSVAL_TO_DOUBLE(val), &rval), L"JS_NewNumberValue"); m_RooterTo.Push(rval); return rval; } if (JSVAL_IS_STRING(val)) { size_t len; const jschar* chars = JS_GetStringCharsAndLength(cxFrom, JSVAL_TO_STRING(val), &len); CLONE_REQUIRE(chars, L"JS_GetStringCharsAndLength"); JSString* str = JS_NewUCStringCopyN(cxTo, chars, len); CLONE_REQUIRE(str, L"JS_NewUCStringCopyN"); jsval rval = STRING_TO_JSVAL(str); m_Mapping[JSVAL_TO_GCTHING(val)] = rval; m_RooterTo.Push(rval); return rval; } ENSURE(JSVAL_IS_OBJECT(val)); JSObject* newObj; if (JS_IsArrayObject(cxFrom, JSVAL_TO_OBJECT(val))) { jsuint length; CLONE_REQUIRE(JS_GetArrayLength(cxFrom, JSVAL_TO_OBJECT(val), &length), L"JS_GetArrayLength"); newObj = JS_NewArrayObject(cxTo, length, NULL); CLONE_REQUIRE(newObj, L"JS_NewArrayObject"); } else { newObj = JS_NewObject(cxTo, NULL, NULL, NULL); CLONE_REQUIRE(newObj, L"JS_NewObject"); } m_Mapping[JSVAL_TO_GCTHING(val)] = OBJECT_TO_JSVAL(newObj); m_RooterTo.Push(newObj); AutoJSIdArray ida (cxFrom, JS_Enumerate(cxFrom, JSVAL_TO_OBJECT(val))); CLONE_REQUIRE(ida.get(), L"JS_Enumerate"); AutoGCRooter idaRooter(scriptInterfaceFrom); idaRooter.Push(ida.get()); for (size_t i = 0; i < ida.length(); ++i) { jsid id = ida[i]; jsval idval, propval; CLONE_REQUIRE(JS_IdToValue(cxFrom, id, &idval), L"JS_IdToValue"); CLONE_REQUIRE(JS_GetPropertyById(cxFrom, JSVAL_TO_OBJECT(val), id, &propval), L"JS_GetPropertyById"); jsval newPropval = GetOrClone(propval); if (JSVAL_IS_INT(idval)) { // int jsids are portable across runtimes CLONE_REQUIRE(JS_SetPropertyById(cxTo, newObj, id, &newPropval), L"JS_SetPropertyById"); } else if (JSVAL_IS_STRING(idval)) { // string jsids are runtime-specific, so we need to copy the string content JSString* idstr = JS_ValueToString(cxFrom, idval); CLONE_REQUIRE(idstr, L"JS_ValueToString (id)"); size_t len; const jschar* chars = JS_GetStringCharsAndLength(cxFrom, idstr, &len); CLONE_REQUIRE(idstr, L"JS_GetStringCharsAndLength (id)"); CLONE_REQUIRE(JS_SetUCProperty(cxTo, newObj, chars, len, &newPropval), L"JS_SetUCProperty"); } else { // this apparently could be an XML object; ignore it } } return OBJECT_TO_JSVAL(newObj); }
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); }