Bug 742444 - Pass the old wrapper or value to the prewrap callback instead of its flags. r=gabor
authorBobby Holley <bobbyholley@gmail.com>
Tue, 22 Jul 2014 16:14:27 -0700
changeset 195613 cd56605c08f6
parent 195612 77ba979e76af
child 195614 d1fa85777c40
push id27188
push usercbook@mozilla.com
push date2014-07-23 13:53 +0000
treeherdermozilla-central@785acfd2ae48 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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
Bug 742444 - Pass the old wrapper or value to the prewrap callback instead of its flags. r=gabor We need this so that we can reason about the origin of the wrapper that previously had a waiver and decide whether or not to extend it.
--- a/js/src/jsapi-tests/testBug604087.cpp
+++ b/js/src/jsapi-tests/testBug604087.cpp
@@ -34,25 +34,26 @@ wrap(JSContext *cx, JS::HandleObject toW
     JSAutoCompartment ac(cx, target);
     JS::RootedObject wrapper(cx, toWrap);
     if (!JS_WrapObject(cx, &wrapper))
         return nullptr;
     return wrapper;
 static JSObject *
-PreWrap(JSContext *cx, JS::HandleObject scope, JS::HandleObject obj, unsigned flags)
+PreWrap(JSContext *cx, JS::HandleObject scope, JS::HandleObject obj,
+        JS::HandleObject objectPassedToWrap)
     return obj;
 static JSObject *
 Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj,
-     JS::HandleObject parent, unsigned flags)
+     JS::HandleObject parent)
     return js::Wrapper::New(cx, obj, parent, &js::CrossCompartmentWrapper::singleton);
 static const JSWrapObjectCallbacks WrapObjectCallbacks = {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -754,26 +754,26 @@ typedef bool
  * destination compartment. |obj| is the object to be wrapped. If |existing| is
  * non-nullptr, it will point to an existing wrapper object that should be
  * re-used if possible. |existing| is guaranteed to be a cross-compartment
  * wrapper with a lazily-defined prototype and the correct global. It is
  * guaranteed not to wrap a function.
 typedef JSObject *
 (* JSWrapObjectCallback)(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj,
-                         JS::HandleObject parent, unsigned flags);
+                         JS::HandleObject parent);
  * 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, JS::HandleObject scope, JS::HandleObject obj,
-                      unsigned flags);
+                      JS::HandleObject objectPassedToWrap);
 struct JSWrapObjectCallbacks
     JSWrapObjectCallback wrap;
     JSPreWrapCallback preWrap;
 typedef void
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -389,18 +389,18 @@ JSCompartment::wrap(JSContext *cx, Mutab
     // If we have a cross-compartment wrapper, make sure that the cx isn't
     // associated with the self-hosting global. We don't want to create
     // wrappers for objects in other runtimes, which may be the case for the
     // self-hosting global.
     JS_ASSERT(!cx->runtime()->isSelfHostingGlobal(global) &&
     // Unwrap the object, but don't unwrap outer windows.
-    unsigned flags = 0;
-    obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true, &flags));
+    RootedObject objectPassedToWrap(cx, obj);
+    obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true));
     if (obj->compartment() == this) {
         MOZ_ASSERT(obj == GetOuterObject(cx, obj));
         return true;
     // Translate StopIteration singleton.
     if (obj->is<StopIterationObject>()) {
@@ -412,17 +412,17 @@ JSCompartment::wrap(JSContext *cx, Mutab
         return true;
     // Invoke the prewrap callback. We're a bit worried about infinite
     // recursion here, so we do a check - see bug 809295.
     JS_CHECK_CHROME_RECURSION(cx, return false);
     if (cb->preWrap) {
-        obj.set(cb->preWrap(cx, global, obj, flags));
+        obj.set(cb->preWrap(cx, global, obj, objectPassedToWrap));
         if (!obj)
             return false;
     MOZ_ASSERT(obj == GetOuterObject(cx, obj));
     if (obj->compartment() == this)
         return true;
@@ -444,17 +444,17 @@ JSCompartment::wrap(JSContext *cx, Mutab
             existing->getClass() != &ProxyObject::uncallableClass_ ||
             existing->getParent() != global ||
             existing = nullptr;
-    obj.set(cb->wrap(cx, existing, obj, global, flags));
+    obj.set(cb->wrap(cx, existing, obj, global));
     if (!obj)
         return false;
     // We maintain the invariant that the key in the cross-compartment wrapper
     // map is always directly wrapped by the value.
     JS_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject());
     return putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*obj));
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -140,17 +140,17 @@ Wrapper::~Wrapper()
 const Wrapper Wrapper::singleton((unsigned)0);
 const Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
 JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
 /* Compartments. */
 extern JSObject *
 js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
-                             HandleObject parent, unsigned flags)
+                             HandleObject parent)
     // Allow wrapping outer window proxies.
     JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
     return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton);
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -241,17 +241,17 @@ class JS_FRIEND_API(DeadObjectProxy) : p
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
                                 MutableHandleObject protop) const MOZ_OVERRIDE;
     static const DeadObjectProxy singleton;
 extern JSObject *
 TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
-                         HandleObject parent, unsigned flags);
+                         HandleObject parent);
 // Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by
 // jsfriendapi users.
 // This variable exists solely to provide a unique address for use as an identifier.
 extern JS_FRIEND_DATA(const char) sWrapperFamily;
 inline bool
 IsWrapper(JSObject *obj)
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -102,30 +102,16 @@ WrapperFactory::WaiveXray(JSContext *cx,
     JSObject *waiver = GetXrayWaiver(obj);
     if (waiver)
         return waiver;
     return CreateXrayWaiver(cx, obj);
-// DoubleWrap is called from PrepareForWrapping to maintain the state that
-// we're supposed to waive Xray wrappers for the given on. On entrance, it
-// expects |cx->compartment != obj->compartment()|. The returned object will
-// be in the same compartment as |obj|.
-JSObject *
-WrapperFactory::DoubleWrap(JSContext *cx, HandleObject obj, unsigned flags)
-    if (flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG) {
-        JSAutoCompartment ac(cx, obj);
-        return WaiveXray(cx, obj);
-    }
-    return obj;
 // In general, we're trying to deprecate COWs incrementally as we introduce
 // Xrays to the corresponding object types. But switching off COWs for certain
 // things would be too tumultuous at present, so we punt on them for later.
 static bool
 ForceCOWBehavior(JSObject *obj)
     JSProtoKey key = IdentifyStandardInstanceOrPrototype(obj);
     if (key == JSProto_Object || key == JSProto_Array || key == JSProto_Function) {
@@ -139,18 +125,19 @@ ForceCOWBehavior(JSObject *obj)
     if (key == JSProto_Proxy)
         return true;
     return false;
 JSObject *
 WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
-                                   HandleObject objArg, unsigned flags)
+                                   HandleObject objArg, HandleObject objectPassedToWrap)
+    bool waive = WrapperFactory::HasWaiveXrayFlag(objectPassedToWrap);
     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
@@ -158,17 +145,17 @@ WrapperFactory::PrepareForWrapping(JSCon
         obj = js::UncheckedUnwrap(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);
+        return waive ? WaiveXray(cx, obj) : obj;
     // Here are the rules for wrapping:
     // We should never get a proxy here (the JS engine unwraps those for us).
     // If the object being wrapped is a prototype for a standard class and the
     // wrapper does not subsumes the wrappee, use the one from the content
     // compartment. This is generally safer all-around, and in the COW case this
@@ -209,17 +196,17 @@ WrapperFactory::PrepareForWrapping(JSCon
     // 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_REFLECTOR(obj) || !js::GetObjectParent(obj))
-        return DoubleWrap(cx, obj, flags);
+        return waive ? WaiveXray(cx, obj) : obj;
     XPCWrappedNative *wn = XPCWrappedNative::Get(obj);
     JSAutoCompartment ac(cx, obj);
     XPCCallContext ccx(JS_CALLER, cx, obj);
     RootedObject wrapScope(cx, scope);
@@ -228,24 +215,24 @@ WrapperFactory::PrepareForWrapping(JSCon
             // 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));
+            NS_ENSURE_SUCCESS(rv, waive ? WaiveXray(cx, obj) : 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 (js::GetObjectCompartment(scope) != js::GetObjectCompartment(wrapScope))
-                return DoubleWrap(cx, obj, flags);
+                return waive ? WaiveXray(cx, obj) : obj;
             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),
@@ -266,17 +253,17 @@ WrapperFactory::PrepareForWrapping(JSCon
                 // 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);
+                    return waive ? WaiveXray(cx, obj) : obj;
                 // 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.
@@ -288,17 +275,17 @@ WrapperFactory::PrepareForWrapping(JSCon
             // 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)) &&
-                return DoubleWrap(cx, obj, flags);
+                return waive ? WaiveXray(cx, obj) : obj;
     // This public WrapNativeToJSVal API enters the compartment of 'wrapScope'
     // so we don't have to.
     RootedValue v(cx);
     nsresult rv =
@@ -320,17 +307,17 @@ WrapperFactory::PrepareForWrapping(JSCon
     // to do this cleverly in the common case to avoid too much overhead.
     XPCWrappedNative *newwn = XPCWrappedNative::Get(obj);
     XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(newwn->GetSet(),
                                                         wn->GetSet(), false);
     if (!unionSet)
         return nullptr;
-    return DoubleWrap(cx, obj, flags);
+    return waive ? WaiveXray(cx, obj) : obj;
 #ifdef DEBUG
 static void
 DEBUG_CheckUnwrapSafety(HandleObject obj, const js::Wrapper *handler,
                         JSCompartment *origin, JSCompartment *target)
     if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
@@ -425,17 +412,17 @@ SelectAddonWrapper(JSContext *cx, Handle
         return &AddonWrapper<PermissiveXrayDOM>::singleton;
     // |wrapper| is not supported for interposition, so we don't do it.
     return wrapper;
 JSObject *
 WrapperFactory::Rewrap(JSContext *cx, HandleObject existing, HandleObject obj,
-                       HandleObject parent, unsigned flags)
+                       HandleObject parent)
     MOZ_ASSERT(!IsWrapper(obj) ||
                GetProxyHandler(obj) == &XrayWaiver ||
                "wrapped object passed to rewrap");
     MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder");
     // We sometimes end up here after nsContentUtils has been shut down but before
@@ -446,17 +433,16 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
     JSCompartment *origin = js::GetObjectCompartment(obj);
     JSCompartment *target = js::GetContextCompartment(cx);
     bool originIsChrome = AccessCheck::isChrome(origin);
     bool targetIsChrome = AccessCheck::isChrome(target);
     bool originSubsumesTarget = AccessCheck::subsumesConsideringDomain(origin, target);
     bool targetSubsumesOrigin = AccessCheck::subsumesConsideringDomain(target, origin);
     bool sameOrigin = targetSubsumesOrigin && originSubsumesTarget;
     XrayType xrayType = GetXrayType(obj);
-    bool waiveXrayFlag = flags & WAIVE_XRAY_WRAPPER_FLAG;
     const Wrapper *wrapper;
     // First, handle the special cases.
     // If UniversalXPConnect is enabled, this is just some dumb mochitest. Use
@@ -502,17 +488,17 @@ WrapperFactory::Rewrap(JSContext *cx, Ha
         // Xrays are a bidirectional protection, since it affords clarity to the
         // caller and privacy to the callee.
         bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays ||
         bool wantXrays = !sameOrigin || sameOriginXrays;
         // If Xrays are warranted, the caller may waive them for non-security
         // wrappers.
-        bool waiveXrays = wantXrays && !securityWrapper && waiveXrayFlag;
+        bool waiveXrays = wantXrays && !securityWrapper && HasWaiveXrayFlag(obj);
         // We have slightly different behavior for the case when the object
         // being wrapped is in an XBL scope.
         bool originIsContentXBLScope = IsContentXBLScope(origin);
         wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays,
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -36,30 +36,27 @@ class WrapperFactory {
     static bool IsCOW(JSObject *wrapper);
     static JSObject *GetXrayWaiver(JS::HandleObject obj);
     static JSObject *CreateXrayWaiver(JSContext *cx, JS::HandleObject obj);
     static JSObject *WaiveXray(JSContext *cx, JSObject *obj);
-    static JSObject *DoubleWrap(JSContext *cx, JS::HandleObject obj, unsigned flags);
     // Prepare a given object for wrapping in a new compartment.
     static JSObject *PrepareForWrapping(JSContext *cx,
                                         JS::HandleObject scope,
                                         JS::HandleObject obj,
-                                        unsigned flags);
+                                        JS::HandleObject objectPassedToWrap);
     // Rewrap an object that is about to cross compartment boundaries.
     static JSObject *Rewrap(JSContext *cx,
                             JS::HandleObject existing,
                             JS::HandleObject obj,
-                            JS::HandleObject parent,
-                            unsigned flags);
+                            JS::HandleObject parent);
     // 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);
     // Returns true if the wrapper is in not shadowing mode for the id.
     static bool XrayWrapperNotShadowing(JSObject *wrapper, jsid id);