static JSBool WrapSameOriginProp(JSContext *cx, JSObject *outerObj, jsval *vp) { // Don't call XPC_XOW_RewrapIfNeeded for same origin properties. We only // need to wrap window, document and location. if (JSVAL_IS_PRIMITIVE(*vp)) { return JS_TRUE; } JSObject *wrappedObj = JSVAL_TO_OBJECT(*vp); JSClass *clasp = STOBJ_GET_CLASS(wrappedObj); if (XPC_XOW_ClassNeedsXOW(clasp->name)) { return XPC_XOW_WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp); } // Check if wrappedObj is an XOW. If so, verify that it's from the // right scope. if (clasp == &sXPC_XOW_JSClass.base && STOBJ_GET_PARENT(wrappedObj) != STOBJ_GET_PARENT(outerObj)) { *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, wrappedObj)); return XPC_XOW_WrapObject(cx, STOBJ_GET_PARENT(outerObj), vp); } return JS_TRUE; }
/* * Shared code to close iterator's state either through an explicit call or * when GC detects that the iterator is no longer reachable. */ void js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) { jsval state; JSObject *iterable; JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); /* Avoid double work if js_CloseNativeIterator was called on obj. */ state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) return; /* Protect against failure to fully initialize obj. */ iterable = STOBJ_GET_PARENT(iterobj); if (iterable) { #if JS_HAS_XML_SUPPORT uintN flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS)); if ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, iterable)) { ((JSXMLObjectOps *) iterable->map->ops)-> enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state, NULL, NULL); } else #endif OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL); } STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL); }
static inline JSObject * GetUnsafeObject(JSObject *obj) { obj = FindSafeObject(obj); if (!obj) { return nsnull; } return STOBJ_GET_PARENT(obj); }
// Because of the drastically different ways that same- and cross-origin XOWs // work, we have to call JS_ClearScope when a XOW changes from being same- // origin to cross-origin. Normally, there are defined places in Gecko where // this happens and they notify us. However, UniversalXPConnect causes the // same transition without any notifications. We could try to detect when this // happens, but doing so would require calling JS_ClearScope from random // hooks, which is bad. // // The compromise is the UXPCObject. When resolving a property on a XOW as // same-origin because of UniversalXPConnect, we actually resolve it on the // UXPCObject (which is just a XOW for the same object). This causes the JS // engine to do all of its work on another object, not polluting the main // object. However, if the get results in calling a setter, the engine still // uses the regular object as 'this', ensuring that the UXPCObject doesn't // leak to script. static JSObject * GetUXPCObject(JSContext *cx, JSObject *obj) { NS_ASSERTION(STOBJ_GET_CLASS(obj) == &sXPC_XOW_JSClass.base, "wrong object"); jsval v; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sFlagsSlot, &v)) { return nsnull; } if (HAS_FLAGS(v, FLAG_IS_UXPC_OBJECT)) { return obj; } if (!JS_GetReservedSlot(cx, obj, sUXPCObjectSlot, &v)) { return nsnull; } if (JSVAL_IS_OBJECT(v)) { return JSVAL_TO_OBJECT(v); } JSObject *uxpco = JS_NewObjectWithGivenProto(cx, &sXPC_XOW_JSClass.base, nsnull, STOBJ_GET_PARENT(obj)); if (!uxpco) { return nsnull; } JSAutoTempValueRooter tvr(cx, uxpco); jsval wrappedObj, parentScope; if (!JS_GetReservedSlot(cx, obj, XPCWrapper::sWrappedObjSlot, &wrappedObj) || !JS_GetReservedSlot(cx, obj, XPC_XOW_ScopeSlot, &parentScope)) { return nsnull; } if (!JS_SetReservedSlot(cx, uxpco, XPCWrapper::sWrappedObjSlot, wrappedObj) || !JS_SetReservedSlot(cx, uxpco, XPCWrapper::sFlagsSlot, INT_TO_JSVAL(FLAG_IS_UXPC_OBJECT)) || !JS_SetReservedSlot(cx, uxpco, XPC_XOW_ScopeSlot, parentScope)) { return nsnull; } if (!JS_SetReservedSlot(cx, obj, sUXPCObjectSlot, OBJECT_TO_JSVAL(uxpco))) { return nsnull; } return uxpco; }
JSBool XPC_XOW_RewrapIfNeeded(JSContext *cx, JSObject *outerObj, jsval *vp) { // Don't need to wrap primitive values. if (JSVAL_IS_PRIMITIVE(*vp)) { return JS_TRUE; } JSObject *obj = JSVAL_TO_OBJECT(*vp); if (JS_ObjectIsFunction(cx, obj)) { return XPC_XOW_WrapFunction(cx, outerObj, obj, vp); } XPCWrappedNative *wn = nsnull; if (STOBJ_GET_CLASS(obj) == &sXPC_XOW_JSClass.base && STOBJ_GET_PARENT(outerObj) != STOBJ_GET_PARENT(obj)) { *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, obj)); } else if (!(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, obj))) { return JS_TRUE; } return XPC_XOW_WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp, wn); }
JSBool xpc_qsXPCOMObjectToJsval(XPCLazyCallContext &lccx, nsISupports *p, nsWrapperCache *cache, const nsIID *iid, XPCNativeInterface **iface, jsval *rval) { // From the T_INTERFACE case in XPCConvert::NativeData2JS. // This is one of the slowest things quick stubs do. JSContext *cx = lccx.GetJSContext(); if(!iface) return xpc_qsThrow(cx, NS_ERROR_XPC_BAD_CONVERT_NATIVE); // XXX The OBJ_IS_NOT_GLOBAL here is not really right. In // fact, this code is depending on the fact that the // global object will not have been collected, and // therefore this NativeInterface2JSObject will not end up // creating a new XPCNativeScriptableShared. nsresult rv; if(!XPCConvert::NativeInterface2JSObject(lccx, rval, nsnull, p, iid, iface, cache, lccx.GetCurrentJSObject(), PR_TRUE, OBJ_IS_NOT_GLOBAL, &rv)) { // I can't tell if NativeInterface2JSObject throws JS exceptions // or not. This is a sloppy stab at the right semantics; the // method really ought to be fixed to behave consistently. if(!JS_IsExceptionPending(cx)) xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); return JS_FALSE; } #ifdef DEBUG JSObject* jsobj = JSVAL_TO_OBJECT(*rval); if(jsobj && !STOBJ_GET_PARENT(jsobj)) NS_ASSERTION(STOBJ_GET_CLASS(jsobj)->flags & JSCLASS_IS_GLOBAL, "Why did we recreate this wrapper?"); #endif return JS_TRUE; }
static JSBool CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) { JSObject *obj, *origobj; jsval state; JSBool foreach; jsid id; JSObject *obj2; JSBool cond; JSClass *clasp; JSExtendedClass *xclasp; JSProperty *prop; JSString *str; JS_ASSERT(flags & JSITER_ENUMERATE); JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); obj = STOBJ_GET_PARENT(iterobj); origobj = STOBJ_GET_PROTO(iterobj); state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) goto stop; foreach = (flags & JSITER_FOREACH) != 0; #if JS_HAS_XML_SUPPORT /* * Treat an XML object specially only when it starts the prototype chain. * Otherwise we need to do the usual deleted and shadowed property checks. */ if (obj == origobj && OBJECT_IS_XML(cx, obj)) { if (foreach) { JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, &id, rval)) { return JS_FALSE; } } else { if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; } STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) goto stop; } else #endif { restart: if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) { #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { /* * We just finished enumerating an XML obj that is present on * the prototype chain of a non-XML origobj. Stop further * prototype chain searches because XML objects don't * enumerate prototypes. */ JS_ASSERT(origobj != obj); JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); } else #endif { obj = OBJ_GET_PROTO(cx, obj); if (obj) { STOBJ_SET_PARENT(iterobj, obj); if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (!JSVAL_IS_NULL(state)) goto restart; } } goto stop; } /* Skip properties not in obj when looking from origobj. */ if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) return JS_FALSE; if (!prop) goto restart; OBJ_DROP_PROPERTY(cx, obj2, prop); /* * If the id was found in a prototype object or an unrelated object * (specifically, not in an inner object for obj), skip it. This step * means that all OBJ_LOOKUP_PROPERTY implementations must return an * object further along on the prototype chain, or else possibly an * object returned by the JSExtendedClass.outerObject optional hook. */ if (obj != obj2) { cond = JS_FALSE; clasp = OBJ_GET_CLASS(cx, obj2); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; cond = xclasp->outerObject && xclasp->outerObject(cx, obj2) == obj; } if (!cond) goto restart; } if (foreach) { /* Get property querying the original object. */ if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) return JS_FALSE; } } if (foreach) { if (flags & JSITER_KEYVALUE) { if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } } else { /* Make rval a string for uniformity and compatibility. */ str = js_ValueToString(cx, ID_TO_VALUE(id)); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); } return JS_TRUE; stop: JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; }