LibWeb+WebContent: Report CSS rule source data

Include parser rule locations and stylesheet identities in the applied
style rule data sent to DevTools. This gives the protocol layer enough
information to map matched rules to existing stylesheet resources
without guessing from displayed rule text.
This commit is contained in:
Sam Atkins 2026-06-04 12:41:20 +01:00
parent 52e1d30404
commit b2b164ebd7
5 changed files with 61 additions and 29 deletions

View file

@ -50,6 +50,7 @@
#include <LibWeb/CSS/StyleComputer.h>
#include <LibWeb/CSS/StyleProperty.h>
#include <LibWeb/CSS/StyleSheet.h>
#include <LibWeb/CSS/StyleSheetIdentifier.h>
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
#include <LibWeb/CSS/StyleValues/BorderRadiusStyleValue.h>
#include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
@ -1750,6 +1751,18 @@ static JsonArray serialize_devtools_selector_specificities(MatchingRule const& r
return specificities;
}
static JsonObject serialize_devtools_style_sheet_identifier(StyleSheetIdentifier const& identifier)
{
JsonObject serialized_identifier;
serialized_identifier.set("type"sv, style_sheet_identifier_type_to_string(identifier.type));
if (identifier.dom_element_unique_id.has_value())
serialized_identifier.set("domElementUniqueId"sv, identifier.dom_element_unique_id->value());
if (identifier.url.has_value())
serialized_identifier.set("url"sv, *identifier.url);
serialized_identifier.set("ruleCount"sv, identifier.rule_count);
return serialized_identifier;
}
static JsonObject serialize_devtools_matching_rule(DOM::Document const& document, MatchingRule const& rule)
{
auto const& declaration = rule.declaration();
@ -1776,6 +1789,17 @@ static JsonObject serialize_devtools_matching_rule(DOM::Document const& document
serialized_rule.set("ruleIndex"sv, rule.rule_index);
serialized_rule.set("isSystem"sv, rule.cascade_origin == CascadeOrigin::UserAgent);
if (auto const& source_location = rule.rule->source_location(); source_location.has_value()) {
// Our positions are 0-based, but DevTools expects them to be 1-based.
serialized_rule.set("line"sv, source_location->line + 1);
serialized_rule.set("column"sv, source_location->column + 1);
}
if (auto const* style_sheet = rule.rule->parent_style_sheet()) {
if (auto identifier = style_sheet_identifier_for(*style_sheet); identifier.has_value())
serialized_rule.set("styleSheet"sv, serialize_devtools_style_sheet_identifier(identifier.release_value()));
}
return serialized_rule;
}

View file

@ -24,6 +24,7 @@ public:
virtual String type() const = 0;
DOM::Element* owner_node() { return m_owner_node; }
DOM::Element const* owner_node() const { return m_owner_node; }
void set_owner_node(DOM::Element*);
Optional<String> href() const;
@ -57,6 +58,7 @@ public:
virtual void set_disabled(bool disabled) { m_disabled = disabled; }
CSSStyleSheet* parent_style_sheet() { return m_parent_style_sheet; }
CSSStyleSheet const* parent_style_sheet() const { return m_parent_style_sheet; }
void set_parent_css_style_sheet(CSSStyleSheet*);
protected:

View file

@ -5,8 +5,11 @@
*/
#include "StyleSheetIdentifier.h"
#include <AK/Debug.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibWeb/CSS/CSSStyleSheet.h>
#include <LibWeb/DOM/Element.h>
namespace Web::CSS {
@ -42,6 +45,34 @@ Optional<StyleSheetIdentifier::Type> style_sheet_identifier_type_from_string(Str
return {};
}
Optional<StyleSheetIdentifier> style_sheet_identifier_for(CSSStyleSheet const& sheet)
{
StyleSheetIdentifier identifier {};
if (sheet.owner_rule()) {
identifier.type = StyleSheetIdentifier::Type::ImportRule;
} else if (auto* node = sheet.owner_node()) {
if (node->is_html_style_element() || node->is_svg_style_element()) {
identifier.type = StyleSheetIdentifier::Type::StyleElement;
} else if (node->is_html_link_element()) {
identifier.type = StyleSheetIdentifier::Type::LinkElement;
} else {
dbgln("Can't identify where style sheet came from; owner node is {}", node->debug_description());
identifier.type = StyleSheetIdentifier::Type::StyleElement;
}
identifier.dom_element_unique_id = node->unique_id();
} else {
dbgln("Style sheet has no owner rule or owner node; skipping");
return {};
}
if (auto sheet_url = sheet.href(); sheet_url.has_value())
identifier.url = sheet_url.release_value();
identifier.rule_count = sheet.rules().length();
return identifier;
}
}
namespace IPC {

View file

@ -35,6 +35,7 @@ struct StyleSheetIdentifier {
StringView style_sheet_identifier_type_to_string(StyleSheetIdentifier::Type);
Optional<StyleSheetIdentifier::Type> style_sheet_identifier_type_from_string(StringView);
WEB_API Optional<StyleSheetIdentifier> style_sheet_identifier_for(CSSStyleSheet const&);
}

View file

@ -20,6 +20,7 @@
#include <LibJS/Runtime/ConsoleObject.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/CSS/CSSImportRule.h>
#include <LibWeb/CSS/StyleSheetIdentifier.h>
#include <LibWeb/CSS/StyleSheetList.h>
#include <LibWeb/DOM/CharacterData.h>
#include <LibWeb/DOM/Document.h>
@ -28,7 +29,6 @@
#include <LibWeb/DOM/NodeList.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/EventLoop/EventLoop.h>
#include <LibWeb/HTML/HTMLLinkElement.h>
#include <LibWeb/HTML/Navigable.h>
#include <LibWeb/HTML/Scripting/ClassicScript.h>
#include <LibWeb/HTML/TraversableNavigable.h>
@ -1043,34 +1043,8 @@ void PageClient::console_peer_did_misbehave(char const* reason)
static void gather_style_sheets(Vector<Web::CSS::StyleSheetIdentifier>& results, Web::CSS::CSSStyleSheet& sheet)
{
Web::CSS::StyleSheetIdentifier identifier {};
bool valid = true;
if (sheet.owner_rule()) {
identifier.type = Web::CSS::StyleSheetIdentifier::Type::ImportRule;
} else if (auto* node = sheet.owner_node()) {
if (node->is_html_style_element() || node->is_svg_style_element()) {
identifier.type = Web::CSS::StyleSheetIdentifier::Type::StyleElement;
} else if (is<Web::HTML::HTMLLinkElement>(node)) {
identifier.type = Web::CSS::StyleSheetIdentifier::Type::LinkElement;
} else {
dbgln("Can't identify where style sheet came from; owner node is {}", node->debug_description());
identifier.type = Web::CSS::StyleSheetIdentifier::Type::StyleElement;
}
identifier.dom_element_unique_id = node->unique_id();
} else {
dbgln("Style sheet has no owner rule or owner node; skipping");
valid = false;
}
if (valid) {
if (auto sheet_url = sheet.href(); sheet_url.has_value())
identifier.url = sheet_url.release_value();
identifier.rule_count = sheet.rules().length();
results.append(move(identifier));
}
if (auto identifier = Web::CSS::style_sheet_identifier_for(sheet); identifier.has_value())
results.append(identifier.release_value());
for (auto& import_rule : sheet.import_rules()) {
if (import_rule->loaded_style_sheet()) {