diff --git a/Tests/LibWeb/Text/expected/css/CSSStyleSheet-replace.txt b/Tests/LibWeb/Text/expected/css/CSSStyleSheet-replace.txt new file mode 100644 index 0000000000..d16d62df70 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/CSSStyleSheet-replace.txt @@ -0,0 +1,7 @@ +Exception thrown when calling replace() on non-constructed stylesheet: NotAllowedError +Number of CSS rules after replace(): 2 +Rule: .test { font-size: 14px; } +Rule: .test2 { font-size: 16px; } +cssRules returns the same object before and after replace(): true +@import rule should be not appear below: +Rule: .test { padding: 100px; } diff --git a/Tests/LibWeb/Text/input/css/CSSStyleSheet-replace.html b/Tests/LibWeb/Text/input/css/CSSStyleSheet-replace.html new file mode 100644 index 0000000000..666c37e9ca --- /dev/null +++ b/Tests/LibWeb/Text/input/css/CSSStyleSheet-replace.html @@ -0,0 +1,50 @@ + + + + diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp index ac954c0305..13f3604a86 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace Web::CSS { @@ -130,7 +131,9 @@ WebIDL::ExceptionOr CSSStyleSheet::insert_rule(StringView rule, unsign { // FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception. - // FIXME: 2. If the disallow modification flag is set, throw a NotAllowedError DOMException. + // If the disallow modification flag is set, throw a NotAllowedError DOMException. + if (disallow_modification()) + return WebIDL::NotAllowedError::create(realm(), "Can't call insert_rule() on non-modifiable stylesheets."_fly_string); // 3. Let parsed rule be the return value of invoking parse a rule with rule. auto context = m_style_sheet_list ? CSS::Parser::ParsingContext { m_style_sheet_list->document() } : CSS::Parser::ParsingContext { realm() }; @@ -165,7 +168,9 @@ WebIDL::ExceptionOr CSSStyleSheet::delete_rule(unsigned index) { // FIXME: 1. If the origin-clean flag is unset, throw a SecurityError exception. - // FIXME: 2. If the disallow modification flag is set, throw a NotAllowedError DOMException. + // 2. If the disallow modification flag is set, throw a NotAllowedError DOMException. + if (disallow_modification()) + return WebIDL::NotAllowedError::create(realm(), "Can't call delete_rule() on non-modifiable stylesheets."_fly_string); // 3. Remove a CSS rule in the CSS rules at index. auto result = m_rules->remove_a_css_rule(index); @@ -178,6 +183,53 @@ WebIDL::ExceptionOr CSSStyleSheet::delete_rule(unsigned index) return result; } +// https://drafts.csswg.org/cssom/#dom-cssstylesheet-replace +JS::NonnullGCPtr CSSStyleSheet::replace(String text) +{ + // 1. Let promise be a promise + auto promise = JS::Promise::create(realm()); + + // 2. If the constructed flag is not set, or the disallow modification flag is set, reject promise with a NotAllowedError DOMException and return promise. + if (!constructed()) { + promise->reject(WebIDL::NotAllowedError::create(realm(), "Can't call replace() on non-constructed stylesheets"_fly_string)); + return promise; + } + + if (disallow_modification()) { + promise->reject(WebIDL::NotAllowedError::create(realm(), "Can't call replace() on non-modifiable stylesheets"_fly_string)); + return promise; + } + + // 3. Set the disallow modification flag. + set_disallow_modification(true); + + // 4. In parallel, do these steps: + Platform::EventLoopPlugin::the().deferred_invoke([this, text = move(text), promise] { + // 1. Let rules be the result of running parse a stylesheet’s contents from text. + auto context = m_style_sheet_list ? CSS::Parser::ParsingContext { m_style_sheet_list->document() } : CSS::Parser::ParsingContext { realm() }; + auto* parsed_stylesheet = parse_css_stylesheet(context, text); + auto& rules = parsed_stylesheet->rules(); + + // 2. If rules contains one or more @import rules, remove those rules from rules. + JS::MarkedVector> rules_without_import(realm().heap()); + for (auto rule : rules) { + if (rule->type() != CSSRule::Type::Import) + rules_without_import.append(rule); + } + + // 3. Set sheet’s CSS rules to rules. + m_rules->set_rules({}, rules_without_import); + + // 4. Unset sheet’s disallow modification flag. + set_disallow_modification(false); + + // 5. Resolve promise with sheet. + promise->fulfill(this); + }); + + return promise; +} + // https://www.w3.org/TR/cssom/#dom-cssstylesheet-removerule WebIDL::ExceptionOr CSSStyleSheet::remove_rule(unsigned index) { diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h index d7ed4424b1..236db5d6b2 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h @@ -52,6 +52,8 @@ public: WebIDL::ExceptionOr remove_rule(unsigned index); WebIDL::ExceptionOr delete_rule(unsigned index); + JS::NonnullGCPtr replace(String text); + void for_each_effective_style_rule(Function const& callback) const; // Returns whether the match state of any media queries changed after evaluation. bool evaluate_media_queries(HTML::Window const&); @@ -70,6 +72,8 @@ public: JS::GCPtr constructor_document() const { return m_constructor_document; } void set_constructor_document(JS::GCPtr constructor_document) { m_constructor_document = constructor_document; } + bool disallow_modification() const { return m_disallow_modification; } + private: CSSStyleSheet(JS::Realm&, CSSRuleList&, MediaList&, Optional location); @@ -79,6 +83,7 @@ private: void recalculate_namespaces(); void set_constructed(bool constructed) { m_constructed = constructed; } + void set_disallow_modification(bool disallow_modification) { m_disallow_modification = disallow_modification; } JS::GCPtr m_rules; JS::GCPtr m_default_namespace_rule; @@ -90,6 +95,7 @@ private: Optional m_base_url; JS::GCPtr m_constructor_document; bool m_constructed { false }; + bool m_disallow_modification { false }; }; } diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.idl b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.idl index 07605d2992..cdf2450ae7 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.idl +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.idl @@ -13,7 +13,7 @@ interface CSSStyleSheet : StyleSheet { unsigned long insertRule(CSSOMString rule, optional unsigned long index = 0); undefined deleteRule(unsigned long index); - // FIXME: Promise replace(USVString text); + Promise replace(USVString text); // FIXME: undefined replaceSync(USVString text); // https://drafts.csswg.org/cssom/#legacy-css-style-sheet-members