diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index 10d9b88a76..0da0caf253 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -2549,6 +2549,169 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_json) )~~~"); } +static void generate_named_properties_object_declarations(IDL::Interface const& interface, StringBuilder& builder) +{ + SourceGenerator generator { builder }; + + generator.set("named_properties_class", DeprecatedString::formatted("{}Properties", interface.name)); + + generator.append(R"~~~( +class @named_properties_class@ : public JS::Object { + JS_OBJECT(@named_properties_class@, JS::Object); +public: + explicit @named_properties_class@(JS::Realm&); + virtual void initialize(JS::Realm&) override; + virtual ~@named_properties_class@() override; + + JS::Realm& realm() const { return m_realm; } +private: + virtual JS::ThrowCompletionOr> internal_get_own_property(JS::PropertyKey const&) const override; + virtual JS::ThrowCompletionOr internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) override; + virtual JS::ThrowCompletionOr internal_delete(JS::PropertyKey const&) override; + virtual JS::ThrowCompletionOr internal_set_prototype_of(JS::Object* prototype) override; + virtual JS::ThrowCompletionOr internal_prevent_extensions() override; + + JS::Realm& m_realm; // [[Realm]] +}; +)~~~"); +} + +static void generate_named_properties_object_definitions(IDL::Interface const& interface, StringBuilder& builder) +{ + SourceGenerator generator { builder }; + + generator.set("name", interface.name); + generator.set("parent_name", interface.parent_name); + generator.set("prototype_base_class", interface.prototype_base_class); + generator.set("named_properties_class", DeprecatedString::formatted("{}Properties", interface.name)); + + // https://webidl.spec.whatwg.org/#create-a-named-properties-object + generator.append(R"~~~( +#include + +@named_properties_class@::@named_properties_class@(JS::Realm& realm) + : JS::Object(realm, nullptr) + , m_realm(realm) +{ +} + +@named_properties_class@::~@named_properties_class@() +{ +} + +void @named_properties_class@::initialize(JS::Realm& realm) +{ + auto& vm = realm.vm(); + + // The class string of a named properties object is the concatenation of the interface's identifier and the string "Properties". + define_direct_property(vm.well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm, "@named_properties_class@"_string), JS::Attribute::Configurable); +)~~~"); + + // 1. Let proto be null + // 2. If interface is declared to inherit from another interface, then set proto to the interface prototype object in realm for the inherited interface. + // 3. Otherwise, set proto to realm.[[Intrinsics]].[[%Object.prototype%]]. + // NOTE: Steps 4-9 handled by constructor + other overridden functions + // 10. Set obj.[[Prototype]] to proto. + if (interface.prototype_base_class == "ObjectPrototype") { + generator.append(R"~~~( + + set_prototype(realm.intrinsics().object_prototype()); +)~~~"); + } else { + generator.append(R"~~~( + + set_prototype(&ensure_web_prototype<@prototype_base_class@>(realm, "@parent_name@")); +)~~~"); + } + + generator.append(R"~~~( +}; + +// https://webidl.spec.whatwg.org/#named-properties-object-getownproperty +JS::ThrowCompletionOr> @named_properties_class@::internal_get_own_property(JS::PropertyKey const& property_name) const +{ + auto& vm = this->vm(); + auto& realm = this->realm(); + + // 1. Let A be the interface for the named properties object O. + using A = @name@; + + // 2. Let object be O.[[Realm]]'s global object. + // 3. Assert: object implements A. + auto& object = verify_cast(realm.global_object()); + + // 4. If the result of running the named property visibility algorithm with property name P and object object is true, then: + if (TRY(is_named_property_exposed_on_object({ &object }, property_name))) { + auto property_name_string = property_name.to_string(); + + // 1. Let operation be the operation used to declare the named property getter. + // 2. Let value be an uninitialized variable. + // 3. If operation was defined without an identifier, then set value to the result of performing the steps listed in the interface description to determine the value of a named property with P as the name. + // 4. Otherwise, operation was defined with an identifier. Set value to the result of performing the method steps of operation with « P » as the only argument value. + auto value = TRY(throw_dom_exception_if_needed(vm, [&] { return object.named_item_value(property_name_string); })); + + // 5. Let desc be a newly created Property Descriptor with no fields. + JS::PropertyDescriptor descriptor; + + // 6. Set desc.[[Value]] to the result of converting value to an ECMAScript value. + descriptor.value = value; +)~~~"); + if (interface.extended_attributes.contains("LegacyUnenumerableNamedProperties")) + generator.append(R"~~~( + // 7. If A implements an interface with the [LegacyUnenumerableNamedProperties] extended attribute, then set desc.[[Enumerable]] to false, otherwise set it to true. + descriptor.enumerable = true; +)~~~"); + else { + generator.append(R"~~~( + // 7. If A implements an interface with the [LegacyUnenumerableNamedProperties] extended attribute, then set desc.[[Enumerable]] to false, otherwise set it to true. + descriptor.enumerable = false; +)~~~"); + } + generator.append(R"~~~( + // 8. Set desc.[[Writable]] to true and desc.[[Configurable]] to true. + descriptor.writable = true; + descriptor.configurable = true; + + // 9. Return desc. + return descriptor; + } + + // 5. Return OrdinaryGetOwnProperty(O, P). + return JS::Object::internal_get_own_property(property_name); +} + +// https://webidl.spec.whatwg.org/#named-properties-object-defineownproperty +JS::ThrowCompletionOr @named_properties_class@::internal_define_own_property(JS::PropertyKey const&, JS::PropertyDescriptor const&) +{ + // 1. Return false. + return false; +} + +// https://webidl.spec.whatwg.org/#named-properties-object-delete +JS::ThrowCompletionOr @named_properties_class@::internal_delete(JS::PropertyKey const&) +{ + // 1. Return false. + return false; +} + +// https://webidl.spec.whatwg.org/#named-properties-object-setprototypeof +JS::ThrowCompletionOr @named_properties_class@::internal_set_prototype_of(JS::Object* prototype) +{ + // 1. Return ? SetImmutablePrototype(O, V). + return set_immutable_prototype(prototype); +} + +// https://webidl.spec.whatwg.org/#named-properties-object-preventextensions +JS::ThrowCompletionOr @named_properties_class@::internal_prevent_extensions() +{ + // 1. Return false. + // Note: this keeps named properties object extensible by making [[PreventExtensions]] fail. + return false; +} +)~~~"); +} + +// https://webidl.spec.whatwg.org/#interface-prototype-object static void generate_prototype_or_global_mixin_definitions(IDL::Interface const& interface, StringBuilder& builder) { SourceGenerator generator { builder }; @@ -2561,12 +2724,14 @@ static void generate_prototype_or_global_mixin_definitions(IDL::Interface const& generator.set("fully_qualified_name", interface.fully_qualified_name); generator.set("parent_name", interface.parent_name); generator.set("prototype_base_class", interface.prototype_base_class); + generator.set("prototype_name", interface.prototype_class); // Used for Global Mixin if (interface.pair_iterator_types.has_value()) { generator.set("iterator_name", DeprecatedString::formatted("{}Iterator", interface.name)); } if (is_global_interface) { + generator.set("named_properties_class", DeprecatedString::formatted("{}Properties", interface.name)); // Doing this with macros is not super nice, but simplifies codegen a lot. generator.append(R"~~~( #define define_direct_property (object.define_direct_property) @@ -2598,6 +2763,10 @@ void @class_name@::initialize(JS::Realm& realm) set_prototype(realm.intrinsics().object_prototype()); +)~~~"); + } else if (is_global_interface && interface.supports_named_properties()) { + generator.append(R"~~~( + set_prototype(&ensure_web_prototype<@prototype_name@>(realm, "@name@")); )~~~"); } else { generator.append(R"~~~( @@ -3671,6 +3840,9 @@ private: generator.append(R"~~~( }; )~~~"); + if (interface.supports_named_properties()) { + generate_named_properties_object_declarations(interface, builder); + } } else { generate_prototype_or_global_mixin_declarations(interface, builder); } @@ -3816,15 +3988,27 @@ namespace Web::Bindings { } )~~~"); - // Generate an empty prototype object for global interfaces. + // Generate a mostly empty prototype object for global interfaces. auto is_global_interface = interface.extended_attributes.contains("Global"); if (is_global_interface) { generator.append(R"~~~( void @prototype_class@::initialize(JS::Realm& realm) { Base::initialize(realm); +)~~~"); + if (interface.supports_named_properties()) { + generator.set("named_properties_class", DeprecatedString::formatted("{}Properties", interface.name)); + generator.set("namespaced_name", interface.namespaced_name); + generator.append(R"~~~( + define_direct_property(vm().well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm(), "@namespaced_name@"_string), JS::Attribute::Configurable); + set_prototype(&ensure_web_prototype<@prototype_class@>(realm, "@named_properties_class@")); +)~~~"); + } + generator.append(R"~~~( } )~~~"); + if (interface.supports_named_properties()) + generate_named_properties_object_definitions(interface, builder); } else { generate_prototype_or_global_mixin_definitions(interface, builder); } diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp index f87872c5fc..087307a717 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp @@ -76,7 +76,7 @@ namespace Web::Bindings { class @namespace_class@;)~~~"); }; - auto add_interface = [](SourceGenerator& gen, StringView prototype_class, StringView constructor_class, Optional const& legacy_constructor) { + auto add_interface = [](SourceGenerator& gen, StringView prototype_class, StringView constructor_class, Optional const& legacy_constructor, StringView named_properties_class) { gen.set("prototype_class", prototype_class); gen.set("constructor_class", constructor_class); @@ -89,15 +89,25 @@ class @constructor_class@;)~~~"); gen.append(R"~~~( class @legacy_constructor_class@;)~~~"); } + if (!named_properties_class.is_empty()) { + gen.set("named_properties_class", named_properties_class); + gen.append(R"~~~( +class @named_properties_class@;)~~~"); + } }; for (auto& interface : exposed_interfaces) { auto gen = generator.fork(); + String named_properties_class; + if (interface.extended_attributes.contains("Global") && interface.supports_named_properties()) { + named_properties_class = MUST(String::formatted("{}Properties", interface.name)); + } + if (interface.is_namespace) add_namespace(gen, interface.namespace_class); else - add_interface(gen, interface.prototype_class, interface.constructor_class, lookup_legacy_constructor(interface)); + add_interface(gen, interface.prototype_class, interface.constructor_class, lookup_legacy_constructor(interface), named_properties_class); } generator.append(R"~~~( @@ -178,7 +188,7 @@ void Intrinsics::create_web_namespace<@namespace_class@>(JS::Realm& realm) )~~~"); }; - auto add_interface = [](SourceGenerator& gen, StringView name, StringView prototype_class, StringView constructor_class, Optional const& legacy_constructor) { + auto add_interface = [](SourceGenerator& gen, StringView name, StringView prototype_class, StringView constructor_class, Optional const& legacy_constructor, StringView named_properties_class) { gen.set("interface_name", name); gen.set("prototype_class", prototype_class); gen.set("constructor_class", constructor_class); @@ -189,6 +199,16 @@ void Intrinsics::create_web_prototype_and_constructor<@prototype_class@>(JS::Rea { auto& vm = realm.vm(); +)~~~"); + if (!named_properties_class.is_empty()) { + gen.set("named_properties_class", named_properties_class); + gen.append(R"~~~( + auto named_properties_object = heap().allocate<@named_properties_class@>(realm, realm); + m_prototypes.set("@named_properties_class@"sv, named_properties_object); + +)~~~"); + } + gen.append(R"~~~( auto prototype = heap().allocate<@prototype_class@>(realm, realm); m_prototypes.set("@interface_name@"sv, prototype); @@ -217,10 +237,15 @@ void Intrinsics::create_web_prototype_and_constructor<@prototype_class@>(JS::Rea for (auto& interface : exposed_interfaces) { auto gen = generator.fork(); + String named_properties_class; + if (interface.extended_attributes.contains("Global") && interface.supports_named_properties()) { + named_properties_class = MUST(String::formatted("{}Properties", interface.name)); + } + if (interface.is_namespace) add_namespace(gen, interface.name, interface.namespace_class); else - add_interface(gen, interface.namespaced_name, interface.prototype_class, interface.constructor_class, lookup_legacy_constructor(interface)); + add_interface(gen, interface.namespaced_name, interface.prototype_class, interface.constructor_class, lookup_legacy_constructor(interface), named_properties_class); } generator.append(R"~~~( diff --git a/Tests/LibWeb/Text/expected/HTML/Window-prototype.txt b/Tests/LibWeb/Text/expected/HTML/Window-prototype.txt new file mode 100644 index 0000000000..4162e0107f --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/Window-prototype.txt @@ -0,0 +1,6 @@ +[object Window] +[object Window] +[object WindowProperties] +[object EventTarget] +[object Object] +null \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/Window-prototype.html b/Tests/LibWeb/Text/input/HTML/Window-prototype.html new file mode 100644 index 0000000000..5f35f3415d --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/Window-prototype.html @@ -0,0 +1,11 @@ + + diff --git a/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.cpp b/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.cpp index cd8a46a49f..a531b2b870 100644 --- a/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.cpp +++ b/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.cpp @@ -18,52 +18,6 @@ LegacyPlatformObject::LegacyPlatformObject(JS::Realm& realm) LegacyPlatformObject::~LegacyPlatformObject() = default; -// https://webidl.spec.whatwg.org/#dfn-named-property-visibility -JS::ThrowCompletionOr LegacyPlatformObject::is_named_property_exposed_on_object(JS::PropertyKey const& property_key) const -{ - // The spec doesn't say anything about the type of the property name here. - // Numbers can be converted to a string, which is fine and what other engines do. - // However, since a symbol cannot be converted to a string, it cannot be a supported property name. Return early if it's a symbol. - if (property_key.is_symbol()) - return false; - - // 1. If P is not a supported property name of O, then return false. - // NOTE: This is in it's own variable to enforce the type. - Vector supported_property_names = this->supported_property_names(); - auto property_key_string = property_key.to_string(); - if (!supported_property_names.contains_slow(property_key_string)) - return false; - - // 2. If O has an own property named P, then return false. - // NOTE: This has to be done manually instead of using Object::has_own_property, as that would use the overridden internal_get_own_property. - auto own_property_named_p = MUST(Object::internal_get_own_property(property_key)); - - if (own_property_named_p.has_value()) - return false; - - // 3. If O implements an interface that has the [LegacyOverrideBuiltIns] extended attribute, then return true. - if (has_legacy_override_built_ins_interface_extended_attribute()) - return true; - - // 4. Let prototype be O.[[GetPrototypeOf]](). - auto* prototype = TRY(internal_get_prototype_of()); - - // 5. While prototype is not null: - while (prototype) { - // FIXME: 1. If prototype is not a named properties object, and prototype has an own property named P, then return false. - // (It currently does not check for named property objects) - bool prototype_has_own_property_named_p = TRY(prototype->has_own_property(property_key)); - if (prototype_has_own_property_named_p) - return false; - - // 2. Set prototype to prototype.[[GetPrototypeOf]](). - prototype = TRY(prototype->internal_get_prototype_of()); - } - - // 6. Return true. - return true; -} - // https://webidl.spec.whatwg.org/#LegacyPlatformObjectGetOwnProperty JS::ThrowCompletionOr> LegacyPlatformObject::legacy_platform_object_get_own_property(JS::PropertyKey const& property_name, IgnoreNamedProps ignore_named_props) const { @@ -106,7 +60,7 @@ JS::ThrowCompletionOr> LegacyPlatformObject::le // 2. If O supports named properties and ignoreNamedProps is false, then: if (supports_named_properties() && ignore_named_props == IgnoreNamedProps::No) { // 1. If the result of running the named property visibility algorithm with property name P and object O is true, then: - if (TRY(is_named_property_exposed_on_object(property_name))) { + if (TRY(WebIDL::is_named_property_exposed_on_object({ this }, property_name))) { // FIXME: It's unfortunate that this is done twice, once in is_named_property_exposed_on_object and here. auto property_name_string = property_name.to_string(); @@ -322,7 +276,7 @@ JS::ThrowCompletionOr LegacyPlatformObject::internal_delete(JS::PropertyKe // 2. If O supports named properties, O does not implement an interface with the [Global] extended attribute and // the result of calling the named property visibility algorithm with property name P and object O is true, then: - if (supports_named_properties() && !has_global_interface_extended_attribute() && TRY(is_named_property_exposed_on_object(property_name))) { + if (supports_named_properties() && !has_global_interface_extended_attribute() && TRY(WebIDL::is_named_property_exposed_on_object({ this }, property_name))) { // 1. If O does not implement an interface with a named property deleter, then return false. if (!has_named_property_deleter()) return false; @@ -394,7 +348,7 @@ JS::ThrowCompletionOr> LegacyPlatformObject::interna // 3. If O supports named properties, then for each P of O’s supported property names that is visible according to the named property visibility algorithm, append P to keys. if (supports_named_properties()) { for (auto& named_property : supported_property_names()) { - if (TRY(is_named_property_exposed_on_object(named_property))) + if (TRY(WebIDL::is_named_property_exposed_on_object({ this }, named_property))) keys.append(JS::PrimitiveString::create(vm, named_property)); } } diff --git a/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.h b/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.h index 5f13227b97..69e8e95eb8 100644 --- a/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.h +++ b/Userland/Libraries/LibWeb/Bindings/LegacyPlatformObject.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include namespace Web::Bindings { @@ -16,6 +17,8 @@ namespace Web::Bindings { class LegacyPlatformObject : public PlatformObject { WEB_PLATFORM_OBJECT(LegacyPlatformObject, PlatformObject); + virtual bool has_legacy_override_built_ins_interface_extended_attribute() const = 0; + public: virtual ~LegacyPlatformObject() override; @@ -26,8 +29,6 @@ public: virtual JS::ThrowCompletionOr internal_prevent_extensions() override; virtual JS::ThrowCompletionOr> internal_own_property_keys() const override; - JS::ThrowCompletionOr is_named_property_exposed_on_object(JS::PropertyKey const&) const; - enum class IgnoreNamedProps { No, Yes, @@ -80,7 +81,6 @@ protected: virtual bool has_named_property_deleter() const = 0; - virtual bool has_legacy_override_built_ins_interface_extended_attribute() const = 0; virtual bool has_legacy_unenumerable_named_properties_interface_extended_attribute() const = 0; virtual bool has_global_interface_extended_attribute() const = 0; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 8380604898..b205bdbddb 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -30,6 +30,7 @@ enum class StateAndProperties; namespace Web::Bindings { class Intrinsics; +class LegacyPlatformObject; class OptionConstructor; enum class AudioContextLatencyCategory; diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index 4eb7b56cda..35dd0768af 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -1437,4 +1437,16 @@ OrderedHashMap> Window::document_tree_child_ return names; } +// https://html.spec.whatwg.org/#named-access-on-the-window-object +Vector Window::supported_property_names() +{ + return {}; +} + +// https://html.spec.whatwg.org/#named-access-on-the-window-object +WebIDL::ExceptionOr Window::named_item_value(DeprecatedFlyString const& name) +{ + return JS::js_undefined(); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/Window.h b/Userland/Libraries/LibWeb/HTML/Window.h index 6d090805ea..c67e02a9d3 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.h +++ b/Userland/Libraries/LibWeb/HTML/Window.h @@ -193,6 +193,9 @@ public: [[nodiscard]] OrderedHashMap> document_tree_child_navigable_target_name_property_set(); + [[nodiscard]] Vector supported_property_names(); + [[nodiscard]] WebIDL::ExceptionOr named_item_value(DeprecatedFlyString const&); + private: explicit Window(JS::Realm&); diff --git a/Userland/Libraries/LibWeb/HTML/Window.idl b/Userland/Libraries/LibWeb/HTML/Window.idl index 3ddccfadfe..ca19c0697a 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.idl +++ b/Userland/Libraries/LibWeb/HTML/Window.idl @@ -37,6 +37,11 @@ interface Window : EventTarget { readonly attribute Element? frameElement; WindowProxy? open(optional USVString url = "", optional DOMString target = "_blank", optional [LegacyNullToEmptyString] DOMString features = ""); + // Since this is the global object, the IDL named getter adds a NamedPropertiesObject exotic + // object on the prototype chain. Indeed, this does not make the global object an exotic object. + // Indexed access is taken care of by the WindowProxy exotic object. + getter object (DOMString name); + // the user agent readonly attribute Navigator navigator; [ImplementedAs=navigator] readonly attribute Navigator clientInformation; // legacy alias of .navigator diff --git a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp index 0c1a80204d..9459ad10a2 100644 --- a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.cpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #include #include @@ -301,4 +304,54 @@ JS::Completion construct(WebIDL::CallbackType& callback, JS::MarkedVector is_named_property_exposed_on_object(Variant const& variant, JS::PropertyKey const& property_key) +{ + auto const& object = *variant.visit([](auto& o) { return static_cast(o); }); + + // The spec doesn't say anything about the type of the property name here. + // Numbers can be converted to a string, which is fine and what other engines do. + // However, since a symbol cannot be converted to a string, it cannot be a supported property name. Return early if it's a symbol. + if (property_key.is_symbol()) + return false; + + // 1. If P is not a supported property name of O, then return false. + // NOTE: This is in it's own variable to enforce the type. + Vector supported_property_names = variant.visit([](auto* o) { return o->supported_property_names(); }); + auto property_key_string = property_key.to_string(); + if (!supported_property_names.contains_slow(property_key_string)) + return false; + + // 2. If O has an own property named P, then return false. + // NOTE: This has to be done manually instead of using Object::has_own_property, as that would use the overridden internal_get_own_property. + auto own_property_named_p = MUST(object.JS::Object::internal_get_own_property(property_key)); + + if (own_property_named_p.has_value()) + return false; + + // 3. If O implements an interface that has the [LegacyOverrideBuiltIns] extended attribute, then return true. + if (variant.has() && variant.get()->has_legacy_override_built_ins_interface_extended_attribute()) + return true; + + // 4. Let prototype be O.[[GetPrototypeOf]](). + auto* prototype = TRY(object.internal_get_prototype_of()); + + // 5. While prototype is not null: + while (prototype) { + // 1. If prototype is not a named properties object, and prototype has an own property named P, then return false. + // FIXME: Are there other named properties objects? + if (!is(prototype)) { + bool prototype_has_own_property_named_p = TRY(prototype->has_own_property(property_key)); + if (prototype_has_own_property_named_p) + return false; + } + + // 2. Set prototype to prototype.[[GetPrototypeOf]](). + prototype = TRY(prototype->internal_get_prototype_of()); + } + + // 6. Return true. + return true; +} + } diff --git a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h index 2406285247..be19603e49 100644 --- a/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h +++ b/Userland/Libraries/LibWeb/WebIDL/AbstractOperations.h @@ -61,4 +61,6 @@ JS::Completion construct(WebIDL::CallbackType& callback, Args&&... args) return construct(callback, move(arguments_list)); } +JS::ThrowCompletionOr is_named_property_exposed_on_object(Variant const& variant, JS::PropertyKey const& property_key); + }