michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 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 file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_dom_DOMJSProxyHandler_h michael@0: #define mozilla_dom_DOMJSProxyHandler_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Likely.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsproxy.h" michael@0: #include "nsString.h" michael@0: michael@0: #define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: class DOMClass; michael@0: michael@0: enum { michael@0: JSPROXYSLOT_EXPANDO = 0 michael@0: }; michael@0: michael@0: template struct Prefable; michael@0: michael@0: // This variable exists solely to provide a unique address for use as an identifier. michael@0: extern const char HandlerFamily; michael@0: inline const void* ProxyFamily() { return &HandlerFamily; } michael@0: michael@0: inline bool IsDOMProxy(JSObject *obj) michael@0: { michael@0: const js::Class* clasp = js::GetObjectClass(obj); michael@0: return clasp->isProxy() && michael@0: js::GetProxyHandler(obj)->family() == ProxyFamily(); michael@0: } michael@0: michael@0: class BaseDOMProxyHandler : public js::BaseProxyHandler michael@0: { michael@0: public: michael@0: BaseDOMProxyHandler(const void* aProxyFamily) michael@0: : js::BaseProxyHandler(aProxyFamily) michael@0: {} michael@0: michael@0: // Implementations of traps that can be implemented in terms of michael@0: // fundamental traps. michael@0: bool enumerate(JSContext* cx, JS::Handle proxy, michael@0: JS::AutoIdVector& props) MOZ_OVERRIDE; michael@0: bool getPropertyDescriptor(JSContext* cx, JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) MOZ_OVERRIDE; michael@0: bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) MOZ_OVERRIDE; michael@0: michael@0: bool watch(JSContext* cx, JS::Handle proxy, JS::Handle id, michael@0: JS::Handle callable) MOZ_OVERRIDE; michael@0: bool unwatch(JSContext* cx, JS::Handle proxy, michael@0: JS::Handle id) MOZ_OVERRIDE; michael@0: virtual bool getOwnPropertyNames(JSContext* cx, JS::Handle proxy, michael@0: JS::AutoIdVector &props) MOZ_OVERRIDE; michael@0: // We override keys() and implement it directly instead of using the michael@0: // default implementation, which would getOwnPropertyNames and then michael@0: // filter out the non-enumerable ones. This avoids doing michael@0: // unnecessary work during enumeration. michael@0: virtual bool keys(JSContext* cx, JS::Handle proxy, michael@0: JS::AutoIdVector &props) MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: // Hook for subclasses to implement shared getOwnPropertyNames()/keys() michael@0: // functionality. The "flags" argument is either JSITER_OWNONLY (for keys()) michael@0: // or JSITER_OWNONLY | JSITER_HIDDEN (for getOwnPropertyNames()). michael@0: virtual bool ownPropNames(JSContext* cx, JS::Handle proxy, michael@0: unsigned flags, michael@0: JS::AutoIdVector& props) = 0; michael@0: michael@0: // Hook for subclasses to allow set() to ignore named props while other things michael@0: // that look at property descriptors see them. This is intentionally not michael@0: // named getOwnPropertyDescriptor to avoid subclasses that override it hiding michael@0: // our public getOwnPropertyDescriptor. michael@0: virtual bool getOwnPropDescriptor(JSContext* cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: bool ignoreNamedProps, michael@0: JS::MutableHandle desc) = 0; michael@0: }; michael@0: michael@0: class DOMProxyHandler : public BaseDOMProxyHandler michael@0: { michael@0: public: michael@0: DOMProxyHandler() michael@0: : BaseDOMProxyHandler(ProxyFamily()) michael@0: { michael@0: } michael@0: michael@0: bool preventExtensions(JSContext *cx, JS::Handle proxy) MOZ_OVERRIDE; michael@0: bool defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, michael@0: JS::MutableHandle desc) MOZ_OVERRIDE michael@0: { michael@0: bool unused; michael@0: return defineProperty(cx, proxy, id, desc, &unused); michael@0: } michael@0: virtual bool defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, michael@0: JS::MutableHandle desc, bool* defined); michael@0: bool set(JSContext *cx, JS::Handle proxy, JS::Handle receiver, michael@0: JS::Handle id, bool strict, JS::MutableHandle vp) MOZ_OVERRIDE; michael@0: bool delete_(JSContext* cx, JS::Handle proxy, michael@0: JS::Handle id, bool* bp) MOZ_OVERRIDE; michael@0: bool has(JSContext* cx, JS::Handle proxy, JS::Handle id, bool* bp) MOZ_OVERRIDE; michael@0: bool isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) MOZ_OVERRIDE; michael@0: michael@0: /* michael@0: * If assigning to proxy[id] hits a named setter with OverrideBuiltins or michael@0: * an indexed setter, call it and set *done to true on success. Otherwise, set michael@0: * *done to false. michael@0: */ michael@0: virtual bool setCustom(JSContext* cx, JS::Handle proxy, JS::Handle id, michael@0: JS::MutableHandle vp, bool *done); michael@0: michael@0: static JSObject* GetExpandoObject(JSObject* obj) michael@0: { michael@0: MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object"); michael@0: JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); michael@0: if (v.isObject()) { michael@0: return &v.toObject(); michael@0: } michael@0: michael@0: if (v.isUndefined()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: js::ExpandoAndGeneration* expandoAndGeneration = michael@0: static_cast(v.toPrivate()); michael@0: v = expandoAndGeneration->expando; michael@0: return v.isUndefined() ? nullptr : &v.toObject(); michael@0: } michael@0: /* GetAndClearExpandoObject does not DROP or clear the preserving wrapper flag. */ michael@0: static JSObject* GetAndClearExpandoObject(JSObject* obj); michael@0: static JSObject* EnsureExpandoObject(JSContext* cx, michael@0: JS::Handle obj); michael@0: }; michael@0: michael@0: inline DOMProxyHandler* michael@0: GetDOMProxyHandler(JSObject* obj) michael@0: { michael@0: MOZ_ASSERT(IsDOMProxy(obj)); michael@0: return static_cast(js::GetProxyHandler(obj)); michael@0: } michael@0: michael@0: extern jsid s_length_id; michael@0: michael@0: int32_t IdToInt32(JSContext* cx, JS::Handle id); michael@0: michael@0: // XXXbz this should really return uint32_t, with the maximum value michael@0: // meaning "not an index"... michael@0: inline int32_t michael@0: GetArrayIndexFromId(JSContext* cx, JS::Handle id) michael@0: { michael@0: if (MOZ_LIKELY(JSID_IS_INT(id))) { michael@0: return JSID_TO_INT(id); michael@0: } michael@0: if (MOZ_LIKELY(id == s_length_id)) { michael@0: return -1; michael@0: } michael@0: if (MOZ_LIKELY(JSID_IS_ATOM(id))) { michael@0: JSAtom* atom = JSID_TO_ATOM(id); michael@0: jschar s = *js::GetAtomChars(atom); michael@0: if (MOZ_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z')) michael@0: return -1; michael@0: michael@0: uint32_t i; michael@0: JSLinearString* str = js::AtomToLinearString(JSID_TO_ATOM(id)); michael@0: return js::StringIsArrayIndex(str, &i) ? i : -1; michael@0: } michael@0: return IdToInt32(cx, id); michael@0: } michael@0: michael@0: inline bool michael@0: IsArrayIndex(int32_t index) michael@0: { michael@0: return index >= 0; michael@0: } michael@0: michael@0: inline void michael@0: FillPropertyDescriptor(JS::MutableHandle desc, michael@0: JSObject* obj, bool readonly, bool enumerable = true) michael@0: { michael@0: desc.object().set(obj); michael@0: desc.setAttributes((readonly ? JSPROP_READONLY : 0) | michael@0: (enumerable ? JSPROP_ENUMERATE : 0)); michael@0: desc.setGetter(nullptr); michael@0: desc.setSetter(nullptr); michael@0: } michael@0: michael@0: inline void michael@0: FillPropertyDescriptor(JS::MutableHandle desc, michael@0: JSObject* obj, JS::Value v, michael@0: bool readonly, bool enumerable = true) michael@0: { michael@0: desc.value().set(v); michael@0: FillPropertyDescriptor(desc, obj, readonly, enumerable); michael@0: } michael@0: michael@0: inline void michael@0: FillPropertyDescriptor(JS::MutableHandle desc, michael@0: JSObject* obj, unsigned attributes, JS::Value v) michael@0: { michael@0: desc.object().set(obj); michael@0: desc.value().set(v); michael@0: desc.setAttributes(attributes); michael@0: desc.setGetter(nullptr); michael@0: desc.setSetter(nullptr); michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: #endif /* mozilla_dom_DOMProxyHandler_h */