mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 19:45:12 +00:00
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
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:
parent
7853b757c2
commit
c7d25301d3
Notes:
github-actions[bot]
2025-03-28 00:10:01 +00:00
Author: https://github.com/Lubrsi Commit: https://github.com/LadybirdBrowser/ladybird/commit/c7d25301d31 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3973 Reviewed-by: https://github.com/tcl3 ✅
15 changed files with 511 additions and 76 deletions
|
@ -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(';');
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"~~~(
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
Loading…
Add table
Reference in a new issue