JSObject * WrapperFactory::WaiveXray(JSContext *cx, JSObject *obj) { obj = UnwrapObject(obj); // We have to make sure that if we're wrapping an outer window, that // the .wrappedJSObject also wraps the outer window. obj = GetCurrentOuter(cx, obj); { // See if we already have a waiver wrapper for this object. CompartmentPrivate *priv = (CompartmentPrivate *)JS_GetCompartmentPrivate(js::GetObjectCompartment(obj)); JSObject *wobj = nsnull; if (priv && priv->waiverWrapperMap) { wobj = priv->waiverWrapperMap->Find(obj); xpc_UnmarkGrayObject(wobj); } // No wrapper yet, make one. if (!wobj) { JSObject *proto = js::GetObjectProto(obj); if (proto && !(proto = WaiveXray(cx, proto))) return nsnull; JSAutoEnterCompartment ac; if (!ac.enter(cx, obj) || !JS_WrapObject(cx, &proto)) return nsnull; wobj = Wrapper::New(cx, obj, proto, JS_GetGlobalForObject(cx, obj), &WaiveXrayWrapperWrapper); if (!wobj) return nsnull; // Add the new wrapper so we find it next time. if (priv) { if (!priv->waiverWrapperMap) { priv->waiverWrapperMap = JSObject2JSObjectMap::newMap(XPC_WRAPPER_MAP_SIZE); if (!priv->waiverWrapperMap) return nsnull; } if (!priv->waiverWrapperMap->Add(obj, wobj)) return nsnull; } } obj = wobj; } return obj; }
// Call WaiveXrayAndWrap when you have a JS object that you don't want to be // wrapped in an Xray wrapper. cx->compartment is the compartment that will be // using the returned object. If the object to be wrapped is already in the // correct compartment, then this returns the unwrapped object. bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) { if (JSVAL_IS_PRIMITIVE(*vp)) return JS_WrapValue(cx, vp); JSObject *obj = js::UnwrapObject(JSVAL_TO_OBJECT(*vp)); obj = GetCurrentOuter(cx, obj); if (js::IsObjectInContextCompartment(obj, cx)) { *vp = OBJECT_TO_JSVAL(obj); return true; } obj = WaiveXray(cx, obj); if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
// Call WaiveXrayAndWrap when you have a JS object that you don't want to be // wrapped in an Xray wrapper. cx->compartment is the compartment that will be // using the returned object. If the object to be wrapped is already in the // correct compartment, then this returns the unwrapped object. bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, jsval *vp) { if (JSVAL_IS_PRIMITIVE(*vp)) return JS_WrapValue(cx, vp); JSObject *obj = JSVAL_TO_OBJECT(*vp)->unwrap(); obj = GetCurrentOuter(cx, obj); if (obj->compartment() == cx->compartment) { *vp = OBJECT_TO_JSVAL(obj); return true; } obj = WaiveXray(cx, obj); if (!obj) return false; *vp = OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, vp); }
JSObject * WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, unsigned flags) { // Don't unwrap an outer window, just double wrap it if needed. if (js::GetObjectClass(obj)->ext.innerObject) return DoubleWrap(cx, obj, flags); // Here are the rules for wrapping: // We should never get a proxy here (the JS engine unwraps those for us). JS_ASSERT(!IsWrapper(obj)); // As soon as an object is wrapped in a security wrapper, it morphs to be // a fat wrapper. (see also: bug XXX). if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) return nsnull; // We only hand out outer objects to script. obj = GetCurrentOuter(cx, obj); if (js::GetObjectClass(obj)->ext.innerObject) return DoubleWrap(cx, obj, flags); // Now, our object is ready to be wrapped, but several objects (notably // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of // those objects in a security wrapper, then we need to hand back the // wrapper for the new scope instead. Also, global objects don't move // between scopes so for those we also want to return the wrapper. So... if (!IS_WN_WRAPPER(obj) || !js::GetObjectParent(obj)) return DoubleWrap(cx, obj, flags); XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj)); JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return nsnull; XPCCallContext ccx(JS_CALLER, cx, obj); { if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) { // We have a precreate hook. This object might enforce that we only // ever create JS object for it. JSObject *originalScope = scope; nsresult rv = wn->GetScriptableInfo()->GetCallback()-> PreCreate(wn->Native(), cx, scope, &scope); NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags)); // If the handed back scope differs from the passed-in scope and is in // a separate compartment, then this object is explicitly requesting // that we don't create a second JS object for it: create a security // wrapper. if (js::GetObjectCompartment(originalScope) != js::GetObjectCompartment(scope)) return DoubleWrap(cx, obj, flags); // Note: this penalizes objects that only have one wrapper, but are // being accessed across compartments. We would really prefer to // replace the above code with a test that says "do you only have one // wrapper?" } } // NB: Passing a holder here inhibits slim wrappers under // WrapNativeToJSVal. nsCOMPtr<nsIXPConnectJSObjectHolder> holder; // This public WrapNativeToJSVal API enters the compartment of 'scope' // so we don't have to. jsval v; nsresult rv = nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull, &NS_GET_IID(nsISupports), false, &v, getter_AddRefs(holder)); if (NS_SUCCEEDED(rv)) { obj = JSVAL_TO_OBJECT(v); NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object"); // Because the underlying native didn't have a PreCreate hook, we had // to a new (or possibly pre-existing) XPCWN in our compartment. // This could be a problem for chrome code that passes XPCOM objects // across compartments, because the effects of QI would disappear across // compartments. // // So whenever we pull an XPCWN across compartments in this manner, we // give the destination object the union of the two native sets. We try // to do this cleverly in the common case to avoid too much overhead. XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj)); XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(ccx, newwn->GetSet(), wn->GetSet(), false); if (!unionSet) return nsnull; newwn->SetSet(unionSet); } return DoubleWrap(cx, obj, flags); }
JSObject * WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags) { // Don't unwrap an outer window, just double wrap it if needed. if (obj->getClass()->ext.innerObject) return DoubleWrap(cx, obj, flags); // Here are the rules for wrapping: // We should never get a proxy here (the JS engine unwraps those for us). JS_ASSERT(!obj->isWrapper()); // As soon as an object is wrapped in a security wrapper, it morphs to be // a fat wrapper. (see also: bug XXX). if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj)) return nsnull; // We only hand out outer objects to script. obj = GetCurrentOuter(cx, obj); if (obj->getClass()->ext.innerObject) return DoubleWrap(cx, obj, flags); // Now, our object is ready to be wrapped, but several objects (notably // nsJSIIDs) have a wrapper per scope. If we are about to wrap one of // those objects in a security wrapper, then we need to hand back the // wrapper for the new scope instead. Also, global objects don't move // between scopes so for those we also want to return the wrapper. So... if (!IS_WN_WRAPPER(obj) || !obj->getParent()) return DoubleWrap(cx, obj, flags); XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj)); // If the object doesn't have classinfo we want to return the same // XPCWrappedNative so that we keep the same set of interfaces. if (!wn->GetClassInfo()) return DoubleWrap(cx, obj, flags); JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return nsnull; XPCCallContext ccx(JS_CALLER, cx, obj); { if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) { // We have a precreate hook. This object might enforce that we only // ever create JS object for it. JSObject *originalScope = scope; nsresult rv = wn->GetScriptableInfo()->GetCallback()-> PreCreate(wn->Native(), cx, scope, &scope); NS_ENSURE_SUCCESS(rv, DoubleWrap(cx, obj, flags)); // If the handed back scope differs from the passed-in scope and is in // a separate compartment, then this object is explicitly requesting // that we don't create a second JS object for it: create a security // wrapper. if (originalScope->compartment() != scope->getCompartment()) return DoubleWrap(cx, obj, flags); // Note: this penalizes objects that only have one wrapper, but are // being accessed across compartments. We would really prefer to // replace the above code with a test that says "do you only have one // wrapper?" } } // NB: Passing a holder here inhibits slim wrappers under // WrapNativeToJSVal. nsCOMPtr<nsIXPConnectJSObjectHolder> holder; // This public WrapNativeToJSVal API enters the compartment of 'scope' // so we don't have to. jsval v; nsresult rv = nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull, &NS_GET_IID(nsISupports), PR_FALSE, &v, getter_AddRefs(holder)); if (NS_SUCCEEDED(rv)) { obj = JSVAL_TO_OBJECT(v); NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object"); XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj)); if (newwn->GetSet()->GetInterfaceCount() < wn->GetSet()->GetInterfaceCount()) newwn->SetSet(wn->GetSet()); } return DoubleWrap(cx, obj, flags); }