LibWeb: Implement the LegacyUnforgeable attribute
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

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.
This commit is contained in:
Luke Wilde 2024-12-09 17:04:20 +00:00 committed by Tim Ledbetter
parent 7853b757c2
commit c7d25301d3
Notes: github-actions[bot] 2025-03-28 00:10:01 +00:00
15 changed files with 511 additions and 76 deletions

View file

@ -504,7 +504,7 @@ void Parser::parse_stringifier(HashMap<ByteString, ByteString>& 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(';');
}

View file

@ -280,7 +280,7 @@ public:
Vector<Function> functions;
Vector<Function> static_functions;
bool has_stringifier { false };
Optional<ByteString> stringifier_attribute;
Optional<Attribute> stringifier_attribute;
bool has_unscopable_member { false };
Optional<NonnullRefPtr<Type const>> value_iterator_type;

View file

@ -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<Selection::Selection>(realm, *this);

View file

@ -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)

View file

@ -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();

View file

@ -731,6 +731,7 @@ WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironm
WEB_SET_PROTOTYPE_FOR_INTERFACE(Window);
Bindings::WindowGlobalMixin::initialize(realm, *this);
Bindings::WindowGlobalMixin::define_unforgeable_attributes(realm, *this);
WindowOrWorkerGlobalScopeMixin::initialize(realm);
if (s_internals_object_exposed)

View file

@ -3338,8 +3338,12 @@ void @named_properties_class@::visit_edges(Visitor& visitor)
)~~~");
}
// https://webidl.spec.whatwg.org/#interface-prototype-object
static void generate_prototype_or_global_mixin_definitions(IDL::Interface const& interface, StringBuilder& builder)
enum class GenerateUnforgeables {
No,
Yes,
};
static void generate_prototype_or_global_mixin_initialization(IDL::Interface const& interface, StringBuilder& builder, GenerateUnforgeables generate_unforgeables)
{
SourceGenerator generator { builder };
@ -3357,15 +3361,27 @@ static void generate_prototype_or_global_mixin_definitions(IDL::Interface const&
generator.set("iterator_name", ByteString::formatted("{}Iterator", interface.name));
}
if (is_global_interface) {
generator.set("named_properties_class", ByteString::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)
#define define_native_accessor (object.define_native_accessor)
#define define_native_function (object.define_native_function)
#define set_prototype (object.set_prototype)
bool define_on_existing_object = is_global_interface || generate_unforgeables == GenerateUnforgeables::Yes;
if (define_on_existing_object) {
generator.set("define_direct_property", "object.define_direct_property");
generator.set("define_native_accessor", "object.define_native_accessor");
generator.set("define_native_function", "object.define_native_function");
generator.set("set_prototype", "object.set_prototype");
} else {
generator.set("define_direct_property", "define_direct_property");
generator.set("define_native_accessor", "define_native_accessor");
generator.set("define_native_function", "define_native_function");
generator.set("set_prototype", "set_prototype");
}
if (generate_unforgeables == GenerateUnforgeables::Yes) {
generator.append(R"~~~(
void @class_name@::define_unforgeable_attributes(JS::Realm& realm, [[maybe_unused]] JS::Object& object)
{
)~~~");
} else if (is_global_interface) {
generator.append(R"~~~(
void @class_name@::initialize(JS::Realm& realm, JS::Object& object)
{
)~~~");
@ -3376,38 +3392,49 @@ void @class_name@::initialize(JS::Realm& realm)
)~~~");
}
// FIXME: Currently almost everything gets default_attributes but it should be configurable per attribute.
// See the spec links for details
generator.append(R"~~~(
[[maybe_unused]] auto& vm = realm.vm();
)~~~");
// FIXME: Currently almost everything gets default_attributes but it should be configurable per attribute.
// See the spec links for details
if (generate_unforgeables == GenerateUnforgeables::No) {
generator.append(R"~~~(
[[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable | JS::Attribute::Writable;
)~~~");
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"~~~(
[[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"~~~(

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
test(() => {
const unforgeableProperties = [
"location",
];
const documents = [
document,
new Document(),
document.implementation.createDocument("http://www.w3.org/1999/xhtml", null, null),
document.implementation.createDocument("http://www.w3.org/2000/svg", null, null),
document.implementation.createDocument(null, null, null),
document.implementation.createHTMLDocument(),
];
for (const document of documents) {
const documentPrototype = Object.getPrototypeOf(document);
for (const property of unforgeableProperties) {
println(`${property} exists on document itself: ${Object.hasOwn(document, property)}`);
println(`${property} does not exist on LocationPrototype: ${!Object.hasOwn(documentPrototype, property)}`);
const propertyDescriptor = Object.getOwnPropertyDescriptor(document, property);
println(`${property} descriptor is not undefined: ${propertyDescriptor !== undefined}`);
println(`${property} enumerable: ${propertyDescriptor.enumerable}`);
println(`${property} configurable: ${propertyDescriptor.configurable}`);
println(`${property} writable: ${propertyDescriptor.writable}`);
}
}
});
</script>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
test(() => {
const unforgeableProperties = [
"isTrusted",
];
for (const eventType of [Event, UIEvent, CustomEvent]) {
const event = new eventType("test");
const eventPrototype = Object.getPrototypeOf(event);
for (const property of unforgeableProperties) {
println(`${property} exists on ${eventType.name} instance itself: ${Object.hasOwn(event, property)}`);
println(`${property} does not exist on LocationPrototype: ${!Object.hasOwn(eventPrototype, property)}`);
const propertyDescriptor = Object.getOwnPropertyDescriptor(event, property);
println(`${property} descriptor is not undefined: ${propertyDescriptor !== undefined}`);
println(`${property} enumerable: ${propertyDescriptor.enumerable}`);
println(`${property} configurable: ${propertyDescriptor.configurable}`);
println(`${property} writable: ${propertyDescriptor.writable}`);
}
}
});
</script>

View file

@ -0,0 +1,37 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
test(() => {
for (const { location, name } of [{ location: window.location, name: "location" }, { location: document.location, name: "document.location" }]) {
const unforgeableProperties = [
"href",
"origin",
"protocol",
"host",
"hostname",
"port",
"pathname",
"search",
"hash",
"assign",
"replace",
"reload",
// FIXME: "ancestorOrigins",
"toString",
];
const locationPrototype = Object.getPrototypeOf(location);
for (const property of unforgeableProperties) {
println(`${property} exists on ${name} itself: ${Object.hasOwn(location, property)}`);
println(`${property} does not exist on LocationPrototype: ${!Object.hasOwn(locationPrototype, property)}`);
const propertyDescriptor = Object.getOwnPropertyDescriptor(location, property);
println(`${property} descriptor is not undefined: ${propertyDescriptor !== undefined}`);
println(`${property} enumerable: ${propertyDescriptor.enumerable}`);
println(`${property} configurable: ${propertyDescriptor.configurable}`);
println(`${property} writable: ${propertyDescriptor.writable}`);
}
}
});
</script>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<script>
test(() => {
const unforgeableProperties = [
"window",
"document",
"location",
"top",
];
const windowPrototype = Object.getPrototypeOf(window);
for (const property of unforgeableProperties) {
println(`${property} exists on window itself: ${Object.hasOwn(window, property)}`);
println(`${property} does not exist on LocationPrototype: ${!Object.hasOwn(windowPrototype, property)}`);
const propertyDescriptor = Object.getOwnPropertyDescriptor(window, property);
println(`${property} descriptor is not undefined: ${propertyDescriptor !== undefined}`);
println(`${property} enumerable: ${propertyDescriptor.enumerable}`);
println(`${property} configurable: ${propertyDescriptor.configurable}`);
println(`${property} writable: ${propertyDescriptor.writable}`);
}
});
</script>