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)); }