Bug 926012 - Part 3: Convert wrappers to using dynamic prototype hooks. (r=bholley)

This commit is contained in:
Eric Faust 2013-12-13 12:01:30 -08:00
parent 7f87debdf9
commit 46a65bdd10
13 changed files with 178 additions and 76 deletions

View File

@ -1011,11 +1011,7 @@ static JSObject*
NewOuterWindowProxy(JSContext *cx, JS::Handle<JSObject*> parent, bool isChrome)
{
JSAutoCompartment ac(cx, parent);
JS::Rooted<JSObject*> proto(cx);
if (!js::GetObjectProto(cx, parent, &proto))
return nullptr;
JSObject *obj = js::Wrapper::New(cx, parent, proto, parent,
JSObject *obj = js::Wrapper::New(cx, parent, parent,
isChrome ? &nsChromeOuterWindowProxy::singleton
: &nsOuterWindowProxy::singleton);
@ -2103,26 +2099,11 @@ nsGlobalWindow::CreateOuterObject(nsGlobalWindow* aNewInner)
js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
return SetOuterObject(cx, outer);
}
nsresult
nsGlobalWindow::SetOuterObject(JSContext* aCx, JS::Handle<JSObject*> aOuterObject)
{
JSAutoCompartment ac(aCx, aOuterObject);
JSAutoCompartment ac(cx, outer);
// Inform the nsJSContext, which is the canonical holder of the outer.
MOZ_ASSERT(IsOuterWindow());
mContext->SetWindowProxy(aOuterObject);
// Set up the prototype for the outer object.
JS::Rooted<JSObject*> inner(aCx, JS_GetParent(aOuterObject));
JS::Rooted<JSObject*> proto(aCx);
if (!JS_GetPrototype(aCx, inner, &proto)) {
return NS_ERROR_FAILURE;
}
JS_SetPrototype(aCx, aOuterObject, proto);
mContext->SetWindowProxy(outer);
return NS_OK;
}
@ -2457,8 +2438,9 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject);
JS::Rooted<JSObject*> obj(cx, mJSObject);
rv = SetOuterObject(cx, obj);
NS_ENSURE_SUCCESS(rv, rv);
// Inform the nsJSContext, which is the canonical holder of the outer.
mContext->SetWindowProxy(obj);
NS_ASSERTION(!JS_IsExceptionPending(cx),
"We might overwrite a pending exception!");

View File

@ -60,13 +60,12 @@ static JSObject *
Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj,
JS::HandleObject proto, JS::HandleObject parent, unsigned flags)
{
return js::Wrapper::New(cx, obj, proto, parent, &js::CrossCompartmentWrapper::singleton);
return js::Wrapper::New(cx, obj, parent, &js::CrossCompartmentWrapper::singleton);
}
BEGIN_TEST(testBug604087)
{
JS::RootedObject outerObj(cx, js::Wrapper::New(cx, global, global->getProto(), global,
&OuterWrapper::singleton));
JS::RootedObject outerObj(cx, js::Wrapper::New(cx, global, global, &OuterWrapper::singleton));
JS::RootedObject compartment2(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook));
JS::RootedObject compartment3(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook));
JS::RootedObject compartment4(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook));
@ -87,8 +86,7 @@ BEGIN_TEST(testBug604087)
JS::RootedObject next(cx);
{
JSAutoCompartment ac(cx, compartment2);
next = js::Wrapper::New(cx, compartment2, compartment2->getProto(), compartment2,
&OuterWrapper::singleton);
next = js::Wrapper::New(cx, compartment2, compartment2, &OuterWrapper::singleton);
CHECK(next);
}

View File

@ -516,6 +516,19 @@ DirectProxyHandler::hasInstance(JSContext *cx, HandleObject proxy, MutableHandle
return true;
}
bool
DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
{
RootedObject target(cx, proxy->as<ProxyObject>().target());
return JSObject::getProto(cx, target, protop);
}
bool
DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp)
{
RootedObject target(cx, proxy->as<ProxyObject>().target());
return JSObject::setProto(cx, target, proto, bp);
}
bool
DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,

View File

@ -252,6 +252,8 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler
CallArgs args) MOZ_OVERRIDE;
virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
bool *bp) MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp);
virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
JSContext *cx) MOZ_OVERRIDE;
virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;

View File

@ -39,7 +39,7 @@ Wrapper::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHan
}
JSObject *
Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wrapper *handler)
Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler)
{
JS_ASSERT(parent);
@ -48,7 +48,7 @@ Wrapper::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, Wr
RootedValue priv(cx, ObjectValue(*obj));
ProxyOptions options;
options.setCallable(obj->isCallable());
return NewProxyObject(cx, handler, priv, proto, parent, options);
return NewProxyObject(cx, handler, priv, Proxy::LazyProto, parent, options);
}
JSObject *
@ -141,7 +141,8 @@ js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject
{
// Allow wrapping outer window proxies.
JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
return Wrapper::New(cx, obj, wrappedProto, parent, &CrossCompartmentWrapper::singleton);
JS_ASSERT(wrappedProto == Proxy::LazyProto);
return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton);
}
ErrorCopier::~ErrorCopier()
@ -578,11 +579,6 @@ bool
CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper,
MutableHandleObject protop)
{
if (!wrapper->getTaggedProto().isLazy()) {
protop.set(wrapper->getTaggedProto().toObjectOrNull());
return true;
}
{
RootedObject wrapped(cx, wrappedObject(wrapper));
AutoCompartment call(cx, wrapped);
@ -595,6 +591,17 @@ CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper,
return cx->compartment()->wrap(cx, protop);
}
bool
CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper,
HandleObject proto, bool *bp)
{
RootedObject protoCopy(cx, proto);
PIERCE(cx, wrapper,
cx->compartment()->wrap(cx, &protoCopy),
Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp),
NOTHING);
}
CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
/* Security wrappers. */
@ -646,6 +653,15 @@ SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeIm
return false;
}
template <class Base>
bool
SecurityWrapper<Base>::setPrototypeOf(JSContext *cx, HandleObject wrapper,
HandleObject proto, bool *bp)
{
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNWRAP_DENIED);
return false;
}
// For security wrappers, we run the DefaultValue algorithm on the wrapper
// itself, which means that the existing security policy on operations like
// toString() will take effect and do the right thing here.

View File

@ -49,8 +49,7 @@ class JS_FRIEND_API(Wrapper) : public DirectProxyHandler
void setSafeToUnwrap(bool safe) { mSafeToUnwrap = safe; }
bool isSafeToUnwrap() { return mSafeToUnwrap; }
static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto,
JSObject *parent, Wrapper *handler);
static JSObject *New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler);
static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler);
@ -120,7 +119,10 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE;
virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
MutableHandleValue vp) MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
MutableHandleObject protop) MOZ_OVERRIDE;
virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
bool *bp) MOZ_OVERRIDE;
static CrossCompartmentWrapper singleton;
static CrossCompartmentWrapper singletonWithPrototype;
@ -155,6 +157,9 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto,
bool *bp) MOZ_OVERRIDE;
virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
JS::HandleObject callable) MOZ_OVERRIDE;
virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) MOZ_OVERRIDE;

View File

@ -3743,6 +3743,34 @@ ThisFilename(JSContext *cx, unsigned argc, Value *vp)
return true;
}
/*
* Internal class for testing hasPrototype easily.
* Uses passed in prototype instead of target's.
*/
class WrapperWithProto : public Wrapper
{
public:
explicit WrapperWithProto(unsigned flags)
: Wrapper(flags, true)
{ }
static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
Wrapper *handler);
};
/* static */ JSObject *
WrapperWithProto::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
Wrapper *handler)
{
JS_ASSERT(parent);
AutoMarkInDeadZone amd(cx->zone());
RootedValue priv(cx, ObjectValue(*obj));
ProxyOptions options;
options.setCallable(obj->isCallable());
return NewProxyObject(cx, handler, priv, proto, parent, options);
}
static bool
Wrap(JSContext *cx, unsigned argc, jsval *vp)
{
@ -3753,10 +3781,7 @@ Wrap(JSContext *cx, unsigned argc, jsval *vp)
}
RootedObject obj(cx, JSVAL_TO_OBJECT(v));
RootedObject proto(cx);
if (!JSObject::getProto(cx, obj, &proto))
return false;
JSObject *wrapped = Wrapper::New(cx, obj, proto, &obj->global(),
JSObject *wrapped = Wrapper::New(cx, obj, &obj->global(),
&Wrapper::singleton);
if (!wrapped)
return false;
@ -3779,9 +3804,9 @@ WrapWithProto(JSContext *cx, unsigned argc, jsval *vp)
return false;
}
JSObject *wrapped = Wrapper::New(cx, &obj.toObject(), proto.toObjectOrNull(),
&obj.toObject().global(),
&Wrapper::singletonWithPrototype);
JSObject *wrapped = WrapperWithProto::New(cx, &obj.toObject(), proto.toObjectOrNull(),
&obj.toObject().global(),
&Wrapper::singletonWithPrototype);
if (!wrapped)
return false;

View File

@ -91,4 +91,11 @@ WaiveXrayWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test,
WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
}
bool
WaiveXrayWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, MutableHandleObject protop)
{
return CrossCompartmentWrapper::getPrototypeOf(cx, wrapper, protop) &&
(!protop || WrapperFactory::WaiveXrayAndWrap(cx, protop));
}
}

View File

@ -38,6 +38,9 @@ class WaiveXrayWrapper : public js::CrossCompartmentWrapper {
virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test,
JS::NativeImpl impl, JS::CallArgs args) MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, JS::Handle<JSObject*> wrapper,
JS::MutableHandle<JSObject*> protop) MOZ_OVERRIDE;
static WaiveXrayWrapper singleton;
};

View File

@ -75,18 +75,8 @@ WrapperFactory::CreateXrayWaiver(JSContext *cx, HandleObject obj)
MOZ_ASSERT(!GetXrayWaiver(obj));
XPCWrappedNativeScope *scope = GetObjectScope(obj);
// Get a waiver for the proto.
RootedObject proto(cx);
if (!js::GetObjectProto(cx, obj, &proto))
return nullptr;
if (proto && !(proto = WaiveXray(cx, proto)))
return nullptr;
// Create the waiver.
JSAutoCompartment ac(cx, obj);
if (!JS_WrapObject(cx, &proto))
return nullptr;
JSObject *waiver = Wrapper::New(cx, obj, proto,
JSObject *waiver = Wrapper::New(cx, obj,
JS_GetGlobalForObject(cx, obj),
&XrayWaiver);
if (!waiver)
@ -409,10 +399,6 @@ WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj,
XrayType xrayType = GetXrayType(obj);
bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG;
// By default we use the wrapped proto of the underlying object as the
// prototype for our wrapper, but we may select something different below.
RootedObject proxyProto(cx, wrappedProto);
Wrapper *wrapper;
CompartmentPrivate *targetdata = EnsureCompartmentPrivate(target);
@ -498,10 +484,10 @@ WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj,
DEBUG_CheckUnwrapSafety(obj, wrapper, origin, target);
if (existing && proxyProto == wrappedProto)
if (existing)
return Wrapper::Renew(cx, existing, obj, wrapper);
return Wrapper::New(cx, obj, proxyProto, parent, wrapper);
return Wrapper::New(cx, obj, parent, wrapper);
}
JSObject *
@ -549,10 +535,22 @@ WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp)
if (vp.isPrimitive())
return JS_WrapValue(cx, vp);
JSObject *obj = js::UncheckedUnwrap(&vp.toObject());
RootedObject obj(cx, &vp.toObject());
if (!WaiveXrayAndWrap(cx, &obj))
return false;
vp.setObject(*obj);
return true;
}
bool
WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleObject argObj)
{
MOZ_ASSERT(argObj);
RootedObject obj(cx, js::UncheckedUnwrap(argObj));
MOZ_ASSERT(!js::IsInnerObject(obj));
if (js::IsObjectInContextCompartment(obj, cx)) {
vp.setObject(*obj);
argObj.set(obj);
return true;
}
@ -570,24 +568,23 @@ WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleValue vp)
if (!obj)
return false;
vp.setObject(*obj);
return JS_WrapValue(cx, vp);
if (!JS_WrapObject(cx, &obj))
return false;
argObj.set(obj);
return true;
}
JSObject *
WrapperFactory::WrapSOWObject(JSContext *cx, JSObject *objArg)
{
RootedObject obj(cx, objArg);
RootedObject proto(cx);
// If we're not allowing XBL scopes, that means we're running as a remote
// XUL domain, in which we can't have SOWs. We should never be called in
// that case.
MOZ_ASSERT(xpc::AllowXBLScope(js::GetContextCompartment(cx)));
if (!JS_GetPrototype(cx, obj, &proto))
return nullptr;
JSObject *wrapperObj =
Wrapper::New(cx, obj, proto, JS_GetGlobalForObject(cx, obj),
Wrapper::New(cx, obj, JS_GetGlobalForObject(cx, obj),
&FilteringWrapper<SameCompartmentSecurityWrapper,
Opaque>::singleton);
return wrapperObj;
@ -603,11 +600,8 @@ WrapperFactory::IsComponentsObject(JSObject *obj)
JSObject *
WrapperFactory::WrapComponentsObject(JSContext *cx, HandleObject obj)
{
RootedObject proto(cx);
if (!JS_GetPrototype(cx, obj, &proto))
return nullptr;
JSObject *wrapperObj =
Wrapper::New(cx, obj, proto, JS_GetGlobalForObject(cx, obj),
Wrapper::New(cx, obj, JS_GetGlobalForObject(cx, obj),
&FilteringWrapper<SameCompartmentSecurityWrapper, ComponentsObjectPolicy>::singleton);
return wrapperObj;

View File

@ -64,6 +64,7 @@ class WrapperFactory {
// Wrap wrapped object into a waiver wrapper and then re-wrap it.
static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleValue vp);
static bool WaiveXrayAndWrap(JSContext *cx, JS::MutableHandleObject object);
// Wrap a (same compartment) object in a SOW.
static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj);

View File

@ -304,6 +304,7 @@ enum ExpandoSlots {
JSSLOT_EXPANDO_NEXT = 0,
JSSLOT_EXPANDO_ORIGIN,
JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
JSSLOT_EXPANDO_PROTOTYPE,
JSSLOT_EXPANDO_COUNT
};
@ -1792,6 +1793,56 @@ XrayWrapper<Base, Traits>::defaultValue(JSContext *cx, HandleObject wrapper,
return js::DefaultValue(cx, wrapper, hint, vp);
}
template <typename Base, typename Traits>
bool
XrayWrapper<Base, Traits>::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
JS::MutableHandleObject protop)
{
RootedObject target(cx, Traits::getTargetObject(wrapper));
RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper));
// We want to keep the Xray's prototype distinct from that of content, but only
// if there's been a set. If there's not an expando, or the expando slot is |undefined|,
// hand back content's proto, appropriately wrapped.
//
// NB: Our baseclass's getPrototypeOf() will appropriately wrap its return value, so there is
// no need for us to.
if (!expando)
return Base::getPrototypeOf(cx, wrapper, protop);
RootedValue v(cx);
{
JSAutoCompartment ac(cx, expando);
v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE);
}
if (v.isUndefined())
return Base::getPrototypeOf(cx, wrapper, protop);
protop.set(v.toObjectOrNull());
return JS_WrapObject(cx, protop);
}
template <typename Base, typename Traits>
bool
XrayWrapper<Base, Traits>::setPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
JS::HandleObject proto, bool *bp)
{
RootedObject target(cx, Traits::getTargetObject(wrapper));
RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target));
// The expando lives in the target's compartment, so do our installation there.
JSAutoCompartment ac(cx, target);
RootedValue v(cx, ObjectValue(*proto));
if (!JS_WrapValue(cx, &v))
return false;
JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v);
*bp = true;
return true;
}
/*
* The Permissive / Security variants should be used depending on whether the

View File

@ -106,6 +106,11 @@ class XrayWrapper : public Base {
JSType hint, JS::MutableHandleValue vp)
MOZ_OVERRIDE;
virtual bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
JS::MutableHandleObject protop) MOZ_OVERRIDE;
virtual bool setPrototypeOf(JSContext *cx, JS::HandleObject wrapper,
JS::HandleObject proto, bool *bp) MOZ_OVERRIDE;
static XrayWrapper singleton;
private: