Fixing bug 623810. r=jst@mozilla.org, gal@uci.edu, a=blocker. CLOSED TREE
authorBlake Kaplan <mrbkap@gmail.com>
Fri, 11 Feb 2011 16:17:25 -0800
changeset 62975 42e7f9088975
parent 62974 afab01be11a8
child 62976 d322a1c8537e
child 62979 67ee5b40edd5
push id18970
push userjst@mozilla.com
push dateWed, 23 Feb 2011 02:07:00 +0000
treeherdermozilla-central@42e7f9088975 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, blocker
bugs623810
milestone2.0b12pre
first release with
nightly linux32
42e7f9088975 / 4.0b12pre / 20110222191702 / files
nightly linux64
42e7f9088975 / 4.0b12pre / 20110222191821 / files
nightly mac
42e7f9088975 / 4.0b12pre / 20110222191823 / files
nightly win32
42e7f9088975 / 4.0b12pre / 20110222191848 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Fixing bug 623810. r=jst@mozilla.org, gal@uci.edu, a=blocker. CLOSED TREE
js/src/jsapi.cpp
js/src/jsapi.h
js/src/xpconnect/src/xpcwrappednative.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1364,16 +1364,113 @@ JS_TransplantObject(JSContext *cx, JSObj
         if (!origobj->swap(cx, tobj))
             return NULL;
         origobj->getCompartment()->crossCompartmentWrappers.put(targetv, origv);
     }
 
     return obj;
 }
 
+/*
+ * The location object is special. There is the location object itself and
+ * then the location object wrapper. Because there are no direct references to
+ * the location object itself, we don't want the old obj (|origobj| here) to
+ * become the new wrapper but the wrapper itself instead. This leads to very
+ * subtle differences between js_TransplantObjectWithWrapper and
+ * JS_TransplantObject.
+ */
+JS_FRIEND_API(JSObject *)
+js_TransplantObjectWithWrapper(JSContext *cx,
+                               JSObject *origobj,
+                               JSObject *origwrapper,
+                               JSObject *targetobj,
+                               JSObject *targetwrapper)
+{
+    JSObject *obj;
+    JSCompartment *destination = targetobj->getCompartment();
+    WrapperMap &map = destination->crossCompartmentWrappers;
+
+    // |origv| is the map entry we're looking up. The map entries are going to
+    // be for the location object itself.
+    Value origv = ObjectValue(*origobj);
+
+    // There might already be a wrapper for the original object in the new
+    // compartment.
+    if (WrapperMap::Ptr p = map.lookup(origv)) {
+        // There is. Make the existing wrapper a same compartment location
+        // wrapper (swapping it with the given new wrapper).
+        obj = &p->value.toObject();
+        map.remove(p);
+        if (!obj->swap(cx, targetwrapper))
+            return NULL;
+    } else {
+        // Otherwise, use the passed-in wrapper as the same compartment
+        // location wrapper.
+        obj = targetwrapper;
+    }
+
+    // Now, iterate through other scopes looking for references to the old
+    // location object. Note that the entries in the maps are for |origobj|
+    // and not |origwrapper|. They need to be updated to point at the new
+    // location object.
+    Value targetv = ObjectValue(*targetobj);
+    WrapperVector &vector = cx->runtime->compartments;
+    AutoValueVector toTransplant(cx);
+    toTransplant.reserve(vector.length());
+
+    for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) {
+        WrapperMap &pmap = (*p)->crossCompartmentWrappers;
+        if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
+            // We found a wrapper. Remember and root it.
+            toTransplant.append(wp->value);
+        }
+    }
+
+    for (Value *begin = toTransplant.begin(), *end = toTransplant.end(); begin != end; ++begin) {
+        JSObject *wobj = &begin->toObject();
+        JSCompartment *wcompartment = wobj->compartment();
+        WrapperMap &pmap = wcompartment->crossCompartmentWrappers;
+        JS_ASSERT(pmap.lookup(origv));
+        pmap.remove(origv);
+
+        // First, we wrap it in the new compartment. This will return a
+        // new wrapper.
+        AutoCompartment ac(cx, wobj);
+
+        JSObject *tobj = targetobj;
+        if (!ac.enter() || !wcompartment->wrap(cx, &tobj))
+            return NULL;
+
+        // Now, because we need to maintain object identity, we do a brain
+        // transplant on the old object. At the same time, we update the
+        // entry in the compartment's wrapper map to point to the old
+        // wrapper.
+        JS_ASSERT(tobj != wobj);
+        if (!wobj->swap(cx, tobj))
+            return NULL;
+        pmap.put(targetv, ObjectValue(*wobj));
+    }
+
+    // Lastly, update the original object to point to the new one. However, as
+    // mentioned above, we do the transplant on the wrapper, not the object
+    // itself, since all of the references are to the object itself.
+    {
+        AutoCompartment ac(cx, origobj);
+        JSObject *tobj = obj;
+        if (!ac.enter() || !JS_WrapObject(cx, &tobj))
+            return NULL;
+        if (!origwrapper->swap(cx, tobj))
+            return NULL;
+        origwrapper->getCompartment()->crossCompartmentWrappers.put(targetv,
+                                                                    ObjectValue(*origwrapper));
+    }
+
+    return obj;
+}
+
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalObject(JSContext *cx)
 {
     return cx->globalObject;
 }
 
 JS_PUBLIC_API(void)
 JS_SetGlobalObject(JSContext *cx, JSObject *obj)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1011,16 +1011,23 @@ JS_TransplantObject(JSContext *cx, JSObj
 
 extern JS_FRIEND_API(JSObject *)
 js_TransplantObjectWithWrapper(JSContext *cx,
                                JSObject *origobj,
                                JSObject *origwrapper,
                                JSObject *targetobj,
                                JSObject *targetwrapper);
 
+extern JS_FRIEND_API(JSObject *)
+js_TransplantObjectWithWrapper(JSContext *cx,
+                               JSObject *origobj,
+                               JSObject *origwrapper,
+                               JSObject *targetobj,
+                               JSObject *targetwrapper);
+
 #ifdef __cplusplus
 JS_END_EXTERN_C
 
 class JS_PUBLIC_API(JSAutoEnterCompartment)
 {
     JSCrossCompartmentCall *call;
 
   public:
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -1623,19 +1623,48 @@ XPCWrappedNative::ReparentWrapperIfFound
 
                 JS_SetPrivate(ccx, flat, nsnull);
 
                 JSObject *propertyHolder =
                     JS_NewObjectWithGivenProto(ccx, NULL, NULL, aNewParent);
                 if(!propertyHolder || !propertyHolder->copyPropertiesFrom(ccx, flat))
                     return NS_ERROR_OUT_OF_MEMORY;
 
-                flat = JS_TransplantObject(ccx, flat, newobj);
-                if(!flat)
-                    return NS_ERROR_FAILURE;
+                JSObject *ww = wrapper->GetWrapper();
+                if(ww)
+                {
+                    JSObject *newwrapper;
+                    if(xpc::WrapperFactory::IsLocationObject(flat))
+                    {
+                        newwrapper = xpc::WrapperFactory::WrapLocationObject(ccx, newobj);
+                        if(!newwrapper)
+                            return NS_ERROR_FAILURE;
+                    }
+                    else
+                    {
+                        NS_ASSERTION(wrapper->NeedsSOW(), "weird wrapper wrapper");
+                        newwrapper = xpc::WrapperFactory::WrapSOWObject(ccx, newobj);
+                        if(!newwrapper)
+                            return NS_ERROR_FAILURE;
+                    }
+
+                    ww = js_TransplantObjectWithWrapper(ccx, flat, ww, newobj,
+                                                        newwrapper);
+                    if(!ww)
+                        return NS_ERROR_FAILURE;
+                    flat = newobj;
+                    wrapper->SetWrapper(ww);
+                }
+                else
+                {
+                    flat = JS_TransplantObject(ccx, flat, newobj);
+                    if(!flat)
+                        return NS_ERROR_FAILURE;
+                }
+
                 wrapper->mFlatJSObject = flat;
                 if(cache)
                     cache->SetWrapper(flat);
                 if (!flat->copyPropertiesFrom(ccx, propertyHolder))
                     return NS_ERROR_FAILURE;
             }
             else
             {