summaryrefslogtreecommitdiffstats
path: root/dom/bindings/DOMJSClass.h
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/bindings/DOMJSClass.h
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/bindings/DOMJSClass.h')
-rw-r--r--dom/bindings/DOMJSClass.h467
1 files changed, 467 insertions, 0 deletions
diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h
new file mode 100644
index 000000000..6e779840f
--- /dev/null
+++ b/dom/bindings/DOMJSClass.h
@@ -0,0 +1,467 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DOMJSClass_h
+#define mozilla_dom_DOMJSClass_h
+
+#include "jsfriendapi.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Likely.h"
+
+#include "mozilla/dom/PrototypeList.h" // auto-generated
+
+#include "mozilla/dom/JSSlots.h"
+
+class nsCycleCollectionParticipant;
+
+// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
+#define DOM_PROTOTYPE_SLOT JSCLASS_GLOBAL_SLOT_COUNT
+
+// Keep this count up to date with any extra global slots added above.
+#define DOM_GLOBAL_SLOTS 1
+
+// We use these flag bits for the new bindings.
+#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT1
+#define JSCLASS_IS_DOMIFACEANDPROTOJSCLASS JSCLASS_USERBIT2
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Returns true if code running in the given JSContext is allowed to access
+ * [SecureContext] API on the given JSObject.
+ *
+ * [SecureContext] API exposure is restricted to use by code in a Secure
+ * Contexts:
+ *
+ * https://w3c.github.io/webappsec-secure-contexts/
+ *
+ * Since we want [SecureContext] exposure to depend on the privileges of the
+ * running code (rather than the privileges of an object's creator), this
+ * function checks to see whether the given JSContext's Compartment is flagged
+ * as a Secure Context. That allows us to make sure that system principal code
+ * (which is marked as a Secure Context) can access Secure Context API on an
+ * object in a different compartment, regardless of whether the other
+ * compartment is a Secure Context or not.
+ *
+ * Checking the JSContext's Compartment doesn't work for expanded principal
+ * globals accessing a Secure Context web page though (e.g. those used by frame
+ * scripts). To handle that we fall back to checking whether the JSObject came
+ * from a Secure Context.
+ *
+ * Note: We'd prefer this function to live in BindingUtils.h, but we need to
+ * call it in this header, and BindingUtils.h includes us (i.e. we'd have a
+ * circular dependency between headers if it lived there).
+ */
+inline bool
+IsSecureContextOrObjectIsFromSecureContext(JSContext* aCx, JSObject* aObj)
+{
+ return JS::CompartmentCreationOptionsRef(js::GetContextCompartment(aCx)).secureContext() ||
+ JS::CompartmentCreationOptionsRef(js::GetObjectCompartment(aObj)).secureContext();
+}
+
+typedef bool
+(* ResolveOwnProperty)(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc);
+
+typedef bool
+(* EnumerateOwnProperties)(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj,
+ JS::AutoIdVector& props);
+
+typedef bool
+(* DeleteNamedProperty)(JSContext* cx, JS::Handle<JSObject*> wrapper,
+ JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+ JS::ObjectOpResult& opresult);
+
+// Returns true if the given global is of a type whose bit is set in
+// aNonExposedGlobals.
+bool
+IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
+ uint32_t aNonExposedGlobals);
+
+struct ConstantSpec
+{
+ const char* name;
+ JS::Value value;
+};
+
+typedef bool (*PropertyEnabled)(JSContext* cx, JSObject* global);
+
+namespace GlobalNames {
+// The names of our possible globals. These are the names of the actual
+// interfaces, not of the global names used to refer to them in IDL [Exposed]
+// annotations.
+static const uint32_t Window = 1u << 0;
+static const uint32_t BackstagePass = 1u << 1;
+static const uint32_t DedicatedWorkerGlobalScope = 1u << 2;
+static const uint32_t SharedWorkerGlobalScope = 1u << 3;
+static const uint32_t ServiceWorkerGlobalScope = 1u << 4;
+static const uint32_t WorkerDebuggerGlobalScope = 1u << 5;
+static const uint32_t WorkletGlobalScope = 1u << 6;
+} // namespace GlobalNames
+
+struct PrefableDisablers {
+ inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
+ // Reading "enabled" on a worker thread is technically undefined behavior,
+ // because it's written only on main threads, with no barriers of any sort.
+ // So we want to avoid doing that. But we don't particularly want to make
+ // expensive NS_IsMainThread calls here.
+ //
+ // The good news is that "enabled" is only written for things that have a
+ // Pref annotation, and such things can never be exposed on non-Window
+ // globals; our IDL parser enforces that. So as long as we check our
+ // exposure set before checking "enabled" we will be ok.
+ if (nonExposedGlobals &&
+ IsNonExposedGlobal(cx, js::GetGlobalForObjectCrossCompartment(obj),
+ nonExposedGlobals)) {
+ return false;
+ }
+ if (!enabled) {
+ return false;
+ }
+ if (secureContext && !IsSecureContextOrObjectIsFromSecureContext(cx, obj)) {
+ return false;
+ }
+ if (enabledFunc &&
+ !enabledFunc(cx, js::GetGlobalForObjectCrossCompartment(obj))) {
+ return false;
+ }
+ return true;
+ }
+
+ // A boolean indicating whether this set of specs is enabled. Not const
+ // because it will change at runtime if the corresponding pref is changed.
+ bool enabled;
+
+ // A boolean indicating whether a Secure Context is required.
+ const bool secureContext;
+
+ // Bitmask of global names that we should not be exposed in.
+ const uint16_t nonExposedGlobals;
+
+ // A function pointer to a function that can say the property is disabled
+ // even if "enabled" is set to true. If the pointer is null the value of
+ // "enabled" is used as-is.
+ const PropertyEnabled enabledFunc;
+};
+
+template<typename T>
+struct Prefable {
+ inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
+ if (MOZ_LIKELY(!disablers)) {
+ return true;
+ }
+ return disablers->isEnabled(cx, obj);
+ }
+
+ // Things that can disable this set of specs. |nullptr| means "cannot be
+ // disabled".
+ PrefableDisablers* const disablers;
+
+ // Array of specs, terminated in whatever way is customary for T.
+ // Null to indicate a end-of-array for Prefable, when such an
+ // indicator is needed.
+ const T* const specs;
+};
+
+// Conceptually, NativeProperties has seven (Prefable<T>*, jsid*, T*) trios
+// (where T is one of JSFunctionSpec, JSPropertySpec, or ConstantSpec), one for
+// each of: static methods and attributes, methods and attributes, unforgeable
+// methods and attributes, and constants.
+//
+// That's 21 pointers, but in most instances most of the trios are all null,
+// and there are many instances. To save space we use a variable-length type,
+// NativePropertiesN<N>, to hold the data and getters to access it. It has N
+// actual trios (stored in trios[]), plus four bits for each of the 7 possible
+// trios: 1 bit that states if that trio is present, and 3 that state that
+// trio's offset (if present) in trios[].
+//
+// All trio accesses should be done via the getters, which contain assertions
+// that check we don't overrun the end of the struct. (The trio data members are
+// public only so they can be statically initialized.) These assertions should
+// never fail so long as (a) accesses to the variable-length part are guarded by
+// appropriate Has*() calls, and (b) all instances are well-formed, i.e. the
+// value of N matches the number of mHas* members that are true.
+//
+// Finally, we define a typedef of NativePropertiesN<7>, NativeProperties, which
+// we use as a "base" type used to refer to all instances of NativePropertiesN.
+// (7 is used because that's the maximum valid parameter, though any other
+// value 1..6 could also be used.) This is reasonable because of the
+// aforementioned assertions in the getters. Upcast() is used to convert
+// specific instances to this "base" type.
+//
+template <int N>
+struct NativePropertiesN {
+ // Trio structs are stored in the trios[] array, and each element in the
+ // array could require a different T. Therefore, we can't use the correct
+ // type for mPrefables and mSpecs. Instead we use void* and cast to the
+ // correct type in the getters.
+ struct Trio {
+ const /*Prefable<const T>*/ void* const mPrefables;
+ const jsid* const mIds;
+ const /*T*/ void* const mSpecs;
+ };
+
+ const int32_t iteratorAliasMethodIndex;
+
+ constexpr const NativePropertiesN<7>* Upcast() const {
+ return reinterpret_cast<const NativePropertiesN<7>*>(this);
+ }
+
+#define DO(SpecT, FieldName) \
+public: \
+ /* The bitfields indicating the trio's presence and (if present) offset. */ \
+ const uint32_t mHas##FieldName##s:1; \
+ const uint32_t m##FieldName##sOffset:3; \
+private: \
+ const Trio* FieldName##sTrio() const { \
+ MOZ_ASSERT(Has##FieldName##s()); \
+ return &trios[m##FieldName##sOffset]; \
+ } \
+public: \
+ bool Has##FieldName##s() const { \
+ return mHas##FieldName##s; \
+ } \
+ const Prefable<const SpecT>* FieldName##s() const { \
+ return static_cast<const Prefable<const SpecT>*> \
+ (FieldName##sTrio()->mPrefables); \
+ } \
+ const jsid* FieldName##Ids() const { \
+ return FieldName##sTrio()->mIds; \
+ } \
+ const SpecT* FieldName##Specs() const { \
+ return static_cast<const SpecT*>(FieldName##sTrio()->mSpecs); \
+ }
+
+ DO(JSFunctionSpec, StaticMethod)
+ DO(JSPropertySpec, StaticAttribute)
+ DO(JSFunctionSpec, Method)
+ DO(JSPropertySpec, Attribute)
+ DO(JSFunctionSpec, UnforgeableMethod)
+ DO(JSPropertySpec, UnforgeableAttribute)
+ DO(ConstantSpec, Constant)
+
+#undef DO
+
+ const Trio trios[N];
+};
+
+// Ensure the struct has the expected size. The 8 is for the
+// iteratorAliasMethodIndex plus the bitfields; the rest is for trios[].
+static_assert(sizeof(NativePropertiesN<1>) == 8 + 3*sizeof(void*), "1 size");
+static_assert(sizeof(NativePropertiesN<2>) == 8 + 6*sizeof(void*), "2 size");
+static_assert(sizeof(NativePropertiesN<3>) == 8 + 9*sizeof(void*), "3 size");
+static_assert(sizeof(NativePropertiesN<4>) == 8 + 12*sizeof(void*), "4 size");
+static_assert(sizeof(NativePropertiesN<5>) == 8 + 15*sizeof(void*), "5 size");
+static_assert(sizeof(NativePropertiesN<6>) == 8 + 18*sizeof(void*), "6 size");
+static_assert(sizeof(NativePropertiesN<7>) == 8 + 21*sizeof(void*), "7 size");
+
+// The "base" type.
+typedef NativePropertiesN<7> NativeProperties;
+
+struct NativePropertiesHolder
+{
+ const NativeProperties* regular;
+ const NativeProperties* chromeOnly;
+};
+
+// Helper structure for Xrays for DOM binding objects. The same instance is used
+// for instances, interface objects and interface prototype objects of a
+// specific interface.
+struct NativePropertyHooks
+{
+ // The hook to call for resolving indexed or named properties. May be null if
+ // there can't be any.
+ ResolveOwnProperty mResolveOwnProperty;
+ // The hook to call for enumerating indexed or named properties. May be null
+ // if there can't be any.
+ EnumerateOwnProperties mEnumerateOwnProperties;
+ // The hook to call to delete a named property. May be null if there are no
+ // named properties or no named property deleter. On success (true return)
+ // the "found" argument will be set to true if there was in fact such a named
+ // property and false otherwise. If it's set to false, the caller is expected
+ // to proceed with whatever deletion behavior it would have if there were no
+ // named properties involved at all (i.e. if the hook were null). If it's set
+ // to true, it will indicate via opresult whether the delete actually
+ // succeeded.
+ DeleteNamedProperty mDeleteNamedProperty;
+
+ // The property arrays for this interface.
+ NativePropertiesHolder mNativeProperties;
+
+ // This will be set to the ID of the interface prototype object for the
+ // interface, if it has one. If it doesn't have one it will be set to
+ // prototypes::id::_ID_Count.
+ prototypes::ID mPrototypeID;
+
+ // This will be set to the ID of the interface object for the interface, if it
+ // has one. If it doesn't have one it will be set to
+ // constructors::id::_ID_Count.
+ constructors::ID mConstructorID;
+
+ // The NativePropertyHooks instance for the parent interface (for
+ // ShimInterfaceInfo).
+ const NativePropertyHooks* mProtoHooks;
+
+ // The JSClass to use for expandos on our Xrays. Can be null, in which case
+ // Xrays will use a default class of their choice.
+ const JSClass* mXrayExpandoClass;
+};
+
+enum DOMObjectType : uint8_t {
+ eInstance,
+ eGlobalInstance,
+ eInterface,
+ eInterfacePrototype,
+ eGlobalInterfacePrototype,
+ eNamedPropertiesObject
+};
+
+inline
+bool
+IsInstance(DOMObjectType type)
+{
+ return type == eInstance || type == eGlobalInstance;
+}
+
+inline
+bool
+IsInterfacePrototype(DOMObjectType type)
+{
+ return type == eInterfacePrototype || type == eGlobalInterfacePrototype;
+}
+
+typedef JSObject* (*AssociatedGlobalGetter)(JSContext* aCx,
+ JS::Handle<JSObject*> aObj);
+
+typedef JSObject* (*ProtoGetter)(JSContext* aCx);
+
+/**
+ * Returns a handle to the relevant WebIDL prototype object for the current
+ * compartment global (which may be a handle to null on out of memory). Once
+ * allocated, the prototype object is guaranteed to exist as long as the global
+ * does, since the global traces its array of WebIDL prototypes and
+ * constructors.
+ */
+typedef JS::Handle<JSObject*> (*ProtoHandleGetter)(JSContext* aCx);
+
+// Special JSClass for reflected DOM objects.
+struct DOMJSClass
+{
+ // It would be nice to just inherit from JSClass, but that precludes pure
+ // compile-time initialization of the form |DOMJSClass = {...};|, since C++
+ // only allows brace initialization for aggregate/POD types.
+ const js::Class mBase;
+
+ // A list of interfaces that this object implements, in order of decreasing
+ // derivedness.
+ const prototypes::ID mInterfaceChain[MAX_PROTOTYPE_CHAIN_LENGTH];
+
+ // We store the DOM object in reserved slot with index DOM_OBJECT_SLOT or in
+ // the proxy private if we use a proxy object.
+ // Sometimes it's an nsISupports and sometimes it's not; this class tells
+ // us which it is.
+ const bool mDOMObjectIsISupports;
+
+ const NativePropertyHooks* mNativeHooks;
+
+ // A callback to find the associated global for our C++ object. Note that
+ // this is used in cases when that global is _changing_, so it will not match
+ // the global of the JSObject* passed in to this function!
+ AssociatedGlobalGetter mGetAssociatedGlobal;
+ ProtoHandleGetter mGetProto;
+
+ // This stores the CC participant for the native, null if this class does not
+ // implement cycle collection or if it inherits from nsISupports (we can get
+ // the CC participant by QI'ing in that case).
+ nsCycleCollectionParticipant* mParticipant;
+
+ static const DOMJSClass* FromJSClass(const JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS);
+ return reinterpret_cast<const DOMJSClass*>(base);
+ }
+
+ static const DOMJSClass* FromJSClass(const js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+
+ const JSClass* ToJSClass() const { return Jsvalify(&mBase); }
+};
+
+// Special JSClass for DOM interface and interface prototype objects.
+struct DOMIfaceAndProtoJSClass
+{
+ // It would be nice to just inherit from js::Class, but that precludes pure
+ // compile-time initialization of the form
+ // |DOMJSInterfaceAndPrototypeClass = {...};|, since C++ only allows brace
+ // initialization for aggregate/POD types.
+ const js::Class mBase;
+
+ // Either eInterface, eInterfacePrototype, eGlobalInterfacePrototype or
+ // eNamedPropertiesObject.
+ DOMObjectType mType; // uint8_t
+
+ // Boolean indicating whether this object wants a @@hasInstance property
+ // pointing to InterfaceHasInstance defined on it. Only ever true for the
+ // eInterface case.
+ bool wantsInterfaceHasInstance;
+
+ const prototypes::ID mPrototypeID; // uint16_t
+ const uint32_t mDepth;
+
+ const NativePropertyHooks* mNativeHooks;
+
+ // The value to return for toString() on this interface or interface prototype
+ // object.
+ const char* mToString;
+
+ ProtoGetter mGetParentProto;
+
+ static const DOMIfaceAndProtoJSClass* FromJSClass(const JSClass* base) {
+ MOZ_ASSERT(base->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS);
+ return reinterpret_cast<const DOMIfaceAndProtoJSClass*>(base);
+ }
+ static const DOMIfaceAndProtoJSClass* FromJSClass(const js::Class* base) {
+ return FromJSClass(Jsvalify(base));
+ }
+
+ const JSClass* ToJSClass() const { return Jsvalify(&mBase); }
+};
+
+class ProtoAndIfaceCache;
+
+inline bool
+DOMGlobalHasProtoAndIFaceCache(JSObject* global)
+{
+ MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
+ // This can be undefined if we GC while creating the global
+ return !js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).isUndefined();
+}
+
+inline bool
+HasProtoAndIfaceCache(JSObject* global)
+{
+ if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
+ return false;
+ }
+ return DOMGlobalHasProtoAndIFaceCache(global);
+}
+
+inline ProtoAndIfaceCache*
+GetProtoAndIfaceCache(JSObject* global)
+{
+ MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
+ return static_cast<ProtoAndIfaceCache*>(
+ js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate());
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_DOMJSClass_h */