Przeglądaj źródła

Ladybird+LibWeb: Add optional IDL call tracing

When launched with the new --enable-idl-tracing option, we now log
every call to web platform APIs declared via IDL, along with the
arguments passed.

This can be very helpful when trying to figure out what a site is
doing, especially if it's not doing what you'd expect.
Andreas Kling 1 rok temu
rodzic
commit
f4f4f7781d

+ 2 - 0
Ladybird/HelperProcess.cpp

@@ -67,6 +67,8 @@ ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
                 arguments.append("--wait-for-debugger"sv);
                 arguments.append("--wait-for-debugger"sv);
             if (web_content_options.log_all_js_exceptions == Ladybird::LogAllJSExceptions::Yes)
             if (web_content_options.log_all_js_exceptions == Ladybird::LogAllJSExceptions::Yes)
                 arguments.append("--log-all-js-exceptions"sv);
                 arguments.append("--log-all-js-exceptions"sv);
+            if (web_content_options.enable_idl_tracing == Ladybird::EnableIDLTracing::Yes)
+                arguments.append("--enable-idl-tracing"sv);
             if (auto server = mach_server_name(); server.has_value()) {
             if (auto server = mach_server_name(); server.has_value()) {
                 arguments.append("--mach-server-name"sv);
                 arguments.append("--mach-server-name"sv);
                 arguments.append(server.value());
                 arguments.append(server.value());

+ 3 - 0
Ladybird/Qt/main.cpp

@@ -117,6 +117,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     bool use_gpu_painting = false;
     bool use_gpu_painting = false;
     bool debug_web_content = false;
     bool debug_web_content = false;
     bool log_all_js_exceptions = false;
     bool log_all_js_exceptions = false;
+    bool enable_idl_tracing = false;
 
 
     Core::ArgsParser args_parser;
     Core::ArgsParser args_parser;
     args_parser.set_general_help("The Ladybird web browser :^)");
     args_parser.set_general_help("The Ladybird web browser :^)");
@@ -129,6 +130,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     args_parser.add_option(debug_web_content, "Wait for debugger to attach to WebContent", "debug-web-content", 0);
     args_parser.add_option(debug_web_content, "Wait for debugger to attach to WebContent", "debug-web-content", 0);
     args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
     args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
     args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions", 0);
     args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions", 0);
+    args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing", 0);
     args_parser.parse(arguments);
     args_parser.parse(arguments);
 
 
     WebView::ProcessManager::initialize();
     WebView::ProcessManager::initialize();
@@ -173,6 +175,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         .use_lagom_networking = enable_qt_networking ? Ladybird::UseLagomNetworking::No : Ladybird::UseLagomNetworking::Yes,
         .use_lagom_networking = enable_qt_networking ? Ladybird::UseLagomNetworking::No : Ladybird::UseLagomNetworking::Yes,
         .wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No,
         .wait_for_debugger = debug_web_content ? Ladybird::WaitForDebugger::Yes : Ladybird::WaitForDebugger::No,
         .log_all_js_exceptions = log_all_js_exceptions ? Ladybird::LogAllJSExceptions::Yes : Ladybird::LogAllJSExceptions::No,
         .log_all_js_exceptions = log_all_js_exceptions ? Ladybird::LogAllJSExceptions::Yes : Ladybird::LogAllJSExceptions::No,
+        .enable_idl_tracing = enable_idl_tracing ? Ladybird::EnableIDLTracing::Yes : Ladybird::EnableIDLTracing::No,
     };
     };
 
 
     Ladybird::BrowserWindow window(initial_urls, cookie_jar, web_content_options, webdriver_content_ipc_path);
     Ladybird::BrowserWindow window(initial_urls, cookie_jar, web_content_options, webdriver_content_ipc_path);

+ 6 - 0
Ladybird/Types.h

@@ -40,6 +40,11 @@ enum class LogAllJSExceptions {
     Yes
     Yes
 };
 };
 
 
+enum class EnableIDLTracing {
+    No,
+    Yes
+};
+
 struct WebContentOptions {
 struct WebContentOptions {
     String command_line;
     String command_line;
     String executable_path;
     String executable_path;
@@ -50,6 +55,7 @@ struct WebContentOptions {
     UseLagomNetworking use_lagom_networking { UseLagomNetworking::No };
     UseLagomNetworking use_lagom_networking { UseLagomNetworking::No };
     WaitForDebugger wait_for_debugger { WaitForDebugger::No };
     WaitForDebugger wait_for_debugger { WaitForDebugger::No };
     LogAllJSExceptions log_all_js_exceptions { LogAllJSExceptions::No };
     LogAllJSExceptions log_all_js_exceptions { LogAllJSExceptions::No };
+    EnableIDLTracing enable_idl_tracing { EnableIDLTracing::No };
 };
 };
 
 
 }
 }

+ 10 - 0
Ladybird/WebContent/main.cpp

@@ -57,6 +57,10 @@ namespace JS {
 extern bool g_log_all_js_exceptions;
 extern bool g_log_all_js_exceptions;
 }
 }
 
 
+namespace Web::WebIDL {
+extern bool g_enable_idl_tracing;
+}
+
 ErrorOr<int> serenity_main(Main::Arguments arguments)
 ErrorOr<int> serenity_main(Main::Arguments arguments)
 {
 {
     AK::set_rich_debug_enabled(true);
     AK::set_rich_debug_enabled(true);
@@ -94,6 +98,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     bool use_gpu_painting = false;
     bool use_gpu_painting = false;
     bool wait_for_debugger = false;
     bool wait_for_debugger = false;
     bool log_all_js_exceptions = false;
     bool log_all_js_exceptions = false;
+    bool enable_idl_tracing = false;
 
 
     Core::ArgsParser args_parser;
     Core::ArgsParser args_parser;
     args_parser.add_option(command_line, "Chrome process command line", "command-line", 0, "command_line");
     args_parser.add_option(command_line, "Chrome process command line", "command-line", 0, "command_line");
@@ -106,6 +111,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     args_parser.add_option(wait_for_debugger, "Wait for debugger", "wait-for-debugger", 0);
     args_parser.add_option(wait_for_debugger, "Wait for debugger", "wait-for-debugger", 0);
     args_parser.add_option(mach_server_name, "Mach server name", "mach-server-name", 0, "mach_server_name");
     args_parser.add_option(mach_server_name, "Mach server name", "mach-server-name", 0, "mach_server_name");
     args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions", 0);
     args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions", 0);
+    args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing", 0);
 
 
     args_parser.parse(arguments);
     args_parser.parse(arguments);
 
 
@@ -144,6 +150,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         JS::g_log_all_js_exceptions = true;
         JS::g_log_all_js_exceptions = true;
     }
     }
 
 
+    if (enable_idl_tracing) {
+        Web::WebIDL::g_enable_idl_tracing = true;
+    }
+
     auto maybe_content_filter_error = load_content_filters();
     auto maybe_content_filter_error = load_content_filters();
     if (maybe_content_filter_error.is_error())
     if (maybe_content_filter_error.is_error())
         dbgln("Failed to load content filters: {}", maybe_content_filter_error.error());
         dbgln("Failed to load content filters: {}", maybe_content_filter_error.error());

+ 24 - 1
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -1909,6 +1909,7 @@ static void generate_function(SourceGenerator& generator, IDL::Function const& f
     function_generator.append(R"~~~(
     function_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@@overload_suffix@)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@@overload_suffix@)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::@function.name:snakecase@@overload_suffix@");
     [[maybe_unused]] auto& realm = *vm.current_realm();
     [[maybe_unused]] auto& realm = *vm.current_realm();
 )~~~");
 )~~~");
 
 
@@ -2185,11 +2186,13 @@ static void generate_overload_arbiter(SourceGenerator& generator, auto const& ov
 JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct(JS::FunctionObject& new_target)
 JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct(JS::FunctionObject& new_target)
 {
 {
     auto& vm = this->vm();
     auto& vm = this->vm();
+    WebIDL::log_trace(vm, "@constructor_class@::construct");
 )~~~");
 )~~~");
     } else {
     } else {
         function_generator.append(R"~~~(
         function_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@function.name:snakecase@)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::@function.name:snakecase@");
 )~~~");
 )~~~");
     }
     }
 
 
@@ -2463,6 +2466,7 @@ static void generate_constructor(SourceGenerator& generator, IDL::Constructor co
     constructor_generator.append(R"~~~(
     constructor_generator.append(R"~~~(
 JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct@overload_suffix@([[maybe_unused]] FunctionObject& new_target)
 JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct@overload_suffix@([[maybe_unused]] FunctionObject& new_target)
 {
 {
+    WebIDL::log_trace(vm(), "@constructor_class@::construct@overload_suffix@");
 )~~~");
 )~~~");
 
 
     generator.append(R"~~~(
     generator.append(R"~~~(
@@ -2522,6 +2526,7 @@ static void generate_constructors(SourceGenerator& generator, IDL::Interface con
         generator.append(R"~~~(
         generator.append(R"~~~(
 JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct([[maybe_unused]] FunctionObject& new_target)
 JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::construct([[maybe_unused]] FunctionObject& new_target)
 {
 {
+    WebIDL::log_trace(vm(), "@constructor_class@::construct");
 )~~~");
 )~~~");
         generator.set("constructor.length", "0");
         generator.set("constructor.length", "0");
         generator.append(R"~~~(
         generator.append(R"~~~(
@@ -2769,6 +2774,7 @@ static void generate_default_to_json_function(SourceGenerator& generator, ByteSt
     function_generator.append(R"~~~(
     function_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_json)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_json)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::to_json");
     auto& realm = *vm.current_realm();
     auto& realm = *vm.current_realm();
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 
 
@@ -3220,6 +3226,7 @@ static JS::ThrowCompletionOr<@fully_qualified_name@*> impl_from(JS::VM& vm)
         attribute_generator.append(R"~~~(
         attribute_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::@attribute.getter_callback@");
     [[maybe_unused]] auto& realm = *vm.current_realm();
     [[maybe_unused]] auto& realm = *vm.current_realm();
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 )~~~");
 )~~~");
@@ -3424,6 +3431,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@)
             attribute_generator.append(R"~~~(
             attribute_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::@attribute.setter_callback@");
     [[maybe_unused]] auto& realm = *vm.current_realm();
     [[maybe_unused]] auto& realm = *vm.current_realm();
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 
 
@@ -3508,6 +3516,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
                 attribute_generator.append(R"~~~(
                 attribute_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::@attribute.setter_callback@");
     auto this_value = vm.this_value();
     auto this_value = vm.this_value();
     JS::GCPtr<Window> window;
     JS::GCPtr<Window> window;
     if (this_value.is_object()) {
     if (this_value.is_object()) {
@@ -3532,6 +3541,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
                 attribute_generator.append(R"~~~(
                 attribute_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::@attribute.setter_callback@");
     auto this_value = vm.this_value();
     auto this_value = vm.this_value();
     if (!this_value.is_object() || !is<@fully_qualified_name@>(this_value.as_object()))
     if (!this_value.is_object() || !is<@fully_qualified_name@>(this_value.as_object()))
         return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@namespaced_name@");
         return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "@namespaced_name@");
@@ -3546,6 +3556,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
             attribute_generator.append(R"~~~(
             attribute_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::@attribute.setter_callback@");
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
     auto value = vm.argument(0);
     auto value = vm.argument(0);
 
 
@@ -3588,6 +3599,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
         stringifier_generator.append(R"~~~(
         stringifier_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_string)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_string)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::to_string");
     [[maybe_unused]] auto& realm = *vm.current_realm();
     [[maybe_unused]] auto& realm = *vm.current_realm();
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 
 
@@ -3613,6 +3625,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::to_string)
         iterator_generator.append(R"~~~(
         iterator_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::entries)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::entries)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::entries");
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 
 
     return TRY(throw_dom_exception_if_needed(vm, [&] { return @iterator_name@::create(*impl, Object::PropertyKind::KeyAndValue); }));
     return TRY(throw_dom_exception_if_needed(vm, [&] { return @iterator_name@::create(*impl, Object::PropertyKind::KeyAndValue); }));
@@ -3620,6 +3633,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::entries)
 
 
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::for_each)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::for_each)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::for_each");
     [[maybe_unused]] auto& realm = *vm.current_realm();
     [[maybe_unused]] auto& realm = *vm.current_realm();
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 
 
@@ -3642,6 +3656,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::for_each)
 
 
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::keys)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::keys)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::keys");
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 
 
     return TRY(throw_dom_exception_if_needed(vm, [&] { return @iterator_name@::create(*impl, Object::PropertyKind::Key);  }));
     return TRY(throw_dom_exception_if_needed(vm, [&] { return @iterator_name@::create(*impl, Object::PropertyKind::Key);  }));
@@ -3649,6 +3664,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::keys)
 
 
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::values)
 JS_DEFINE_NATIVE_FUNCTION(@class_name@::values)
 {
 {
+    WebIDL::log_trace(vm, "@class_name@::values");
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
 
 
     return TRY(throw_dom_exception_if_needed(vm, [&] { return @iterator_name@::create(*impl, Object::PropertyKind::Value); }));
     return TRY(throw_dom_exception_if_needed(vm, [&] { return @iterator_name@::create(*impl, Object::PropertyKind::Value); }));
@@ -3735,6 +3751,7 @@ void generate_namespace_implementation(IDL::Interface const& interface, StringBu
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/Buffers.h>
 #include <LibWeb/WebIDL/Buffers.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
+#include <LibWeb/WebIDL/Tracing.h>
 #include <LibWeb/WebIDL/Types.h>
 #include <LibWeb/WebIDL/Types.h>
 
 
 )~~~");
 )~~~");
@@ -3976,8 +3993,9 @@ void generate_constructor_implementation(IDL::Interface const& interface, String
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/Buffers.h>
 #include <LibWeb/WebIDL/Buffers.h>
 #include <LibWeb/WebIDL/CallbackType.h>
 #include <LibWeb/WebIDL/CallbackType.h>
-#include <LibWeb/WebIDL/Types.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
+#include <LibWeb/WebIDL/Tracing.h>
+#include <LibWeb/WebIDL/Types.h>
 
 
 )~~~");
 )~~~");
 
 
@@ -4123,6 +4141,7 @@ void @constructor_class@::initialize(JS::Realm& realm)
         attribute_generator.append(R"~~~(
         attribute_generator.append(R"~~~(
 JS_DEFINE_NATIVE_FUNCTION(@constructor_class@::@attribute.getter_callback@)
 JS_DEFINE_NATIVE_FUNCTION(@constructor_class@::@attribute.getter_callback@)
 {
 {
+    WebIDL::log_trace(vm, "@constructor_class@::@attribute.getter_callback@");
     auto retval = TRY(throw_dom_exception_if_needed(vm, [&] { return @fully_qualified_name@::@attribute.cpp_name@(vm); }));
     auto retval = TRY(throw_dom_exception_if_needed(vm, [&] { return @fully_qualified_name@::@attribute.cpp_name@(vm); }));
 )~~~");
 )~~~");
 
 
@@ -4225,6 +4244,7 @@ void generate_prototype_implementation(IDL::Interface const& interface, StringBu
 #include <LibWeb/HTML/WindowProxy.h>
 #include <LibWeb/HTML/WindowProxy.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/Buffers.h>
 #include <LibWeb/WebIDL/Buffers.h>
+#include <LibWeb/WebIDL/Tracing.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
 #include <LibWeb/WebIDL/Types.h>
 #include <LibWeb/WebIDL/Types.h>
 
 
@@ -4417,6 +4437,7 @@ void generate_iterator_prototype_implementation(IDL::Interface const& interface,
 #include <LibWeb/Bindings/@prototype_class@.h>
 #include <LibWeb/Bindings/@prototype_class@.h>
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/Intrinsics.h>
 #include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/WebIDL/Tracing.h>
 
 
 #if __has_include(<LibWeb/@possible_include_path@.h>)
 #if __has_include(<LibWeb/@possible_include_path@.h>)
 #    include <LibWeb/@possible_include_path@.h>
 #    include <LibWeb/@possible_include_path@.h>
@@ -4483,6 +4504,7 @@ static JS::ThrowCompletionOr<@fully_qualified_name@*> impl_from(JS::VM& vm)
 
 
 JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::next)
 JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::next)
 {
 {
+    WebIDL::log_trace(vm, "@prototype_class@::next");
     auto* impl = TRY(impl_from(vm));
     auto* impl = TRY(impl_from(vm));
     return TRY(throw_dom_exception_if_needed(vm, [&] { return impl->next(); }));
     return TRY(throw_dom_exception_if_needed(vm, [&] { return impl->next(); }));
 }
 }
@@ -4552,6 +4574,7 @@ void generate_global_mixin_implementation(IDL::Interface const& interface, Strin
 #include <LibWeb/HTML/WindowProxy.h>
 #include <LibWeb/HTML/WindowProxy.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
 #include <LibWeb/WebIDL/OverloadResolution.h>
+#include <LibWeb/WebIDL/Tracing.h>
 #include <LibWeb/WebIDL/Types.h>
 #include <LibWeb/WebIDL/Types.h>
 
 
 )~~~");
 )~~~");

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -675,6 +675,7 @@ set(SOURCES
     WebIDL/ObservableArray.cpp
     WebIDL/ObservableArray.cpp
     WebIDL/OverloadResolution.cpp
     WebIDL/OverloadResolution.cpp
     WebIDL/Promise.cpp
     WebIDL/Promise.cpp
+    WebIDL/Tracing.cpp
     WebSockets/WebSocket.cpp
     WebSockets/WebSocket.cpp
     XHR/EventNames.cpp
     XHR/EventNames.cpp
     XHR/FormData.cpp
     XHR/FormData.cpp

+ 42 - 0
Userland/Libraries/LibWeb/WebIDL/Tracing.cpp

@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2024, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/Format.h>
+#include <AK/StringBuilder.h>
+#include <LibJS/Runtime/VM.h>
+#include <LibWeb/WebIDL/Tracing.h>
+
+namespace Web::WebIDL {
+
+bool g_enable_idl_tracing = false;
+
+void log_trace_impl(JS::VM& vm, char const* function)
+{
+    if (!g_enable_idl_tracing)
+        return;
+
+    StringBuilder builder;
+    for (size_t i = 0; i < vm.argument_count(); ++i) {
+        if (i != 0)
+            builder.append(", "sv);
+        auto argument = vm.argument(i);
+        if (argument.is_string())
+            builder.append_code_point('"');
+        auto string = argument.to_string_without_side_effects();
+        for (auto code_point : string.code_points()) {
+            if (code_point < 0x20) {
+                builder.appendff("\\u{:04x}", code_point);
+                continue;
+            }
+            builder.append_code_point(code_point);
+        }
+        if (argument.is_string())
+            builder.append_code_point('"');
+    }
+    dbgln("{}({})", function, builder.string_view());
+}
+
+}

+ 22 - 0
Userland/Libraries/LibWeb/WebIDL/Tracing.h

@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2024, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Forward.h>
+
+namespace Web::WebIDL {
+
+extern bool g_enable_idl_tracing;
+
+inline void log_trace(JS::VM& vm, char const* function)
+{
+    void log_trace_impl(JS::VM&, char const*);
+    if (g_enable_idl_tracing)
+        log_trace_impl(vm, function);
+}
+
+}