Example #1
0
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;
}
Example #2
0
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));
}
Example #3
0
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));
}