зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
0e8ed043cb
Коммит
a6e9a41e46
|
@ -1148,10 +1148,13 @@ JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(JSWrapObjectCallback)
|
||||
JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback)
|
||||
JS_SetWrapObjectCallbacks(JSRuntime *rt,
|
||||
JSWrapObjectCallback callback,
|
||||
JSPreWrapCallback precallback)
|
||||
{
|
||||
JSWrapObjectCallback old = rt->wrapObjectCallback;
|
||||
rt->wrapObjectCallback = callback;
|
||||
rt->preWrapObjectCallback = precallback;
|
||||
return old;
|
||||
}
|
||||
|
||||
|
|
|
@ -937,7 +937,9 @@ extern JS_PUBLIC_API(JSCompartmentCallback)
|
|||
JS_SetCompartmentCallback(JSRuntime *rt, JSCompartmentCallback callback);
|
||||
|
||||
extern JS_PUBLIC_API(JSWrapObjectCallback)
|
||||
JS_SetWrapObjectCallback(JSRuntime *rt, JSWrapObjectCallback callback);
|
||||
JS_SetWrapObjectCallbacks(JSRuntime *rt,
|
||||
JSWrapObjectCallback callback,
|
||||
JSPreWrapCallback precallback);
|
||||
|
||||
extern JS_PUBLIC_API(JSCrossCompartmentCall *)
|
||||
JS_EnterCrossCompartmentCall(JSContext *cx, JSObject *target);
|
||||
|
|
|
@ -1691,6 +1691,7 @@ struct JSRuntime {
|
|||
#endif
|
||||
|
||||
JSWrapObjectCallback wrapObjectCallback;
|
||||
JSPreWrapCallback preWrapObjectCallback;
|
||||
|
||||
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. */
|
||||
if (vp->isObject()) {
|
||||
JSObject *obj = &vp->toObject();
|
||||
|
@ -145,10 +163,16 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
|||
/* Don't unwrap an outer window proxy. */
|
||||
if (!obj->getClass()->ext.innerObject) {
|
||||
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);
|
||||
|
||||
/* If the wrapped object is already in this compartment, we are done. */
|
||||
if (obj->getCompartment() == this)
|
||||
return true;
|
||||
} else {
|
||||
|
@ -156,12 +180,13 @@ JSCompartment::wrap(JSContext *cx, Value *vp)
|
|||
vp->setObject(*obj);
|
||||
}
|
||||
|
||||
OBJ_TO_OUTER_OBJECT(cx, obj);
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
JS_ASSERT(obj->getCompartment() == vp->toObject().getCompartment());
|
||||
vp->setObject(*obj);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
JSObject *outer = obj;
|
||||
OBJ_TO_OUTER_OBJECT(cx, outer);
|
||||
JS_ASSERT(outer && outer == obj);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 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))
|
||||
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
|
||||
* 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,
|
||||
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 {
|
||||
JSCOMPARTMENT_NEW, /* XXX Does it make sense to have a NEW? */
|
||||
JSCOMPARTMENT_DESTROY
|
||||
|
|
|
@ -1145,7 +1145,9 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
|||
JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
|
||||
JS_SetGCCallbackRT(mJSRuntime, GCCallback);
|
||||
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);
|
||||
|
||||
mJSRuntime->setActivityCallback(ActivityCallback, this);
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "FilteringWrapper.h"
|
||||
#include "XrayWrapper.h"
|
||||
#include "AccessCheck.h"
|
||||
#include "XPCWrapper.h"
|
||||
|
||||
#include "xpcprivate.h"
|
||||
|
||||
|
@ -65,6 +66,71 @@ JSWrapper WaiveXrayWrapperWrapper(WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG);
|
|||
// off it.
|
||||
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 *
|
||||
WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSObject *parent,
|
||||
uintN flags)
|
||||
|
@ -73,9 +139,6 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *obj, JSObject *wrappedProto, JSO
|
|||
"wrapped object passed to rewrap");
|
||||
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 *target = cx->compartment;
|
||||
JSObject *xrayHolder = nsnull;
|
||||
|
|
|
@ -58,6 +58,12 @@ class WrapperFactory {
|
|||
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.
|
||||
static JSObject *Rewrap(JSContext *cx,
|
||||
JSObject *obj,
|
||||
|
|
Загрузка…
Ссылка в новой задаче