LibWeb: Add styleSheets and adoptedStyleSheets attributes in ShadowRoot

Co-authored-by: Simon Wanner <simon+git@skyrising.xyz>
This commit is contained in:
Aliaksandr Kalenik 2024-03-08 19:27:24 +01:00 committed by Andreas Kling
parent f19c92d78e
commit 8ce8697a66
Notes: sideshowbarker 2024-07-17 01:46:00 +09:00
11 changed files with 138 additions and 37 deletions

View file

@ -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

View file

@ -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 values 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;
}
}

View file

@ -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);
}

View file

@ -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 values 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)

View file

@ -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;

View file

@ -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;
};

View file

@ -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 {};
}
}

View file

@ -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<>

View file

@ -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" };

View file

@ -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>

View file

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