diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 629ef34a7d58..ce03cd762375 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1369,6 +1369,103 @@ JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target) 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) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index a598b04ad233..8e51d73ca850 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1009,6 +1009,13 @@ JS_WrapValue(JSContext *cx, jsval *vp); extern JS_PUBLIC_API(JSObject *) JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target); +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, diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index cb4e198fcdd7..6987556a0a60 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -1628,9 +1628,38 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, 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);