From c5a22a1a97de663ae1b6365a80c0939fdf21296a Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 4 Mar 2025 08:45:36 -0500 Subject: [PATCH] LibDevTools+LibWebView: Implement basic support for console logging This implements support for basic usage of console.log and friends. It does not implement console.assert, console.trace, console.group, etc. --- Libraries/LibDevTools/Actors/FrameActor.cpp | 116 +++++++++++++++++++- Libraries/LibDevTools/Actors/FrameActor.h | 11 ++ Libraries/LibDevTools/DevToolsDelegate.h | 7 ++ Libraries/LibWebView/ConsoleOutput.h | 22 ++++ Libraries/LibWebView/Forward.h | 1 + 5 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 Libraries/LibWebView/ConsoleOutput.h diff --git a/Libraries/LibDevTools/Actors/FrameActor.cpp b/Libraries/LibDevTools/Actors/FrameActor.cpp index ba1e277fef8..f96182a5631 100644 --- a/Libraries/LibDevTools/Actors/FrameActor.cpp +++ b/Libraries/LibDevTools/Actors/FrameActor.cpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include namespace DevTools { @@ -28,9 +31,25 @@ FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr , m_inspector(move(inspector)) , m_thread(move(thread)) { + if (auto tab = m_tab.strong_ref()) { + devtools.delegate().listen_for_console_messages( + tab->description(), + [weak_self = make_weak_ptr()](i32 message_index) { + if (auto self = weak_self.strong_ref()) + self->console_message_available(message_index); + }, + [weak_self = make_weak_ptr()](i32 start_index, Vector console_output) { + if (auto self = weak_self.strong_ref()) + self->console_messages_received(start_index, move(console_output)); + }); + } } -FrameActor::~FrameActor() = default; +FrameActor::~FrameActor() +{ + if (auto tab = m_tab.strong_ref()) + devtools().delegate().stop_listening_for_console_messages(tab->description()); +} void FrameActor::handle_message(StringView type, JsonObject const&) { @@ -38,8 +57,10 @@ void FrameActor::handle_message(StringView type, JsonObject const&) response.set("from"sv, name()); if (type == "detach"sv) { - if (auto tab = m_tab.strong_ref()) + if (auto tab = m_tab.strong_ref()) { + devtools().delegate().stop_listening_for_console_messages(tab->description()); tab->reset_selected_node(); + } send_message(move(response)); return; @@ -107,4 +128,95 @@ JsonObject FrameActor::serialize_target() const return target; } +void FrameActor::console_message_available(i32 message_index) +{ + if (message_index <= m_highest_received_message_index) { + dbgln("Notified about console message we already have"); + return; + } + if (message_index <= m_highest_notified_message_index) { + dbgln("Notified about console message we're already aware of"); + return; + } + + m_highest_notified_message_index = message_index; + + if (!m_waiting_for_messages) + request_console_messages(); +} + +void FrameActor::console_messages_received(i32 start_index, Vector console_output) +{ + auto end_index = start_index + static_cast(console_output.size()) - 1; + if (end_index <= m_highest_received_message_index) { + dbgln("Received old console messages"); + return; + } + + JsonArray messages; + messages.ensure_capacity(console_output.size()); + + for (auto& output : console_output) { + JsonObject message; + + switch (output.level) { + case JS::Console::LogLevel::Debug: + message.set("level"sv, "debug"sv); + break; + case JS::Console::LogLevel::Error: + message.set("level"sv, "error"sv); + break; + case JS::Console::LogLevel::Info: + message.set("level"sv, "info"sv); + break; + case JS::Console::LogLevel::Log: + message.set("level"sv, "log"sv); + break; + case JS::Console::LogLevel::Warn: + message.set("level"sv, "warn"sv); + break; + default: + // FIXME: Implement remaining console levels. + continue; + } + + message.set("filename"sv, ""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) }); + + 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)); + + JsonObject message; + message.set("from"sv, name()); + message.set("type"sv, "resources-available-array"sv); + message.set("array"sv, move(array)); + send_message(move(message)); + + m_highest_received_message_index = end_index; + m_waiting_for_messages = false; + + if (m_highest_received_message_index < m_highest_notified_message_index) + request_console_messages(); +} + +void FrameActor::request_console_messages() +{ + VERIFY(!m_waiting_for_messages); + + if (auto tab = m_tab.strong_ref()) { + devtools().delegate().request_console_messages(m_tab->description(), m_highest_received_message_index + 1); + m_waiting_for_messages = true; + } +} + } diff --git a/Libraries/LibDevTools/Actors/FrameActor.h b/Libraries/LibDevTools/Actors/FrameActor.h index 7074ee09d5f..5fce83bb444 100644 --- a/Libraries/LibDevTools/Actors/FrameActor.h +++ b/Libraries/LibDevTools/Actors/FrameActor.h @@ -7,7 +7,10 @@ #pragma once #include +#include +#include #include +#include namespace DevTools { @@ -26,12 +29,20 @@ public: private: FrameActor(DevToolsServer&, String name, WeakPtr, WeakPtr, WeakPtr, WeakPtr, WeakPtr); + void console_message_available(i32 message_index); + void console_messages_received(i32 start_index, Vector); + void request_console_messages(); + WeakPtr m_tab; WeakPtr m_css_properties; WeakPtr m_console; WeakPtr m_inspector; WeakPtr m_thread; + + i32 m_highest_notified_message_index { -1 }; + i32 m_highest_received_message_index { -1 }; + bool m_waiting_for_messages { false }; }; } diff --git a/Libraries/LibDevTools/DevToolsDelegate.h b/Libraries/LibDevTools/DevToolsDelegate.h index 2b38b6cb60d..ff5fcdb93d9 100644 --- a/Libraries/LibDevTools/DevToolsDelegate.h +++ b/Libraries/LibDevTools/DevToolsDelegate.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace DevTools { @@ -38,6 +39,12 @@ public: using OnScriptEvaluationComplete = Function)>; virtual void evaluate_javascript(TabDescription const&, String, OnScriptEvaluationComplete) const { } + + using OnConsoleMessageAvailable = Function; + using OnReceivedConsoleMessages = Function)>; + virtual void listen_for_console_messages(TabDescription const&, OnConsoleMessageAvailable, OnReceivedConsoleMessages) const { } + virtual void stop_listening_for_console_messages(TabDescription const&) const { } + virtual void request_console_messages(TabDescription const&, i32) const { } }; } diff --git a/Libraries/LibWebView/ConsoleOutput.h b/Libraries/LibWebView/ConsoleOutput.h new file mode 100644 index 00000000000..bba5d30e60f --- /dev/null +++ b/Libraries/LibWebView/ConsoleOutput.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace WebView { + +struct ConsoleOutput { + JS::Console::LogLevel level; + UnixDateTime timestamp; + Vector arguments; +}; + +} diff --git a/Libraries/LibWebView/Forward.h b/Libraries/LibWebView/Forward.h index d6fd5519128..4da025bd409 100644 --- a/Libraries/LibWebView/Forward.h +++ b/Libraries/LibWebView/Forward.h @@ -19,6 +19,7 @@ class ViewImplementation; class WebContentClient; struct Attribute; +struct ConsoleOutput; struct CookieStorageKey; struct ProcessHandle; struct SearchEngine;