mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 601803 - Support adopting a node cross-compartment. r=gal/jst
This commit is contained in:
parent
9ee8ffad9f
commit
6deeefba3d
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 sw=2 et tw=99: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
@ -64,6 +65,7 @@
|
||||
#include "nsImageLoadingContent.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsgc.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
@ -461,6 +463,11 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
|
||||
// attributes and children).
|
||||
|
||||
nsresult rv;
|
||||
if (aCx) {
|
||||
rv = xpc_MorphSlimWrapper(aCx, aNode);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsNodeInfoManager *nodeInfoManager = aNewNodeInfoManager;
|
||||
|
||||
// aNode.
|
||||
@ -516,11 +523,6 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
|
||||
}
|
||||
}
|
||||
else if (nodeInfoManager) {
|
||||
// FIXME Bug 601803 Need to support adopting a node cross-compartment
|
||||
if (aCx && aOldScope->compartment() != aNewScope->compartment()) {
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
}
|
||||
|
||||
nsIDocument* oldDoc = aNode->GetOwnerDoc();
|
||||
PRBool wasRegistered = PR_FALSE;
|
||||
if (oldDoc && aNode->IsElement()) {
|
||||
@ -583,9 +585,28 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep,
|
||||
if (aCx) {
|
||||
nsIXPConnect *xpc = nsContentUtils::XPConnect();
|
||||
if (xpc) {
|
||||
nsWrapperCache *cache;
|
||||
CallQueryInterface(aNode, &cache);
|
||||
JSObject *preservedWrapper = nsnull;
|
||||
|
||||
// If reparenting moves us to a new compartment, preserving causes
|
||||
// problems. In that case, we release ourselves and re-preserve after
|
||||
// reparenting so we're sure to have the right JS object preserved.
|
||||
// We use a JSObject stack copy of the wrapper to protect it from GC
|
||||
// under ReparentWrappedNativeIfFound.
|
||||
if (cache && cache->PreservingWrapper()) {
|
||||
preservedWrapper = cache->GetWrapper();
|
||||
nsContentUtils::ReleaseWrapper(aNode, cache);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
|
||||
rv = xpc->ReparentWrappedNativeIfFound(aCx, aOldScope, aNewScope, aNode,
|
||||
getter_AddRefs(oldWrapper));
|
||||
|
||||
if (preservedWrapper) {
|
||||
nsContentUtils::PreserveWrapper(aNode, cache);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
aNode->mNodeInfo.swap(nodeInfo);
|
||||
|
||||
|
@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=601803
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.onmessage = function (event) {
|
||||
todo(event.data == "false", "Shouldn't throw when adopting a node cross-compartment");
|
||||
is(event.data, "false", "Shouldn't throw when adopting a node cross-compartment");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -2050,7 +2050,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
outerObject = JS_TransplantWrapper(cx, mJSObject, outerObject);
|
||||
outerObject = JS_TransplantObject(cx, mJSObject, outerObject);
|
||||
if (!outerObject) {
|
||||
NS_ERROR("unable to transplant wrappers, probably OOM");
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -344,3 +344,4 @@ MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE, 261, 0, JSEXN_TYPEERR, "unsupported type f
|
||||
MSG_DEF(JSMSG_SC_RECURSION, 262, 0, JSEXN_INTERNALERR, "recursive object")
|
||||
MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT, 263, 0, JSEXN_TYPEERR, "can't wrap XML objects")
|
||||
MSG_DEF(JSMSG_BAD_CLONE_VERSION, 264, 0, JSEXN_ERR, "unsupported structured clone version")
|
||||
MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 265, 0, JSEXN_TYPEERR, "can't clone object")
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=99:
|
||||
*
|
||||
* Tests JS_TransplantWrappers
|
||||
* Tests JS_TransplantObject
|
||||
*/
|
||||
|
||||
#include "tests.h"
|
||||
@ -78,7 +78,7 @@ BEGIN_TEST(testBug604087)
|
||||
}
|
||||
|
||||
JS_SetWrapObjectCallbacks(JS_GetRuntime(cx), Wrap, PreWrap);
|
||||
CHECK(JS_TransplantWrapper(cx, outerObj, next));
|
||||
CHECK(JS_TransplantObject(cx, outerObj, next));
|
||||
return true;
|
||||
}
|
||||
END_TEST(testBug604087)
|
||||
|
@ -1217,31 +1217,29 @@ JS_WrapValue(JSContext *cx, jsval *vp)
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target)
|
||||
JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target)
|
||||
{
|
||||
JS_ASSERT(wrapper->isWrapper());
|
||||
|
||||
/*
|
||||
* This function is called when a window is navigating. In that case, we
|
||||
* need to "move" the window from wrapper's compartment to target's
|
||||
* compartment.
|
||||
*/
|
||||
// This function is called when an object moves between two different
|
||||
// compartments. In that case, we need to "move" the window from origobj's
|
||||
// compartment to target's compartment.
|
||||
JSCompartment *destination = target->getCompartment();
|
||||
if (wrapper->getCompartment() == destination) {
|
||||
// If the wrapper is in the same compartment as the destination, then
|
||||
// we know that we won't find wrapper in the destination's cross
|
||||
// compartment map and that the same object will continue to work.
|
||||
if (!wrapper->swap(cx, target))
|
||||
if (origobj->getCompartment() == destination) {
|
||||
// If the original object is in the same compartment as the
|
||||
// destination, then we know that we won't find wrapper in the
|
||||
// destination's cross compartment map and that the same object
|
||||
// will continue to work.
|
||||
if (!origobj->swap(cx, target))
|
||||
return NULL;
|
||||
return wrapper;
|
||||
return origobj;
|
||||
}
|
||||
|
||||
JSObject *obj;
|
||||
WrapperMap &map = destination->crossCompartmentWrappers;
|
||||
Value wrapperv = ObjectValue(*wrapper);
|
||||
Value origv = ObjectValue(*origobj);
|
||||
|
||||
// There might already be a wrapper for the window in the new compartment.
|
||||
if (WrapperMap::Ptr p = map.lookup(wrapperv)) {
|
||||
// There might already be a wrapper for the original object in the new
|
||||
// compartment.
|
||||
if (WrapperMap::Ptr p = map.lookup(origv)) {
|
||||
// If there is, make it the primary outer window proxy around the
|
||||
// inner (accomplished by swapping target's innards with the old,
|
||||
// possibly security wrapper, innards).
|
||||
@ -1266,7 +1264,7 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target)
|
||||
|
||||
for (JSCompartment **p = vector.begin(), **end = vector.end(); p != end; ++p) {
|
||||
WrapperMap &pmap = (*p)->crossCompartmentWrappers;
|
||||
if (WrapperMap::Ptr wp = pmap.lookup(wrapperv)) {
|
||||
if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
|
||||
// We found a wrapper. Remember and root it.
|
||||
toTransplant.append(wp->value);
|
||||
}
|
||||
@ -1276,8 +1274,8 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target)
|
||||
JSObject *wobj = &begin->toObject();
|
||||
JSCompartment *wcompartment = wobj->compartment();
|
||||
WrapperMap &pmap = wcompartment->crossCompartmentWrappers;
|
||||
JS_ASSERT(pmap.lookup(wrapperv));
|
||||
pmap.remove(wrapperv);
|
||||
JS_ASSERT(pmap.lookup(origv));
|
||||
pmap.remove(origv);
|
||||
|
||||
// First, we wrap it in the new compartment. This will return a
|
||||
// new wrapper.
|
||||
@ -1296,15 +1294,15 @@ JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target)
|
||||
pmap.put(targetv, ObjectValue(*wobj));
|
||||
}
|
||||
|
||||
// Lastly, update the old outer window proxy to point to the new one.
|
||||
// Lastly, update the original object to point to the new one.
|
||||
{
|
||||
AutoCompartment ac(cx, wrapper);
|
||||
AutoCompartment ac(cx, origobj);
|
||||
JSObject *tobj = obj;
|
||||
if (!ac.enter() || !JS_WrapObject(cx, &tobj))
|
||||
return NULL;
|
||||
if (!wrapper->swap(cx, tobj))
|
||||
if (!origobj->swap(cx, tobj))
|
||||
return NULL;
|
||||
wrapper->getCompartment()->crossCompartmentWrappers.put(targetv, wrapperv);
|
||||
origobj->getCompartment()->crossCompartmentWrappers.put(targetv, origv);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
@ -965,7 +965,7 @@ extern JS_PUBLIC_API(JSBool)
|
||||
JS_WrapValue(JSContext *cx, jsval *vp);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_TransplantWrapper(JSContext *cx, JSObject *wrapper, JSObject *target);
|
||||
JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target);
|
||||
|
||||
#ifdef __cplusplus
|
||||
JS_END_EXTERN_C
|
||||
|
207
js/src/jsobj.cpp
207
js/src/jsobj.cpp
@ -77,6 +77,7 @@
|
||||
#include "jstracer.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "json.h"
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
@ -3296,6 +3297,155 @@ GetObjectSize(JSObject *obj)
|
||||
: sizeof(JSObject) + sizeof(js::Value) * obj->numFixedSlots();
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::copyPropertiesFrom(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
// If we're not native, then we cannot copy properties.
|
||||
JS_ASSERT(isNative() == obj->isNative());
|
||||
if (!isNative())
|
||||
return true;
|
||||
|
||||
Vector<const Shape *> shapes(cx);
|
||||
for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
|
||||
if (!shapes.append(&r.front()))
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t n = shapes.length();
|
||||
while (n > 0) {
|
||||
const Shape *shape = shapes[--n];
|
||||
uintN attrs = shape->attributes();
|
||||
PropertyOp getter = shape->getter();
|
||||
if ((attrs & JSPROP_GETTER) && !cx->compartment->wrap(cx, &getter))
|
||||
return false;
|
||||
PropertyOp setter = shape->setter();
|
||||
if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter))
|
||||
return false;
|
||||
Value v = shape->hasSlot() ? obj->getSlot(shape->slot) : UndefinedValue();
|
||||
if (!cx->compartment->wrap(cx, &v))
|
||||
return false;
|
||||
if (!defineProperty(cx, shape->id, v, getter, setter, attrs))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CopySlots(JSContext *cx, JSObject *from, JSObject *to)
|
||||
{
|
||||
JS_ASSERT(!from->isNative() && !to->isNative());
|
||||
size_t nslots = from->numSlots();
|
||||
if (to->ensureSlots(cx, nslots))
|
||||
return false;
|
||||
|
||||
size_t n = 0;
|
||||
if (to->isWrapper() &&
|
||||
(JSWrapper::wrapperHandler(to)->flags() & JSWrapper::CROSS_COMPARTMENT)) {
|
||||
to->slots[0] = from->slots[0];
|
||||
to->slots[1] = from->slots[1];
|
||||
n = 2;
|
||||
}
|
||||
|
||||
for (; n < nslots; ++n) {
|
||||
Value v = from->slots[n];
|
||||
if (!cx->compartment->wrap(cx, &v))
|
||||
return false;
|
||||
to->slots[n] = v;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
|
||||
{
|
||||
/*
|
||||
* We can only clone native objects and proxies. Dense arrays are slowified if
|
||||
* we try to clone them.
|
||||
*/
|
||||
if (!isNative()) {
|
||||
if (isDenseArray()) {
|
||||
if (!makeDenseArraySlow(cx))
|
||||
return NULL;
|
||||
} else if (!isProxy()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_CANT_CLONE_OBJECT);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
JSObject *clone = NewObject<WithProto::Given>(cx, getClass(),
|
||||
proto, parent,
|
||||
gc::FinalizeKind(finalizeKind()));
|
||||
if (!clone)
|
||||
return NULL;
|
||||
if (isNative()) {
|
||||
if (clone->isFunction() && (compartment() != clone->compartment())) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_CANT_CLONE_OBJECT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (getClass()->flags & JSCLASS_HAS_PRIVATE)
|
||||
clone->setPrivate(getPrivate());
|
||||
} else {
|
||||
JS_ASSERT(isProxy());
|
||||
if (!CopySlots(cx, this, clone))
|
||||
return NULL;
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
static void
|
||||
TradeGuts(JSObject *a, JSObject *b)
|
||||
{
|
||||
JS_ASSERT(a->compartment() == b->compartment());
|
||||
JS_ASSERT(a->isFunction() == b->isFunction());
|
||||
|
||||
bool aInline = !a->hasSlotsArray();
|
||||
bool bInline = !b->hasSlotsArray();
|
||||
|
||||
/* Trade the guts of the objects. */
|
||||
const size_t size = GetObjectSize(a);
|
||||
if (size == GetObjectSize(b)) {
|
||||
/*
|
||||
* If the objects are the same size, then we make no assumptions about
|
||||
* whether they have dynamically allocated slots and instead just copy
|
||||
* them over wholesale.
|
||||
*/
|
||||
char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
|
||||
JS_ASSERT(size <= sizeof(tmp));
|
||||
|
||||
memcpy(tmp, a, size);
|
||||
memcpy(a, b, size);
|
||||
memcpy(b, tmp, size);
|
||||
|
||||
/* Fixup pointers for inline slots on the objects. */
|
||||
if (aInline)
|
||||
b->slots = b->fixedSlots();
|
||||
if (bInline)
|
||||
a->slots = a->fixedSlots();
|
||||
} else {
|
||||
/*
|
||||
* If the objects are of differing sizes, then we only copy over the
|
||||
* JSObject portion (things like class, etc.) and leave it to
|
||||
* JSObject::clone to copy over the dynamic slots for us.
|
||||
*/
|
||||
if (a->isFunction()) {
|
||||
JSFunction tmp;
|
||||
memcpy(&tmp, a, sizeof tmp);
|
||||
memcpy(a, b, sizeof tmp);
|
||||
memcpy(b, &tmp, sizeof tmp);
|
||||
} else {
|
||||
JSObject tmp;
|
||||
memcpy(&tmp, a, sizeof tmp);
|
||||
memcpy(a, b, sizeof tmp);
|
||||
memcpy(b, &tmp, sizeof tmp);
|
||||
}
|
||||
|
||||
JS_ASSERT(!aInline);
|
||||
JS_ASSERT(!bInline);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this method with extreme caution. It trades the guts of two objects and updates
|
||||
* scope ownership. This operation is not thread-safe, just as fast array to slow array
|
||||
@ -3305,17 +3455,11 @@ GetObjectSize(JSObject *obj)
|
||||
bool
|
||||
JSObject::swap(JSContext *cx, JSObject *other)
|
||||
{
|
||||
size_t size = GetObjectSize(this);
|
||||
|
||||
if (size != GetObjectSize(other)) {
|
||||
/*
|
||||
* Objects with different numbers of fixed slots can be swapped only if they
|
||||
* are both shapeless non-natives, to preserve the invariant that objects with the
|
||||
* same shape have the same number of fixed slots. Use a dynamic array for both.
|
||||
*/
|
||||
JS_ASSERT(!isNative());
|
||||
JS_ASSERT(!other->isNative());
|
||||
size = sizeof(JSObject);
|
||||
/*
|
||||
* If we are swapping objects with a different number of builtin slots, force
|
||||
* both to not use their inline slots.
|
||||
*/
|
||||
if (GetObjectSize(this) != GetObjectSize(other)) {
|
||||
if (!hasSlotsArray()) {
|
||||
if (!allocSlots(cx, numSlots()))
|
||||
return false;
|
||||
@ -3326,24 +3470,31 @@ JSObject::swap(JSContext *cx, JSObject *other)
|
||||
}
|
||||
}
|
||||
|
||||
bool thisInline = !hasSlotsArray();
|
||||
bool otherInline = !other->hasSlotsArray();
|
||||
if (this->compartment() == other->compartment()) {
|
||||
TradeGuts(this, other);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(FINALIZE_OBJECT_LAST == FINALIZE_OBJECT16);
|
||||
|
||||
char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
|
||||
JS_ASSERT(size <= sizeof(tmp));
|
||||
|
||||
/* Trade the guts of the objects. */
|
||||
memcpy(tmp, this, size);
|
||||
memcpy(this, other, size);
|
||||
memcpy(other, tmp, size);
|
||||
|
||||
/* Fixup pointers for inline slots on the objects. */
|
||||
if (thisInline)
|
||||
other->slots = other->fixedSlots();
|
||||
if (otherInline)
|
||||
this->slots = this->fixedSlots();
|
||||
JSObject *thisClone;
|
||||
JSObject *otherClone;
|
||||
{
|
||||
AutoCompartment ac(cx, other);
|
||||
if (!ac.enter())
|
||||
return false;
|
||||
thisClone = this->clone(cx, other->getProto(), other->getParent());
|
||||
if (!thisClone || !thisClone->copyPropertiesFrom(cx, this))
|
||||
return false;
|
||||
}
|
||||
{
|
||||
AutoCompartment ac(cx, this);
|
||||
if (!ac.enter())
|
||||
return false;
|
||||
otherClone = other->clone(cx, other->getProto(), other->getParent());
|
||||
if (!otherClone || !otherClone->copyPropertiesFrom(cx, other))
|
||||
return false;
|
||||
}
|
||||
TradeGuts(this, otherClone);
|
||||
TradeGuts(other, thisClone);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -552,6 +552,8 @@ struct JSObject : js::gc::Cell {
|
||||
|
||||
inline bool hasPropertyTable() const;
|
||||
|
||||
/* gc::FinalizeKind */ unsigned finalizeKind() const;
|
||||
|
||||
uint32 numSlots() const { return capacity; }
|
||||
|
||||
size_t slotsAndStructSize(uint32 nslots) const;
|
||||
@ -1146,7 +1148,9 @@ struct JSObject : js::gc::Cell {
|
||||
|
||||
inline JSObject *getThrowTypeError() const;
|
||||
|
||||
bool swap(JSContext *cx, JSObject *obj);
|
||||
JS_FRIEND_API(JSObject *) clone(JSContext *cx, JSObject *proto, JSObject *parent);
|
||||
JS_FRIEND_API(bool) copyPropertiesFrom(JSContext *cx, JSObject *obj);
|
||||
bool swap(JSContext *cx, JSObject *other);
|
||||
|
||||
const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index);
|
||||
|
||||
|
@ -252,10 +252,10 @@ JSObject::setPrimitiveThis(const js::Value &pthis)
|
||||
setSlot(JSSLOT_PRIMITIVE_THIS, pthis);
|
||||
}
|
||||
|
||||
inline js::gc::FinalizeKind
|
||||
GetObjectFinalizeKind(const JSObject *obj)
|
||||
inline /* gc::FinalizeKind */ unsigned
|
||||
JSObject::finalizeKind() const
|
||||
{
|
||||
return js::gc::FinalizeKind(obj->arena()->header()->thingKind);
|
||||
return js::gc::FinalizeKind(arena()->header()->thingKind);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
@ -265,7 +265,7 @@ JSObject::numFixedSlots() const
|
||||
return JSObject::FUN_CLASS_RESERVED_SLOTS;
|
||||
if (!hasSlotsArray())
|
||||
return capacity;
|
||||
return js::gc::GetGCKindSlots(GetObjectFinalizeKind(this));
|
||||
return js::gc::GetGCKindSlots(js::gc::FinalizeKind(finalizeKind()));
|
||||
}
|
||||
|
||||
inline size_t
|
||||
@ -1063,7 +1063,7 @@ CopyInitializerObject(JSContext *cx, JSObject *baseobj)
|
||||
JS_ASSERT(baseobj->getClass() == &js_ObjectClass);
|
||||
JS_ASSERT(!baseobj->inDictionaryMode());
|
||||
|
||||
gc::FinalizeKind kind = GetObjectFinalizeKind(baseobj);
|
||||
gc::FinalizeKind kind = gc::FinalizeKind(baseobj->finalizeKind());
|
||||
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
|
||||
|
||||
if (!obj || !obj->ensureSlots(cx, baseobj->numSlots()))
|
||||
|
@ -383,7 +383,8 @@ AutoCompartment::leave()
|
||||
|
||||
/* Cross compartment wrappers. */
|
||||
|
||||
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags) : JSWrapper(flags)
|
||||
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags)
|
||||
: JSWrapper(CROSS_COMPARTMENT | flags)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,14 @@ class JS_FRIEND_API(JSWrapper) : public js::JSProxyHandler {
|
||||
static inline JSObject *wrappedObject(const JSObject *wrapper) {
|
||||
return wrapper->getProxyPrivate().toObjectOrNull();
|
||||
}
|
||||
static inline JSWrapper *wrapperHandler(const JSObject *wrapper) {
|
||||
return static_cast<JSWrapper *>(wrapper->getProxyHandler());
|
||||
}
|
||||
|
||||
enum {
|
||||
CROSS_COMPARTMENT = 1 << 0,
|
||||
LAST_USED_FLAG = CROSS_COMPARTMENT
|
||||
};
|
||||
|
||||
static void *getWrapperFamily();
|
||||
};
|
||||
|
@ -1182,6 +1182,20 @@ nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
xpc_MorphSlimWrapper(JSContext *cx, nsISupports *tomorph)
|
||||
{
|
||||
nsWrapperCache *cache;
|
||||
CallQueryInterface(tomorph, &cache);
|
||||
if(!cache)
|
||||
return NS_OK;
|
||||
|
||||
JSObject *obj = cache->GetWrapper();
|
||||
if(!obj || !IS_SLIM_WRAPPER(obj))
|
||||
return NS_OK;
|
||||
return MorphSlimWrapper(cx, obj);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
NativeInterface2JSObject(XPCLazyCallContext & lccx,
|
||||
JSObject * aScope,
|
||||
@ -1483,10 +1497,14 @@ static JSDHashOperator
|
||||
MoveableWrapperFinder(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
||||
uint32 number, void *arg)
|
||||
{
|
||||
// Every element counts.
|
||||
nsTArray<nsRefPtr<XPCWrappedNative> > *array =
|
||||
static_cast<nsTArray<nsRefPtr<XPCWrappedNative> > *>(arg);
|
||||
array->AppendElement(((Native2WrappedNativeMap::Entry*)hdr)->value);
|
||||
XPCWrappedNative *wn = ((Native2WrappedNativeMap::Entry*)hdr)->value;
|
||||
|
||||
// If a wrapper is expired, then there are no references to it from JS, so
|
||||
// we don't have to move it.
|
||||
if(!wn->IsWrapperExpired())
|
||||
array->AppendElement(wn);
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
#define xpcpublic_h
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nsISupports.h"
|
||||
#include "jsobj.h"
|
||||
#include "nsAString.h"
|
||||
#include "nsIPrincipal.h"
|
||||
@ -59,6 +60,9 @@ xpc_CreateMTGlobalObject(JSContext *cx, JSClass *clasp,
|
||||
nsISupports *ptr, JSObject **global,
|
||||
JSCompartment **compartment);
|
||||
|
||||
nsresult
|
||||
xpc_MorphSlimWrapper(JSContext *cx, nsISupports *tomorph);
|
||||
|
||||
extern JSBool
|
||||
XPC_WN_Equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
|
||||
|
||||
|
@ -519,8 +519,8 @@ XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx,
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> wrappedjs(do_QueryInterface(Object));
|
||||
JSObject *obj;
|
||||
wrappedjs->GetJSObject(&obj);
|
||||
if(xpc::AccessCheck::isChrome(obj->getCompartment()) &&
|
||||
!xpc::AccessCheck::isChrome(Scope->GetGlobalJSObject()->getCompartment()))
|
||||
if(xpc::AccessCheck::isChrome(obj->compartment()) &&
|
||||
!xpc::AccessCheck::isChrome(Scope->GetGlobalJSObject()->compartment()))
|
||||
{
|
||||
needsCOW = JS_TRUE;
|
||||
}
|
||||
@ -1508,6 +1508,16 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool crosscompartment = aOldScope->GetGlobalJSObject()->compartment() !=
|
||||
aNewScope->GetGlobalJSObject()->compartment();
|
||||
#ifdef DEBUG
|
||||
if(crosscompartment)
|
||||
{
|
||||
NS_ASSERTION(aNewParent, "won't be able to find the new parent");
|
||||
NS_ASSERTION(wrapper, "can't transplant slim wrappers");
|
||||
}
|
||||
#endif
|
||||
|
||||
// ReparentWrapperIfFound is really only meant to be called from DOM code
|
||||
// which must happen only on the main thread. Bail if we're on some other
|
||||
// thread or have a non-main-thread-only wrapper.
|
||||
@ -1519,6 +1529,10 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSAutoEnterCompartment ac;
|
||||
if(!ac.enter(ccx, aNewScope->GetGlobalJSObject()))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if(aOldScope != aNewScope)
|
||||
{
|
||||
// Oh, so now we need to move the wrapper to a different scope.
|
||||
@ -1590,20 +1604,46 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
|
||||
// We only try to fixup the __proto__ JSObject if the wrapper
|
||||
// is directly using that of its XPCWrappedNativeProto.
|
||||
|
||||
if(wrapper->HasProto() &&
|
||||
flat->getProto() == oldProto->GetJSProtoObject())
|
||||
if(crosscompartment)
|
||||
{
|
||||
if(!JS_SetPrototype(ccx, flat, newProto->GetJSProtoObject()))
|
||||
{
|
||||
// this is bad, very bad
|
||||
NS_ERROR("JS_SetPrototype failed");
|
||||
JSObject *newobj = flat->clone(ccx, newProto->GetJSProtoObject(),
|
||||
aNewParent);
|
||||
if(!newobj)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
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;
|
||||
wrapper->mFlatJSObject = flat;
|
||||
if(cache)
|
||||
cache->SetWrapper(flat);
|
||||
if (!flat->copyPropertiesFrom(ccx, propertyHolder))
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_WARNING("Moving XPConnect wrappedNative to new scope, "
|
||||
"but can't fixup __proto__");
|
||||
if(wrapper->HasProto() &&
|
||||
flat->getProto() == oldProto->GetJSProtoObject())
|
||||
{
|
||||
if(!JS_SetPrototype(ccx, flat, newProto->GetJSProtoObject()))
|
||||
{
|
||||
// this is bad, very bad
|
||||
NS_ERROR("JS_SetPrototype failed");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_WARNING("Moving XPConnect wrappedNative to new scope, "
|
||||
"but can't fixup __proto__");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -57,6 +57,7 @@ _CHROME_FILES = \
|
||||
test_cows.xul \
|
||||
test_bug517163.xul \
|
||||
test_bug571849.xul \
|
||||
test_bug601803.xul \
|
||||
$(NULL)
|
||||
|
||||
# Disabled until this test gets updated to test the new proxy based
|
||||
|
40
js/src/xpconnect/tests/chrome/test_bug601803.xul
Normal file
40
js/src/xpconnect/tests/chrome/test_bug601803.xul
Normal file
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=500931
|
||||
-->
|
||||
<window title="Mozilla Bug 601803"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=601803"
|
||||
target="_blank">Mozilla Bug 601803</a>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"><![CDATA[
|
||||
|
||||
/** Test for Bug 601803 **/
|
||||
|
||||
function go() {
|
||||
var ifr = document.getElementById('ifr');
|
||||
var elem = document.createElementNS("http://www.w3.org/1999/xhtml","html:div");
|
||||
elem.appendChild(document.createTextNode("hello, world"));
|
||||
elem.expando = 42;
|
||||
ifr.contentDocument.body.appendChild(elem);
|
||||
is(elem.wrappedJSObject.expando, 42, "expando is preserved");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
]]></script>
|
||||
<iframe type="content" src="about:blank" onload="go()" id="ifr" />
|
||||
</body>
|
||||
</window>
|
@ -44,11 +44,11 @@ namespace xpc {
|
||||
|
||||
class WrapperFactory {
|
||||
public:
|
||||
enum { WAIVE_XRAY_WRAPPER_FLAG = (1<<0),
|
||||
IS_XRAY_WRAPPER_FLAG = (1<<1),
|
||||
SCRIPT_ACCESS_ONLY_FLAG = (1<<2),
|
||||
PARTIALLY_TRANSPARENT = (1<<3),
|
||||
SOW_FLAG = (1<<4) };
|
||||
enum { WAIVE_XRAY_WRAPPER_FLAG = JSWrapper::LAST_USED_FLAG << 1,
|
||||
IS_XRAY_WRAPPER_FLAG = WAIVE_XRAY_WRAPPER_FLAG << 1,
|
||||
SCRIPT_ACCESS_ONLY_FLAG = IS_XRAY_WRAPPER_FLAG << 1,
|
||||
PARTIALLY_TRANSPARENT = SCRIPT_ACCESS_ONLY_FLAG << 1,
|
||||
SOW_FLAG = PARTIALLY_TRANSPARENT << 1 };
|
||||
|
||||
// Return true if any of any of the nested wrappers have the flag set.
|
||||
static bool HasWrapperFlag(JSObject *wrapper, uintN flag) {
|
||||
|
Loading…
Reference in New Issue
Block a user