Bladeren bron

LibWeb: Add styleSheets and adoptedStyleSheets attributes in ShadowRoot

Co-authored-by: Simon Wanner <simon+git@skyrising.xyz>
Aliaksandr Kalenik 1 jaar geleden
bovenliggende
commit
8ce8697a66

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

@@ -135,6 +135,7 @@ set(SOURCES
     DOM/AbortSignal.cpp
     DOM/AbstractRange.cpp
     DOM/AccessibilityTreeNode.cpp
+    DOM/AdoptedStyleSheets.cpp
     DOM/Attr.cpp
     DOM/CDATASection.cpp
     DOM/CharacterData.cpp

+ 47 - 0
Userland/Libraries/LibWeb/DOM/AdoptedStyleSheets.cpp

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/CSS/StyleComputer.h>
+#include <LibWeb/DOM/AdoptedStyleSheets.h>
+#include <LibWeb/DOM/Document.h>
+
+namespace Web::DOM {
+
+JS::NonnullGCPtr<WebIDL::ObservableArray> create_adopted_style_sheets_list(Document& document)
+{
+    auto adopted_style_sheets = WebIDL::ObservableArray::create(document.realm());
+    adopted_style_sheets->set_on_set_an_indexed_value_callback([&document](JS::Value& value) -> WebIDL::ExceptionOr<void> {
+        auto& vm = document.vm();
+        if (!value.is_object())
+            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "CSSStyleSheet");
+        auto& object = value.as_object();
+        if (!is<CSS::CSSStyleSheet>(object))
+            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "CSSStyleSheet");
+        auto& style_sheet = static_cast<CSS::CSSStyleSheet&>(object);
+
+        // The set an indexed value algorithm for adoptedStyleSheets, given value and index, is the following:
+        // 1. If value’s constructed flag is not set, or its constructor document is not equal to this
+        //    DocumentOrShadowRoot's node document, throw a "NotAllowedError" DOMException.
+        if (!style_sheet.constructed())
+            return WebIDL::NotAllowedError::create(document.realm(), "StyleSheet's constructed flag is not set."_fly_string);
+        if (!style_sheet.constructed() || style_sheet.constructor_document().ptr() != &document)
+            return WebIDL::NotAllowedError::create(document.realm(), "Sharing a StyleSheet between documents is not allowed."_fly_string);
+
+        document.style_computer().load_fonts_from_sheet(style_sheet);
+        document.style_computer().invalidate_rule_cache();
+        document.invalidate_style();
+        return {};
+    });
+    adopted_style_sheets->set_on_delete_an_indexed_value_callback([&document]() -> WebIDL::ExceptionOr<void> {
+        document.style_computer().invalidate_rule_cache();
+        document.invalidate_style();
+        return {};
+    });
+
+    return adopted_style_sheets;
+}
+
+}

+ 16 - 0
Userland/Libraries/LibWeb/DOM/AdoptedStyleSheets.h

@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibWeb/Forward.h>
+#include <LibWeb/WebIDL/ObservableArray.h>
+
+namespace Web::DOM {
+
+JS::NonnullGCPtr<WebIDL::ObservableArray> create_adopted_style_sheets_list(Document& document);
+
+}

+ 1 - 34
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -31,6 +31,7 @@
 #include <LibWeb/CSS/SystemColor.h>
 #include <LibWeb/CSS/VisualViewport.h>
 #include <LibWeb/Cookie/ParsedCookie.h>
+#include <LibWeb/DOM/AdoptedStyleSheets.h>
 #include <LibWeb/DOM/Attr.h>
 #include <LibWeb/DOM/CDATASection.h>
 #include <LibWeb/DOM/Comment.h>
@@ -4564,40 +4565,6 @@ bool Document::has_skipped_resize_observations()
     return false;
 }
 
-static JS::NonnullGCPtr<WebIDL::ObservableArray> create_adopted_style_sheets_list(Document& document)
-{
-    auto adopted_style_sheets = WebIDL::ObservableArray::create(document.realm());
-    adopted_style_sheets->set_on_set_an_indexed_value_callback([&document](JS::Value& value) -> WebIDL::ExceptionOr<void> {
-        auto& vm = document.vm();
-        if (!value.is_object())
-            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "CSSStyleSheet");
-        auto& object = value.as_object();
-        if (!is<CSS::CSSStyleSheet>(object))
-            return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "CSSStyleSheet");
-        auto& style_sheet = static_cast<CSS::CSSStyleSheet&>(object);
-
-        // The set an indexed value algorithm for adoptedStyleSheets, given value and index, is the following:
-        // 1. If value’s constructed flag is not set, or its constructor document is not equal to this
-        //    DocumentOrShadowRoot's node document, throw a "NotAllowedError" DOMException.
-        if (!style_sheet.constructed())
-            return WebIDL::NotAllowedError::create(document.realm(), "StyleSheet's constructed flag is not set."_fly_string);
-        if (!style_sheet.constructed() || style_sheet.constructor_document().ptr() != &document)
-            return WebIDL::NotAllowedError::create(document.realm(), "Sharing a StyleSheet between documents is not allowed."_fly_string);
-
-        document.style_computer().load_fonts_from_sheet(style_sheet);
-        document.style_computer().invalidate_rule_cache();
-        document.invalidate_style();
-        return {};
-    });
-    adopted_style_sheets->set_on_delete_an_indexed_value_callback([&document]() -> WebIDL::ExceptionOr<void> {
-        document.style_computer().invalidate_rule_cache();
-        document.invalidate_style();
-        return {};
-    });
-
-    return adopted_style_sheets;
-}
-
 JS::NonnullGCPtr<WebIDL::ObservableArray> Document::adopted_style_sheets() const
 {
     if (!m_adopted_style_sheets)

+ 2 - 3
Userland/Libraries/LibWeb/DOM/Document.idl

@@ -4,6 +4,7 @@
 #import <DOM/Comment.idl>
 #import <DOM/DOMImplementation.idl>
 #import <DOM/DocumentFragment.idl>
+#import <DOM/DocumentOrShadowRoot.idl>
 #import <DOM/DocumentType.idl>
 #import <DOM/Element.idl>
 #import <DOM/Event.idl>
@@ -94,9 +95,6 @@ interface Document : Node {
     [CEReactions, NewObject] Node importNode(Node node, optional boolean deep = false);
     [CEReactions, ImplementedAs=adopt_node_binding] Node adoptNode(Node node);
 
-    [ImplementedAs=style_sheets_for_bindings] readonly attribute StyleSheetList styleSheets;
-    attribute any adoptedStyleSheets;
-
     readonly attribute DOMString compatMode;
     readonly attribute DocumentType? doctype;
 
@@ -141,3 +139,4 @@ dictionary ElementCreationOptions {
 };
 Document includes ParentNode;
 Document includes GlobalEventHandlers;
+Document includes DocumentOrShadowRoot;

+ 8 - 0
Userland/Libraries/LibWeb/DOM/DocumentOrShadowRoot.idl

@@ -0,0 +1,8 @@
+#import <CSS/StyleSheetList.idl>
+
+// https://dom.spec.whatwg.org/#documentorshadowroot
+interface mixin DocumentOrShadowRoot {
+    // https://w3c.github.io/csswg-drafts/cssom/#extensions-to-the-document-or-shadow-root-interface
+    [SameObject, ImplementedAs=style_sheets_for_bindings] readonly attribute StyleSheetList styleSheets;
+    attribute any adoptedStyleSheets;
+};

+ 44 - 0
Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <LibWeb/DOM/AdoptedStyleSheets.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Event.h>
 #include <LibWeb/DOM/ShadowRoot.h>
@@ -54,4 +55,47 @@ WebIDL::ExceptionOr<void> ShadowRoot::set_inner_html(StringView markup)
     return {};
 }
 
+CSS::StyleSheetList& ShadowRoot::style_sheets()
+{
+    if (!m_style_sheets)
+        m_style_sheets = CSS::StyleSheetList::create(document());
+    return *m_style_sheets;
+}
+
+CSS::StyleSheetList const& ShadowRoot::style_sheets() const
+{
+    return const_cast<ShadowRoot*>(this)->style_sheets();
+}
+
+void ShadowRoot::visit_edges(Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_style_sheets);
+    visitor.visit(m_adopted_style_sheets);
+}
+
+JS::NonnullGCPtr<WebIDL::ObservableArray> ShadowRoot::adopted_style_sheets() const
+{
+    if (!m_adopted_style_sheets)
+        m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast<Document&>(document()));
+    return *m_adopted_style_sheets;
+}
+
+WebIDL::ExceptionOr<void> ShadowRoot::set_adopted_style_sheets(JS::Value new_value)
+{
+    if (!m_adopted_style_sheets)
+        m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast<Document&>(document()));
+
+    m_adopted_style_sheets->clear();
+    auto iterator_record = TRY(get_iterator(vm(), new_value, JS::IteratorHint::Sync));
+    while (true) {
+        auto next = TRY(iterator_step_value(vm(), iterator_record));
+        if (!next.has_value())
+            break;
+        TRY(m_adopted_style_sheets->append(*next));
+    }
+
+    return {};
+}
+
 }

+ 15 - 0
Userland/Libraries/LibWeb/DOM/ShadowRoot.h

@@ -8,6 +8,7 @@
 
 #include <LibWeb/Bindings/ShadowRootPrototype.h>
 #include <LibWeb/DOM/DocumentFragment.h>
+#include <LibWeb/WebIDL/ObservableArray.h>
 
 namespace Web::DOM {
 
@@ -33,6 +34,17 @@ public:
     WebIDL::ExceptionOr<String> inner_html() const;
     WebIDL::ExceptionOr<void> set_inner_html(StringView);
 
+    CSS::StyleSheetList& style_sheets();
+    CSS::StyleSheetList const& style_sheets() const;
+
+    CSS::StyleSheetList* style_sheets_for_bindings() { return &style_sheets(); }
+
+    JS::NonnullGCPtr<WebIDL::ObservableArray> adopted_style_sheets() const;
+    WebIDL::ExceptionOr<void> set_adopted_style_sheets(JS::Value);
+
+protected:
+    virtual void visit_edges(Cell::Visitor&) override;
+
 private:
     ShadowRoot(Document&, Element& host, Bindings::ShadowRootMode);
     virtual void initialize(JS::Realm&) override;
@@ -46,6 +58,9 @@ private:
     Bindings::SlotAssignmentMode m_slot_assignment { Bindings::SlotAssignmentMode::Named };
     bool m_delegates_focus { false };
     bool m_available_to_element_internals { false };
+
+    JS::GCPtr<CSS::StyleSheetList> m_style_sheets;
+    mutable JS::GCPtr<WebIDL::ObservableArray> m_adopted_style_sheets;
 };
 
 template<>

+ 2 - 0
Userland/Libraries/LibWeb/DOM/ShadowRoot.idl

@@ -1,4 +1,5 @@
 #import <DOM/DocumentFragment.idl>
+#import <DOM/DocumentOrShadowRoot.idl>
 #import <DOM/InnerHTML.idl>
 
 // https://dom.spec.whatwg.org/#shadowroot
@@ -12,6 +13,7 @@ interface ShadowRoot : DocumentFragment {
 };
 
 ShadowRoot includes InnerHTML;
+ShadowRoot includes DocumentOrShadowRoot;
 
 enum ShadowRootMode { "open", "closed" };
 enum SlotAssignmentMode { "manual", "named" };

+ 1 - 0
Userland/Libraries/LibWeb/DOM/StyleElementUtils.cpp

@@ -5,6 +5,7 @@
  */
 
 #include <LibWeb/CSS/Parser/Parser.h>
+#include <LibWeb/CSS/StyleComputer.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/StyleElementUtils.h>
 #include <LibWeb/Infra/Strings.h>

+ 1 - 0
Userland/Libraries/LibWeb/DOM/StyleElementUtils.h

@@ -8,6 +8,7 @@
 
 #include <LibWeb/CSS/CSSStyleSheet.h>
 #include <LibWeb/DOM/Element.h>
+#include <LibWeb/WebIDL/ObservableArray.h>
 
 namespace Web::DOM {