LibDevTools+LibWebView: Implement getting document style sheets

This commit is contained in:
Timothy Flynn 2025-03-11 17:14:54 -04:00 committed by Tim Flynn
parent cf601a49bb
commit 0b86bcfba7
Notes: github-actions[bot] 2025-03-13 20:57:24 +00:00
10 changed files with 274 additions and 6 deletions

View file

@ -4,12 +4,14 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Enumerate.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <LibDevTools/Actors/CSSPropertiesActor.h>
#include <LibDevTools/Actors/ConsoleActor.h>
#include <LibDevTools/Actors/FrameActor.h>
#include <LibDevTools/Actors/InspectorActor.h>
#include <LibDevTools/Actors/StyleSheetsActor.h>
#include <LibDevTools/Actors/TabActor.h>
#include <LibDevTools/Actors/ThreadActor.h>
#include <LibDevTools/DevToolsDelegate.h>
@ -18,17 +20,18 @@
namespace DevTools {
NonnullRefPtr<FrameActor> FrameActor::create(DevToolsServer& devtools, String name, WeakPtr<TabActor> tab, WeakPtr<CSSPropertiesActor> css_properties, WeakPtr<ConsoleActor> console, WeakPtr<InspectorActor> inspector, WeakPtr<ThreadActor> thread)
NonnullRefPtr<FrameActor> FrameActor::create(DevToolsServer& devtools, String name, WeakPtr<TabActor> tab, WeakPtr<CSSPropertiesActor> css_properties, WeakPtr<ConsoleActor> console, WeakPtr<InspectorActor> inspector, WeakPtr<StyleSheetsActor> style_sheets, WeakPtr<ThreadActor> thread)
{
return adopt_ref(*new FrameActor(devtools, move(name), move(tab), move(css_properties), move(console), move(inspector), move(thread)));
return adopt_ref(*new FrameActor(devtools, move(name), move(tab), move(css_properties), move(console), move(inspector), move(style_sheets), move(thread)));
}
FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr<TabActor> tab, WeakPtr<CSSPropertiesActor> css_properties, WeakPtr<ConsoleActor> console, WeakPtr<InspectorActor> inspector, WeakPtr<ThreadActor> thread)
FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr<TabActor> tab, WeakPtr<CSSPropertiesActor> css_properties, WeakPtr<ConsoleActor> console, WeakPtr<InspectorActor> inspector, WeakPtr<StyleSheetsActor> style_sheets, WeakPtr<ThreadActor> thread)
: Actor(devtools, move(name))
, m_tab(move(tab))
, m_css_properties(move(css_properties))
, m_console(move(console))
, m_inspector(move(inspector))
, m_style_sheets(move(style_sheets))
, m_thread(move(thread))
{
if (auto tab = m_tab.strong_ref()) {
@ -42,6 +45,12 @@ FrameActor::FrameActor(DevToolsServer& devtools, String name, WeakPtr<TabActor>
if (auto self = weak_self.strong_ref())
self->console_messages_received(start_index, move(console_output));
});
// FIXME: We should adopt WebContent to inform us when style sheets are available or removed.
devtools.delegate().retrieve_style_sheets(tab->description(),
async_handler<FrameActor>({}, [](auto& self, auto style_sheets, auto& response) {
self.style_sheets_available(response, move(style_sheets));
}));
}
}
@ -59,6 +68,7 @@ void FrameActor::handle_message(Message const& message)
if (auto tab = m_tab.strong_ref()) {
devtools().delegate().stop_listening_for_dom_mutations(tab->description());
devtools().delegate().stop_listening_for_console_messages(tab->description());
devtools().delegate().stop_listening_for_style_sheet_sources(tab->description());
tab->reset_selected_node();
}
@ -121,12 +131,78 @@ JsonObject FrameActor::serialize_target() const
target.set("consoleActor"sv, console->name());
if (auto inspector = m_inspector.strong_ref())
target.set("inspectorActor"sv, inspector->name());
if (auto style_sheets = m_style_sheets.strong_ref())
target.set("styleSheetsActor"sv, style_sheets->name());
if (auto thread = m_thread.strong_ref())
target.set("threadActor"sv, thread->name());
return target;
}
void FrameActor::style_sheets_available(JsonObject& response, Vector<Web::CSS::StyleSheetIdentifier> style_sheets)
{
JsonArray sheets;
String tab_url;
if (auto tab_actor = m_tab.strong_ref())
tab_url = tab_actor->description().url;
auto style_sheets_actor = m_style_sheets.strong_ref();
if (!style_sheets_actor)
return;
for (auto const& [i, style_sheet] : enumerate(style_sheets)) {
auto resource_id = MUST(String::formatted("{}-stylesheet:{}", style_sheets_actor->name(), i));
JsonValue href;
JsonValue source_map_base_url;
JsonValue title;
if (style_sheet.url.has_value()) {
// LibWeb sets the URL to a style sheet name for UA style sheets. DevTools would reject these invalid URLs.
if (style_sheet.type == Web::CSS::StyleSheetIdentifier::Type::UserAgent) {
title = *style_sheet.url;
source_map_base_url = tab_url;
} else {
href = *style_sheet.url;
source_map_base_url = *style_sheet.url;
}
} else {
source_map_base_url = tab_url;
}
JsonObject sheet;
sheet.set("atRules"sv, JsonArray {});
sheet.set("constructed"sv, false);
sheet.set("disabled"sv, false);
sheet.set("fileName"sv, JsonValue {});
sheet.set("href"sv, move(href));
sheet.set("isNew"sv, false);
sheet.set("nodeHref"sv, tab_url);
sheet.set("resourceId"sv, move(resource_id));
sheet.set("ruleCount"sv, style_sheet.rule_count);
sheet.set("sourceMapBaseURL"sv, move(source_map_base_url));
sheet.set("sourceMapURL"sv, ""sv);
sheet.set("styleSheetIndex"sv, i);
sheet.set("system"sv, false);
sheet.set("title"sv, move(title));
sheets.must_append(move(sheet));
}
JsonArray stylesheets;
stylesheets.must_append("stylesheet"sv);
stylesheets.must_append(move(sheets));
JsonArray array;
array.must_append(move(stylesheets));
response.set("type"sv, "resources-available-array"sv);
response.set("array"sv, move(array));
style_sheets_actor->set_style_sheets(move(style_sheets));
}
void FrameActor::console_message_available(i32 message_index)
{
if (message_index <= m_highest_received_message_index) {

View file

@ -10,6 +10,7 @@
#include <AK/Types.h>
#include <AK/Vector.h>
#include <LibDevTools/Actor.h>
#include <LibWeb/Forward.h>
#include <LibWebView/Forward.h>
namespace DevTools {
@ -18,7 +19,7 @@ class FrameActor final : public Actor {
public:
static constexpr auto base_name = "frame"sv;
static NonnullRefPtr<FrameActor> create(DevToolsServer&, String name, WeakPtr<TabActor>, WeakPtr<CSSPropertiesActor>, WeakPtr<ConsoleActor>, WeakPtr<InspectorActor>, WeakPtr<ThreadActor>);
static NonnullRefPtr<FrameActor> create(DevToolsServer&, String name, WeakPtr<TabActor>, WeakPtr<CSSPropertiesActor>, WeakPtr<ConsoleActor>, WeakPtr<InspectorActor>, WeakPtr<StyleSheetsActor>, WeakPtr<ThreadActor>);
virtual ~FrameActor() override;
void send_frame_update_message();
@ -26,7 +27,9 @@ public:
JsonObject serialize_target() const;
private:
FrameActor(DevToolsServer&, String name, WeakPtr<TabActor>, WeakPtr<CSSPropertiesActor>, WeakPtr<ConsoleActor>, WeakPtr<InspectorActor>, WeakPtr<ThreadActor>);
FrameActor(DevToolsServer&, String name, WeakPtr<TabActor>, WeakPtr<CSSPropertiesActor>, WeakPtr<ConsoleActor>, WeakPtr<InspectorActor>, WeakPtr<StyleSheetsActor>, WeakPtr<ThreadActor>);
void style_sheets_available(JsonObject& response, Vector<Web::CSS::StyleSheetIdentifier> style_sheets);
virtual void handle_message(Message const&) override;
@ -39,6 +42,7 @@ private:
WeakPtr<CSSPropertiesActor> m_css_properties;
WeakPtr<ConsoleActor> m_console;
WeakPtr<InspectorActor> m_inspector;
WeakPtr<StyleSheetsActor> m_style_sheets;
WeakPtr<ThreadActor> m_thread;
i32 m_highest_notified_message_index { -1 };

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/JsonObject.h>
#include <LibDevTools/Actors/InspectorActor.h>
#include <LibDevTools/Actors/StyleSheetsActor.h>
#include <LibDevTools/Actors/TabActor.h>
#include <LibDevTools/DevToolsDelegate.h>
#include <LibDevTools/DevToolsServer.h>
namespace DevTools {
NonnullRefPtr<StyleSheetsActor> StyleSheetsActor::create(DevToolsServer& devtools, String name, WeakPtr<TabActor> tab)
{
return adopt_ref(*new StyleSheetsActor(devtools, move(name), move(tab)));
}
StyleSheetsActor::StyleSheetsActor(DevToolsServer& devtools, String name, WeakPtr<TabActor> tab)
: Actor(devtools, move(name))
, m_tab(move(tab))
{
if (auto tab = m_tab.strong_ref()) {
devtools.delegate().listen_for_style_sheet_sources(
tab->description(),
[weak_self = make_weak_ptr<StyleSheetsActor>()](Web::CSS::StyleSheetIdentifier const& style_sheet, String source) {
if (auto self = weak_self.strong_ref())
self->style_sheet_source_received(style_sheet, move(source));
});
}
}
StyleSheetsActor::~StyleSheetsActor()
{
if (auto tab = m_tab.strong_ref())
devtools().delegate().stop_listening_for_style_sheet_sources(tab->description());
}
void StyleSheetsActor::handle_message(Message const& message)
{
if (message.type == "getText"sv) {
auto resource_id = get_required_parameter<String>(message, "resourceId"sv);
if (!resource_id.has_value())
return;
auto index = resource_id->bytes_as_string_view().find_last_split_view(':').to_number<size_t>();
if (!index.has_value() || *index >= m_style_sheets.size()) {
send_unknown_actor_error(message, *resource_id);
return;
}
if (auto tab = m_tab.strong_ref()) {
devtools().delegate().retrieve_style_sheet_source(tab->description(), m_style_sheets[*index]);
m_pending_style_sheet_source_requests.set(*index, { .id = message.id });
}
return;
}
send_unrecognized_packet_type_error(message);
}
void StyleSheetsActor::set_style_sheets(Vector<Web::CSS::StyleSheetIdentifier> style_sheets)
{
m_style_sheets = move(style_sheets);
}
void StyleSheetsActor::style_sheet_source_received(Web::CSS::StyleSheetIdentifier const& style_sheet, String source)
{
auto index = m_style_sheets.find_first_index_if([&](auto const& candidate) {
return candidate.type == style_sheet.type && candidate.url == style_sheet.url;
});
if (!index.has_value())
return;
auto pending_message = m_pending_style_sheet_source_requests.take(*index);
if (!pending_message.has_value())
return;
// FIXME: Support the `longString` message type so that we don't have to send the entire style sheet
// source at once for large sheets.
JsonObject response;
response.set("text"sv, move(source));
send_response(*pending_message, move(response));
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashMap.h>
#include <AK/NonnullRefPtr.h>
#include <LibDevTools/Actor.h>
#include <LibWeb/Forward.h>
namespace DevTools {
class StyleSheetsActor final : public Actor {
public:
static constexpr auto base_name = "style-sheets"sv;
static NonnullRefPtr<StyleSheetsActor> create(DevToolsServer&, String name, WeakPtr<TabActor>);
virtual ~StyleSheetsActor() override;
void set_style_sheets(Vector<Web::CSS::StyleSheetIdentifier>);
private:
StyleSheetsActor(DevToolsServer&, String name, WeakPtr<TabActor>);
virtual void handle_message(Message const&) override;
void style_sheet_source_received(Web::CSS::StyleSheetIdentifier const&, String source);
WeakPtr<TabActor> m_tab;
Vector<Web::CSS::StyleSheetIdentifier> m_style_sheets;
HashMap<size_t, Message> m_pending_style_sheet_source_requests;
};
}

View file

@ -11,6 +11,7 @@
#include <LibDevTools/Actors/ConsoleActor.h>
#include <LibDevTools/Actors/FrameActor.h>
#include <LibDevTools/Actors/InspectorActor.h>
#include <LibDevTools/Actors/StyleSheetsActor.h>
#include <LibDevTools/Actors/TabActor.h>
#include <LibDevTools/Actors/TargetConfigurationActor.h>
#include <LibDevTools/Actors/ThreadActor.h>
@ -92,9 +93,10 @@ void WatcherActor::handle_message(Message const& message)
auto& css_properties = devtools().register_actor<CSSPropertiesActor>();
auto& console = devtools().register_actor<ConsoleActor>(m_tab);
auto& inspector = devtools().register_actor<InspectorActor>(m_tab);
auto& style_sheets = devtools().register_actor<StyleSheetsActor>(m_tab);
auto& thread = devtools().register_actor<ThreadActor>();
auto& target = devtools().register_actor<FrameActor>(m_tab, css_properties, console, inspector, thread);
auto& target = devtools().register_actor<FrameActor>(m_tab, css_properties, console, inspector, style_sheets, thread);
m_target = target;
response.set("type"sv, "target-available-form"sv);

View file

@ -12,6 +12,7 @@ set(SOURCES
Actors/PreferenceActor.cpp
Actors/ProcessActor.cpp
Actors/RootActor.cpp
Actors/StyleSheetsActor.cpp
Actors/TabActor.cpp
Actors/TargetConfigurationActor.cpp
Actors/ThreadActor.cpp

View file

@ -15,6 +15,7 @@
#include <LibDevTools/Actors/TabActor.h>
#include <LibDevTools/Forward.h>
#include <LibWeb/CSS/Selector.h>
#include <LibWeb/CSS/StyleSheetIdentifier.h>
#include <LibWeb/Forward.h>
#include <LibWebView/Forward.h>
@ -55,6 +56,13 @@ public:
virtual void clone_dom_node(TabDescription const&, Web::UniqueNodeID, OnDOMNodeEditComplete) const { }
virtual void remove_dom_node(TabDescription const&, Web::UniqueNodeID, OnDOMNodeEditComplete) const { }
using OnStyleSheetsReceived = Function<void(ErrorOr<Vector<Web::CSS::StyleSheetIdentifier>>)>;
using OnStyleSheetSourceReceived = Function<void(Web::CSS::StyleSheetIdentifier const&, String)>;
virtual void retrieve_style_sheets(TabDescription const&, OnStyleSheetsReceived) const { }
virtual void retrieve_style_sheet_source(TabDescription const&, Web::CSS::StyleSheetIdentifier const&) const { }
virtual void listen_for_style_sheet_sources(TabDescription const&, OnStyleSheetSourceReceived) const { }
virtual void stop_listening_for_style_sheet_sources(TabDescription const&) const { }
using OnScriptEvaluationComplete = Function<void(ErrorOr<JsonValue>)>;
virtual void evaluate_javascript(TabDescription const&, String const&, OnScriptEvaluationComplete) const { }

View file

@ -24,6 +24,7 @@ class PageStyleActor;
class PreferenceActor;
class ProcessActor;
class RootActor;
class StyleSheetsActor;
class TabActor;
class TargetConfigurationActor;
class ThreadActor;

View file

@ -603,6 +603,51 @@ void Application::remove_dom_node(DevTools::TabDescription const& description, W
});
}
void Application::retrieve_style_sheets(DevTools::TabDescription const& description, OnStyleSheetsReceived on_complete) const
{
auto view = ViewImplementation::find_view_by_id(description.id);
if (!view.has_value()) {
on_complete(Error::from_string_literal("Unable to locate tab"));
return;
}
view->on_received_style_sheet_list = [&view = *view, on_complete = move(on_complete)](Vector<Web::CSS::StyleSheetIdentifier> style_sheets) {
view.on_received_style_sheet_list = nullptr;
on_complete(move(style_sheets));
};
view->list_style_sheets();
}
void Application::retrieve_style_sheet_source(DevTools::TabDescription const& description, Web::CSS::StyleSheetIdentifier const& style_sheet) const
{
auto view = ViewImplementation::find_view_by_id(description.id);
if (!view.has_value())
return;
view->request_style_sheet_source(style_sheet);
}
void Application::listen_for_style_sheet_sources(DevTools::TabDescription const& description, OnStyleSheetSourceReceived on_style_sheet_source_received) const
{
auto view = ViewImplementation::find_view_by_id(description.id);
if (!view.has_value())
return;
view->on_received_style_sheet_source = [&view = *view, on_style_sheet_source_received = move(on_style_sheet_source_received)](auto const& style_sheet, auto const&, auto const& source) {
on_style_sheet_source_received(style_sheet, source);
};
}
void Application::stop_listening_for_style_sheet_sources(DevTools::TabDescription const& description) const
{
auto view = ViewImplementation::find_view_by_id(description.id);
if (!view.has_value())
return;
view->on_received_style_sheet_source = nullptr;
}
void Application::evaluate_javascript(DevTools::TabDescription const& description, String const& script, OnScriptEvaluationComplete on_complete) const
{
auto view = ViewImplementation::find_view_by_id(description.id);

View file

@ -111,6 +111,10 @@ private:
virtual void insert_dom_node_before(DevTools::TabDescription const&, Web::UniqueNodeID, Web::UniqueNodeID, Optional<Web::UniqueNodeID>, OnDOMNodeEditComplete) const override;
virtual void clone_dom_node(DevTools::TabDescription const&, Web::UniqueNodeID, OnDOMNodeEditComplete) const override;
virtual void remove_dom_node(DevTools::TabDescription const&, Web::UniqueNodeID, OnDOMNodeEditComplete) const override;
virtual void retrieve_style_sheets(DevTools::TabDescription const&, OnStyleSheetsReceived) const override;
virtual void retrieve_style_sheet_source(DevTools::TabDescription const&, Web::CSS::StyleSheetIdentifier const&) const override;
virtual void listen_for_style_sheet_sources(DevTools::TabDescription const&, OnStyleSheetSourceReceived) const override;
virtual void stop_listening_for_style_sheet_sources(DevTools::TabDescription const&) const override;
virtual void evaluate_javascript(DevTools::TabDescription const&, String const&, OnScriptEvaluationComplete) const override;
virtual void listen_for_console_messages(DevTools::TabDescription const&, OnConsoleMessageAvailable, OnReceivedConsoleMessages) const override;
virtual void stop_listening_for_console_messages(DevTools::TabDescription const&) const override;