JSObject * JSObject::unwrap(uintN *flagsp) { JSObject *wrapped = this; uintN flags = 0; if (wrapped->isWrapper()) { flags |= static_cast<JSWrapper *>(wrapped->getProxyHandler())->flags(); wrapped = wrapped->getProxyPrivate().toObjectOrNull(); } if (flagsp) *flagsp = flags; return wrapped; }
void DEBUG_CheckForComponentsInScope(JSContext* cx, JSObject* obj, JSObject* startingObj, JSBool OKIfNotInitialized, XPCJSRuntime* runtime) { if(OKIfNotInitialized) return; if(!(JS_GetOptions(cx) & JSOPTION_PRIVATE_IS_NSISUPPORTS)) return; const char* name = runtime->GetStringName(XPCJSRuntime::IDX_COMPONENTS); jsval prop; if(JS_LookupProperty(cx, obj, name, &prop) && !JSVAL_IS_PRIMITIVE(prop)) return; // This is pretty much always bad. It usually means that native code is // making a callback to an interface implemented in JavaScript, but the // document where the JS object was created has already been cleared and the // global properties of that document's window are *gone*. Generally this // indicates a problem that should be addressed in the design and use of the // callback code. NS_ERROR("XPConnect is being called on a scope without a 'Components' property! (stack and details follow)"); printf("The current JS stack is:\n"); xpc_DumpJSStack(cx, JS_TRUE, JS_TRUE, JS_TRUE); printf("And the object whose scope lacks a 'Components' property is:\n"); js_DumpObject(startingObj); JSObject *p = startingObj; while(p->isWrapper()) { p = p->getProxyPrivate().toObjectOrNull(); if(!p) break; printf("which is a wrapper for:\n"); js_DumpObject(p); } }
bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); uintN flags = 0; JS_CHECK_RECURSION(cx, return false); /* Only GC things have to be wrapped or copied. */ if (!vp->isMarkable()) return true; if (vp->isString()) { JSString *str = vp->toString(); /* Static atoms do not have to be wrapped. */ if (str->isStaticAtom()) return true; /* If the string is already in this compartment, we are done. */ if (str->compartment() == this) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->compartment() == cx->runtime->atomsCompartment); return true; } } /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ JSObject *global; if (cx->hasfp()) { global = cx->fp()->scopeChain().getGlobal(); } else { global = cx->globalObject; if (!NULLABLE_OBJ_TO_INNER_OBJECT(cx, global)) return false; } /* Unwrap incoming objects. */ if (vp->isObject()) { JSObject *obj = &vp->toObject(); /* If the object is already in this compartment, we are done. */ if (obj->compartment() == this) return true; /* Translate StopIteration singleton. */ if (obj->isStopIteration()) return js_FindClassObject(cx, NULL, JSProto_StopIteration, vp); /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = vp->toObject().unwrap(&flags); vp->setObject(*obj); if (obj->getCompartment() == this) return true; if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } vp->setObject(*obj); if (obj->getCompartment() == this) return true; } else { if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject); vp->setObject(*obj); } #ifdef DEBUG { JSObject *outer = obj; OBJ_TO_OUTER_OBJECT(cx, outer); JS_ASSERT(outer && outer == obj); } #endif } /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) { *vp = p->value; if (vp->isObject()) { JSObject *obj = &vp->toObject(); JS_ASSERT(IsCrossCompartmentWrapper(obj)); if (global->getJSClass() != &js_dummy_class && obj->getParent() != global) { do { obj->setParent(global); obj = obj->getProto(); } while (obj && IsCrossCompartmentWrapper(obj)); } } return true; } if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); const jschar *chars = str->getChars(cx); if (!chars) return false; JSString *wrapped = js_NewStringCopyN(cx, chars, str->length()); if (!wrapped) return false; vp->setString(wrapped); return crossCompartmentWrappers.put(orig, *vp); } JSObject *obj = &vp->toObject(); /* * Recurse to wrap the prototype. Long prototype chains will run out of * stack, causing an error in CHECK_RECURSE. * * Wrapping the proto before creating the new wrapper and adding it to the * cache helps avoid leaving a bad entry in the cache on OOM. But note that * if we wrapped both proto and parent, we would get infinite recursion * here (since Object.prototype->parent->proto leads to Object.prototype * itself). */ JSObject *proto = obj->getProto(); if (!wrap(cx, &proto)) return false; /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied * to the object. */ JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, global, flags); if (!wrapper) return false; vp->setObject(*wrapper); if (wrapper->getProto() != proto && !SetProto(cx, wrapper, proto, false)) return false; if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp)) return false; wrapper->setParent(global); return true; }
bool JSCompartment::wrap(JSContext *cx, Value *vp) { JS_ASSERT(cx->compartment == this); uintN flags = 0; JS_CHECK_RECURSION(cx, return false); /* Only GC things have to be wrapped or copied. */ if (!vp->isMarkable()) return true; /* Static strings do not have to be wrapped. */ if (vp->isString() && JSString::isStatic(vp->toString())) return true; /* Unwrap incoming objects. */ if (vp->isObject()) { JSObject *obj = &vp->toObject(); /* If the object is already in this compartment, we are done. */ if (obj->getCompartment(cx) == this) return true; /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { obj = vp->toObject().unwrap(&flags); OBJ_TO_OUTER_OBJECT(cx, obj); if (!obj) return false; vp->setObject(*obj); } /* If the wrapped object is already in this compartment, we are done. */ if (obj->getCompartment(cx) == this) return true; } /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(*vp)) { *vp = p->value; return true; } if (vp->isString()) { Value orig = *vp; JSString *str = vp->toString(); JSString *wrapped = js_NewStringCopyN(cx, str->chars(), str->length()); if (!wrapped) return false; vp->setString(wrapped); return crossCompartmentWrappers.put(orig, *vp); } JSObject *obj = &vp->toObject(); /* * Recurse to wrap the prototype. Long prototype chains will run out of * stack, causing an error in CHECK_RECURSE. * * Wrapping the proto before creating the new wrapper and adding it to the * cache helps avoid leaving a bad entry in the cache on OOM. But note that * if we wrapped both proto and parent, we would get infinite recursion * here (since Object.prototype->parent->proto leads to Object.prototype * itself). */ JSObject *proto = obj->getProto(); if (!wrap(cx, &proto)) return false; /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied * to the object. */ JSObject *wrapper = cx->runtime->wrapObjectCallback(cx, obj, proto, flags); if (!wrapper) return false; wrapper->setProto(proto); vp->setObject(*wrapper); if (!crossCompartmentWrappers.put(wrapper->getProxyPrivate(), *vp)) return false; /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ JSObject *global; if (cx->hasfp()) { global = cx->fp()->scopeChain().getGlobal(); } else { global = cx->globalObject; OBJ_TO_INNER_OBJECT(cx, global); if (!global) return false; } wrapper->setParent(global); return true; }