diff --git a/Libraries/LibWeb/CSS/CSSRuleList.cpp b/Libraries/LibWeb/CSS/CSSRuleList.cpp index 1def5ad1731..76b9971f45e 100644 --- a/Libraries/LibWeb/CSS/CSSRuleList.cpp +++ b/Libraries/LibWeb/CSS/CSSRuleList.cpp @@ -90,7 +90,48 @@ WebIDL::ExceptionOr CSSRuleList::insert_a_css_rule(Variant types, size_t index) { + for (size_t i = 0; i < index; i++) { + if (!any_of(types, [&](auto type) { return m_rules[i]->type() == type; })) + return true; + } + return false; + }; + + auto has_rule_of_type_at_or_after_index = [&](CSSRule::Type type, size_t index) { + for (size_t i = index; i < m_rules.size(); i++) { + if (m_rules[i]->type() == type) + return true; + } + return false; + }; + + // 6. If new rule cannot be inserted into list at the zero-indexed position index due to constraints specified by CSS, then throw a HierarchyRequestError exception. [CSS21] + // "Any @import rules must precede all other valid at-rules and style rules in a style sheet + // (ignoring @charset and @layer statement rules) and must not have any other valid at-rules + // or style rules between it and previous @import rules, or else the @import rule is invalid." + // https://drafts.csswg.org/css-cascade-5/#at-import + // + // "Any @namespace rules must follow all @charset and @import rules and precede all other + // non-ignored at-rules and style rules in a style sheet. + + auto rule_is_disallowed = false; + switch (new_rule->type()) { + case CSSRule::Type::LayerStatement: + break; + case CSSRule::Type::Import: + rule_is_disallowed = has_rule_of_type_other_than_specified_before_index({ CSSRule::Type::Import, CSSRule::Type::LayerStatement }, index); + break; + case CSSRule::Type::Namespace: + rule_is_disallowed = has_rule_of_type_at_or_after_index(CSSRule::Type::Import, index) || has_rule_of_type_other_than_specified_before_index({ CSSRule::Type::Import, CSSRule::Type::Namespace, CSSRule::Type::LayerStatement }, index); + break; + default: + rule_is_disallowed = has_rule_of_type_at_or_after_index(CSSRule::Type::Import, index) || has_rule_of_type_at_or_after_index(CSSRule::Type::Namespace, index); + break; + } + + if (rule_is_disallowed) + return WebIDL::HierarchyRequestError::create(realm(), "Cannot insert rule at specified index."_string); // 7. If new rule is an @namespace at-rule, and list contains anything other than @import at-rules, and @namespace at-rules, throw an InvalidStateError exception. if (new_rule->type() == CSSRule::Type::Namespace && any_of(m_rules, [](auto existing_rule) { return existing_rule->type() != CSSRule::Type::Import && existing_rule->type() != CSSRule::Type::Namespace; })) diff --git a/Tests/LibWeb/Text/expected/css/insert-rule-constraints.txt b/Tests/LibWeb/Text/expected/css/insert-rule-constraints.txt new file mode 100644 index 00000000000..4a0707bb020 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/insert-rule-constraints.txt @@ -0,0 +1,5 @@ +Inserting import rules after style rule throws: HierarchyRequestError +Inserting namespace rules after style rule throws: HierarchyRequestError +Inserting namespace rules before import rule throws: HierarchyRequestError +Inserting style rules before import rule throws: HierarchyRequestError +Inserting style rules before namespace rule throws: HierarchyRequestError diff --git a/Tests/LibWeb/Text/input/css/insert-rule-constraints.html b/Tests/LibWeb/Text/input/css/insert-rule-constraints.html new file mode 100644 index 00000000000..0e6ea22f4d5 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/insert-rule-constraints.html @@ -0,0 +1,78 @@ + + + + + + + + + +