From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- dom/bindings/BindingUtils.h | 204 +++++++++++++++++++++++++++++++------------- 1 file changed, 145 insertions(+), 59 deletions(-) (limited to 'dom/bindings/BindingUtils.h') 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 global, + JS::Handle protoProto, const DOMIfaceAndProtoJSClass* protoClass, + JS::Heap* protoCache, JS::Handle interfaceProto, + const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs, + bool isConstructorChromeOnly, + const Span& legacyFactoryFunctions, + JS::Heap* 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 +inline void CreateInterfaceObjects( JSContext* cx, JS::Handle global, JS::Handle protoProto, const DOMIfaceAndProtoJSClass* protoClass, - JS::Heap* protoCache, JS::Handle constructorProto, - const DOMIfaceJSClass* constructorClass, unsigned ctorNargs, + JS::Heap* protoCache, JS::Handle interfaceProto, + const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs, bool isConstructorChromeOnly, - const LegacyFactoryFunction* legacyFactoryFunctions, + const Span& legacyFactoryFunctions, JS::Heap* 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 global, + JS::Handle namespaceProto, + const DOMIfaceAndProtoJSClass& namespaceClass, + JS::Heap* 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(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(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 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 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 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 -- cgit v1.2.3