diff --git a/Tests/LibWeb/Text/expected/document-adopted-style-sheets.txt b/Tests/LibWeb/Text/expected/document-adopted-style-sheets.txt
new file mode 100644
index 0000000000..493750a428
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/document-adopted-style-sheets.txt
@@ -0,0 +1,11 @@
+ color with no adopted style sheets: rgb(0, 0, 0)
+document.adoptedStyleSheets.length=(1)
+add style sheet using Array.prototype.push(): rgb(255, 0, 0)
+document.adoptedStyleSheets.length=(0)
+delete added style sheet using Array.prototype.pop(): rgb(0, 0, 0)
+document.adoptedStyleSheets.length=(1)
+add style by assigning array to document.adoptedStyleSheets: rgb(255, 0, 0)
+document.adoptedStyleSheets.length=(1)
+add style by assigning Set to document.adoptedStyleSheets: rgb(0, 128, 0)
+assignment of non-iterable value to document.adoptedStyleSheets throws "1 is not iterable"
+assignment of value that is not CSSStyleSheet throws "Not an object of type CSSStyleSheet"
diff --git a/Tests/LibWeb/Text/input/document-adopted-style-sheets.html b/Tests/LibWeb/Text/input/document-adopted-style-sheets.html
new file mode 100644
index 0000000000..f8bc245bd9
--- /dev/null
+++ b/Tests/LibWeb/Text/input/document-adopted-style-sheets.html
@@ -0,0 +1,72 @@
+
+
+
diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
index 4942f76400..b959e5f17f 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
@@ -266,8 +266,9 @@ void StyleComputer::for_each_stylesheet(CascadeOrigin cascade_origin, Callback c
callback(*m_user_style_sheet);
}
if (cascade_origin == CascadeOrigin::Author) {
- for (auto const& sheet : document().style_sheets().sheets())
- callback(*sheet);
+ document().for_each_css_style_sheet([&](CSSStyleSheet& sheet) {
+ callback(sheet);
+ });
}
}
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp
index 2eebc56413..89a7e11d1c 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Document.cpp
@@ -483,6 +483,8 @@ void Document::visit_edges(Cell::Visitor& visitor)
visitor.visit(event.event);
visitor.visit(event.target);
}
+
+ visitor.visit(m_adopted_style_sheets);
}
// https://w3c.github.io/selection-api/#dom-document-getselection
@@ -4562,4 +4564,78 @@ bool Document::has_skipped_resize_observations()
return false;
}
+static JS::NonnullGCPtr 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 {
+ auto& vm = document.vm();
+ if (!value.is_object())
+ return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "CSSStyleSheet");
+ auto& object = value.as_object();
+ if (!is(object))
+ return vm.throw_completion(JS::ErrorType::NotAnObjectOfType, "CSSStyleSheet");
+ auto& style_sheet = static_cast(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 {
+ document.style_computer().invalidate_rule_cache();
+ document.invalidate_style();
+ return {};
+ });
+
+ return adopted_style_sheets;
+}
+
+JS::NonnullGCPtr Document::adopted_style_sheets() const
+{
+ if (!m_adopted_style_sheets)
+ m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast(*this));
+ return *m_adopted_style_sheets;
+}
+
+WebIDL::ExceptionOr Document::set_adopted_style_sheets(JS::Value new_value)
+{
+ if (!m_adopted_style_sheets)
+ m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast(*this));
+
+ 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 {};
+}
+
+void Document::for_each_css_style_sheet(Function&& callback) const
+{
+ for (auto& style_sheet : m_style_sheets->sheets())
+ callback(*style_sheet);
+
+ if (m_adopted_style_sheets) {
+ for (auto& entry : m_adopted_style_sheets->indexed_properties()) {
+ auto value_and_attributes = m_adopted_style_sheets->indexed_properties().storage()->get(entry.index());
+ if (value_and_attributes.has_value()) {
+ auto& style_sheet = verify_cast(value_and_attributes->value.as_object());
+ callback(style_sheet);
+ }
+ }
+ }
+}
+
}
diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h
index 530ca53522..b5f9c14041 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.h
+++ b/Userland/Libraries/LibWeb/DOM/Document.h
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
namespace Web::DOM {
@@ -131,6 +132,8 @@ public:
CSS::StyleSheetList& style_sheets();
CSS::StyleSheetList const& style_sheets() const;
+ void for_each_css_style_sheet(Function&& callback) const;
+
CSS::StyleSheetList* style_sheets_for_bindings() { return &style_sheets(); }
virtual FlyString node_name() const override { return "#document"_fly_string; }
@@ -598,6 +601,9 @@ public:
[[nodiscard]] bool has_active_resize_observations();
[[nodiscard]] bool has_skipped_resize_observations();
+ JS::NonnullGCPtr adopted_style_sheets() const;
+ WebIDL::ExceptionOr set_adopted_style_sheets(JS::Value);
+
protected:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
@@ -832,6 +838,8 @@ private:
bool m_design_mode_enabled { false };
bool m_needs_to_resolve_paint_only_properties { true };
+
+ mutable JS::GCPtr m_adopted_style_sheets;
};
template<>
diff --git a/Userland/Libraries/LibWeb/DOM/Document.idl b/Userland/Libraries/LibWeb/DOM/Document.idl
index 5054da003d..c00419ea27 100644
--- a/Userland/Libraries/LibWeb/DOM/Document.idl
+++ b/Userland/Libraries/LibWeb/DOM/Document.idl
@@ -95,6 +95,7 @@ interface Document : Node {
[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;