Make enumeration over SJOWs walk the prototype chain. Also make SJOWs unwrap same-origin XOWs. bug 410090, r+sr=jst

This commit is contained in:
mrbkap@gmail.com 2007-12-29 15:40:50 -08:00
Родитель 8d20f88740
Коммит 5ec6a2cf11
5 изменённых файлов: 205 добавлений и 130 удалений

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

@ -1013,118 +1013,16 @@ XPC_XOW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
equality(cx, obj, OBJECT_TO_JSVAL(test), bp);
}
JS_STATIC_DLL_CALLBACK(void)
IteratorFinalize(JSContext *cx, JSObject *obj)
{
jsval v;
JS_GetReservedSlot(cx, obj, 0, &v);
JSIdArray *ida = reinterpret_cast<JSIdArray *>(JSVAL_TO_PRIVATE(v));
if (ida) {
JS_DestroyIdArray(cx, ida);
}
}
JS_STATIC_DLL_CALLBACK(JSBool)
IteratorNext(JSContext *cx, uintN argc, jsval *vp)
{
JSObject *obj = JSVAL_TO_OBJECT(vp[1]);
jsval v;
JS_GetReservedSlot(cx, obj, 0, &v);
JSIdArray *ida = reinterpret_cast<JSIdArray *>(JSVAL_TO_PRIVATE(v));
JS_GetReservedSlot(cx, obj, 1, &v);
jsint idx = JSVAL_TO_INT(v);
if (idx == ida->length) {
return JS_ThrowStopIteration(cx);
}
JS_GetReservedSlot(cx, obj, 2, &v);
jsid id = ida->vector[idx++];
if (JSVAL_TO_BOOLEAN(v)) {
if (!JS_IdToValue(cx, id, &v)) {
return JS_FALSE;
}
*vp = v;
} else {
// We need to return an [id, value] pair.
if (!OBJ_GET_PROPERTY(cx, JS_GetParent(cx, obj), id, &v)) {
return JS_FALSE;
}
jsval name;
if (!JS_IdToValue(cx, id, &name)) {
return JS_FALSE;
}
jsval vec[2] = { name, v };
JSAutoTempValueRooter tvr(cx, 2, vec);
JSObject *array = JS_NewArrayObject(cx, 2, vec);
if (!array) {
return JS_FALSE;
}
*vp = OBJECT_TO_JSVAL(array);
}
JS_SetReservedSlot(cx, obj, 1, INT_TO_JSVAL(idx));
return JS_TRUE;
}
static JSClass IteratorClass = {
"XOW iterator", JSCLASS_HAS_RESERVED_SLOTS(3),
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, IteratorFinalize,
JSCLASS_NO_OPTIONAL_MEMBERS
};
JS_STATIC_DLL_CALLBACK(JSObject *)
XPC_XOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
{
// This is rather ugly: we want to use the trick seen in Enumerate,
// where we use our wrapper's resolve hook to determine if we should
// enumerate a given property. However, we don't want to pollute the
// identifiers with a next method, so we create an object that
// delegates (via the __proto__ link) to a XOW.
jsval root = JSVAL_NULL;
// Root v's address so we can set it and have the right value rooted.
JSAutoTempValueRooter tvr(cx, 1, &root);
JSObject *wrapperIter = JS_NewObject(cx, &sXPC_XOW_JSClass.base, nsnull,
JS_GetGlobalForObject(cx, obj));
if (!wrapperIter) {
return nsnull;
}
root = OBJECT_TO_JSVAL(wrapperIter);
JSObject *iterObj = JS_NewObject(cx, &IteratorClass, wrapperIter, obj);
if (!iterObj) {
return nsnull;
}
root = OBJECT_TO_JSVAL(iterObj);
// Do this sooner rather than later to avoid complications in
// IteratorFinalize.
if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(nsnull))) {
return nsnull;
}
// Initialize iterObj.
if (!JS_DefineFunction(cx, iterObj, "next", (JSNative)IteratorNext, 0,
JSFUN_FAST_NATIVE)) {
return nsnull;
}
JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter));
// Initialize our XOW.
JSObject *innerObj = GetWrappedObject(cx, obj);
@ -1142,29 +1040,8 @@ XPC_XOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
return nsnull;
}
// Start enumerating over all of our properties.
do {
if (!XPCWrapper::Enumerate(cx, iterObj, innerObj)) {
return nsnull;
}
} while ((innerObj = JS_GetPrototype(cx, innerObj)) != nsnull);
JSIdArray *ida = JS_Enumerate(cx, iterObj);
if (!ida) {
return nsnull;
}
if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(ida)) ||
!JS_SetReservedSlot(cx, iterObj, 1, JSVAL_ZERO) ||
!JS_SetReservedSlot(cx, iterObj, 2, BOOLEAN_TO_JSVAL(keysonly))) {
return nsnull;
}
if (!JS_SetPrototype(cx, iterObj, nsnull)) {
return nsnull;
}
return iterObj;
return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, innerObj,
keysonly);
}
JS_STATIC_DLL_CALLBACK(JSObject *)

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

@ -82,6 +82,9 @@ XPC_SJOW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
JS_STATIC_DLL_CALLBACK(JSBool)
XPC_SJOW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);
JS_STATIC_DLL_CALLBACK(JSObject *)
XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
JS_STATIC_DLL_CALLBACK(JSObject *)
XPC_SJOW_WrappedObject(JSContext *cx, JSObject *obj);
@ -203,7 +206,7 @@ JSExtendedClass sXPC_SJOW_JSClass = {
XPC_SJOW_Equality,
nsnull, // outerObject
nsnull, // innerObject
nsnull, // iteratorObject
XPC_SJOW_Iterator,
XPC_SJOW_WrappedObject,
JSCLASS_NO_RESERVED_MEMBERS
};
@ -898,6 +901,17 @@ XPC_SJOW_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
return ThrowException(NS_ERROR_INVALID_ARG, cx);
}
if (JS_GET_CLASS(cx, objToWrap) == &sXPC_XOW_JSClass.base) {
// We're being asked to wrap a XOW. By using XPCWrapper::Unwrap,
// we guarantee that the wrapped object is same-origin to us. If
// it isn't, then just wrap the XOW for an added layer of wrapping.
JSObject *maybeInner = XPCWrapper::Unwrap(cx, objToWrap);
if (maybeInner) {
objToWrap = maybeInner;
}
}
// Check that the caller can access the unsafe object.
if (!CanCallerAccess(cx, objToWrap)) {
// CanCallerAccess() already threw for us.
@ -954,6 +968,36 @@ XPC_SJOW_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
return JS_TRUE;
}
JS_STATIC_DLL_CALLBACK(JSObject *)
XPC_SJOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
{
JSObject *innerObj = GetUnsafeObject(cx, obj);
if (!innerObj) {
ThrowException(NS_ERROR_INVALID_ARG, cx);
return nsnull;
}
// Create our dummy SJOW.
JSObject *wrapperIter = ::JS_NewObject(cx, &sXPC_SJOW_JSClass.base, nsnull,
nsnull);
if (!wrapperIter ||
!::JS_SetParent(cx, wrapperIter, innerObj) ||
!::JS_SetPrototype(cx, wrapperIter, nsnull)) {
return nsnull;
}
if (!::JS_SetReservedSlot(cx, wrapperIter, XPC_SJOW_SLOT_IS_RESOLVING,
BOOLEAN_TO_JSVAL(JS_FALSE))) {
return JS_FALSE;
}
JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(wrapperIter));
// Initialize the wrapper.
return XPCWrapper::CreateIteratorObj(cx, wrapperIter, obj, innerObj,
keysonly);
}
JS_STATIC_DLL_CALLBACK(JSObject *)
XPC_SJOW_WrappedObject(JSContext *cx, JSObject *obj)
{

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

@ -54,6 +54,133 @@ XPCWrapper::sNumSlots = 2;
JSNative
XPCWrapper::sEvalNative = nsnull;
JS_STATIC_DLL_CALLBACK(void)
IteratorFinalize(JSContext *cx, JSObject *obj)
{
jsval v;
JS_GetReservedSlot(cx, obj, 0, &v);
JSIdArray *ida = reinterpret_cast<JSIdArray *>(JSVAL_TO_PRIVATE(v));
if (ida) {
JS_DestroyIdArray(cx, ida);
}
}
JS_STATIC_DLL_CALLBACK(JSBool)
IteratorNext(JSContext *cx, uintN argc, jsval *vp)
{
JSObject *obj = JSVAL_TO_OBJECT(vp[1]);
jsval v;
JS_GetReservedSlot(cx, obj, 0, &v);
JSIdArray *ida = reinterpret_cast<JSIdArray *>(JSVAL_TO_PRIVATE(v));
JS_GetReservedSlot(cx, obj, 1, &v);
jsint idx = JSVAL_TO_INT(v);
if (idx == ida->length) {
return JS_ThrowStopIteration(cx);
}
JS_GetReservedSlot(cx, obj, 2, &v);
jsid id = ida->vector[idx++];
if (JSVAL_TO_BOOLEAN(v)) {
if (!JS_IdToValue(cx, id, &v)) {
return JS_FALSE;
}
*vp = v;
} else {
// We need to return an [id, value] pair.
if (!OBJ_GET_PROPERTY(cx, JS_GetParent(cx, obj), id, &v)) {
return JS_FALSE;
}
jsval name;
if (!JS_IdToValue(cx, id, &name)) {
return JS_FALSE;
}
jsval vec[2] = { name, v };
JSAutoTempValueRooter tvr(cx, 2, vec);
JSObject *array = JS_NewArrayObject(cx, 2, vec);
if (!array) {
return JS_FALSE;
}
*vp = OBJECT_TO_JSVAL(array);
}
JS_SetReservedSlot(cx, obj, 1, INT_TO_JSVAL(idx));
return JS_TRUE;
}
static JSClass IteratorClass = {
"XOW iterator", JSCLASS_HAS_RESERVED_SLOTS(3),
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, IteratorFinalize,
JSCLASS_NO_OPTIONAL_MEMBERS
};
// static
JSObject *
XPCWrapper::CreateIteratorObj(JSContext *cx, JSObject *tempWrapper,
JSObject *wrapperObj, JSObject *innerObj,
JSBool keysonly)
{
// This is rather ugly: we want to use the trick seen in Enumerate,
// where we use our wrapper's resolve hook to determine if we should
// enumerate a given property. However, we don't want to pollute the
// identifiers with a next method, so we create an object that
// delegates (via the __proto__ link) to the wrapper.
JSObject *iterObj = JS_NewObject(cx, &IteratorClass, tempWrapper, wrapperObj);
if (!iterObj) {
return nsnull;
}
JSAutoTempValueRooter tvr(cx, OBJECT_TO_JSVAL(iterObj));
// Do this sooner rather than later to avoid complications in
// IteratorFinalize.
if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(nsnull))) {
return nsnull;
}
// Initialize iterObj.
if (!JS_DefineFunction(cx, iterObj, "next", (JSNative)IteratorNext, 0,
JSFUN_FAST_NATIVE)) {
return nsnull;
}
// Start enumerating over all of our properties.
do {
if (!XPCWrapper::Enumerate(cx, iterObj, innerObj)) {
return nsnull;
}
} while ((innerObj = JS_GetPrototype(cx, innerObj)) != nsnull);
JSIdArray *ida = JS_Enumerate(cx, iterObj);
if (!ida) {
return nsnull;
}
if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(ida)) ||
!JS_SetReservedSlot(cx, iterObj, 1, JSVAL_ZERO) ||
!JS_SetReservedSlot(cx, iterObj, 2, BOOLEAN_TO_JSVAL(keysonly))) {
return nsnull;
}
if (!JS_SetPrototype(cx, iterObj, nsnull)) {
return nsnull;
}
return iterObj;
}
// static
JSBool
XPCWrapper::AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

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

@ -177,8 +177,7 @@ public:
}
/**
* Unwraps an XPCSafeJSObjectWrapper or an XPCCrossOriginWrapper into its
* wrapped native.
* Unwraps a XPCCrossOriginWrapper into its wrapped native.
*/
static JSObject *Unwrap(JSContext *cx, JSObject *wrapper) {
if (JS_GET_CLASS(cx, wrapper) != &sXPC_XOW_JSClass.base) {
@ -231,6 +230,18 @@ public:
: XPC_XOW_WrapFunction(cx, wrapperObj, funobj, v);
}
/**
* Creates an iterator object that walks up the prototype of
* wrappedObj. This is suitable for for-in loops over a wrapper. If
* a property is not supposed to be reflected, the resolve hook
* is expected to censor it. tempWrapper must be rooted already.
*/
static JSObject *CreateIteratorObj(JSContext *cx,
JSObject *tempWrapper,
JSObject *wrapperObj,
JSObject *innerObj,
JSBool keysonly);
/**
* Called for the common part of adding a property to obj.
*/

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

@ -26,7 +26,23 @@
is(answer.sort().toString(),
expected.sort().toString(),
'enumeration does not work');
'enumeration over XOWs walks the prototype chain');
answer = [];
let (obj = { a: 3 }) {
for (let i in XPCSafeJSObjectWrapper({ __proto__: obj}))
answer.push(i);
is(answer.toString(), 'a',
'enumeration over SJOWs walks the prototype chain');
}
answer = [];
for (let i in XPCSafeJSObjectWrapper(location))
answer.push(i);
is(answer.sort().toString(),
expected.sort().toString(),
'enumeration over SJOWs walks the prototype chain and works over XOWs');
var origProto = window.__proto__;
try {