author | Eric Faust <efaustbmo@gmail.com> |
Fri, 13 Dec 2013 12:01:30 -0800 | |
changeset 177473 | ae50af3377666ac4f78992828e5ea38590fa2b29 |
parent 177472 | 8ba79063973d486a5f32d7f8bb67a22523399705 |
child 177474 | ac58cfd40672895c6c9dcd28e5d65f00cccde6b7 |
push id | 462 |
push user | raliiev@mozilla.com |
push date | Tue, 22 Apr 2014 00:22:30 +0000 |
treeherder | mozilla-release@ac5db8c74ac0 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bholley |
bugs | 926012 |
milestone | 29.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1006,21 +1006,17 @@ nsChromeOuterWindowProxy::className(JSCo nsChromeOuterWindowProxy nsChromeOuterWindowProxy::singleton; 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); NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class"); return obj; } //***************************************************************************** @@ -2098,36 +2094,21 @@ nsGlobalWindow::CreateOuterObject(nsGlob JS::Rooted<JSObject*> global(cx, aNewInner->FastGetGlobalJSObject()); JS::Rooted<JSObject*> outer(cx, NewOuterWindowProxy(cx, global, IsChromeWindow())); if (!outer) { return NS_ERROR_FAILURE; } 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; } // We need certain special behavior for remote XUL whitelisted domains, but we // don't want that behavior to take effect in automation, because we whitelist // all the mochitest domains. So we need to check a pref here. static bool TreatAsRemoteXUL(nsIPrincipal* aPrincipal) @@ -2452,18 +2433,19 @@ nsGlobalWindow::SetNewDocument(nsIDocume SetWrapper(mJSObject); { JSAutoCompartment ac(cx, mJSObject); 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!"); XPCWrappedNativeScope* scope = xpc::GetObjectScope(mJSObject); if (scope->mWaiverWrapperMap) { scope->mWaiverWrapperMap->Reparent(cx, newInnerWindow->mJSObject); } }
--- a/js/src/jsapi-tests/testBug604087.cpp +++ b/js/src/jsapi-tests/testBug604087.cpp @@ -55,23 +55,22 @@ PreWrap(JSContext *cx, JS::HandleObject JS_GC(JS_GetRuntime(cx)); return obj; } 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)); JS::RootedObject c2wrapper(cx, wrap(cx, outerObj, compartment2)); CHECK(c2wrapper); c2wrapper->as<js::ProxyObject>().setExtra(0, js::Int32Value(2)); @@ -82,18 +81,17 @@ BEGIN_TEST(testBug604087) JS::RootedObject c4wrapper(cx, wrap(cx, outerObj, compartment4)); CHECK(c4wrapper); c4wrapper->as<js::ProxyObject>().setExtra(0, js::Int32Value(4)); compartment4 = c4wrapper = nullptr; 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); } JS_SetWrapObjectCallbacks(JS_GetRuntime(cx), Wrap, SameCompartmentWrap, PreWrap); CHECK(JS_TransplantObject(cx, outerObj, next)); return true; } END_TEST(testBug604087)
--- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -511,16 +511,29 @@ DirectProxyHandler::hasInstance(JSContex bool b; RootedObject target(cx, proxy->as<ProxyObject>().target()); if (!JS_HasInstance(cx, target, v, &b)) return false; *bp = !!b; 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, JSContext *cx) { RootedObject target(cx, proxy->as<ProxyObject>().target()); return ObjectClassIs(target, classValue, cx); }
--- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -247,16 +247,18 @@ class JS_PUBLIC_API(DirectProxyHandler) /* Spidermonkey extensions. */ virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE; virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, 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; virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE; virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; virtual JSObject *weakmapKeyDelegate(JSObject *proxy);
--- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -34,26 +34,26 @@ Wrapper::defaultValue(JSContext *cx, Han { vp.set(ObjectValue(*proxy->as<ProxyObject>().target())); if (hint == JSTYPE_VOID) return ToPrimitive(cx, vp); return ToPrimitive(cx, hint, vp); } 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); AutoMarkInDeadZone amd(cx->zone()); 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 * Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler) { JS_ASSERT(!obj->isCallable()); existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj)); return existing; @@ -136,17 +136,18 @@ Wrapper Wrapper::singletonWithPrototype( extern JSObject * js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj, HandleObject wrappedProto, HandleObject parent, unsigned flags) { // 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() { JSContext *cx = ac.ref().context()->asJSContext(); if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) { RootedValue exc(cx, cx->getPendingException()); if (exc.isObject() && exc.toObject().is<ErrorObject>()) { @@ -573,33 +574,39 @@ CrossCompartmentWrapper::defaultValue(JS Wrapper::defaultValue(cx, wrapper, hint, vp), cx->compartment()->wrap(cx, vp)); } 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); if (!JSObject::getProto(cx, wrapped, protop)) return false; if (protop) protop->setDelegate(cx); } 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. */ template <class Base> SecurityWrapper<Base>::SecurityWrapper(unsigned flags) : Base(flags) { @@ -641,16 +648,25 @@ template <class Base> bool SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED); 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. template <class Base> bool SecurityWrapper<Base>::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, MutableHandleValue vp) {
--- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -44,18 +44,17 @@ class JS_FRIEND_API(Wrapper) : public Di * Wrappers can explicitly specify that they are unsafe to unwrap from a * security perspective (as is the case for SecurityWrappers). If a wrapper * is not safe to unwrap, operations requiring full access to the underlying * object (via CheckedUnwrap) will throw. Otherwise, they will succeed. */ 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); static Wrapper *wrapperHandler(JSObject *wrapper); static JSObject *wrappedObject(JSObject *wrapper); unsigned flags() const { @@ -115,17 +114,20 @@ class JS_FRIEND_API(CrossCompartmentWrap virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, bool *bp) MOZ_OVERRIDE; virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; virtual JSString *fun_toString(JSContext *cx, HandleObject wrapper, unsigned indent) MOZ_OVERRIDE; 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; }; /* * Base class for security wrappers. A security wrapper is potentially hiding * all or part of some wrapped object thus SecurityWrapper defaults to denying @@ -150,16 +152,19 @@ class JS_FRIEND_API(SecurityWrapper) : p virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, MutableHandleValue vp) MOZ_OVERRIDE; virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; 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; /* * Allow our subclasses to select the superclass behavior they want without * needing to specify an exact superclass. */
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3738,30 +3738,55 @@ ThisFilename(JSContext *cx, unsigned arg } JSString *filename = JS_NewStringCopyZ(cx, script->filename()); if (!filename) return false; args.rval().setString(filename); 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) { jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : UndefinedValue(); if (JSVAL_IS_PRIMITIVE(v)) { JS_SET_RVAL(cx, vp, v); return true; } 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; JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped)); return true; } @@ -3774,19 +3799,19 @@ WrapWithProto(JSContext *cx, unsigned ar proto = JS_ARGV(cx, vp)[1]; } if (!obj.isObject() || !proto.isObjectOrNull()) { JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "wrapWithProto"); 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; JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped)); return true; } static JSObject *
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp @@ -86,9 +86,16 @@ WaiveXrayWrapper::construct(JSContext *c bool WaiveXrayWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl, JS::CallArgs args) { return CrossCompartmentWrapper::nativeCall(cx, test, impl, args) && 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)); } + +}
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.h +++ b/js/xpconnect/wrappers/WaiveXrayWrapper.h @@ -33,14 +33,17 @@ class WaiveXrayWrapper : public js::Cros virtual bool call(JSContext *cx, JS::Handle<JSObject*> wrapper, const JS::CallArgs &args) MOZ_OVERRIDE; virtual bool construct(JSContext *cx, JS::Handle<JSObject*> wrapper, const JS::CallArgs &args) MOZ_OVERRIDE; 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; }; } #endif
--- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -70,28 +70,18 @@ WrapperFactory::GetXrayWaiver(HandleObje JSObject * WrapperFactory::CreateXrayWaiver(JSContext *cx, HandleObject obj) { // The caller is required to have already done a lookup. // NB: This implictly performs the assertions of GetXrayWaiver. 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) return nullptr; // Add the new waiver to the map. It's important that we only ever have // one waiver for the lifetime of the target object. if (!scope->mWaiverWrapperMap) { @@ -404,20 +394,16 @@ WrapperFactory::Rewrap(JSContext *cx, Ha bool originIsChrome = AccessCheck::isChrome(origin); bool targetIsChrome = AccessCheck::isChrome(target); bool originSubsumesTarget = AccessCheck::subsumes(origin, target); bool targetSubsumesOrigin = AccessCheck::subsumes(target, origin); bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget; 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); // // First, handle the special cases. // // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use @@ -493,20 +479,20 @@ WrapperFactory::Rewrap(JSContext *cx, Ha JS_ReportError(cx, "Not allowed to access chrome eval or Function from content"); return nullptr; } } } 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 * WrapperFactory::WrapForSameCompartment(JSContext *cx, HandleObject objArg) { RootedObject obj(cx, objArg); MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx)); @@ -544,20 +530,32 @@ WrapperFactory::WrapForSameCompartment(J // 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, 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; } // Even though waivers have no effect on access by scopes that don't subsume // the underlying object, good defense-in-depth dictates that we should avoid // handing out waivers to callers that can't use them. The transitive waiving // machinery unconditionally calls WaiveXrayAndWrap on return values from // waived functions, even though the return value might be not be same-origin @@ -565,54 +563,50 @@ WrapperFactory::WaiveXrayAndWrap(JSConte // |cx|, we should check whether the caller has any business with waivers // to things in |obj|'s compartment. JSCompartment *target = js::GetContextCompartment(cx); JSCompartment *origin = js::GetObjectCompartment(obj); obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj; 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; } bool WrapperFactory::IsComponentsObject(JSObject *obj) { const char *name = js::GetObjectClass(obj)->name; return name[0] == 'n' && !strcmp(name, "nsXPCComponents"); } 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; } bool WrapperFactory::XrayWrapperNotShadowing(JSObject *wrapper, jsid id) {
--- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -59,16 +59,17 @@ class WrapperFactory { unsigned flags); // Wrap an object for same-compartment access. static JSObject *WrapForSameCompartment(JSContext *cx, JS::HandleObject obj); // 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); // Return true if this is a Components object. static bool IsComponentsObject(JSObject *obj); // Wrap a (same compartment) Components object.
--- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -299,16 +299,17 @@ GetXrayTraits(JSObject *obj) * not imply that code in the target compartment should be allowed to inspect * them. They are private to the origin that placed them. */ enum ExpandoSlots { JSSLOT_EXPANDO_NEXT = 0, JSSLOT_EXPANDO_ORIGIN, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, + JSSLOT_EXPANDO_PROTOTYPE, JSSLOT_EXPANDO_COUNT }; static nsIPrincipal* ObjectPrincipal(JSObject *obj) { return GetCompartmentPrincipal(js::GetObjectCompartment(obj)); } @@ -1787,16 +1788,66 @@ XrayWrapper<Base, Traits>::defaultValue( // Even if this isn't a security wrapper, Xray semantics dictate that we // run the DefaultValue algorithm directly on the Xray wrapper. // // NB: We don't have to worry about things with special [[DefaultValue]] // behavior like Date because we'll never have an XrayWrapper to them. 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 * compartment of the wrapper is guranteed to subsume the compartment of the * wrapped object (i.e. - whether it is safe from a security perspective to * unwrap the wrapper). */
--- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -101,16 +101,21 @@ class XrayWrapper : public Base { const JS::CallArgs &args) MOZ_OVERRIDE; virtual bool construct(JSContext *cx, JS::Handle<JSObject*> wrapper, const JS::CallArgs &args) MOZ_OVERRIDE; virtual bool defaultValue(JSContext *cx, JS::HandleObject wrapper, 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: bool enumerate(JSContext *cx, JS::Handle<JSObject*> wrapper, unsigned flags, JS::AutoIdVector &props); }; #define PermissiveXrayXPCWN xpc::XrayWrapper<js::CrossCompartmentWrapper, xpc::XPCWrappedNativeXrayTraits>