JSObject * WrapperFactory::WrapForSameCompartment(JSContext *cx, HandleObject objArg) { RootedObject obj(cx, objArg); MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx)); // NB: The contract of WrapForSameCompartment says that |obj| may or may not // be a security wrapper. These checks implicitly handle the security // wrapper case. // Outerize if necessary. This, in combination with the check in // PrepareForUnwrapping, means that calling JS_Wrap* always outerizes. obj = JS_ObjectToOuterObject(cx, obj); NS_ENSURE_TRUE(obj, nullptr); if (dom::GetSameCompartmentWrapperForDOMBinding(*obj.address())) { return obj; } MOZ_ASSERT(!dom::IsDOMObject(obj)); if (!IS_WN_WRAPPER(obj)) return obj; // Extract the WN. It should exist. XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj)); MOZ_ASSERT(wn, "Trying to wrap a dead WN!"); // The WN knows what to do. RootedObject wrapper(cx, wn->GetSameCompartmentSecurityWrapper(cx)); MOZ_ASSERT_IF(wrapper != obj && IsComponentsObject(js::UncheckedUnwrap(obj)), !Wrapper::wrapperHandler(wrapper)->isSafeToUnwrap()); return wrapper; }
static XPCWrappedNative * GetWrappedNative(JSContext *cx, JSObject *obj) { obj = JS_ObjectToInnerObject(cx, obj); return IS_WN_WRAPPER(obj) ? static_cast<XPCWrappedNative *>(js::GetObjectPrivate(obj)) : nsnull; }
static XPCWrappedNative * GetWrappedNative(JSContext *cx, JSObject *obj) { OBJ_TO_INNER_OBJECT(cx, obj); return IS_WN_WRAPPER(obj) ? static_cast<XPCWrappedNative *>(obj->getPrivate()) : nsnull; }
static bool CanXray(JSObject *obj, bool *proxy) { if (IS_WN_WRAPPER(obj) || js::GetObjectClass(obj)->ext.innerObject) { *proxy = false; return true; } return (*proxy = mozilla::dom::binding::instanceIsProxy(obj)); }
XPCVariant::XPCVariant(JSContext* cx, jsval aJSVal) : mJSVal(aJSVal), mCCGeneration(0) { nsVariant::Initialize(&mData); if (!JSVAL_IS_PRIMITIVE(mJSVal)) { // XXXbholley - The innerization here was from bug 638026. Blake says // the basic problem was that we were storing the C++ inner but the JS // outer, which meant that, after navigation, the JS inner could be // collected, which would cause us to try to recreate the JS inner at // some later point after teardown, which would crash. This is shouldn't // be a problem anymore because SetParentToWindow will do the right // thing, but I'm saving the cleanup here for another day. Blake thinks // that we should just not store the WN if we're creating a variant for // an outer window. JSObject *obj = JS_ObjectToInnerObject(cx, JSVAL_TO_OBJECT(mJSVal)); mJSVal = OBJECT_TO_JSVAL(obj); JSObject *unwrapped = js::UnwrapObjectChecked(obj, /* stopAtOuter = */ false); mReturnRawObject = !(unwrapped && IS_WN_WRAPPER(unwrapped)); } else mReturnRawObject = false; }
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, HandleObject scope, HandleObject objArg, unsigned flags) { RootedObject obj(cx, objArg); // Outerize any raw inner objects at the entry point here, so that we don't // have to worry about them for the rest of the wrapping code. if (js::IsInnerObject(obj)) { JSAutoCompartment ac(cx, obj); obj = JS_ObjectToOuterObject(cx, obj); NS_ENSURE_TRUE(obj, nullptr); // The outerization hook wraps, which means that we can end up with a // CCW here if |obj| was a navigated-away-from inner. Strip any CCWs. obj = js::UncheckedUnwrap(obj); MOZ_ASSERT(js::IsOuterObject(obj)); } // If we've got an outer window, there's nothing special that needs to be // done here, and we can move on to the next phase of wrapping. We handle // this case first to allow us to assert against wrappers below. if (js::IsOuterObject(obj)) 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). MOZ_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 nullptr; // 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)); JSAutoCompartment ac(cx, obj); XPCCallContext ccx(JS_CALLER, cx, obj); RootedObject wrapScope(cx, scope); { if (NATIVE_HAS_FLAG(&ccx, WantPreCreate)) { // We have a precreate hook. This object might enforce that we only // ever create JS object for it. // 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?" nsresult rv = wn->GetScriptableInfo()->GetCallback()-> PreCreate(wn->Native(), cx, scope, wrapScope.address()); 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(scope) != js::GetObjectCompartment(wrapScope)) return DoubleWrap(cx, obj, flags); RootedObject currentScope(cx, JS_GetGlobalForObject(cx, obj)); if (MOZ_UNLIKELY(wrapScope != currentScope)) { // The wrapper claims it wants to be in the new scope, but // currently has a reflection that lives in the old scope. This // can mean one of two things, both of which are rare: // // 1 - The object has a PreCreate hook (we checked for it above), // but is deciding to request one-wrapper-per-scope (rather than // one-wrapper-per-native) for some reason. Usually, a PreCreate // hook indicates one-wrapper-per-native. In this case we want to // make a new wrapper in the new scope. // // 2 - We're midway through wrapper reparenting. The document has // moved to a new scope, but |wn| hasn't been moved yet, and // we ended up calling JS_WrapObject() on its JS object. In this // case, we want to return the existing wrapper. // // So we do a trick: call PreCreate _again_, but say that we're // wrapping for the old scope, rather than the new one. If (1) is // the case, then PreCreate will return the scope we pass to it // (the old scope). If (2) is the case, PreCreate will return the // scope of the document (the new scope). RootedObject probe(cx); rv = wn->GetScriptableInfo()->GetCallback()-> PreCreate(wn->Native(), cx, currentScope, probe.address()); // Check for case (2). if (probe != currentScope) { MOZ_ASSERT(probe == wrapScope); return DoubleWrap(cx, obj, flags); } // Ok, must be case (1). Fall through and create a new wrapper. } // Nasty hack for late-breaking bug 781476. This will confuse identity checks, // but it's probably better than any of our alternatives. // // Note: We have to ignore domain here. The JS engine assumes that, given a // compartment c, if c->wrap(x) returns a cross-compartment wrapper at time t0, // it will also return a cross-compartment wrapper for any time t1 > t0 unless // an explicit transplant is performed. In particular, wrapper recomputation // assumes that recomputing a wrapper will always result in a wrapper. // // This doesn't actually pose a security issue, because we'll still compute // the correct (opaque) wrapper for the object below given the security // characteristics of the two compartments. if (!AccessCheck::isChrome(js::GetObjectCompartment(wrapScope)) && AccessCheck::subsumesIgnoringDomain(js::GetObjectCompartment(wrapScope), js::GetObjectCompartment(obj))) { return DoubleWrap(cx, obj, flags); } } } // NB: Passing a holder here inhibits slim wrappers under // WrapNativeToJSVal. nsCOMPtr<nsIXPConnectJSObjectHolder> holder; // This public WrapNativeToJSVal API enters the compartment of 'wrapScope' // so we don't have to. RootedValue v(cx); nsresult rv = nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr, &NS_GET_IID(nsISupports), false, v.address(), 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 nullptr; newwn->SetSet(unionSet); } return DoubleWrap(cx, obj, flags); }
JSObject * WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, uintN flags) { NS_ASSERTION(!obj->isWrapper() || (obj->isWrapper() && obj->getProxyHandler() == &WaiveXrayWrapperWrapper) || obj->getClass()->ext.innerObject, "wrapped object passed to rewrap"); NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); JSCompartment *origin = obj->compartment(); JSCompartment *target = cx->compartment; JSObject *xrayHolder = nsnull; JSWrapper *wrapper; CompartmentPrivate *targetdata = static_cast<CompartmentPrivate *>(JS_GetCompartmentPrivate(cx, target)); if (AccessCheck::isChrome(target)) { if (AccessCheck::isChrome(origin)) { wrapper = &JSCrossCompartmentWrapper::singleton; } else { bool isSystem; { JSAutoEnterCompartment ac; if (!ac.enter(cx, obj)) return nsnull; JSObject *globalObj = JS_GetGlobalForObject(cx, obj); JS_ASSERT(globalObj); isSystem = JS_IsSystemObject(cx, globalObj); } if (isSystem) { wrapper = &JSCrossCompartmentWrapper::singleton; } else if (flags & WAIVE_XRAY_WRAPPER_FLAG) { // If we waived the X-ray wrapper for this object, wrap it into a // special wrapper to transitively maintain the X-ray waiver. wrapper = &CrossOriginWrapper::singleton; } else { // Native objects must be wrapped into an X-ray wrapper. if (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &NoWaiverWrapper::singleton; } } } } else if (AccessCheck::isChrome(origin)) { if (obj->isFunction()) { JSFunction *fun = obj->getFunctionPrivate(); if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { JS_ReportError(cx, "Not allowed to access chrome eval or Function from content"); return nsnull; } } XPCWrappedNative *wn; if (targetdata && (wn = GetWrappedNative(cx, obj)) && wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, ExposedPropertiesOnly>::singleton; } } else if (AccessCheck::isSameOrigin(origin, target)) { // Same origin we use a transparent wrapper, unless the compartment asks // for an Xray or the wrapper needs a SOW. if (AccessCheck::needsSystemOnlyWrapper(obj)) { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, OnlyIfSubjectIsSystem>::singleton; } else if (targetdata && targetdata->wantXrays && (IS_WN_WRAPPER(obj) || obj->getClass()->ext.innerObject)) { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; wrapper = &Xray::singleton; xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } else { wrapper = &JSCrossCompartmentWrapper::singleton; } } else { NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj), "bad object exposed across origins"); // Cross origin we want to disallow scripting and limit access to // a predefined set of properties. XrayWrapper adds a property // (.wrappedJSObject) which allows bypassing the XrayWrapper, but // we filter out access to that property. if (!IS_WN_WRAPPER(obj) && !obj->getClass()->ext.innerObject) { wrapper = &FilteringWrapper<JSCrossCompartmentWrapper, CrossOriginAccessiblePropertiesOnly>::singleton; } else { typedef XrayWrapper<JSCrossCompartmentWrapper> Xray; // Location objects can become same origin after navigation, so we might // have to grant transparent access later on. if (IsLocationObject(obj)) { wrapper = &FilteringWrapper<Xray, SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton; } else { wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton; } xrayHolder = Xray::createHolder(cx, obj, parent); if (!xrayHolder) return nsnull; } } JSObject *wrapperObj = JSWrapper::New(cx, obj, wrappedProto, parent, wrapper); if (!wrapperObj || !xrayHolder) return wrapperObj; // NB: The fact that the only wrappers to use ProxyExtra are XrayWrappers // is relied on by XPCNativeWrapper.unwrap. wrapperObj->setProxyExtra(js::ObjectValue(*xrayHolder)); return wrapperObj; }
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); }