bug 601803 - Support adopting a node cross-compartment. r=gal/jst

This commit is contained in:
Blake Kaplan 2010-11-15 17:21:25 -08:00
parent f110aa58b7
commit 439ca83a7b
18 changed files with 374 additions and 87 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
JSObject *clone(JSContext *cx, JSObject *proto, JSObject *parent);
bool copyPropertiesFrom(JSContext *cx, JSObject *obj);
bool swap(JSContext *cx, JSObject *other);
const js::Shape *defineBlockVariable(JSContext *cx, jsid id, intN index);

View File

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

View File

@ -383,7 +383,8 @@ AutoCompartment::leave()
/* Cross compartment wrappers. */
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags) : JSWrapper(flags)
JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags)
: JSWrapper(CROSS_COMPARTMENT | flags)
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View 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>

View File

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