mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
c668cc7f5c
commit
f544e27288
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user