This commit is contained in:
Blake Kaplan 2011-02-11 16:17:25 -08:00
Родитель caad5989c1
Коммит 0857373fac
3 изменённых файлов: 136 добавлений и 3 удалений

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -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,

Просмотреть файл

@ -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);