summaryrefslogtreecommitdiffstats
path: root/dom/bindings/BindingUtils.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/bindings/BindingUtils.h')
-rw-r--r--dom/bindings/BindingUtils.h204
1 files changed, 145 insertions, 59 deletions
diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h
index dd7a42b670..1e3049d456 100644
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -698,12 +698,45 @@ struct JSNativeHolder {
const NativePropertyHooks* mPropertyHooks;
};
+// Struct for holding information for WebIDL interface objects (which are
+// function objects). A pointer to this struct is held in the first reserved
+// slot of the function object.
+struct DOMInterfaceInfo {
+ JSNativeHolder nativeHolder;
+
+ ProtoGetter mGetParentProto;
+
+ const prototypes::ID mPrototypeID; // uint16_t
+ const uint32_t mDepth;
+
+ // Boolean indicating whether this object wants a isInstance property
+ // pointing to InterfaceIsInstance defined on it. Only ever true for
+ // interfaces.
+ bool wantsInterfaceIsInstance;
+};
+
struct LegacyFactoryFunction {
const char* mName;
const JSNativeHolder mHolder;
unsigned mNargs;
};
+namespace binding_detail {
+
+void CreateInterfaceObjects(
+ JSContext* cx, JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
+ JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto,
+ const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs,
+ bool isConstructorChromeOnly,
+ const Span<const LegacyFactoryFunction>& legacyFactoryFunctions,
+ JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
+ const NativeProperties* chromeOnlyProperties, const char* name,
+ bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal,
+ const char* const* legacyWindowAliases);
+
+} // namespace binding_detail
+
// clang-format off
/*
* Create a DOM interface object (if constructorClass is non-null) and/or a
@@ -712,24 +745,20 @@ struct LegacyFactoryFunction {
* global is used as the parent of the interface object and the interface
* prototype object
* protoProto is the prototype to use for the interface prototype object.
- * interfaceProto is the prototype to use for the interface object. This can be
- * null if both constructorClass and constructor are null (as in,
- * if we're not creating an interface object at all).
* protoClass is the JSClass to use for the interface prototype object.
* This is null if we should not create an interface prototype
* object.
* protoCache a pointer to a JSObject pointer where we should cache the
* interface prototype object. This must be null if protoClass is and
* vice versa.
- * constructorClass is the JSClass to use for the interface object.
- * This is null if we should not create an interface object or
- * if it should be a function object.
- * constructor holds the JSNative to back the interface object which should be a
- * Function, unless constructorClass is non-null in which case it is
- * ignored. If this is null and constructorClass is also null then
- * we should not create an interface object at all.
+ * interfaceProto is the prototype to use for the interface object. This can be
+ * null if interfaceInfo is null (as in, if we're not creating an
+ * interface object at all).
+ * interfaceInfo is the info to use for the interface object. This can be null
+ * if we're not creating an interface object.
* ctorNargs is the length of the constructor function; 0 if no constructor
* isConstructorChromeOnly if true, the constructor is ChromeOnly.
+ * legacyFactoryFunctions the legacy factory functions (can be empty)
* constructorCache a pointer to a JSObject pointer where we should cache the
* interface object. This must be null if both constructorClass
* and constructor are null, and non-null otherwise.
@@ -761,23 +790,66 @@ struct LegacyFactoryFunction {
* char* names of the legacy window aliases for this
* interface.
*
- * At least one of protoClass, constructorClass or constructor should be
- * non-null. If constructorClass or constructor are non-null, the resulting
- * interface object will be defined on the given global with property name
- * |name|, which must also be non-null.
+ * At least one of protoClass or interfaceInfo should be non-null. If
+ * interfaceInfo is non-null, the resulting interface object will be defined on
+ * the given global with property name |name|, which must also be non-null.
*/
// clang-format on
-void CreateInterfaceObjects(
+template <size_t N>
+inline void CreateInterfaceObjects(
JSContext* cx, JS::Handle<JSObject*> global,
JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
- JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> constructorProto,
- const DOMIfaceJSClass* constructorClass, unsigned ctorNargs,
+ JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto,
+ const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs,
bool isConstructorChromeOnly,
- const LegacyFactoryFunction* legacyFactoryFunctions,
+ const Span<const LegacyFactoryFunction, N>& legacyFactoryFunctions,
JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
const NativeProperties* chromeOnlyProperties, const char* name,
bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal,
- const char* const* legacyWindowAliases, bool isNamespace);
+ const char* const* legacyWindowAliases) {
+ // We're using 1 slot for the interface info already, so we only have
+ // INTERFACE_OBJECT_MAX_SLOTS - 1 slots for legacy factory functions.
+ static_assert(N <= INTERFACE_OBJECT_MAX_SLOTS -
+ INTERFACE_OBJECT_FIRST_LEGACY_FACTORY_FUNCTION);
+
+ return binding_detail::CreateInterfaceObjects(
+ cx, global, protoProto, protoClass, protoCache, interfaceProto,
+ interfaceInfo, ctorNargs, isConstructorChromeOnly, legacyFactoryFunctions,
+ constructorCache, properties, chromeOnlyProperties, name, defineOnGlobal,
+ unscopableNames, isGlobal, legacyWindowAliases);
+}
+
+/*
+ * Create a namespace object.
+ *
+ * global the global on which to install a property named with name pointing to
+ * the namespace object if defineOnGlobal is true.
+ * namespaceProto is the prototype to use for the namespace object.
+ * namespaceClass is the JSClass to use for the namespace object.
+ * namespaceCache a pointer to a JSObject pointer where we should cache the
+ * namespace object.
+ * properties contains the methods, attributes and constants to be defined on
+ * objects in any compartment.
+ * chromeProperties contains the methods, attributes and constants to be defined
+ * on objects in chrome compartments. This must be null if the
+ * namespace doesn't have any ChromeOnly properties or if the
+ * object is being created in non-chrome compartment.
+ * name the name to use for the WebIDL class string, which is the value
+ * that's used for @@toStringTag, and the name of the property on the
+ * global object that would be set to the namespace object.
+ * defineOnGlobal controls whether properties should be defined on the given
+ * global for the namespace object. This can be false in
+ * situations where we want the properties to only appear on
+ * privileged Xrays but not on the unprivileged underlying
+ * global.
+ */
+void CreateNamespaceObject(JSContext* cx, JS::Handle<JSObject*> global,
+ JS::Handle<JSObject*> namespaceProto,
+ const DOMIfaceAndProtoJSClass& namespaceClass,
+ JS::Heap<JSObject*>* namespaceCache,
+ const NativeProperties* properties,
+ const NativeProperties* chromeOnlyProperties,
+ const char* name, bool defineOnGlobal);
/**
* Define the properties (regular and chrome-only) on obj.
@@ -2267,18 +2339,53 @@ inline bool AddStringToIDVector(JSContext* cx,
name);
}
-// We use one constructor JSNative to represent all DOM interface objects (so
-// we can easily detect when we need to wrap them in an Xray wrapper). We store
-// the real JSNative in the mNative member of a JSNativeHolder in the
-// CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
-// specific interface object. We also store the NativeProperties in the
-// JSNativeHolder.
-// Note that some interface objects are not yet a JSFunction but a normal
-// JSObject with a DOMJSClass, those do not use these slots.
+// We use one JSNative to represent all DOM interface objects (so we can easily
+// detect when we need to wrap them in an Xray wrapper). A pointer to the
+// relevant DOMInterfaceInfo is stored in the
+// INTERFACE_OBJECT_INFO_RESERVED_SLOT slot of the JSFunction object for a
+// specific interface object. We store the real JSNative and the
+// NativeProperties in a JSNativeHolder, held by the DOMInterfaceInfo.
+bool InterfaceObjectJSNative(JSContext* cx, unsigned argc, JS::Value* vp);
+
+inline bool IsInterfaceObject(JSObject* obj) {
+ return JS_IsNativeFunction(obj, InterfaceObjectJSNative);
+}
+
+inline const DOMInterfaceInfo* InterfaceInfoFromObject(JSObject* obj) {
+ MOZ_ASSERT(IsInterfaceObject(obj));
+ const JS::Value& v =
+ js::GetFunctionNativeReserved(obj, INTERFACE_OBJECT_INFO_RESERVED_SLOT);
+ return static_cast<const DOMInterfaceInfo*>(v.toPrivate());
+}
-enum { CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0 };
+inline const JSNativeHolder* NativeHolderFromInterfaceObject(JSObject* obj) {
+ MOZ_ASSERT(IsInterfaceObject(obj));
+ return &InterfaceInfoFromObject(obj)->nativeHolder;
+}
-bool Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
+// We use one JSNative to represent all legacy factory functions (so we can
+// easily detect when we need to wrap them in an Xray wrapper). We store the
+// real JSNative and the NativeProperties in a JSNativeHolder in the
+// LEGACY_FACTORY_FUNCTION_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction
+// object.
+bool LegacyFactoryFunctionJSNative(JSContext* cx, unsigned argc, JS::Value* vp);
+
+inline bool IsLegacyFactoryFunction(JSObject* obj) {
+ return JS_IsNativeFunction(obj, LegacyFactoryFunctionJSNative);
+}
+
+inline const JSNativeHolder* NativeHolderFromLegacyFactoryFunction(
+ JSObject* obj) {
+ MOZ_ASSERT(IsLegacyFactoryFunction(obj));
+ const JS::Value& v = js::GetFunctionNativeReserved(
+ obj, LEGACY_FACTORY_FUNCTION_NATIVE_HOLDER_RESERVED_SLOT);
+ return static_cast<const JSNativeHolder*>(v.toPrivate());
+}
+
+inline const JSNativeHolder* NativeHolderFromObject(JSObject* obj) {
+ return IsInterfaceObject(obj) ? NativeHolderFromInterfaceObject(obj)
+ : NativeHolderFromLegacyFactoryFunction(obj);
+}
// Implementation of the bits that XrayWrapper needs
@@ -2350,8 +2457,11 @@ inline bool XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
protop.set(JS::GetRealmObjectPrototype(cx));
}
} else if (JS_ObjectIsFunction(obj)) {
- MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
- protop.set(JS::GetRealmFunctionPrototype(cx));
+ if (IsLegacyFactoryFunction(obj)) {
+ protop.set(JS::GetRealmFunctionPrototype(cx));
+ } else {
+ protop.set(InterfaceInfoFromObject(obj)->mGetParentProto(cx));
+ }
} else {
const JSClass* clasp = JS::GetClass(obj);
MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
@@ -2426,36 +2536,16 @@ inline JSObject* GetCachedSlotStorageObject(JSContext* cx,
extern NativePropertyHooks sEmptyNativePropertyHooks;
-extern const JSClassOps sBoringInterfaceObjectClassClassOps;
-
-extern const js::ObjectOps sInterfaceObjectClassObjectOps;
+inline bool IsDOMConstructor(JSObject* obj) {
+ return IsInterfaceObject(obj) || IsLegacyFactoryFunction(obj);
+}
inline bool UseDOMXray(JSObject* obj) {
const JSClass* clasp = JS::GetClass(obj);
- return IsDOMClass(clasp) || JS_IsNativeFunction(obj, Constructor) ||
+ return IsDOMClass(clasp) || IsDOMConstructor(obj) ||
IsDOMIfaceAndProtoClass(clasp);
}
-inline bool IsDOMConstructor(JSObject* obj) {
- if (JS_IsNativeFunction(obj, dom::Constructor)) {
- // LegacyFactoryFunction, like Image
- return true;
- }
-
- const JSClass* clasp = JS::GetClass(obj);
- // Check for a DOM interface object.
- return dom::IsDOMIfaceAndProtoClass(clasp) &&
- dom::DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mType ==
- dom::eInterface;
-}
-
-#ifdef DEBUG
-inline bool HasConstructor(JSObject* obj) {
- return JS_IsNativeFunction(obj, Constructor) ||
- JS::GetClass(obj)->getConstruct();
-}
-#endif
-
// Helpers for creating a const version of a type.
template <typename T>
const T& Constify(T& arg) {
@@ -3207,10 +3297,6 @@ void DeprecationWarning(JSContext* aCx, JSObject* aObject,
void DeprecationWarning(const GlobalObject& aGlobal,
DeprecatedOperations aOperation);
-// A callback to perform funToString on an interface object
-JSString* InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
- unsigned /* indent */);
-
namespace binding_detail {
// Get a JS global object that can be used for some temporary allocations. The
// idea is that this should be used for situations when you need to operate in