bool JSCompartment::getNonWrapperObjectForCurrentCompartment(JSContext* cx, MutableHandleObject obj) { // Ensure that we have entered a compartment. MOZ_ASSERT(cx->global()); // If we have a cross-compartment wrapper, make sure that the cx isn't // associated with the self-hosting global. We don't want to create // wrappers for objects in other runtimes, which may be the case for the // self-hosting global. MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(cx->global())); MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(&obj->global())); // The object is already in the right compartment. Normally same- // compartment returns the object itself, however, windows are always // wrapped by a proxy, so we have to check for that case here manually. if (obj->compartment() == this) { obj.set(ToWindowProxyIfWindow(obj)); return true; } // Note that if the object is same-compartment, but has been wrapped into a // different compartment, we need to unwrap it and return the bare same- // compartment object. Note again that windows are always wrapped by a // WindowProxy even when same-compartment so take care not to strip this // particular wrapper. RootedObject objectPassedToWrap(cx, obj); obj.set(UncheckedUnwrap(obj, /* stopAtWindowProxy = */ true)); if (obj->compartment() == this) { MOZ_ASSERT(!IsWindow(obj)); return true; } // Invoke the prewrap callback. The prewrap callback is responsible for // doing similar reification as above, but can account for any additional // embedder requirements. // // We're a bit worried about infinite recursion here, so we do a check - // see bug 809295. auto preWrap = cx->runtime()->wrapObjectCallbacks->preWrap; if (!CheckSystemRecursionLimit(cx)) return false; if (preWrap) { preWrap(cx, cx->global(), obj, objectPassedToWrap, obj); if (!obj) return false; } MOZ_ASSERT(!IsWindow(obj)); return true; }
bool JSCompartment::wrap(JSContext* cx, MutableHandleObject obj) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); if (!obj) return true; AutoDisableProxyCheck adpc(cx->runtime()); // Anything we're wrapping has already escaped into script, so must have // been unmarked-gray at some point in the past. MOZ_ASSERT(!ObjectIsMarkedGray(obj)); // The passed object may already be wrapped, or may fit a number of special // cases that we need to check for and manually correct. if (!getNonWrapperObjectForCurrentCompartment(cx, obj)) return false; // If the reification above did not result in a same-compartment object, // get or create a new wrapper object in this compartment for it. if (obj->compartment() != this) { if (!getOrCreateWrapper(cx, nullptr, obj)) return false; } // Ensure that the wrapper is also exposed. ExposeObjectToActiveJS(obj); return true; }
static bool WrapForSameCompartment(JSContext *cx, MutableHandleObject obj, const JSWrapObjectCallbacks *cb) { JS_ASSERT(cx->compartment() == obj->compartment()); if (!cb->sameCompartmentWrap) return true; RootedObject wrapped(cx, cb->sameCompartmentWrap(cx, obj)); if (!wrapped) return false; obj.set(wrapped); return true; }
static bool WrapForSameCompartment(JSContext *cx, MutableHandleObject obj) { JS_ASSERT(cx->compartment() == obj->compartment()); if (!cx->runtime()->sameCompartmentWrapObjectCallback) return true; RootedObject wrapped(cx); wrapped = cx->runtime()->sameCompartmentWrapObjectCallback(cx, obj); if (!wrapped) return false; obj.set(wrapped); return true; }
bool JSCompartment::rewrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); MOZ_ASSERT(obj); MOZ_ASSERT(existingArg); MOZ_ASSERT(existingArg->compartment() == cx->compartment()); MOZ_ASSERT(IsDeadProxyObject(existingArg)); AutoDisableProxyCheck adpc(cx->runtime()); // It may not be possible to re-use existing; if so, clear it so that we // are forced to create a new wrapper. Note that this cannot call out to // |wrap| because of the different gray unmarking semantics. RootedObject existing(cx, existingArg); if (existing->hasStaticPrototype() || // Note: Class asserted above, so all that's left to check is callability existing->isCallable() || obj->isCallable()) { existing.set(nullptr); } // The passed object may already be wrapped, or may fit a number of special // cases that we need to check for and manually correct. if (!getNonWrapperObjectForCurrentCompartment(cx, obj)) return false; // If the reification above resulted in a same-compartment object, we do // not need to create or return an existing wrapper. if (obj->compartment() == this) return true; return getOrCreateWrapper(cx, existing, obj); }
bool JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existingArg) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); MOZ_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment()); MOZ_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); if (!obj) return true; AutoDisableProxyCheck adpc(cx->runtime()); // Wrappers should really be parented to the wrapped parent of the wrapped // object, but in that case a wrapped global object would have a nullptr // 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. HandleObject global = cx->global(); RootedObject objGlobal(cx, &obj->global()); MOZ_ASSERT(global); MOZ_ASSERT(objGlobal); const JSWrapObjectCallbacks *cb = cx->runtime()->wrapObjectCallbacks; if (obj->compartment() == this) { obj.set(GetOuterObject(cx, obj)); return true; } // If we have a cross-compartment wrapper, make sure that the cx isn't // associated with the self-hosting global. We don't want to create // wrappers for objects in other runtimes, which may be the case for the // self-hosting global. MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(global) && !cx->runtime()->isSelfHostingGlobal(objGlobal)); // Unwrap the object, but don't unwrap outer windows. RootedObject objectPassedToWrap(cx, obj); obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true)); if (obj->compartment() == this) { MOZ_ASSERT(obj == GetOuterObject(cx, obj)); return true; } // Translate StopIteration singleton. if (obj->is<StopIterationObject>()) { // StopIteration isn't a constructor, but it's stored in GlobalObject // as one, out of laziness. Hence the GetBuiltinConstructor call here. RootedObject stopIteration(cx); if (!GetBuiltinConstructor(cx, JSProto_StopIteration, &stopIteration)) return false; obj.set(stopIteration); return true; } // Invoke the prewrap callback. We're a bit worried about infinite // recursion here, so we do a check - see bug 809295. JS_CHECK_SYSTEM_RECURSION(cx, return false); if (cb->preWrap) { obj.set(cb->preWrap(cx, global, obj, objectPassedToWrap)); if (!obj) return false; } MOZ_ASSERT(obj == GetOuterObject(cx, obj)); if (obj->compartment() == this) return true; // If we already have a wrapper for this value, use it. RootedValue key(cx, ObjectValue(*obj)); if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) { obj.set(&p->value().get().toObject()); MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>()); MOZ_ASSERT(obj->getParent() == global); return true; } RootedObject existing(cx, existingArg); if (existing) { // Is it possible to reuse |existing|? if (!existing->getTaggedProto().isLazy() || // Note: Class asserted above, so all that's left to check is callability existing->isCallable() || existing->getParent() != global || obj->isCallable()) { existing = nullptr; } } obj.set(cb->wrap(cx, existing, obj, global)); if (!obj) return false; // We maintain the invariant that the key in the cross-compartment wrapper // map is always directly wrapped by the value. MOZ_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject()); return putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*obj)); }
bool JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existingArg) { JS_ASSERT(!cx->runtime()->isAtomsCompartment(this)); JS_ASSERT(cx->compartment() == this); JS_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment()); JS_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); if (!obj) return true; AutoDisableProxyCheck adpc(cx->runtime()); /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a nullptr * 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. */ HandleObject global = cx->global(); RootedObject objGlobal(cx, &obj->global()); JS_ASSERT(global); JS_ASSERT(objGlobal); const JSWrapObjectCallbacks *cb; if (cx->runtime()->isSelfHostingGlobal(global) || cx->runtime()->isSelfHostingGlobal(objGlobal)) cb = &SelfHostingWrapObjectCallbacks; else cb = cx->runtime()->wrapObjectCallbacks; if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, cb); /* Unwrap the object, but don't unwrap outer windows. */ unsigned flags = 0; obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true, &flags)); if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, cb); /* Translate StopIteration singleton. */ if (obj->is<StopIterationObject>()) { RootedValue v(cx); if (!js_FindClassObject(cx, JSProto_StopIteration, &v)) return false; obj.set(&v.toObject()); return true; } /* Invoke the prewrap callback. We're a bit worried about infinite * recursion here, so we do a check - see bug 809295. */ JS_CHECK_CHROME_RECURSION(cx, return false); if (cb->preWrap) { obj.set(cb->preWrap(cx, global, obj, flags)); if (!obj) return false; } if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, cb); #ifdef DEBUG { JSObject *outer = GetOuterObject(cx, obj); JS_ASSERT(outer && outer == obj); } #endif /* If we already have a wrapper for this value, use it. */ RootedValue key(cx, ObjectValue(*obj)); if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) { obj.set(&p->value().get().toObject()); JS_ASSERT(obj->is<CrossCompartmentWrapperObject>()); JS_ASSERT(obj->getParent() == global); return true; } RootedObject proto(cx, TaggedProto::LazyProto); RootedObject existing(cx, existingArg); if (existing) { /* Is it possible to reuse |existing|? */ if (!existing->getTaggedProto().isLazy() || // Note: don't use is<ObjectProxyObject>() here -- it also matches subclasses! existing->getClass() != &ProxyObject::uncallableClass_ || existing->getParent() != global || obj->isCallable()) { existing = nullptr; } } obj.set(cb->wrap(cx, existing, obj, proto, global, flags)); if (!obj) return false; /* * We maintain the invariant that the key in the cross-compartment wrapper * map is always directly wrapped by the value. */ JS_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject()); return putWrapper(key, ObjectValue(*obj)); }