diff --git a/Libraries/LibDevTools/Actors/ConsoleActor.cpp b/Libraries/LibDevTools/Actors/ConsoleActor.cpp new file mode 100644 index 00000000000..34eb1bc0c17 --- /dev/null +++ b/Libraries/LibDevTools/Actors/ConsoleActor.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DevTools { + +NonnullRefPtr ConsoleActor::create(DevToolsServer& devtools, String name, WeakPtr tab) +{ + return adopt_ref(*new ConsoleActor(devtools, move(name), move(tab))); +} + +ConsoleActor::ConsoleActor(DevToolsServer& devtools, String name, WeakPtr tab) + : Actor(devtools, move(name)) + , m_tab(move(tab)) +{ +} + +ConsoleActor::~ConsoleActor() = default; + +void ConsoleActor::handle_message(StringView type, JsonObject const& message) +{ + JsonObject response; + response.set("from"sv, name()); + + if (type == "autocomplete"sv) { + response.set("matches"sv, JsonArray {}); + response.set("matchProp"sv, String {}); + send_message(move(response)); + return; + } + + if (type == "evaluateJSAsync"sv) { + auto text = message.get_string("text"sv); + if (!text.has_value()) { + send_missing_parameter_error("text"sv); + return; + } + + auto result_id = MUST(String::formatted("{}-{}", name(), m_execution_id++)); + + response.set("resultID"sv, result_id); + send_message(move(response)); + + // FIXME: We do not support eager evaluation of scripts. Just bail for now. + if (message.get_bool("eager"sv).value_or(false)) { + return; + } + + if (auto tab = m_tab.strong_ref()) { + auto block_token = block_responses(); + + devtools().delegate().evaluate_javascript(tab->description(), *text, + [result_id, input = *text, weak_self = make_weak_ptr(), block_token = move(block_token)](ErrorOr result) mutable { + if (result.is_error()) { + dbgln_if(DEVTOOLS_DEBUG, "Unable to inspect DOM node: {}", result.error()); + return; + } + + if (auto self = weak_self.strong_ref()) + self->received_console_result(move(result_id), move(input), result.release_value(), move(block_token)); + }); + } + + return; + } + + send_unrecognized_packet_type_error(type); +} + +void ConsoleActor::received_console_result(String result_id, String input, JsonValue result, BlockToken block_token) +{ + JsonObject message; + message.set("from"sv, name()); + message.set("type"sv, "evaluationResult"_string); + message.set("timestamp"sv, AK::UnixDateTime::now().milliseconds_since_epoch()); + message.set("resultID"sv, move(result_id)); + message.set("input"sv, move(input)); + message.set("result"sv, move(result)); + message.set("exception"sv, JsonValue {}); + message.set("exceptionMessage"sv, JsonValue {}); + message.set("helperResult"sv, JsonValue {}); + + send_message(move(message), move(block_token)); +} + +} diff --git a/Libraries/LibDevTools/Actors/ConsoleActor.h b/Libraries/LibDevTools/Actors/ConsoleActor.h new file mode 100644 index 00000000000..fd0b819ab66 --- /dev/null +++ b/Libraries/LibDevTools/Actors/ConsoleActor.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace DevTools { + +class ConsoleActor final : public Actor { +public: + static constexpr auto base_name = "console"sv; + + static NonnullRefPtr create(DevToolsServer&, String name, WeakPtr); + virtual ~ConsoleActor() override; + + virtual void handle_message(StringView type, JsonObject const&) override; + +private: + ConsoleActor(DevToolsServer&, String name, WeakPtr); + + void received_console_result(String result_id, String input, JsonValue result, BlockToken); + + WeakPtr m_tab; + + u64 m_execution_id { 0 }; +}; + +} diff --git a/Libraries/LibDevTools/Actors/FrameActor.cpp b/Libraries/LibDevTools/Actors/FrameActor.cpp index 92e79237cab..ba1e277fef8 100644 --- a/Libraries/LibDevTools/Actors/FrameActor.cpp +++ b/Libraries/LibDevTools/Actors/FrameActor.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -14,15 +15,16 @@ namespace DevTools { -NonnullRefPtr FrameActor::create(DevToolsServer& devtools, String name, WeakPtr tab, WeakPtr css_properties, WeakPtr inspector, WeakPtr thread) +NonnullRefPtr FrameActor::create(DevToolsServer& devtools, String name, WeakPtr tab, WeakPtr css_properties, WeakPtr console, WeakPtr inspector, WeakPtr thread) { - return adopt_ref(*new FrameActor(devtools, move(name), move(tab), move(css_properties), move(inspector), move(thread))); + return adopt_ref(*new FrameActor(devtools, move(name), move(tab), move(css_properties), move(console), move(inspector), move(thread))); } -FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr tab, WeakPtr css_properties, WeakPtr inspector, WeakPtr thread) +FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr tab, WeakPtr css_properties, WeakPtr console, WeakPtr inspector, WeakPtr thread) : Actor(devtools, move(name)) , m_tab(move(tab)) , m_css_properties(move(css_properties)) + , m_console(move(console)) , m_inspector(move(inspector)) , m_thread(move(thread)) { @@ -95,6 +97,8 @@ JsonObject FrameActor::serialize_target() const if (auto css_properties = m_css_properties.strong_ref()) target.set("cssPropertiesActor"sv, css_properties->name()); + if (auto console = m_console.strong_ref()) + target.set("consoleActor"sv, console->name()); if (auto inspector = m_inspector.strong_ref()) target.set("inspectorActor"sv, inspector->name()); if (auto thread = m_thread.strong_ref()) diff --git a/Libraries/LibDevTools/Actors/FrameActor.h b/Libraries/LibDevTools/Actors/FrameActor.h index 525169157ae..7074ee09d5f 100644 --- a/Libraries/LibDevTools/Actors/FrameActor.h +++ b/Libraries/LibDevTools/Actors/FrameActor.h @@ -15,7 +15,7 @@ class FrameActor final : public Actor { public: static constexpr auto base_name = "frame"sv; - static NonnullRefPtr create(DevToolsServer&, String name, WeakPtr, WeakPtr, WeakPtr, WeakPtr); + static NonnullRefPtr create(DevToolsServer&, String name, WeakPtr, WeakPtr, WeakPtr, WeakPtr, WeakPtr); virtual ~FrameActor() override; virtual void handle_message(StringView type, JsonObject const&) override; @@ -24,11 +24,12 @@ public: JsonObject serialize_target() const; private: - FrameActor(DevToolsServer&, String name, WeakPtr, WeakPtr, WeakPtr, WeakPtr); + FrameActor(DevToolsServer&, String name, WeakPtr, WeakPtr, WeakPtr, WeakPtr, WeakPtr); WeakPtr m_tab; WeakPtr m_css_properties; + WeakPtr m_console; WeakPtr m_inspector; WeakPtr m_thread; }; diff --git a/Libraries/LibDevTools/Actors/WatcherActor.cpp b/Libraries/LibDevTools/Actors/WatcherActor.cpp index a417681752c..33a9d0f381d 100644 --- a/Libraries/LibDevTools/Actors/WatcherActor.cpp +++ b/Libraries/LibDevTools/Actors/WatcherActor.cpp @@ -4,9 +4,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include +#include #include #include #include @@ -66,6 +68,26 @@ void WatcherActor::handle_message(StringView type, JsonObject const& message) return; } + if (type == "watchResources"sv) { + auto resource_types = message.get_array("resourceTypes"sv); + if (!resource_types.has_value()) { + send_missing_parameter_error("resourceTypes"sv); + return; + } + + if constexpr (DEVTOOLS_DEBUG) { + for (auto const& resource_type : resource_types->values()) { + if (!resource_type.is_string()) + continue; + if (resource_type.as_string() != "console-message"sv) + dbgln("Unrecognized `watchResources` resource type: '{}'", resource_type.as_string()); + } + } + + send_message(move(response)); + return; + } + if (type == "watchTargets"sv) { auto target_type = message.get_string("targetType"sv); if (!target_type.has_value()) { @@ -75,10 +97,11 @@ void WatcherActor::handle_message(StringView type, JsonObject const& message) if (target_type == "frame"sv) { auto& css_properties = devtools().register_actor(); + auto& console = devtools().register_actor(m_tab); auto& inspector = devtools().register_actor(m_tab); auto& thread = devtools().register_actor(); - auto& target = devtools().register_actor(m_tab, css_properties, inspector, thread); + auto& target = devtools().register_actor(m_tab, css_properties, console, inspector, thread); m_target = target; response.set("type"sv, "target-available-form"sv); @@ -102,7 +125,7 @@ JsonObject WatcherActor::serialize_description() const { JsonObject resources; resources.set("Cache"sv, false); - resources.set("console-message"sv, false); + resources.set("console-message"sv, true); resources.set("cookies"sv, false); resources.set("css-change"sv, false); resources.set("css-message"sv, false); diff --git a/Libraries/LibDevTools/CMakeLists.txt b/Libraries/LibDevTools/CMakeLists.txt index 279c0036966..e55a1c287de 100644 --- a/Libraries/LibDevTools/CMakeLists.txt +++ b/Libraries/LibDevTools/CMakeLists.txt @@ -1,5 +1,6 @@ set(SOURCES Actor.cpp + Actors/ConsoleActor.cpp Actors/CSSPropertiesActor.cpp Actors/DeviceActor.cpp Actors/FrameActor.cpp diff --git a/Libraries/LibDevTools/DevToolsDelegate.h b/Libraries/LibDevTools/DevToolsDelegate.h index a3efd17bfee..2b38b6cb60d 100644 --- a/Libraries/LibDevTools/DevToolsDelegate.h +++ b/Libraries/LibDevTools/DevToolsDelegate.h @@ -35,6 +35,9 @@ public: virtual void highlight_dom_node(TabDescription const&, Web::UniqueNodeID, Optional) const { } virtual void clear_highlighted_dom_node(TabDescription const&) const { } + + using OnScriptEvaluationComplete = Function)>; + virtual void evaluate_javascript(TabDescription const&, String, OnScriptEvaluationComplete) const { } }; } diff --git a/Libraries/LibDevTools/Forward.h b/Libraries/LibDevTools/Forward.h index ddfa9a3e377..3754322021e 100644 --- a/Libraries/LibDevTools/Forward.h +++ b/Libraries/LibDevTools/Forward.h @@ -10,6 +10,7 @@ namespace DevTools { class Actor; class Connection; +class ConsoleActor; class CSSPropertiesActor; class DeviceActor; class DevToolsDelegate;