mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 03:55:24 +00:00
LibWeb: Add support for IDL callback functions
This commit is contained in:
parent
9ff79c9d54
commit
1c4f128fd1
Notes:
sideshowbarker
2024-07-17 16:26:50 +09:00
Author: https://github.com/IdanHo Commit: https://github.com/SerenityOS/serenity/commit/1c4f128fd1 Pull-request: https://github.com/SerenityOS/serenity/pull/13369 Reviewed-by: https://github.com/alimpfard ✅
4 changed files with 84 additions and 0 deletions
|
@ -640,6 +640,32 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
|
|||
VERIFY(interface.dictionaries.contains(current_dictionary->parent_name));
|
||||
current_dictionary = &interface.dictionaries.find(current_dictionary->parent_name)->value;
|
||||
}
|
||||
} else if (interface.callback_functions.contains(parameter.type->name)) {
|
||||
// https://webidl.spec.whatwg.org/#es-callback-function
|
||||
|
||||
auto callback_function_generator = scoped_generator.fork();
|
||||
auto& callback_function = interface.callback_functions.find(parameter.type->name)->value;
|
||||
|
||||
// An ECMAScript value V is converted to an IDL callback function type value by running the following algorithm:
|
||||
// 1. If the result of calling IsCallable(V) is false and the conversion to an IDL value is not being performed due to V being assigned to an attribute whose type is a nullable callback function that is annotated with [LegacyTreatNonObjectAsNull], then throw a TypeError.
|
||||
if (!callback_function.is_legacy_treat_non_object_as_null) {
|
||||
callback_function_generator.append(R"~~~(
|
||||
if (!@js_name@@js_suffix@.is_function())
|
||||
return vm.throw_completion<JS::TypeError>(global_object, JS::ErrorType::NotAFunction, @js_name@@js_suffix@.to_string_without_side_effects());
|
||||
)~~~");
|
||||
}
|
||||
// 2. Return the IDL callback function type value that represents a reference to the same object that V represents, with the incumbent settings object as the callback context.
|
||||
if (callback_function.is_legacy_treat_non_object_as_null) {
|
||||
callback_function_generator.append(R"~~~(
|
||||
Optional<Bindings::CallbackType> @cpp_name@;
|
||||
if (@js_name@@js_suffix@.is_object())
|
||||
@cpp_name@ = Bindings::CallbackType { JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object() };
|
||||
)~~~");
|
||||
} else {
|
||||
callback_function_generator.append(R"~~~(
|
||||
auto @cpp_name@ = Bindings::CallbackType { JS::make_handle(&@js_name@@js_suffix@.as_object()), HTML::incumbent_settings_object() };
|
||||
)~~~");
|
||||
}
|
||||
} else if (parameter.type->name == "sequence") {
|
||||
// https://webidl.spec.whatwg.org/#es-sequence
|
||||
|
||||
|
@ -1342,6 +1368,28 @@ static void generate_wrap_statement(SourceGenerator& generator, String const& va
|
|||
scoped_generator.append(R"~~~(
|
||||
@result_expression@ JS::js_string(global_object.heap(), Bindings::idl_enum_to_string(@value@));
|
||||
)~~~");
|
||||
} else if (interface.callback_functions.contains(type.name)) {
|
||||
// https://webidl.spec.whatwg.org/#es-callback-function
|
||||
|
||||
auto& callback_function = interface.callback_functions.find(type.name)->value;
|
||||
|
||||
// The result of converting an IDL callback function type value to an ECMAScript value is a reference to the same object that the IDL callback function type value represents.
|
||||
|
||||
if (callback_function.is_legacy_treat_non_object_as_null && !type.nullable) {
|
||||
scoped_generator.append(R"~~~(
|
||||
if (!@value@) {
|
||||
@result_expression@ JS::js_null();
|
||||
} else {
|
||||
VERIFY(!@value@->callback.is_null());
|
||||
@result_expression@ @value@->callback.cell();
|
||||
}
|
||||
)~~~");
|
||||
} else {
|
||||
scoped_generator.append(R"~~~(
|
||||
VERIFY(!@value@->callback.is_null());
|
||||
@result_expression@ @value@->callback.cell();
|
||||
)~~~");
|
||||
}
|
||||
} else {
|
||||
if (wrapping_reference == WrappingReference::No) {
|
||||
scoped_generator.append(R"~~~(
|
||||
|
|
|
@ -722,6 +722,29 @@ void Parser::parse_interface_mixin(Interface& interface)
|
|||
interface.mixins.set(move(name), move(mixin_interface));
|
||||
}
|
||||
|
||||
void Parser::parse_callback_function(HashMap<String, String>& extended_attributes, Interface& interface)
|
||||
{
|
||||
assert_string("callback");
|
||||
consume_whitespace();
|
||||
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
|
||||
consume_whitespace();
|
||||
|
||||
assert_specific('=');
|
||||
consume_whitespace();
|
||||
|
||||
auto return_type = parse_type();
|
||||
consume_whitespace();
|
||||
assert_specific('(');
|
||||
auto parameters = parse_parameters();
|
||||
assert_specific(')');
|
||||
consume_whitespace();
|
||||
assert_specific(';');
|
||||
|
||||
interface.callback_functions.set(name, CallbackFunction { move(return_type), move(parameters), extended_attributes.contains("LegacyTreatNonObjectAsNull") });
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
void Parser::parse_non_interface_entities(bool allow_interface, Interface& interface)
|
||||
{
|
||||
while (!lexer.is_eof()) {
|
||||
|
@ -736,6 +759,8 @@ void Parser::parse_non_interface_entities(bool allow_interface, Interface& inter
|
|||
parse_typedef(interface);
|
||||
} else if (lexer.next_is("interface mixin")) {
|
||||
parse_interface_mixin(interface);
|
||||
} else if (lexer.next_is("callback")) {
|
||||
parse_callback_function(extended_attributes, interface);
|
||||
} else if ((allow_interface && !lexer.next_is("interface")) || !allow_interface) {
|
||||
auto current_offset = lexer.tell();
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
|
||||
|
@ -841,6 +866,9 @@ NonnullOwnPtr<Interface> Parser::parse()
|
|||
report_parsing_error(String::formatted("Mixin '{}' was already defined in {}", mixin.key, mixin.value->module_own_path), filename, input, lexer.tell());
|
||||
interface->mixins.set(mixin.key, move(mixin.value));
|
||||
}
|
||||
|
||||
for (auto& callback_function : import.callback_functions)
|
||||
interface->callback_functions.set(callback_function.key, move(callback_function.value));
|
||||
}
|
||||
|
||||
// Resolve mixins
|
||||
|
|
|
@ -41,6 +41,7 @@ private:
|
|||
void parse_typedef(Interface&);
|
||||
void parse_interface_mixin(Interface&);
|
||||
void parse_dictionary(Interface&);
|
||||
void parse_callback_function(HashMap<String, String>& extended_attributes, Interface&);
|
||||
void parse_constructor(Interface&);
|
||||
void parse_getter(HashMap<String, String>& extended_attributes, Interface&);
|
||||
void parse_setter(HashMap<String, String>& extended_attributes, Interface&);
|
||||
|
|
|
@ -133,6 +133,12 @@ struct Enumeration {
|
|||
bool is_original_definition { true };
|
||||
};
|
||||
|
||||
struct CallbackFunction {
|
||||
NonnullRefPtr<Type> return_type;
|
||||
Vector<Parameter> parameters;
|
||||
bool is_legacy_treat_non_object_as_null { false };
|
||||
};
|
||||
|
||||
struct Interface;
|
||||
|
||||
struct ParameterizedType : public Type {
|
||||
|
@ -191,6 +197,7 @@ struct Interface {
|
|||
HashMap<String, Enumeration> enumerations;
|
||||
HashMap<String, Typedef> typedefs;
|
||||
HashMap<String, NonnullOwnPtr<Interface>> mixins;
|
||||
HashMap<String, CallbackFunction> callback_functions;
|
||||
|
||||
// Added for convenience after parsing
|
||||
String wrapper_class;
|
||||
|
|
Loading…
Add table
Reference in a new issue