mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
LibWeb/CSS: Disallow :has() and pseudo-elements in :has() when parsing
This commit is contained in:
parent
ad1f93504e
commit
7f803c5c3d
Notes:
github-actions[bot]
2024-11-14 19:08:37 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/7f803c5c3d4 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2340
6 changed files with 45 additions and 0 deletions
|
@ -413,6 +413,8 @@ private:
|
|||
};
|
||||
static ContextType context_type_for_at_rule(FlyString const&);
|
||||
Vector<ContextType> m_rule_context;
|
||||
|
||||
Vector<PseudoClass> m_pseudo_class_context; // Stack of pseudo-class functions we're currently inside
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -411,6 +411,11 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
|||
|
||||
// Note: We allow the "ignored" -webkit prefix here for -webkit-progress-bar/-webkit-progress-bar
|
||||
if (auto pseudo_element = Selector::PseudoElement::from_string(pseudo_name); pseudo_element.has_value()) {
|
||||
// :has() is fussy about pseudo-elements inside it
|
||||
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(pseudo_element->type())) {
|
||||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::PseudoElement,
|
||||
.value = pseudo_element.release_value()
|
||||
|
@ -423,6 +428,10 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
|||
// valid at parse time, but ::-webkit-jkl() is not.) If they’re not otherwise recognized and supported, they
|
||||
// must be treated as matching nothing, and are unknown -webkit- pseudo-elements.
|
||||
if (pseudo_name.starts_with_bytes("-webkit-"sv, CaseSensitivity::CaseInsensitive)) {
|
||||
// :has() only allows a limited set of pseudo-elements inside it, which doesn't include unknown ones.
|
||||
if (m_pseudo_class_context.contains_slow(PseudoClass::Has))
|
||||
return ParseError::SyntaxError;
|
||||
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::PseudoElement,
|
||||
// Unknown -webkit- pseudo-elements must be serialized in ASCII lowercase.
|
||||
|
@ -470,6 +479,11 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
|||
case Selector::PseudoElement::Type::Before:
|
||||
case Selector::PseudoElement::Type::FirstLetter:
|
||||
case Selector::PseudoElement::Type::FirstLine:
|
||||
// :has() is fussy about pseudo-elements inside it
|
||||
if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(pseudo_element->type())) {
|
||||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::PseudoElement,
|
||||
.value = pseudo_element.value()
|
||||
|
@ -545,6 +559,16 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
|||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
// "The :has() pseudo-class cannot be nested; :has() is not valid within :has()."
|
||||
// https://drafts.csswg.org/selectors/#relational
|
||||
if (pseudo_class == PseudoClass::Has && m_pseudo_class_context.contains_slow(PseudoClass::Has)) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, ":has() is not allowed inside :has()");
|
||||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
m_pseudo_class_context.append(pseudo_class);
|
||||
ScopeGuard guard = [&] { m_pseudo_class_context.take_last(); };
|
||||
|
||||
switch (metadata.parameter_type) {
|
||||
case PseudoClassMetadata::ParameterType::ANPlusB:
|
||||
return parse_nth_child_selector(pseudo_class, pseudo_function.value, false);
|
||||
|
|
|
@ -687,4 +687,11 @@ SelectorList adapt_nested_relative_selector_list(SelectorList const& selectors)
|
|||
return new_list;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/selectors/#has-allowed-pseudo-element
|
||||
bool is_has_allowed_pseudo_element(Selector::PseudoElement::Type)
|
||||
{
|
||||
// No spec currently defines any pseudo-elements that are allowed in :has()
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -275,6 +275,8 @@ String serialize_a_group_of_selectors(SelectorList const& selectors);
|
|||
|
||||
SelectorList adapt_nested_relative_selector_list(SelectorList const&);
|
||||
|
||||
bool is_has_allowed_pseudo_element(Selector::PseudoElement::Type);
|
||||
|
||||
}
|
||||
|
||||
namespace AK {
|
||||
|
|
|
@ -1 +1,6 @@
|
|||
:has(:yakthonk) should be invalid: PASS
|
||||
:has(:not(:yakthonk)) should be invalid: PASS
|
||||
:has(:has(span)) should be invalid: PASS
|
||||
:has(:not(:has(span))) should be invalid: PASS
|
||||
:has(::before) should be invalid: PASS
|
||||
:has(:not(::before)) should be invalid: PASS
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
test(() => {
|
||||
let selectors = [
|
||||
":has(:yakthonk)",
|
||||
":has(:not(:yakthonk))",
|
||||
":has(:has(span))",
|
||||
":has(:not(:has(span)))",
|
||||
":has(::before)",
|
||||
":has(:not(::before))",
|
||||
];
|
||||
|
||||
let style = document.getElementById("style");
|
||||
|
|
Loading…
Reference in a new issue