JSObject * xpc_CloneJSFunction(XPCCallContext &ccx, JSObject *funobj, JSObject *parent) { JSObject *clone = JS_CloneFunctionObject(ccx, funobj, parent); if(!clone) return nsnull; AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(clone)); XPCWrappedNativeScope *scope = XPCWrappedNativeScope::FindInJSObjectScope(ccx, parent); if (!scope) { return nsnull; } // Make sure to break the prototype chain to the function object // we cloned to prevent its scope from leaking into the clones // scope. JS_SetPrototype(ccx, clone, scope->GetPrototypeJSFunction()); // Copy the reserved slots to the clone. jsval ifaceVal, memberVal; if(!JS_GetReservedSlot(ccx, funobj, 0, &ifaceVal) || !JS_GetReservedSlot(ccx, funobj, 1, &memberVal)) return nsnull; if(!JS_SetReservedSlot(ccx, clone, 0, ifaceVal) || !JS_SetReservedSlot(ccx, clone, 1, memberVal)) return nsnull; return clone; }
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); }
JSBool XPCNativeMember::NewFunctionObject(XPCCallContext& ccx, XPCNativeInterface* iface, JSObject *parent, jsval* pval) { NS_ASSERTION(!IsConstant(), "Only call this if you're sure this is not a constant!"); if(!IsResolved() && !Resolve(ccx, iface)) return JS_FALSE; AUTO_MARK_JSVAL(ccx, &mVal); JSObject* funobj = xpc_CloneJSFunction(ccx, JSVAL_TO_OBJECT(mVal), parent); if(!funobj) return JS_FALSE; *pval = OBJECT_TO_JSVAL(funobj); return JS_TRUE; }
JSBool XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface) { if(IsConstant()) { const nsXPTConstant* constant; if(NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &constant))) return JS_FALSE; const nsXPTCMiniVariant& mv = *constant->GetValue(); // XXX Big Hack! nsXPTCVariant v; v.flags = 0; v.type = constant->GetType(); memcpy(&v.val, &mv.val, sizeof(mv.val)); jsval resultVal; if(!XPCConvert::NativeData2JS(ccx, &resultVal, &v.val, v.type, nsnull, nsnull, nsnull)) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); mVal = resultVal; mFlags |= RESOLVED; } return JS_TRUE; } // else... // This is a method or attribute - we'll be needing a function object intN argc; intN flags; JSNative callback; if(IsMethod()) { const nsXPTMethodInfo* info; if(NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info))) return JS_FALSE; // Note: ASSUMES that retval is last arg. argc = (intN) info->GetParamCount(); if(argc && info->GetParam((uint8)(argc-1)).IsRetval()) argc-- ; flags = 0; callback = XPC_WN_CallMethod; } else { if(IsWritableAttribute()) flags = JSFUN_GETTER | JSFUN_SETTER; else flags = JSFUN_GETTER; argc = 0; callback = XPC_WN_GetterSetter; } // We need to use the safe context for this thread because we don't want // to parent the new (and cached forever!) function object to the current // JSContext's global object. That would be bad! JSContext* cx = ccx.GetSafeJSContext(); if(!cx) return JS_FALSE; const char *memberName = iface->GetMemberName(ccx, this); jsrefcount suspendDepth = 0; if(cx != ccx) { // Switching contexts, suspend the old and enter the new request. suspendDepth = JS_SuspendRequest(ccx); JS_BeginRequest(cx); } JSFunction *fun = JS_NewFunction(cx, callback, argc, flags, nsnull, memberName); if(suspendDepth) { JS_EndRequest(cx); JS_ResumeRequest(ccx, suspendDepth); } if(!fun) return JS_FALSE; JSObject* funobj = JS_GetFunctionObject(fun); if(!funobj) return JS_FALSE; AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(funobj)); STOBJ_CLEAR_PARENT(funobj); STOBJ_CLEAR_PROTO(funobj); if(!JS_SetReservedSlot(ccx, funobj, 0, PRIVATE_TO_JSVAL(iface))|| !JS_SetReservedSlot(ccx, funobj, 1, PRIVATE_TO_JSVAL(this))) return JS_FALSE; { // scoped lock XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); mVal = OBJECT_TO_JSVAL(funobj); mFlags |= RESOLVED; } return JS_TRUE; }
JSBool XPCIDispatchExtension::DefineProperty(XPCCallContext & ccx, JSObject *obj, jsval idval, XPCWrappedNative* wrapperToReflectInterfaceNames, uintN propFlags, JSBool* resolved) { if(!JSVAL_IS_STRING(idval)) return JS_FALSE; // Look up the native interface for IDispatch and then find a tearoff XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(ccx, "IDispatch"); // The native interface isn't defined so just exit with an error if(iface == nsnull) return JS_FALSE; XPCWrappedNativeTearOff* to = wrapperToReflectInterfaceNames->LocateTearOff(ccx, iface); // This object has no IDispatch interface so bail. if(to == nsnull) return JS_FALSE; // Look up the member in the interface const XPCDispInterface::Member * member = to->GetIDispatchInfo()->FindMember(idval); if(!member) { // IDispatch is case insensitive, so if we don't find a case sensitive // match, we'll try a more expensive case-insensisitive search // TODO: We need to create cleaner solution that doesn't create // multiple properties of different case on the JS Object member = to->GetIDispatchInfo()->FindMemberCI(ccx, idval); if(!member) return JS_FALSE; } // Get the function object jsval funval; if(!member->GetValue(ccx, iface, &funval)) return JS_FALSE; // Protect the jsval AUTO_MARK_JSVAL(ccx, funval); // clone a function we can use for this object JSObject* funobj = xpc_CloneJSFunction(ccx, JSVAL_TO_OBJECT(funval), obj); if(!funobj) return JS_FALSE; jsid id; // If this is a function or a parameterized property if(member->IsFunction() || member->IsParameterizedProperty()) { // define the function on the object AutoResolveName arn(ccx, idval); if(resolved) *resolved = JS_TRUE; return JS_ValueToId(ccx, idval, &id) && OBJ_DEFINE_PROPERTY(ccx, obj, id, OBJECT_TO_JSVAL(funobj), nsnull, nsnull, propFlags, nsnull); } // Define the property on the object NS_ASSERTION(member->IsProperty(), "way broken!"); propFlags |= JSPROP_GETTER | JSPROP_SHARED; if(member->IsSetter()) { propFlags |= JSPROP_SETTER; propFlags &= ~JSPROP_READONLY; } AutoResolveName arn(ccx, idval); if(resolved) *resolved = JS_TRUE; return JS_ValueToId(ccx, idval, &id) && OBJ_DEFINE_PROPERTY(ccx, obj, id, JSVAL_VOID, (JSPropertyOp) funobj, (JSPropertyOp) funobj, propFlags, nsnull); }
JSBool XPCDispConvert::COMArrayToJSArray(XPCCallContext& ccx, const VARIANT & src, jsval & dest, nsresult& err) { err = NS_OK; // We only support one dimensional arrays for now if(SafeArrayGetDim(src.parray) != 1) { err = NS_ERROR_FAILURE; return JS_FALSE; } // Get the upper bound; long ubound; if(FAILED(SafeArrayGetUBound(src.parray, 1, &ubound))) { err = NS_ERROR_FAILURE; return JS_FALSE; } // Get the lower bound long lbound; if(FAILED(SafeArrayGetLBound(src.parray, 1, &lbound))) { err = NS_ERROR_FAILURE; return JS_FALSE; } // Create the JS Array JSObject * array = JS_NewArrayObject(ccx, ubound - lbound + 1, nsnull); if(!array) { err = NS_ERROR_OUT_OF_MEMORY; return JS_FALSE; } AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(array)); // Divine the type of our array VARTYPE vartype; if((src.vt & VT_ARRAY) != 0) { vartype = src.vt & ~VT_ARRAY; } else // This was maybe a VT_SAFEARRAY { #ifndef WINCE if(FAILED(SafeArrayGetVartype(src.parray, &vartype))) return JS_FALSE; #else return JS_FALSE; #endif } jsval val = JSVAL_NULL; AUTO_MARK_JSVAL(ccx, &val); for(long index = lbound; index <= ubound; ++index) { HRESULT hr; _variant_t var; if(vartype == VT_VARIANT) { hr = SafeArrayGetElement(src.parray, &index, &var); } else { var.vt = vartype; hr = SafeArrayGetElement(src.parray, &index, &var.byref); } if(FAILED(hr)) { err = NS_ERROR_FAILURE; return JS_FALSE; } if(!COMToJS(ccx, var, val, err)) return JS_FALSE; JS_SetElement(ccx, array, index, &val); } dest = OBJECT_TO_JSVAL(array); return JS_TRUE; }
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); }