diff --git a/Libraries/LibDevTools/Actors/FrameActor.cpp b/Libraries/LibDevTools/Actors/FrameActor.cpp index 2fe33be7690..9ba6afa0eda 100644 --- a/Libraries/LibDevTools/Actors/FrameActor.cpp +++ b/Libraries/LibDevTools/Actors/FrameActor.cpp @@ -229,48 +229,94 @@ void FrameActor::console_messages_received(i32 start_index, Vector"sv); - message.set("line_number"sv, 1); - message.set("column_number"sv, 1); - message.set("time_stamp"sv, output.timestamp.milliseconds_since_epoch()); - message.set("arguments"sv, JsonArray { move(output.arguments) }); + message.set("filename"sv, ""sv); + message.set("lineNumber"sv, 1); + message.set("columnNumber"sv, 1); + message.set("timeStamp"sv, output.timestamp.milliseconds_since_epoch()); + message.set("arguments"sv, JsonArray { move(log.arguments) }); - messages.must_append(move(message)); + console_messages.must_append(move(message)); + }, + [&](WebView::ConsoleError const& error) { + StringBuilder stack; + + for (auto const& frame : error.trace) { + if (frame.function.has_value()) + stack.append(*frame.function); + stack.append('@'); + stack.append(frame.file.map([](auto const& file) -> StringView { return file; }).value_or("unknown"sv)); + stack.appendff(":{}:{}\n", frame.line.value_or(0), frame.column.value_or(0)); + } + + JsonObject preview; + preview.set("kind"sv, "Error"sv); + preview.set("message"sv, error.message); + preview.set("name"sv, error.name); + if (!stack.is_empty()) + preview.set("stack"sv, MUST(stack.to_string())); + + JsonObject exception; + exception.set("class"sv, error.name); + exception.set("isError"sv, true); + exception.set("preview"sv, move(preview)); + + JsonObject page_error; + page_error.set("error"sv, true); + page_error.set("exception"sv, move(exception)); + page_error.set("hasException"sv, !error.trace.is_empty()); + page_error.set("isPromiseRejection"sv, error.inside_promise); + page_error.set("timeStamp"sv, output.timestamp.milliseconds_since_epoch()); + + message.set("pageError"sv, move(page_error)); + error_messages.must_append(move(message)); + }); } - JsonArray console_message; - console_message.must_append("console-message"sv); - console_message.must_append(move(messages)); - JsonArray array; - array.must_append(move(console_message)); + + if (!console_messages.is_empty()) { + JsonArray console_message; + console_message.must_append("console-message"sv); + console_message.must_append(move(console_messages)); + + array.must_append(move(console_message)); + } + if (!error_messages.is_empty()) { + JsonArray error_message; + error_message.must_append("error-message"sv); + error_message.must_append(move(error_messages)); + + array.must_append(move(error_message)); + } JsonObject message; message.set("type"sv, "resources-available-array"sv); diff --git a/Libraries/LibWebView/ConsoleOutput.cpp b/Libraries/LibWebView/ConsoleOutput.cpp index 1bdbe9f7131..fb3d7d2b2cd 100644 --- a/Libraries/LibWebView/ConsoleOutput.cpp +++ b/Libraries/LibWebView/ConsoleOutput.cpp @@ -9,11 +9,72 @@ #include template<> -ErrorOr IPC::encode(Encoder& encoder, WebView::ConsoleOutput const& console_output) +ErrorOr IPC::encode(Encoder& encoder, WebView::ConsoleLog const& log) { - TRY(encoder.encode(console_output.level)); - TRY(encoder.encode(console_output.timestamp)); - TRY(encoder.encode(console_output.arguments)); + TRY(encoder.encode(log.level)); + TRY(encoder.encode(log.arguments)); + + return {}; +} + +template<> +ErrorOr IPC::decode(Decoder& decoder) +{ + auto level = TRY(decoder.decode()); + auto arguments = TRY(decoder.decode>()); + + return WebView::ConsoleLog { level, move(arguments) }; +} + +template<> +ErrorOr IPC::encode(Encoder& encoder, WebView::StackFrame const& frame) +{ + TRY(encoder.encode(frame.function)); + TRY(encoder.encode(frame.file)); + TRY(encoder.encode(frame.line)); + TRY(encoder.encode(frame.column)); + + return {}; +} + +template<> +ErrorOr IPC::decode(Decoder& decoder) +{ + auto function = TRY(decoder.decode>()); + auto file = TRY(decoder.decode>()); + auto line = TRY(decoder.decode>()); + auto column = TRY(decoder.decode>()); + + return WebView::StackFrame { move(function), move(file), line, column }; +} + +template<> +ErrorOr IPC::encode(Encoder& encoder, WebView::ConsoleError const& error) +{ + TRY(encoder.encode(error.name)); + TRY(encoder.encode(error.message)); + TRY(encoder.encode(error.trace)); + TRY(encoder.encode(error.inside_promise)); + + return {}; +} + +template<> +ErrorOr IPC::decode(Decoder& decoder) +{ + auto name = TRY(decoder.decode()); + auto message = TRY(decoder.decode()); + auto trace = TRY(decoder.decode>()); + auto inside_promise = TRY(decoder.decode()); + + return WebView::ConsoleError { move(name), move(message), move(trace), inside_promise }; +} + +template<> +ErrorOr IPC::encode(Encoder& encoder, WebView::ConsoleOutput const& output) +{ + TRY(encoder.encode(output.timestamp)); + TRY(encoder.encode(output.output)); return {}; } @@ -21,9 +82,8 @@ ErrorOr IPC::encode(Encoder& encoder, WebView::ConsoleOutput const& consol template<> ErrorOr IPC::decode(Decoder& decoder) { - auto level = TRY(decoder.decode()); auto timestamp = TRY(decoder.decode()); - auto arguments = TRY(decoder.decode>()); + auto output = TRY(decoder.decode>()); - return WebView::ConsoleOutput { level, timestamp, move(arguments) }; + return WebView::ConsoleOutput { timestamp, move(output) }; } diff --git a/Libraries/LibWebView/ConsoleOutput.h b/Libraries/LibWebView/ConsoleOutput.h index 28d8b0d6a09..a506ed361ed 100644 --- a/Libraries/LibWebView/ConsoleOutput.h +++ b/Libraries/LibWebView/ConsoleOutput.h @@ -7,23 +7,61 @@ #pragma once #include +#include #include +#include #include #include #include namespace WebView { -struct ConsoleOutput { +struct ConsoleLog { JS::Console::LogLevel level; - UnixDateTime timestamp; Vector arguments; }; +struct StackFrame { + Optional function; + Optional file; + Optional line; + Optional column; +}; + +struct ConsoleError { + String name; + String message; + Vector trace; + bool inside_promise { false }; +}; + +struct ConsoleOutput { + UnixDateTime timestamp; + Variant output; +}; + } namespace IPC { +template<> +ErrorOr encode(Encoder&, WebView::ConsoleLog const&); + +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, WebView::StackFrame const&); + +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, WebView::ConsoleError const&); + +template<> +ErrorOr decode(Decoder&); + template<> ErrorOr encode(Encoder&, WebView::ConsoleOutput const&); diff --git a/Services/WebContent/DevToolsConsoleClient.cpp b/Services/WebContent/DevToolsConsoleClient.cpp index fcef20961f1..b57380393ab 100644 --- a/Services/WebContent/DevToolsConsoleClient.cpp +++ b/Services/WebContent/DevToolsConsoleClient.cpp @@ -101,8 +101,43 @@ void DevToolsConsoleClient::handle_result(JS::Value result) void DevToolsConsoleClient::report_exception(JS::Error const& exception, bool in_promise) { - (void)exception; - (void)in_promise; + auto& vm = exception.vm(); + + auto name = exception.get_without_side_effects(vm.names.name).value_or(JS::js_undefined()); + auto message = exception.get_without_side_effects(vm.names.message).value_or(JS::js_undefined()); + + Vector trace; + trace.ensure_capacity(exception.traceback().size()); + + for (auto const& frame : exception.traceback()) { + auto const& source_range = frame.source_range(); + WebView::StackFrame stack_frame; + + if (!frame.function_name.is_empty()) + stack_frame.function = frame.function_name.to_string(); + + if (!source_range.filename().is_empty() || source_range.start.offset != 0 || source_range.end.offset != 0) { + stack_frame.file = String::from_utf8_with_replacement_character(source_range.filename()); + stack_frame.line = source_range.start.line; + stack_frame.column = source_range.start.column; + } + + if (stack_frame.function.has_value() || stack_frame.file.has_value()) + trace.unchecked_append(move(stack_frame)); + } + + WebView::ConsoleOutput console_output { + .timestamp = UnixDateTime::now(), + .output = WebView::ConsoleError { + .name = name.to_string_without_side_effects(), + .message = message.to_string_without_side_effects(), + .trace = move(trace), + .inside_promise = in_promise, + }, + }; + + m_console_output.append(move(console_output)); + m_client->did_output_js_console_message(m_console_output.size() - 1); } void DevToolsConsoleClient::send_messages(i32 start_index) @@ -136,7 +171,15 @@ JS::ThrowCompletionOr DevToolsConsoleClient::printer(JS::Console::Log for (auto value : argument_values) serialized_arguments.unchecked_append(serialize_js_value(m_console->realm(), value)); - m_console_output.empend(log_level, UnixDateTime::now(), move(serialized_arguments)); + WebView::ConsoleOutput console_output { + .timestamp = UnixDateTime::now(), + .output = WebView::ConsoleLog { + .level = log_level, + .arguments = move(serialized_arguments), + }, + }; + + m_console_output.append(move(console_output)); m_client->did_output_js_console_message(m_console_output.size() - 1); return JS::js_undefined();