From c7d25301d31fd4df42790188a3b52112fc704fdf Mon Sep 17 00:00:00 2001 From: Luke Wilde Date: Mon, 9 Dec 2024 17:04:20 +0000 Subject: [PATCH] LibWeb: Implement the LegacyUnforgeable attribute This fixes the frame-ancestors WPT tests from crashing when an iframe is blocked from loading. This is because it would get an undefined location.href from the cross-origin iframe, which causes a crash as it expects it to be there. --- Libraries/LibIDL/IDLParser.cpp | 2 +- Libraries/LibIDL/Types.h | 2 +- Libraries/LibWeb/DOM/Document.cpp | 1 + Libraries/LibWeb/DOM/Event.cpp | 1 + Libraries/LibWeb/HTML/Location.cpp | 1 + Libraries/LibWeb/HTML/Window.cpp | 1 + .../BindingsGenerator/IDLGenerators.cpp | 225 ++++++++++++------ .../Document-has-unforgeable-properties.txt | 36 +++ .../DOM/Event-has-unforgeable-properties.txt | 18 ++ .../Location-has-unforgeable-properties.txt | 156 ++++++++++++ .../Window-has-unforgeable-properties.txt | 24 ++ .../Document-has-unforgeable-properties.html | 33 +++ .../DOM/Event-has-unforgeable-properties.html | 25 ++ .../Location-has-unforgeable-properties.html | 37 +++ .../Window-has-unforgeable-properties.html | 25 ++ 15 files changed, 511 insertions(+), 76 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/DOM/Document-has-unforgeable-properties.txt create mode 100644 Tests/LibWeb/Text/expected/DOM/Event-has-unforgeable-properties.txt create mode 100644 Tests/LibWeb/Text/expected/HTML/Location-has-unforgeable-properties.txt create mode 100644 Tests/LibWeb/Text/expected/HTML/Window-has-unforgeable-properties.txt create mode 100644 Tests/LibWeb/Text/input/DOM/Document-has-unforgeable-properties.html create mode 100644 Tests/LibWeb/Text/input/DOM/Event-has-unforgeable-properties.html create mode 100644 Tests/LibWeb/Text/input/HTML/Location-has-unforgeable-properties.html create mode 100644 Tests/LibWeb/Text/input/HTML/Window-has-unforgeable-properties.html diff --git a/Libraries/LibIDL/IDLParser.cpp b/Libraries/LibIDL/IDLParser.cpp index 52fd9e563be..6be0c76f022 100644 --- a/Libraries/LibIDL/IDLParser.cpp +++ b/Libraries/LibIDL/IDLParser.cpp @@ -504,7 +504,7 @@ void Parser::parse_stringifier(HashMap& extended_attribu interface.has_stringifier = true; if (lexer.next_is("attribute"sv) || lexer.next_is("inherit"sv) || lexer.next_is("readonly"sv)) { parse_attribute(extended_attributes, interface); - interface.stringifier_attribute = interface.attributes.last().name; + interface.stringifier_attribute = interface.attributes.last(); } else { assert_specific(';'); } diff --git a/Libraries/LibIDL/Types.h b/Libraries/LibIDL/Types.h index 1702ab798e9..c8811026117 100644 --- a/Libraries/LibIDL/Types.h +++ b/Libraries/LibIDL/Types.h @@ -280,7 +280,7 @@ public: Vector functions; Vector static_functions; bool has_stringifier { false }; - Optional stringifier_attribute; + Optional stringifier_attribute; bool has_unscopable_member { false }; Optional> value_iterator_type; diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 6289113dd2f..0eaf8ca60bc 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -483,6 +483,7 @@ void Document::initialize(JS::Realm& realm) { Base::initialize(realm); WEB_SET_PROTOTYPE_FOR_INTERFACE(Document); + Bindings::DocumentPrototype::define_unforgeable_attributes(realm, *this); m_selection = realm.create(realm, *this); diff --git a/Libraries/LibWeb/DOM/Event.cpp b/Libraries/LibWeb/DOM/Event.cpp index 78c30aa9bc9..685774ea0d9 100644 --- a/Libraries/LibWeb/DOM/Event.cpp +++ b/Libraries/LibWeb/DOM/Event.cpp @@ -57,6 +57,7 @@ void Event::initialize(JS::Realm& realm) { Base::initialize(realm); WEB_SET_PROTOTYPE_FOR_INTERFACE(Event); + Bindings::EventPrototype::define_unforgeable_attributes(realm, *this); } void Event::visit_edges(Visitor& visitor) diff --git a/Libraries/LibWeb/HTML/Location.cpp b/Libraries/LibWeb/HTML/Location.cpp index af4d69c65b3..baa16bdaf34 100644 --- a/Libraries/LibWeb/HTML/Location.cpp +++ b/Libraries/LibWeb/HTML/Location.cpp @@ -43,6 +43,7 @@ void Location::initialize(JS::Realm& realm) { Base::initialize(realm); WEB_SET_PROTOTYPE_FOR_INTERFACE(Location); + Bindings::LocationPrototype::define_unforgeable_attributes(realm, *this); auto& vm = this->vm(); diff --git a/Libraries/LibWeb/HTML/Window.cpp b/Libraries/LibWeb/HTML/Window.cpp index 18e2edeb1c9..baa1c762bde 100644 --- a/Libraries/LibWeb/HTML/Window.cpp +++ b/Libraries/LibWeb/HTML/Window.cpp @@ -731,6 +731,7 @@ WebIDL::ExceptionOr Window::initialize_web_interfaces(Badge(realm, "@name@"_fly_string)); )~~~"); } else { generator.append(R"~~~( + [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable; +)~~~"); + } - set_prototype(&ensure_web_prototype<@prototype_base_class@>(realm, "@parent_name@"_fly_string)); + if (generate_unforgeables == GenerateUnforgeables::No) { + if (interface.name == "DOMException"sv) { + generator.append(R"~~~( + + @set_prototype@(realm.intrinsics().error_prototype()); +)~~~"); + } + + else if (interface.prototype_base_class == "ObjectPrototype") { + generator.append(R"~~~( + + @set_prototype@(realm.intrinsics().object_prototype()); )~~~"); + } else if (is_global_interface) { + generator.append(R"~~~( + @set_prototype@(&ensure_web_prototype<@prototype_name@>(realm, "@name@"_fly_string)); +)~~~"); + } else { + generator.append(R"~~~( + + @set_prototype@(&ensure_web_prototype<@prototype_base_class@>(realm, "@parent_name@"_fly_string)); + +)~~~"); + } } if (interface.has_unscopable_member) { @@ -3418,11 +3445,15 @@ void @class_name@::initialize(JS::Realm& realm) // https://webidl.spec.whatwg.org/#es-attributes for (auto& attribute : interface.attributes) { + bool has_unforgeable_attribute = attribute.extended_attributes.contains("LegacyUnforgeable"sv); + if ((generate_unforgeables == GenerateUnforgeables::Yes && !has_unforgeable_attribute) || (generate_unforgeables == GenerateUnforgeables::No && has_unforgeable_attribute)) + continue; + if (attribute.extended_attributes.contains("FIXME")) { auto fixme_attribute_generator = generator.fork(); fixme_attribute_generator.set("attribute.name", attribute.name); fixme_attribute_generator.append(R"~~~( - define_direct_property("@attribute.name@"_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented); + @define_direct_property@("@attribute.name@"_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented); )~~~"); continue; } @@ -3443,36 +3474,47 @@ void @class_name@::initialize(JS::Realm& realm) } attribute_generator.append(R"~~~( - define_native_accessor(realm, "@attribute.name@"_fly_string, @attribute.getter_callback@, @attribute.setter_callback@, default_attributes); + @define_native_accessor@(realm, "@attribute.name@"_fly_string, @attribute.getter_callback@, @attribute.setter_callback@, default_attributes); )~~~"); } for (auto& function : interface.functions) { + bool has_unforgeable_attribute = function.extended_attributes.contains("LegacyUnforgeable"sv); + if ((generate_unforgeables == GenerateUnforgeables::Yes && !has_unforgeable_attribute) || (generate_unforgeables == GenerateUnforgeables::No && has_unforgeable_attribute)) + continue; + if (function.extended_attributes.contains("FIXME")) { auto fixme_function_generator = generator.fork(); fixme_function_generator.set("function.name", function.name); fixme_function_generator.append(R"~~~( - define_direct_property("@function.name@"_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented); + @define_direct_property@("@function.name@"_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented); )~~~"); } } // https://webidl.spec.whatwg.org/#es-constants - for (auto& constant : interface.constants) { - // FIXME: Do constants need to be added to the unscopable list? + if (generate_unforgeables == GenerateUnforgeables::No) { + for (auto& constant : interface.constants) { + // FIXME: Do constants need to be added to the unscopable list? - auto constant_generator = generator.fork(); - constant_generator.set("constant.name", constant.name); + auto constant_generator = generator.fork(); + constant_generator.set("constant.name", constant.name); - generate_wrap_statement(constant_generator, constant.value, constant.type, interface, ByteString::formatted("auto constant_{}_value =", constant.name)); + generate_wrap_statement(constant_generator, constant.value, constant.type, interface, ByteString::formatted("auto constant_{}_value =", constant.name)); - constant_generator.append(R"~~~( - define_direct_property("@constant.name@"_fly_string, constant_@constant.name@_value, JS::Attribute::Enumerable); + constant_generator.append(R"~~~( + @define_direct_property@("@constant.name@"_fly_string, constant_@constant.name@_value, JS::Attribute::Enumerable); )~~~"); + } } // https://webidl.spec.whatwg.org/#es-operations for (auto const& overload_set : interface.overload_sets) { + // NOTE: This assumes that every function in the overload set has the same attribute set. + bool has_unforgeable_attribute = any_of(overload_set.value, [](auto const& function) { return function.extended_attributes.contains("LegacyUnforgeable"); }); + if ((generate_unforgeables == GenerateUnforgeables::Yes && !has_unforgeable_attribute) || (generate_unforgeables == GenerateUnforgeables::No && has_unforgeable_attribute)) + continue; + auto function_generator = generator.fork(); function_generator.set("function.name", overload_set.key); function_generator.set("function.name:snakecase", make_input_acceptable_cpp(overload_set.key.to_snakecase())); @@ -3486,95 +3528,103 @@ void @class_name@::initialize(JS::Realm& realm) } function_generator.append(R"~~~( - define_native_function(realm, "@function.name@"_fly_string, @function.name:snakecase@, @function.length@, default_attributes); + @define_native_function@(realm, "@function.name@"_fly_string, @function.name:snakecase@, @function.length@, default_attributes); )~~~"); } - if (interface.has_stringifier) { - // FIXME: Do stringifiers need to be added to the unscopable list? + bool should_generate_stringifier = true; + if (interface.stringifier_attribute.has_value()) { + bool has_unforgeable_attribute = interface.stringifier_attribute.value().extended_attributes.contains("LegacyUnforgeable"sv); + if ((generate_unforgeables == GenerateUnforgeables::Yes && !has_unforgeable_attribute) || (generate_unforgeables == GenerateUnforgeables::No && has_unforgeable_attribute)) + should_generate_stringifier = false; + } + if (interface.has_stringifier && should_generate_stringifier) { + // FIXME: Do stringifiers need to be added to the unscopable list? auto stringifier_generator = generator.fork(); stringifier_generator.append(R"~~~( - define_native_function(realm, "toString"_fly_string, to_string, 0, default_attributes); + @define_native_function@(realm, "toString"_fly_string, to_string, 0, default_attributes); )~~~"); } // https://webidl.spec.whatwg.org/#define-the-iteration-methods // This applies to this if block and the following if block. - if (interface.indexed_property_getter.has_value()) { + if (interface.indexed_property_getter.has_value() && generate_unforgeables == GenerateUnforgeables::No) { auto iterator_generator = generator.fork(); iterator_generator.append(R"~~~( - define_direct_property(vm.well_known_symbol_iterator(), realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.values), JS::Attribute::Configurable | JS::Attribute::Writable); + @define_direct_property@(vm.well_known_symbol_iterator(), realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.values), JS::Attribute::Configurable | JS::Attribute::Writable); )~~~"); if (interface.value_iterator_type.has_value()) { iterator_generator.append(R"~~~( - define_direct_property(vm.names.entries, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.entries), default_attributes); - define_direct_property(vm.names.keys, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.keys), default_attributes); - define_direct_property(vm.names.values, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.values), default_attributes); - define_direct_property(vm.names.forEach, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.forEach), default_attributes); + @define_direct_property@(vm.names.entries, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.entries), default_attributes); + @define_direct_property@(vm.names.keys, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.keys), default_attributes); + @define_direct_property@(vm.names.values, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.values), default_attributes); + @define_direct_property@(vm.names.forEach, realm.intrinsics().array_prototype()->get_without_side_effects(vm.names.forEach), default_attributes); )~~~"); } } - if (interface.pair_iterator_types.has_value()) { + if (interface.pair_iterator_types.has_value() && generate_unforgeables == GenerateUnforgeables::No) { // FIXME: Do pair iterators need to be added to the unscopable list? auto iterator_generator = generator.fork(); iterator_generator.append(R"~~~( - define_native_function(realm, vm.names.entries, entries, 0, default_attributes); - define_native_function(realm, vm.names.forEach, for_each, 1, default_attributes); - define_native_function(realm, vm.names.keys, keys, 0, default_attributes); - define_native_function(realm, vm.names.values, values, 0, default_attributes); + @define_native_function@(realm, vm.names.entries, entries, 0, default_attributes); + @define_native_function@(realm, vm.names.forEach, for_each, 1, default_attributes); + @define_native_function@(realm, vm.names.keys, keys, 0, default_attributes); + @define_native_function@(realm, vm.names.values, values, 0, default_attributes); - define_direct_property(vm.well_known_symbol_iterator(), get_without_side_effects(vm.names.entries), JS::Attribute::Configurable | JS::Attribute::Writable); + @define_direct_property@(vm.well_known_symbol_iterator(), get_without_side_effects(vm.names.entries), JS::Attribute::Configurable | JS::Attribute::Writable); )~~~"); } // https://webidl.spec.whatwg.org/#js-setlike - if (interface.set_entry_type.has_value()) { + if (interface.set_entry_type.has_value() && generate_unforgeables == GenerateUnforgeables::No) { auto setlike_generator = generator.fork(); setlike_generator.append(R"~~~( - define_native_accessor(realm, vm.names.size, get_size, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable); - define_native_function(realm, vm.names.entries, entries, 0, default_attributes); + @define_native_accessor@(realm, vm.names.size, get_size, nullptr, JS::Attribute::Enumerable | JS::Attribute::Configurable); + @define_native_function@(realm, vm.names.entries, entries, 0, default_attributes); // NOTE: Keys intentionally returns values for setlike - define_native_function(realm, vm.names.keys, values, 0, default_attributes); - define_native_function(realm, vm.names.values, values, 0, default_attributes); - define_direct_property(vm.well_known_symbol_iterator(), get_without_side_effects(vm.names.values), JS::Attribute::Configurable | JS::Attribute::Writable); - define_native_function(realm, vm.names.forEach, for_each, 1, default_attributes); - define_native_function(realm, vm.names.has, has, 1, default_attributes); + @define_native_function@(realm, vm.names.keys, values, 0, default_attributes); + @define_native_function@(realm, vm.names.values, values, 0, default_attributes); + @define_direct_property@(vm.well_known_symbol_iterator(), get_without_side_effects(vm.names.values), JS::Attribute::Configurable | JS::Attribute::Writable); + @define_native_function@(realm, vm.names.forEach, for_each, 1, default_attributes); + @define_native_function@(realm, vm.names.has, has, 1, default_attributes); )~~~"); if (!interface.overload_sets.contains("add"sv) && !interface.is_set_readonly) { setlike_generator.append(R"~~~( - define_native_function(realm, vm.names.add, add, 1, default_attributes); + @define_native_function@(realm, vm.names.add, add, 1, default_attributes); )~~~"); } if (!interface.overload_sets.contains("delete"sv) && !interface.is_set_readonly) { setlike_generator.append(R"~~~( - define_native_function(realm, vm.names.delete_, delete_, 1, default_attributes); + @define_native_function@(realm, vm.names.delete_, delete_, 1, default_attributes); )~~~"); } if (!interface.overload_sets.contains("clear"sv) && !interface.is_set_readonly) { setlike_generator.append(R"~~~( - define_native_function(realm, vm.names.clear, clear, 0, default_attributes); + @define_native_function@(realm, vm.names.clear, clear, 0, default_attributes); )~~~"); } } if (interface.has_unscopable_member) { generator.append(R"~~~( - define_direct_property(vm.well_known_symbol_unscopables(), unscopable_object, JS::Attribute::Configurable); + @define_direct_property@(vm.well_known_symbol_unscopables(), unscopable_object, JS::Attribute::Configurable); )~~~"); } - generator.append(R"~~~( - define_direct_property(vm.well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm, "@namespaced_name@"_string), JS::Attribute::Configurable); + if (generate_unforgeables == GenerateUnforgeables::No) { + generator.append(R"~~~( + @define_direct_property@(vm.well_known_symbol_to_string_tag(), JS::PrimitiveString::create(vm, "@namespaced_name@"_string), JS::Attribute::Configurable); )~~~"); + } - if (!is_global_interface) { + if (!define_on_existing_object) { generator.append(R"~~~( Base::initialize(realm); )~~~"); @@ -3583,6 +3633,26 @@ void @class_name@::initialize(JS::Realm& realm) generator.append(R"~~~( } )~~~"); +} + +// 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 }; + + auto is_global_interface = interface.extended_attributes.contains("Global"); + auto class_name = is_global_interface ? interface.global_mixin_class : interface.prototype_class; + generator.set("name", interface.name); + generator.set("namespaced_name", interface.namespaced_name); + generator.set("class_name", class_name); + 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", ByteString::formatted("{}Iterator", interface.name)); + } if (!interface.attributes.is_empty() || !interface.functions.is_empty() || interface.has_stringifier) { generator.append(R"~~~( @@ -4214,7 +4284,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@) auto stringifier_generator = generator.fork(); stringifier_generator.set("class_name", class_name); if (interface.stringifier_attribute.has_value()) - stringifier_generator.set("attribute.cpp_getter_name", interface.stringifier_attribute->to_snakecase()); + stringifier_generator.set("attribute.cpp_getter_name", interface.stringifier_attribute.value().name.to_snakecase()); stringifier_generator.append(R"~~~( JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_string) @@ -4955,6 +5025,8 @@ class @prototype_class@ : public JS::Object { JS_OBJECT(@prototype_class@, JS::Object); GC_DECLARE_ALLOCATOR(@prototype_class@); public: + static void define_unforgeable_attributes(JS::Realm&, JS::Object&); + explicit @prototype_class@(JS::Realm&); virtual void initialize(JS::Realm&) override; virtual ~@prototype_class@() override; @@ -5124,6 +5196,8 @@ void @prototype_class@::initialize(JS::Realm& realm) if (interface.supports_named_properties()) generate_named_properties_object_definitions(interface, builder); } else { + generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::No); + generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::Yes); generate_prototype_or_global_mixin_definitions(interface, builder); } @@ -5250,6 +5324,7 @@ namespace Web::Bindings { class @class_name@ { public: void initialize(JS::Realm&, JS::Object&); + void define_unforgeable_attributes(JS::Realm&, JS::Object&); @class_name@(); virtual ~@class_name@(); @@ -5313,6 +5388,8 @@ namespace Web::Bindings { @class_name@::~@class_name@() = default; )~~~"); + generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::No); + generate_prototype_or_global_mixin_initialization(interface, builder, GenerateUnforgeables::Yes); generate_prototype_or_global_mixin_definitions(interface, builder); generator.append(R"~~~( diff --git a/Tests/LibWeb/Text/expected/DOM/Document-has-unforgeable-properties.txt b/Tests/LibWeb/Text/expected/DOM/Document-has-unforgeable-properties.txt new file mode 100644 index 00000000000..17cc7dae4f8 --- /dev/null +++ b/Tests/LibWeb/Text/expected/DOM/Document-has-unforgeable-properties.txt @@ -0,0 +1,36 @@ +location exists on document itself: true +location does not exist on LocationPrototype: true +location descriptor is not undefined: true +location enumerable: true +location configurable: false +location writable: undefined +location exists on document itself: true +location does not exist on LocationPrototype: true +location descriptor is not undefined: true +location enumerable: true +location configurable: false +location writable: undefined +location exists on document itself: true +location does not exist on LocationPrototype: true +location descriptor is not undefined: true +location enumerable: true +location configurable: false +location writable: undefined +location exists on document itself: true +location does not exist on LocationPrototype: true +location descriptor is not undefined: true +location enumerable: true +location configurable: false +location writable: undefined +location exists on document itself: true +location does not exist on LocationPrototype: true +location descriptor is not undefined: true +location enumerable: true +location configurable: false +location writable: undefined +location exists on document itself: true +location does not exist on LocationPrototype: true +location descriptor is not undefined: true +location enumerable: true +location configurable: false +location writable: undefined diff --git a/Tests/LibWeb/Text/expected/DOM/Event-has-unforgeable-properties.txt b/Tests/LibWeb/Text/expected/DOM/Event-has-unforgeable-properties.txt new file mode 100644 index 00000000000..cd23b047f4d --- /dev/null +++ b/Tests/LibWeb/Text/expected/DOM/Event-has-unforgeable-properties.txt @@ -0,0 +1,18 @@ +isTrusted exists on Event instance itself: true +isTrusted does not exist on LocationPrototype: true +isTrusted descriptor is not undefined: true +isTrusted enumerable: true +isTrusted configurable: false +isTrusted writable: undefined +isTrusted exists on UIEvent instance itself: true +isTrusted does not exist on LocationPrototype: true +isTrusted descriptor is not undefined: true +isTrusted enumerable: true +isTrusted configurable: false +isTrusted writable: undefined +isTrusted exists on CustomEvent instance itself: true +isTrusted does not exist on LocationPrototype: true +isTrusted descriptor is not undefined: true +isTrusted enumerable: true +isTrusted configurable: false +isTrusted writable: undefined diff --git a/Tests/LibWeb/Text/expected/HTML/Location-has-unforgeable-properties.txt b/Tests/LibWeb/Text/expected/HTML/Location-has-unforgeable-properties.txt new file mode 100644 index 00000000000..79aba7324ed --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/Location-has-unforgeable-properties.txt @@ -0,0 +1,156 @@ +href exists on location itself: true +href does not exist on LocationPrototype: true +href descriptor is not undefined: true +href enumerable: true +href configurable: true +href writable: undefined +origin exists on location itself: true +origin does not exist on LocationPrototype: true +origin descriptor is not undefined: true +origin enumerable: true +origin configurable: true +origin writable: undefined +protocol exists on location itself: true +protocol does not exist on LocationPrototype: true +protocol descriptor is not undefined: true +protocol enumerable: true +protocol configurable: true +protocol writable: undefined +host exists on location itself: true +host does not exist on LocationPrototype: true +host descriptor is not undefined: true +host enumerable: true +host configurable: true +host writable: undefined +hostname exists on location itself: true +hostname does not exist on LocationPrototype: true +hostname descriptor is not undefined: true +hostname enumerable: true +hostname configurable: true +hostname writable: undefined +port exists on location itself: true +port does not exist on LocationPrototype: true +port descriptor is not undefined: true +port enumerable: true +port configurable: true +port writable: undefined +pathname exists on location itself: true +pathname does not exist on LocationPrototype: true +pathname descriptor is not undefined: true +pathname enumerable: true +pathname configurable: true +pathname writable: undefined +search exists on location itself: true +search does not exist on LocationPrototype: true +search descriptor is not undefined: true +search enumerable: true +search configurable: true +search writable: undefined +hash exists on location itself: true +hash does not exist on LocationPrototype: true +hash descriptor is not undefined: true +hash enumerable: true +hash configurable: true +hash writable: undefined +assign exists on location itself: true +assign does not exist on LocationPrototype: true +assign descriptor is not undefined: true +assign enumerable: true +assign configurable: true +assign writable: false +replace exists on location itself: true +replace does not exist on LocationPrototype: true +replace descriptor is not undefined: true +replace enumerable: true +replace configurable: true +replace writable: false +reload exists on location itself: true +reload does not exist on LocationPrototype: true +reload descriptor is not undefined: true +reload enumerable: true +reload configurable: true +reload writable: false +toString exists on location itself: true +toString does not exist on LocationPrototype: true +toString descriptor is not undefined: true +toString enumerable: true +toString configurable: true +toString writable: false +href exists on document.location itself: true +href does not exist on LocationPrototype: true +href descriptor is not undefined: true +href enumerable: true +href configurable: true +href writable: undefined +origin exists on document.location itself: true +origin does not exist on LocationPrototype: true +origin descriptor is not undefined: true +origin enumerable: true +origin configurable: true +origin writable: undefined +protocol exists on document.location itself: true +protocol does not exist on LocationPrototype: true +protocol descriptor is not undefined: true +protocol enumerable: true +protocol configurable: true +protocol writable: undefined +host exists on document.location itself: true +host does not exist on LocationPrototype: true +host descriptor is not undefined: true +host enumerable: true +host configurable: true +host writable: undefined +hostname exists on document.location itself: true +hostname does not exist on LocationPrototype: true +hostname descriptor is not undefined: true +hostname enumerable: true +hostname configurable: true +hostname writable: undefined +port exists on document.location itself: true +port does not exist on LocationPrototype: true +port descriptor is not undefined: true +port enumerable: true +port configurable: true +port writable: undefined +pathname exists on document.location itself: true +pathname does not exist on LocationPrototype: true +pathname descriptor is not undefined: true +pathname enumerable: true +pathname configurable: true +pathname writable: undefined +search exists on document.location itself: true +search does not exist on LocationPrototype: true +search descriptor is not undefined: true +search enumerable: true +search configurable: true +search writable: undefined +hash exists on document.location itself: true +hash does not exist on LocationPrototype: true +hash descriptor is not undefined: true +hash enumerable: true +hash configurable: true +hash writable: undefined +assign exists on document.location itself: true +assign does not exist on LocationPrototype: true +assign descriptor is not undefined: true +assign enumerable: true +assign configurable: true +assign writable: false +replace exists on document.location itself: true +replace does not exist on LocationPrototype: true +replace descriptor is not undefined: true +replace enumerable: true +replace configurable: true +replace writable: false +reload exists on document.location itself: true +reload does not exist on LocationPrototype: true +reload descriptor is not undefined: true +reload enumerable: true +reload configurable: true +reload writable: false +toString exists on document.location itself: true +toString does not exist on LocationPrototype: true +toString descriptor is not undefined: true +toString enumerable: true +toString configurable: true +toString writable: false diff --git a/Tests/LibWeb/Text/expected/HTML/Window-has-unforgeable-properties.txt b/Tests/LibWeb/Text/expected/HTML/Window-has-unforgeable-properties.txt new file mode 100644 index 00000000000..aec5335dd84 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/Window-has-unforgeable-properties.txt @@ -0,0 +1,24 @@ +window exists on window itself: true +window does not exist on LocationPrototype: true +window descriptor is not undefined: true +window enumerable: true +window configurable: false +window writable: undefined +document exists on window itself: true +document does not exist on LocationPrototype: true +document descriptor is not undefined: true +document enumerable: true +document configurable: false +document writable: undefined +location exists on window itself: true +location does not exist on LocationPrototype: true +location descriptor is not undefined: true +location enumerable: true +location configurable: false +location writable: undefined +top exists on window itself: true +top does not exist on LocationPrototype: true +top descriptor is not undefined: true +top enumerable: true +top configurable: false +top writable: undefined diff --git a/Tests/LibWeb/Text/input/DOM/Document-has-unforgeable-properties.html b/Tests/LibWeb/Text/input/DOM/Document-has-unforgeable-properties.html new file mode 100644 index 00000000000..1af03440c66 --- /dev/null +++ b/Tests/LibWeb/Text/input/DOM/Document-has-unforgeable-properties.html @@ -0,0 +1,33 @@ + + + diff --git a/Tests/LibWeb/Text/input/DOM/Event-has-unforgeable-properties.html b/Tests/LibWeb/Text/input/DOM/Event-has-unforgeable-properties.html new file mode 100644 index 00000000000..b7a462ca017 --- /dev/null +++ b/Tests/LibWeb/Text/input/DOM/Event-has-unforgeable-properties.html @@ -0,0 +1,25 @@ + + + diff --git a/Tests/LibWeb/Text/input/HTML/Location-has-unforgeable-properties.html b/Tests/LibWeb/Text/input/HTML/Location-has-unforgeable-properties.html new file mode 100644 index 00000000000..08787d6881f --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/Location-has-unforgeable-properties.html @@ -0,0 +1,37 @@ + + + diff --git a/Tests/LibWeb/Text/input/HTML/Window-has-unforgeable-properties.html b/Tests/LibWeb/Text/input/HTML/Window-has-unforgeable-properties.html new file mode 100644 index 00000000000..6ea1e83998f --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/Window-has-unforgeable-properties.html @@ -0,0 +1,25 @@ + + +