Explorar o código

LibWeb: Add support for IDL callback functions

Idan Horowitz %!s(int64=3) %!d(string=hai) anos
pai
achega
1c4f128fd1

+ 48 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLGenerators.cpp

@@ -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"~~~(

+ 28 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.cpp

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

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLParser.h

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

+ 7 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/IDLTypes.h

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