bug 580128 - Allow API consumers to pass in a new object altogether before trying to wrap in a security wrapper. r=gal/peterv

This commit is contained in:
Blake Kaplan 2010-10-10 15:36:38 -07:00
Родитель 0e8ed043cb
Коммит a6e9a41e46
8 изменённых файлов: 124 добавлений и 31 удалений

Просмотреть файл

@ -1148,10 +1148,13 @@ JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback)
} }
JS_PUBLIC_API(JSWrapObjectCallback) JS_PUBLIC_API(JSWrapObjectCallback)
JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback) JS_SetWrapObjectCallbacks(JSRuntime *rt,
JSWrapObjectCallback callback,
JSPreWrapCallback precallback)
{ {
JSWrapObjectCallback old = rt->wrapObjectCallback; JSWrapObjectCallback old = rt->wrapObjectCallback;
rt->wrapObjectCallback = callback; rt->wrapObjectCallback = callback;
rt->preWrapObjectCallback = precallback;
return old; return old;
} }

Просмотреть файл

@ -937,7 +937,9 @@ extern JS_PUBLIC_API(JSCompartmentCallback)
JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback); JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback);
extern JS_PUBLIC_API(JSWrapObjectCallback) extern JS_PUBLIC_API(JSWrapObjectCallback)
JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback); JS_SetWrapObjectCallbacks(JSRuntime *rt,
JSWrapObjectCallback callback,
JSPreWrapCallback precallback);
extern JS_PUBLIC_API(JSCrossCompartmentCall *) extern JS_PUBLIC_API(JSCrossCompartmentCall *)
JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target); JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target);

Просмотреть файл

@ -1691,6 +1691,7 @@ struct JSRuntime {
#endif #endif
JSWrapObjectCallback wrapObjectCallback; JSWrapObjectCallback wrapObjectCallback;
JSPreWrapCallback preWrapObjectCallback;
JSC::ExecutableAllocator *regExpAllocator; JSC::ExecutableAllocator *regExpAllocator;

Просмотреть файл

@ -134,6 +134,24 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
} }
} }
/*
* Wrappers should really be parented to the wrapped parent of the wrapped
* object, but in that case a wrapped global object would have a NULL
* parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead
,
* we parent all wrappers to the global object in their home compartment.
* This loses us some transparency, and is generally very cheesy.
*/
JSObject *global;
if (cx->hasfp()) {
global = cx->fp()->scopeChain().getGlobal();
} else {
global = cx->globalObject;
OBJ_TO_INNER_OBJECT(cx, global);
if (!global)
return false;
}
/* Unwrap incoming objects. */ /* Unwrap incoming objects. */
if (vp->isObject()) { if (vp->isObject()) {
JSObject *obj = &vp->toObject(); JSObject *obj = &vp->toObject();
@ -145,10 +163,16 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
/* Don't unwrap an outer window proxy. */ /* Don't unwrap an outer window proxy. */
if (!obj->getClass()->ext.innerObject) { if (!obj->getClass()->ext.innerObject) {
obj = vp->toObject().unwrap(&flags); obj = vp->toObject().unwrap(&flags);
vp->setObject(*obj);
if (obj->getCompartment() == this)
return true;
if (cx->runtime->preWrapObjectCallback)
obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
if (!obj)
return false;
vp->setObject(*obj); vp->setObject(*obj);
/* If the wrapped object is already in this compartment, we are done. */
if (obj->getCompartment() == this) if (obj->getCompartment() == this)
return true; return true;
} else { } else {
@ -156,12 +180,13 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
vp->setObject(*obj); vp->setObject(*obj);
} }
OBJ_TO_OUTER_OBJECT(cx, obj); #ifdef DEBUG
if (!obj) {
return false; JSObject *outer = obj;
OBJ_TO_OUTER_OBJECT(cx, outer);
JS_ASSERT(obj->getCompartment() == vp->toObject().getCompartment()); JS_ASSERT(outer && outer == obj);
vp->setObject(*obj); }
#endif
} }
/* If we already have a wrapper for this value, use it. */ /* If we already have a wrapper for this value, use it. */
@ -196,23 +221,6 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
if (!wrap(cx, &proto)) if (!wrap(cx, &proto))
return false; return false;
/*
* Wrappers should really be parented to the wrapped parent of the wrapped
* object, but in that case a wrapped global object would have a NULL
* parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
* we parent all wrappers to the global object in their home compartment.
* This loses us some transparency, and is generally very cheesy.
*/
JSObject *global;
if (cx->hasfp()) {
global = cx->fp()->scopeChain().getGlobal();
} else {
global = cx->globalObject;
OBJ_TO_INNER_OBJECT(cx, global);
if (!global)
return false;
}
/* /*
* We hand in the original wrapped object into the wrap hook to allow * We hand in the original wrapped object into the wrap hook to allow
* the wrap hook to reason over what wrappers are currently applied * the wrap hook to reason over what wrappers are currently applied

Просмотреть файл

@ -562,6 +562,14 @@ typedef JSObject *
(* JSWrapObjectCallback)(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, (* JSWrapObjectCallback)(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
uintN flags); uintN flags);
/*
* Callback used by the wrap hook to ask the embedding to prepare an object
* for wrapping in a context. This might include unwrapping other wrappers
* or even finding a more suitable object for the new compartment.
*/
typedef JSObject *
(* JSPreWrapCallback)(JSContext *cx, JSObject *scope, JSObject *obj, uintN flags);
typedef enum { typedef enum {
JSCOMPARTMENT_NEW, /* XXX Does it make sense to have a NEW? */ JSCOMPARTMENT_NEW, /* XXX Does it make sense to have a NEW? */
JSCOMPARTMENT_DESTROY JSCOMPARTMENT_DESTROY

Просмотреть файл

@ -1145,7 +1145,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
JS_SetGCCallbackRT(mJSRuntime, GCCallback); JS_SetGCCallbackRT(mJSRuntime, GCCallback);
JS_SetExtraGCRoots(mJSRuntime, TraceJS, this); JS_SetExtraGCRoots(mJSRuntime, TraceJS, this);
JS_SetWrapObjectCallback(mJSRuntime, xpc::WrapperFactory::Rewrap); JS_SetWrapObjectCallbacks(mJSRuntime,
xpc::WrapperFactory::Rewrap,
xpc::WrapperFactory::PrepareForWrapping);
mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock); mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock);
mJSRuntime->setActivityCallback(ActivityCallback, this); mJSRuntime->setActivityCallback(ActivityCallback, this);

Просмотреть файл

@ -45,6 +45,7 @@
#include "FilteringWrapper.h" #include "FilteringWrapper.h"
#include "XrayWrapper.h" #include "XrayWrapper.h"
#include "AccessCheck.h" #include "AccessCheck.h"
#include "XPCWrapper.h"
#include "xpcprivate.h" #include "xpcprivate.h"
@ -65,6 +66,71 @@ JSWrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
// off it. // off it.
CrossOriginWrapper XrayWrapperWaivedWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG); CrossOriginWrapper XrayWrapperWaivedWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
JSObject *
WrapperFactory::PrepareForWrapping(JSContext *cx, JSObject *scope, JSObject *obj, uintN 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() || obj->getClass()->ext.innerObject);
// 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_TO_OUTER_OBJECT(cx, obj);
// 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. So...
if (!IS_WN_WRAPPER(obj))
return obj;
XPCWrappedNative *wn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
// We know that DOM objects only allow one object, we can return early.
if (wn->GetProto()->ClassIsDOMObject())
return obj;
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, obj);
// 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->getCompartment() != scope->getCompartment())
return obj;
// 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?"
}
// The object we're looking at might allow us to create a new wrapped
// native in the new scope. Try it and continue wrapping on the
// possibly-new object.
JSAutoEnterCompartment ac;
if (!ac.enter(cx, scope))
return obj;
jsval v;
nsresult rv =
nsXPConnect::FastGetXPConnect()->WrapNativeToJSVal(cx, scope, wn->Native(), nsnull,
&NS_GET_IID(nsISupports), PR_FALSE,
&v, nsnull);
NS_ENSURE_SUCCESS(rv, obj);
return JSVAL_TO_OBJECT(v);
}
JSObject * JSObject *
WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent, WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
uintN flags) uintN flags)
@ -73,9 +139,6 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
"wrapped object passed to rewrap"); "wrapped object passed to rewrap");
NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder"); NS_ASSERTION(JS_GET_CLASS(cx, obj) != &XrayUtils::HolderClass, "trying to wrap a holder");
if (IS_SLIM_WRAPPER(obj) && !MorphSlimWrapper(cx, obj))
return nsnull;
JSCompartment *origin = obj->getCompartment(); JSCompartment *origin = obj->getCompartment();
JSCompartment *target = cx->compartment; JSCompartment *target = cx->compartment;
JSObject *xrayHolder = nsnull; JSObject *xrayHolder = nsnull;

Просмотреть файл

@ -58,6 +58,12 @@ class WrapperFactory {
return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG); return HasWrapperFlag(wrapper, IS_XRAY_WRAPPER_FLAG);
} }
// Prepare a given object for wrapping in a new compartment.
static JSObject *PrepareForWrapping(JSContext *cx,
JSObject *scope,
JSObject *obj,
uintN flags);
// Rewrap an object that is about to cross compartment boundaries. // Rewrap an object that is about to cross compartment boundaries.
static JSObject *Rewrap(JSContext *cx, static JSObject *Rewrap(JSContext *cx,
JSObject *obj, JSObject *obj,