michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jswrapper_h michael@0: #define jswrapper_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "jsproxy.h" michael@0: michael@0: namespace js { michael@0: michael@0: class DummyFrameGuard; michael@0: michael@0: /* michael@0: * Helper for Wrapper::New default options. michael@0: * michael@0: * Callers of Wrapper::New() who wish to specify a prototype for the created michael@0: * Wrapper, *MUST* construct a WrapperOptions with a JSContext. michael@0: */ michael@0: class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions { michael@0: public: michael@0: WrapperOptions() : ProxyOptions(false, nullptr), michael@0: proto_() michael@0: {} michael@0: michael@0: WrapperOptions(JSContext *cx) : ProxyOptions(false, nullptr), michael@0: proto_() michael@0: { michael@0: proto_.construct(cx); michael@0: } michael@0: michael@0: inline JSObject *proto() const; michael@0: WrapperOptions &setProto(JSObject *protoArg) { michael@0: JS_ASSERT(!proto_.empty()); michael@0: proto_.ref() = protoArg; michael@0: return *this; michael@0: } michael@0: michael@0: private: michael@0: mozilla::Maybe proto_; michael@0: }; michael@0: michael@0: /* michael@0: * A wrapper is a proxy with a target object to which it generally forwards michael@0: * operations, but may restrict access to certain operations or instrument michael@0: * the trap operations in various ways. A wrapper is distinct from a Direct Proxy michael@0: * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying michael@0: * object (Direct Proxy Handlers have an underlying target object, but don't michael@0: * expect to expose this object via any kind of unwrapping operation). Callers michael@0: * should be careful to avoid unwrapping security wrappers in the wrong context. michael@0: */ michael@0: class JS_FRIEND_API(Wrapper) : public DirectProxyHandler michael@0: { michael@0: unsigned mFlags; michael@0: michael@0: public: michael@0: using BaseProxyHandler::Action; michael@0: michael@0: enum Flags { michael@0: CROSS_COMPARTMENT = 1 << 0, michael@0: LAST_USED_FLAG = CROSS_COMPARTMENT michael@0: }; michael@0: michael@0: virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, michael@0: MutableHandleValue vp) MOZ_OVERRIDE; michael@0: michael@0: static JSObject *New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler, michael@0: const WrapperOptions *options = nullptr); michael@0: michael@0: static JSObject *Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler); michael@0: michael@0: static Wrapper *wrapperHandler(JSObject *wrapper); michael@0: michael@0: static JSObject *wrappedObject(JSObject *wrapper); michael@0: michael@0: unsigned flags() const { michael@0: return mFlags; michael@0: } michael@0: michael@0: explicit Wrapper(unsigned flags, bool hasPrototype = false); michael@0: michael@0: virtual ~Wrapper(); michael@0: michael@0: virtual bool finalizeInBackground(Value priv) MOZ_OVERRIDE; michael@0: michael@0: static Wrapper singleton; michael@0: static Wrapper singletonWithPrototype; michael@0: michael@0: static JSObject *defaultProto; michael@0: }; michael@0: michael@0: inline JSObject * michael@0: WrapperOptions::proto() const michael@0: { michael@0: return proto_.empty() ? Wrapper::defaultProto : proto_.ref(); michael@0: } michael@0: michael@0: /* Base class for all cross compartment wrapper handlers. */ michael@0: class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper michael@0: { michael@0: public: michael@0: CrossCompartmentWrapper(unsigned flags, bool hasPrototype = false); michael@0: michael@0: virtual ~CrossCompartmentWrapper(); michael@0: michael@0: /* ES5 Harmony fundamental wrapper traps. */ michael@0: virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) MOZ_OVERRIDE; michael@0: virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) MOZ_OVERRIDE; michael@0: virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; michael@0: virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE; michael@0: michael@0: /* ES5 Harmony derived wrapper traps. */ michael@0: virtual bool has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; michael@0: virtual bool hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; michael@0: virtual bool get(JSContext *cx, HandleObject wrapper, HandleObject receiver, michael@0: HandleId id, MutableHandleValue vp) MOZ_OVERRIDE; michael@0: virtual bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, michael@0: HandleId id, bool strict, MutableHandleValue vp) MOZ_OVERRIDE; michael@0: virtual bool keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE; michael@0: virtual bool iterate(JSContext *cx, HandleObject wrapper, unsigned flags, michael@0: MutableHandleValue vp) MOZ_OVERRIDE; michael@0: michael@0: /* Spidermonkey extensions. */ michael@0: virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) MOZ_OVERRIDE; michael@0: virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE; michael@0: virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE; michael@0: virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, michael@0: CallArgs args) MOZ_OVERRIDE; michael@0: virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v, michael@0: bool *bp) MOZ_OVERRIDE; michael@0: virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; michael@0: virtual JSString *fun_toString(JSContext *cx, HandleObject wrapper, michael@0: unsigned indent) MOZ_OVERRIDE; michael@0: virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; michael@0: virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, michael@0: MutableHandleValue vp) MOZ_OVERRIDE; michael@0: virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, michael@0: MutableHandleObject protop) MOZ_OVERRIDE; michael@0: virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, michael@0: bool *bp) MOZ_OVERRIDE; michael@0: michael@0: static CrossCompartmentWrapper singleton; michael@0: static CrossCompartmentWrapper singletonWithPrototype; michael@0: }; michael@0: michael@0: /* michael@0: * Base class for security wrappers. A security wrapper is potentially hiding michael@0: * all or part of some wrapped object thus SecurityWrapper defaults to denying michael@0: * access to the wrappee. This is the opposite of Wrapper which tries to be michael@0: * completely transparent. michael@0: * michael@0: * NB: Currently, only a few ProxyHandler operations are overridden to deny michael@0: * access, relying on derived SecurityWrapper to block access when necessary. michael@0: */ michael@0: template michael@0: class JS_FRIEND_API(SecurityWrapper) : public Base michael@0: { michael@0: public: michael@0: SecurityWrapper(unsigned flags); michael@0: michael@0: virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) MOZ_OVERRIDE; michael@0: virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) MOZ_OVERRIDE; michael@0: virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Wrapper::Action act, michael@0: bool *bp) MOZ_OVERRIDE; michael@0: virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, michael@0: CallArgs args) MOZ_OVERRIDE; michael@0: virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint, michael@0: MutableHandleValue vp) MOZ_OVERRIDE; michael@0: virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, michael@0: JSContext *cx) MOZ_OVERRIDE; michael@0: virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; michael@0: virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: michael@0: virtual bool setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, michael@0: bool *bp) MOZ_OVERRIDE; michael@0: michael@0: virtual bool watch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, michael@0: JS::HandleObject callable) MOZ_OVERRIDE; michael@0: virtual bool unwatch(JSContext *cx, JS::HandleObject proxy, JS::HandleId id) MOZ_OVERRIDE; michael@0: michael@0: /* michael@0: * Allow our subclasses to select the superclass behavior they want without michael@0: * needing to specify an exact superclass. michael@0: */ michael@0: typedef Base Permissive; michael@0: typedef SecurityWrapper Restrictive; michael@0: }; michael@0: michael@0: typedef SecurityWrapper SameCompartmentSecurityWrapper; michael@0: typedef SecurityWrapper CrossCompartmentSecurityWrapper; michael@0: michael@0: class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler michael@0: { michael@0: public: michael@0: // This variable exists solely to provide a unique address for use as an identifier. michael@0: static const char sDeadObjectFamily; michael@0: michael@0: explicit DeadObjectProxy(); michael@0: michael@0: /* ES5 Harmony fundamental wrapper traps. */ michael@0: virtual bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; michael@0: virtual bool getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, michael@0: MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool getOwnPropertyNames(JSContext *cx, HandleObject wrapper, michael@0: AutoIdVector &props) MOZ_OVERRIDE; michael@0: virtual bool delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp) MOZ_OVERRIDE; michael@0: virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE; michael@0: michael@0: /* Spidermonkey extensions. */ michael@0: virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE; michael@0: virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; michael@0: virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE; michael@0: virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, michael@0: CallArgs args) MOZ_OVERRIDE; michael@0: virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, michael@0: bool *bp) MOZ_OVERRIDE; michael@0: virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, michael@0: JSContext *cx) MOZ_OVERRIDE; michael@0: virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE; michael@0: virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE; michael@0: virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE; michael@0: virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, michael@0: MutableHandleValue vp) MOZ_OVERRIDE; michael@0: virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, michael@0: MutableHandleObject protop) MOZ_OVERRIDE; michael@0: michael@0: static DeadObjectProxy singleton; michael@0: }; michael@0: michael@0: extern JSObject * michael@0: TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj, michael@0: HandleObject wrappedProto, HandleObject parent, michael@0: unsigned flags); michael@0: michael@0: // Proxy family for wrappers. Public so that IsWrapper() can be fully inlined by michael@0: // jsfriendapi users. michael@0: // This variable exists solely to provide a unique address for use as an identifier. michael@0: extern JS_FRIEND_DATA(const char) sWrapperFamily; michael@0: michael@0: inline bool michael@0: IsWrapper(JSObject *obj) michael@0: { michael@0: return IsProxy(obj) && GetProxyHandler(obj)->family() == &sWrapperFamily; michael@0: } michael@0: michael@0: // Given a JSObject, returns that object stripped of wrappers. If michael@0: // stopAtOuter is true, then this returns the outer window if it was michael@0: // previously wrapped. Otherwise, this returns the first object for michael@0: // which JSObject::isWrapper returns false. michael@0: JS_FRIEND_API(JSObject *) michael@0: UncheckedUnwrap(JSObject *obj, bool stopAtOuter = true, unsigned *flagsp = nullptr); michael@0: michael@0: // Given a JSObject, returns that object stripped of wrappers. At each stage, michael@0: // the security wrapper has the opportunity to veto the unwrap. Since checked michael@0: // code should never be unwrapping outer window wrappers, we always stop at michael@0: // outer windows. michael@0: JS_FRIEND_API(JSObject *) michael@0: CheckedUnwrap(JSObject *obj, bool stopAtOuter = true); michael@0: michael@0: // Unwrap only the outermost security wrapper, with the same semantics as michael@0: // above. This is the checked version of Wrapper::wrappedObject. michael@0: JS_FRIEND_API(JSObject *) michael@0: UnwrapOneChecked(JSObject *obj, bool stopAtOuter = true); michael@0: michael@0: JS_FRIEND_API(bool) michael@0: IsCrossCompartmentWrapper(JSObject *obj); michael@0: michael@0: bool michael@0: IsDeadProxyObject(JSObject *obj); michael@0: michael@0: JSObject * michael@0: NewDeadProxyObject(JSContext *cx, JSObject *parent, michael@0: const ProxyOptions &options = ProxyOptions()); michael@0: michael@0: void michael@0: NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper); michael@0: michael@0: bool michael@0: RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget); michael@0: michael@0: JS_FRIEND_API(bool) michael@0: RemapAllWrappersForObject(JSContext *cx, JSObject *oldTarget, michael@0: JSObject *newTarget); michael@0: michael@0: // API to recompute all cross-compartment wrappers whose source and target michael@0: // match the given filters. michael@0: JS_FRIEND_API(bool) michael@0: RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter, michael@0: const CompartmentFilter &targetFilter); michael@0: michael@0: /* michael@0: * This auto class should be used around any code, such as brain transplants, michael@0: * that may touch dead zones. Brain transplants can cause problems michael@0: * because they operate on all compartments, whether live or dead. A brain michael@0: * transplant can cause a formerly dead object to be "reanimated" by causing a michael@0: * read or write barrier to be invoked on it during the transplant. In this way, michael@0: * a zone becomes a zombie, kept alive by repeatedly consuming michael@0: * (transplanted) brains. michael@0: * michael@0: * To work around this issue, we observe when mark bits are set on objects in michael@0: * dead zones. If this happens during a brain transplant, we do a full, michael@0: * non-incremental GC at the end of the brain transplant. This will clean up any michael@0: * objects that were improperly marked. michael@0: */ michael@0: struct JS_FRIEND_API(AutoMaybeTouchDeadZones) michael@0: { michael@0: // The version that takes an object just uses it for its runtime. michael@0: AutoMaybeTouchDeadZones(JSContext *cx); michael@0: AutoMaybeTouchDeadZones(JSObject *obj); michael@0: ~AutoMaybeTouchDeadZones(); michael@0: michael@0: private: michael@0: JSRuntime *runtime; michael@0: unsigned markCount; michael@0: bool inIncremental; michael@0: bool manipulatingDeadZones; michael@0: }; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* jswrapper_h */