Browse Source

LibWeb: Add method for listing all style sheets on a page

This will be used by the inspector, for showing style sheet contents.

Identifying a specific style sheet is a bit tricky. Depending on where
it came from, a style sheet may have a URL, it might be associated with
a DOM element, both, or neither. This varied information is wrapped in
a new StyleSheetIdentifier struct.
Sam Atkins 10 tháng trước cách đây
mục cha
commit
51a426cc05

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -102,6 +102,7 @@ set(SOURCES
     CSS/StyleProperties.cpp
     CSS/StyleProperty.cpp
     CSS/StyleSheet.cpp
+    CSS/StyleSheetIdentifier.cpp
     CSS/StyleSheetList.cpp
     CSS/StyleValues/AngleStyleValue.cpp
     CSS/StyleValues/BackgroundRepeatStyleValue.cpp

+ 73 - 0
Userland/Libraries/LibWeb/CSS/StyleSheetIdentifier.cpp

@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "StyleSheetIdentifier.h"
+#include <LibIPC/Decoder.h>
+#include <LibIPC/Encoder.h>
+
+namespace Web::CSS {
+
+StringView style_sheet_identifier_type_to_string(StyleSheetIdentifier::Type type)
+{
+    switch (type) {
+    case StyleSheetIdentifier::Type::StyleElement:
+        return "StyleElement"sv;
+    case StyleSheetIdentifier::Type::LinkElement:
+        return "LinkElement"sv;
+    case StyleSheetIdentifier::Type::ImportRule:
+        return "ImportRule"sv;
+    case StyleSheetIdentifier::Type::UserAgent:
+        return "UserAgent"sv;
+    case StyleSheetIdentifier::Type::UserStyle:
+        return "UserStyle"sv;
+    }
+    VERIFY_NOT_REACHED();
+}
+
+Optional<StyleSheetIdentifier::Type> style_sheet_identifier_type_from_string(StringView string)
+{
+    if (string == "StyleElement"sv)
+        return StyleSheetIdentifier::Type::StyleElement;
+    if (string == "LinkElement"sv)
+        return StyleSheetIdentifier::Type::LinkElement;
+    if (string == "ImportRule"sv)
+        return StyleSheetIdentifier::Type::ImportRule;
+    if (string == "UserAgent"sv)
+        return StyleSheetIdentifier::Type::UserAgent;
+    if (string == "UserStyle"sv)
+        return StyleSheetIdentifier::Type::UserStyle;
+    return {};
+}
+
+}
+
+namespace IPC {
+
+template<>
+ErrorOr<void> encode(Encoder& encoder, Web::CSS::StyleSheetIdentifier const& style_sheet_source)
+{
+    TRY(encoder.encode(style_sheet_source.type));
+    TRY(encoder.encode(style_sheet_source.dom_element_unique_id));
+    TRY(encoder.encode(style_sheet_source.url));
+
+    return {};
+}
+
+template<>
+ErrorOr<Web::CSS::StyleSheetIdentifier> decode(Decoder& decoder)
+{
+    auto type = TRY(decoder.decode<Web::CSS::StyleSheetIdentifier::Type>());
+    auto dom_element_unique_id = TRY(decoder.decode<Optional<i32>>());
+    auto url = TRY(decoder.decode<Optional<String>>());
+
+    return Web::CSS::StyleSheetIdentifier {
+        .type = type,
+        .dom_element_unique_id = move(dom_element_unique_id),
+        .url = move(url),
+    };
+}
+
+}

+ 38 - 0
Userland/Libraries/LibWeb/CSS/StyleSheetIdentifier.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibIPC/Forward.h>
+#include <LibURL/URL.h>
+
+namespace Web::CSS {
+
+struct StyleSheetIdentifier {
+    enum class Type : u8 {
+        StyleElement,
+        LinkElement,
+        ImportRule,
+        UserAgent,
+        UserStyle,
+    } type;
+    Optional<i32> dom_element_unique_id {};
+    Optional<String> url {};
+};
+
+StringView style_sheet_identifier_type_to_string(StyleSheetIdentifier::Type);
+Optional<StyleSheetIdentifier::Type> style_sheet_identifier_type_from_string(StringView);
+}
+
+namespace IPC {
+
+template<>
+ErrorOr<void> encode(Encoder&, Web::CSS::StyleSheetIdentifier const&);
+
+template<>
+ErrorOr<Web::CSS::StyleSheetIdentifier> decode(Decoder&);
+
+}

+ 3 - 0
Userland/Libraries/LibWeb/DOM/Node.h

@@ -83,6 +83,7 @@ public:
     virtual bool is_svg_element() const { return false; }
     virtual bool is_svg_graphics_element() const { return false; }
     virtual bool is_svg_script_element() const { return false; }
+    virtual bool is_svg_style_element() const { return false; }
     virtual bool is_svg_svg_element() const { return false; }
     virtual bool is_svg_use_element() const { return false; }
 
@@ -100,8 +101,10 @@ public:
     virtual bool is_html_base_element() const { return false; }
     virtual bool is_html_body_element() const { return false; }
     virtual bool is_html_input_element() const { return false; }
+    virtual bool is_html_link_element() const { return false; }
     virtual bool is_html_progress_element() const { return false; }
     virtual bool is_html_script_element() const { return false; }
+    virtual bool is_html_style_element() const { return false; }
     virtual bool is_html_template_element() const { return false; }
     virtual bool is_html_table_element() const { return false; }
     virtual bool is_html_table_section_element() const { return false; }

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -200,6 +200,7 @@ class StringStyleValue;
 class StyleComputer;
 class StyleProperties;
 class StyleSheet;
+struct StyleSheetIdentifier;
 class StyleSheetList;
 class StyleValueList;
 class Supports;

+ 4 - 1
Userland/Libraries/LibWeb/HTML/HTMLLinkElement.h

@@ -53,7 +53,10 @@ private:
     virtual void resource_did_fail() override;
     virtual void resource_did_load() override;
 
-    // ^ HTMLElement
+    // ^DOM::Node
+    virtual bool is_html_link_element() const override { return true; }
+
+    // ^HTMLElement
     virtual void visit_edges(Cell::Visitor&) override;
 
     struct LinkProcessingOptions {

+ 3 - 0
Userland/Libraries/LibWeb/HTML/HTMLStyleElement.h

@@ -32,6 +32,9 @@ public:
 private:
     HTMLStyleElement(DOM::Document&, DOM::QualifiedName);
 
+    // ^DOM::Node
+    virtual bool is_html_style_element() const override { return true; }
+
     virtual void initialize(JS::Realm&) override;
     virtual void visit_edges(Cell::Visitor&) override;
 

+ 3 - 0
Userland/Libraries/LibWeb/SVG/SVGStyleElement.h

@@ -28,6 +28,9 @@ public:
 private:
     SVGStyleElement(DOM::Document&, DOM::QualifiedName);
 
+    // ^DOM::Node
+    virtual bool is_svg_style_element() const override { return true; }
+
     virtual void initialize(JS::Realm&) override;
     virtual void visit_edges(Cell::Visitor&) override;
 

+ 85 - 0
Userland/Services/WebContent/PageClient.cpp

@@ -10,9 +10,12 @@
 #include <LibJS/Console.h>
 #include <LibJS/Runtime/ConsoleObject.h>
 #include <LibWeb/Bindings/MainThreadVM.h>
+#include <LibWeb/CSS/CSSImportRule.h>
 #include <LibWeb/Cookie/ParsedCookie.h>
 #include <LibWeb/DOM/Attr.h>
 #include <LibWeb/DOM/NamedNodeMap.h>
+#include <LibWeb/HTML/HTMLLinkElement.h>
+#include <LibWeb/HTML/HTMLStyleElement.h>
 #include <LibWeb/HTML/Scripting/ClassicScript.h>
 #include <LibWeb/HTML/TraversableNavigable.h>
 #include <LibWeb/Layout/Viewport.h>
@@ -739,6 +742,88 @@ void PageClient::did_get_js_console_messages(i32 start_index, Vector<ByteString>
     client().async_did_get_js_console_messages(m_id, start_index, move(message_types), move(messages));
 }
 
+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 location = sheet.location(); location.has_value())
+            identifier.url = location.release_value();
+
+        results.append(move(identifier));
+    }
+
+    for (auto& import_rule : sheet.import_rules()) {
+        if (import_rule->loaded_style_sheet()) {
+            gather_style_sheets(results, *import_rule->loaded_style_sheet());
+        } else {
+            // We can gather this anyway, and hope it loads later
+            results.append({ .type = Web::CSS::StyleSheetIdentifier::Type::ImportRule,
+                .url = MUST(import_rule->url().to_string()) });
+        }
+    }
+}
+
+Vector<Web::CSS::StyleSheetIdentifier> PageClient::list_style_sheets() const
+{
+    Vector<Web::CSS::StyleSheetIdentifier> results;
+
+    auto const* document = page().top_level_browsing_context().active_document();
+    if (document) {
+        for (auto& sheet : document->style_sheets().sheets()) {
+            gather_style_sheets(results, sheet);
+        }
+    }
+
+    // User style
+    if (page().user_style().has_value()) {
+        results.append({
+            .type = Web::CSS::StyleSheetIdentifier::Type::UserStyle,
+        });
+    }
+
+    // User-agent
+    results.append({
+        .type = Web::CSS::StyleSheetIdentifier::Type::UserAgent,
+        .url = "CSS/Default.css"_string,
+    });
+    if (document && document->in_quirks_mode()) {
+        results.append({
+            .type = Web::CSS::StyleSheetIdentifier::Type::UserAgent,
+            .url = "CSS/QuirksMode.css"_string,
+        });
+    }
+    results.append({
+        .type = Web::CSS::StyleSheetIdentifier::Type::UserAgent,
+        .url = "MathML/Default.css"_string,
+    });
+    results.append({
+        .type = Web::CSS::StyleSheetIdentifier::Type::UserAgent,
+        .url = "SVG/Default.css"_string,
+    });
+
+    return results;
+}
+
 Web::DisplayListPlayerType PageClient::display_list_player_type() const
 {
     switch (s_use_skia_painter) {

+ 3 - 0
Userland/Services/WebContent/PageClient.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <LibGfx/Rect.h>
+#include <LibWeb/CSS/StyleSheetIdentifier.h>
 #include <LibWeb/HTML/AudioPlayState.h>
 #include <LibWeb/HTML/FileFilter.h>
 #include <LibWeb/Page/Page.h>
@@ -83,6 +84,8 @@ public:
     void console_peer_did_misbehave(char const* reason);
     void did_get_js_console_messages(i32 start_index, Vector<ByteString> message_types, Vector<ByteString> messages);
 
+    Vector<Web::CSS::StyleSheetIdentifier> list_style_sheets() const;
+
     virtual double device_pixels_per_css_pixel() const override { return m_device_pixels_per_css_pixel; }
 
     virtual Web::DisplayListPlayerType display_list_player_type() const override;