Parcourir la source

LibWeb: Add style sheets to the shadow root if applicable

If a style element belongs to a shadow tree, its CSSStyleSheet is now
added to the corresponding ShadowRoot instead of the document.

Co-authored-by: Simon Wanner <simon+git@skyrising.xyz>
Aliaksandr Kalenik il y a 2 ans
Parent
commit
c6e69d501f

+ 2 - 0
Tests/LibWeb/Text/expected/shadow-root-style-sheets.txt

@@ -0,0 +1,2 @@
+  shadow.styleSheets.length=1
+document.styleSheets.length=0

+ 18 - 0
Tests/LibWeb/Text/input/shadow-root-style-sheets.html

@@ -0,0 +1,18 @@
+<script src="include.js"></script>
+<my-custom-element></my-custom-element>
+<script>
+    test(() => {
+        class MyCustomElement extends HTMLElement {
+            constructor() {
+                super();
+                const shadow = this.attachShadow({ mode: 'open' });
+                const style = document.createElement('style');
+                shadow.appendChild(style);
+                println(`shadow.styleSheets.length=${shadow.styleSheets.length}`);
+                println(`document.styleSheets.length=${document.styleSheets.length}`);
+            }
+        }
+
+        customElements.define('my-custom-element', MyCustomElement);
+    });
+</script>

+ 9 - 0
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -2380,4 +2380,13 @@ auto Element::ensure_custom_element_reaction_queue() -> CustomElementReactionQue
     return *m_custom_element_reaction_queue;
 }
 
+CSS::StyleSheetList& Element::document_or_shadow_root_style_sheets()
+{
+    auto& root_node = root();
+    if (is<DOM::ShadowRoot>(root_node))
+        return static_cast<DOM::ShadowRoot&>(root_node).style_sheets();
+
+    return document().style_sheets();
+}
+
 }

+ 2 - 0
Userland/Libraries/LibWeb/DOM/Element.h

@@ -183,6 +183,8 @@ public:
 
     CSS::CSSStyleDeclaration* style_for_bindings();
 
+    CSS::StyleSheetList& document_or_shadow_root_style_sheets();
+
     WebIDL::ExceptionOr<String> inner_html() const;
     WebIDL::ExceptionOr<void> set_inner_html(StringView);
 

+ 8 - 8
Userland/Libraries/LibWeb/DOM/StyleElementUtils.cpp

@@ -33,7 +33,7 @@ void StyleElementUtils::update_a_style_block(DOM::Element& style_element)
     // 2. If element has an associated CSS style sheet, remove the CSS style sheet in question.
 
     if (m_associated_css_style_sheet) {
-        remove_a_css_style_sheet(style_element.document(), *m_associated_css_style_sheet);
+        remove_a_css_style_sheet(style_element.document_or_shadow_root_style_sheets(), *m_associated_css_style_sheet);
 
         // FIXME: This should probably be handled by StyleSheet::set_owner_node().
         m_associated_css_style_sheet = nullptr;
@@ -61,7 +61,7 @@ void StyleElementUtils::update_a_style_block(DOM::Element& style_element)
 
     // 6. Create a CSS style sheet with the following properties...
     create_a_css_style_sheet(
-        style_element.document(),
+        style_element.document_or_shadow_root_style_sheets(),
         "text/css"_string,
         &style_element,
         style_element.attribute(HTML::AttributeNames::media).value_or({}),
@@ -77,10 +77,10 @@ void StyleElementUtils::update_a_style_block(DOM::Element& style_element)
 }
 
 // https://www.w3.org/TR/cssom/#remove-a-css-style-sheet
-void StyleElementUtils::remove_a_css_style_sheet(DOM::Document& document, CSS::CSSStyleSheet& sheet)
+void StyleElementUtils::remove_a_css_style_sheet(CSS::StyleSheetList& style_sheets, CSS::CSSStyleSheet& sheet)
 {
     // 1. Remove the CSS style sheet from the list of document or shadow root CSS style sheets.
-    document.style_sheets().remove_sheet(sheet);
+    style_sheets.remove_sheet(sheet);
 
     // 2. Set the CSS style sheet’s parent CSS style sheet, owner node and owner CSS rule to null.
     sheet.set_parent_css_style_sheet(nullptr);
@@ -89,7 +89,7 @@ void StyleElementUtils::remove_a_css_style_sheet(DOM::Document& document, CSS::C
 }
 
 // https://www.w3.org/TR/cssom/#create-a-css-style-sheet
-void StyleElementUtils::create_a_css_style_sheet(DOM::Document& document, String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<String> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet& sheet)
+void StyleElementUtils::create_a_css_style_sheet(CSS::StyleSheetList& style_sheets, String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<String> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet& sheet)
 {
     // 1. Create a new CSS style sheet object and set its properties as specified.
     // FIXME: We receive `sheet` from the caller already. This is weird.
@@ -105,14 +105,14 @@ void StyleElementUtils::create_a_css_style_sheet(DOM::Document& document, String
     sheet.set_location(move(location));
 
     // 2. Then run the add a CSS style sheet steps for the newly created CSS style sheet.
-    add_a_css_style_sheet(document, sheet);
+    add_a_css_style_sheet(style_sheets, sheet);
 }
 
 // https://www.w3.org/TR/cssom/#add-a-css-style-sheet
-void StyleElementUtils::add_a_css_style_sheet(DOM::Document& document, CSS::CSSStyleSheet& sheet)
+void StyleElementUtils::add_a_css_style_sheet(CSS::StyleSheetList& style_sheets, CSS::CSSStyleSheet& sheet)
 {
     // 1. Add the CSS style sheet to the list of document or shadow root CSS style sheets at the appropriate location. The remainder of these steps deal with the disabled flag.
-    document.style_sheets().add_sheet(sheet);
+    style_sheets.add_sheet(sheet);
 
     // 2. If the disabled flag is set, then return.
     if (sheet.disabled())

+ 3 - 3
Userland/Libraries/LibWeb/DOM/StyleElementUtils.h

@@ -20,9 +20,9 @@ public:
     CSS::CSSStyleSheet const* sheet() const { return m_associated_css_style_sheet; }
 
 private:
-    void remove_a_css_style_sheet(DOM::Document& document, CSS::CSSStyleSheet& sheet);
-    void create_a_css_style_sheet(DOM::Document& document, String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<String> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet& sheet);
-    void add_a_css_style_sheet(DOM::Document& document, CSS::CSSStyleSheet& sheet);
+    void remove_a_css_style_sheet(CSS::StyleSheetList&, CSS::CSSStyleSheet& sheet);
+    void create_a_css_style_sheet(CSS::StyleSheetList&, String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<String> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet& sheet);
+    void add_a_css_style_sheet(CSS::StyleSheetList&, CSS::CSSStyleSheet& sheet);
 
     // https://www.w3.org/TR/cssom/#associated-css-style-sheet
     JS::GCPtr<CSS::CSSStyleSheet> m_associated_css_style_sheet;

+ 3 - 2
Userland/Libraries/LibWeb/HTML/HTMLLinkElement.cpp

@@ -14,6 +14,7 @@
 #include <LibWeb/CSS/Parser/Parser.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/ShadowRoot.h>
 #include <LibWeb/Fetch/Fetching/Fetching.h>
 #include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
 #include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
@@ -112,7 +113,7 @@ void HTMLLinkElement::attribute_changed(FlyString const& name, Optional<String>
     // FIXME: Handle alternate stylesheets properly
     if (m_relationship & Relationship::Stylesheet && !(m_relationship & Relationship::Alternate)) {
         if (name == HTML::AttributeNames::disabled && m_loaded_style_sheet)
-            document().style_sheets().remove_sheet(*m_loaded_style_sheet);
+            document_or_shadow_root_style_sheets().remove_sheet(*m_loaded_style_sheet);
 
         // https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet:fetch-and-process-the-linked-resource
         // The appropriate times to fetch and process this type of link are:
@@ -311,7 +312,7 @@ void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastru
 
     // 3. If el has an associated CSS style sheet, remove the CSS style sheet.
     if (m_loaded_style_sheet) {
-        document().style_sheets().remove_sheet(*m_loaded_style_sheet);
+        document_or_shadow_root_style_sheets().remove_sheet(*m_loaded_style_sheet);
         m_loaded_style_sheet = nullptr;
     }