mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 20:57:41 +00:00
LibWeb: Implement the :is() selector
This lets us finally get rid of a FIXME in the default style sheet. :^)
This commit is contained in:
parent
88d218721b
commit
c148ed50bb
6 changed files with 37 additions and 21 deletions
|
@ -213,18 +213,11 @@ ol {
|
||||||
list-style-type: decimal;
|
list-style-type: decimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: Implement these using :is() :^) */
|
:is(ul, ol) ul {
|
||||||
/* :is(ul, ol) ul */
|
|
||||||
ul ul,
|
|
||||||
ol ul {
|
|
||||||
list-style-type: circle;
|
list-style-type: circle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :is(ul, ol) :is(ul, ol) ul */
|
:is(ul, ol) :is(ul, ol) ul {
|
||||||
ul ul ul,
|
|
||||||
ol ul ul,
|
|
||||||
ul ol ul,
|
|
||||||
ol ol ul {
|
|
||||||
list-style-type: square;
|
list-style-type: square;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -584,7 +584,14 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
|
||||||
};
|
};
|
||||||
|
|
||||||
auto& pseudo_function = pseudo_class_token.function();
|
auto& pseudo_function = pseudo_class_token.function();
|
||||||
if (pseudo_function.name().equals_ignoring_case("not")) {
|
if (pseudo_function.name().equals_ignoring_case("is"sv)) {
|
||||||
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Is;
|
||||||
|
auto function_token_stream = TokenStream(pseudo_function.values());
|
||||||
|
auto is_selector = parse_a_selector_list(function_token_stream, SelectorParsingMode::Forgiving);
|
||||||
|
// NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list.
|
||||||
|
VERIFY(!is_selector.is_error());
|
||||||
|
simple_selector.pseudo_class.argument_selector_list = is_selector.release_value();
|
||||||
|
} else if (pseudo_function.name().equals_ignoring_case("not"sv)) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
|
||||||
auto function_token_stream = TokenStream(pseudo_function.values());
|
auto function_token_stream = TokenStream(pseudo_function.values());
|
||||||
auto not_selector = parse_a_selector_list(function_token_stream);
|
auto not_selector = parse_a_selector_list(function_token_stream);
|
||||||
|
@ -592,20 +599,20 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Invalid selector in :not() clause");
|
dbgln_if(CSS_PARSER_DEBUG, "Invalid selector in :not() clause");
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
}
|
}
|
||||||
simple_selector.pseudo_class.not_selector = not_selector.release_value();
|
simple_selector.pseudo_class.argument_selector_list = not_selector.release_value();
|
||||||
} else if (pseudo_function.name().equals_ignoring_case("nth-child")) {
|
} else if (pseudo_function.name().equals_ignoring_case("nth-child"sv)) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild;
|
||||||
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
} else if (pseudo_function.name().equals_ignoring_case("nth-last-child")) {
|
} else if (pseudo_function.name().equals_ignoring_case("nth-last-child"sv)) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild;
|
||||||
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
} else if (pseudo_function.name().equals_ignoring_case("nth-of-type")) {
|
} else if (pseudo_function.name().equals_ignoring_case("nth-of-type"sv)) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthOfType;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthOfType;
|
||||||
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
} else if (pseudo_function.name().equals_ignoring_case("nth-last-of-type")) {
|
} else if (pseudo_function.name().equals_ignoring_case("nth-last-of-type"sv)) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastOfType;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastOfType;
|
||||||
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
|
|
|
@ -158,6 +158,7 @@ String Selector::SimpleSelector::serialize() const
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
case Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Not:
|
case Selector::SimpleSelector::PseudoClass::Type::Not:
|
||||||
|
case Selector::SimpleSelector::PseudoClass::Type::Is:
|
||||||
// Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028),
|
// Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028),
|
||||||
// followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s.
|
// followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s.
|
||||||
s.append(':');
|
s.append(':');
|
||||||
|
@ -167,9 +168,11 @@ String Selector::SimpleSelector::serialize() const
|
||||||
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastChild) {
|
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastChild) {
|
||||||
// The result of serializing the value using the rules to serialize an <an+b> value.
|
// The result of serializing the value using the rules to serialize an <an+b> value.
|
||||||
s.append(pseudo_class.nth_child_pattern.serialize());
|
s.append(pseudo_class.nth_child_pattern.serialize());
|
||||||
} else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Not) {
|
} else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Not
|
||||||
|
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Is) {
|
||||||
// The result of serializing the value using the rules for serializing a group of selectors.
|
// The result of serializing the value using the rules for serializing a group of selectors.
|
||||||
s.append(serialize_a_group_of_selectors(pseudo_class.not_selector));
|
// NOTE: `:is()` isn't in the spec for this yet, but it should be!
|
||||||
|
s.append(serialize_a_group_of_selectors(pseudo_class.argument_selector_list));
|
||||||
}
|
}
|
||||||
s.append(')');
|
s.append(')');
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -75,6 +75,7 @@ public:
|
||||||
Disabled,
|
Disabled,
|
||||||
Enabled,
|
Enabled,
|
||||||
Checked,
|
Checked,
|
||||||
|
Is,
|
||||||
Not,
|
Not,
|
||||||
Active,
|
Active,
|
||||||
};
|
};
|
||||||
|
@ -84,7 +85,7 @@ public:
|
||||||
// Only used when "pseudo_class" is "NthChild" or "NthLastChild".
|
// Only used when "pseudo_class" is "NthChild" or "NthLastChild".
|
||||||
ANPlusBPattern nth_child_pattern;
|
ANPlusBPattern nth_child_pattern;
|
||||||
|
|
||||||
SelectorList not_selector {};
|
SelectorList argument_selector_list {};
|
||||||
};
|
};
|
||||||
PseudoClass pseudo_class {};
|
PseudoClass pseudo_class {};
|
||||||
PseudoElement pseudo_element { PseudoElement::None };
|
PseudoElement pseudo_element { PseudoElement::None };
|
||||||
|
@ -211,6 +212,8 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty
|
||||||
return "nth-child"sv;
|
return "nth-child"sv;
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
||||||
return "nth-last-child"sv;
|
return "nth-last-child"sv;
|
||||||
|
case Selector::SimpleSelector::PseudoClass::Type::Is:
|
||||||
|
return "is"sv;
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Not:
|
case Selector::SimpleSelector::PseudoClass::Type::Not:
|
||||||
return "not"sv;
|
return "not"sv;
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::None:
|
case Selector::SimpleSelector::PseudoClass::Type::None:
|
||||||
|
|
|
@ -154,8 +154,14 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
return true;
|
return true;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked:
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked:
|
||||||
return matches_checked_pseudo_class(element);
|
return matches_checked_pseudo_class(element);
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::Is:
|
||||||
|
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||||
|
if (matches(selector, element))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
|
||||||
for (auto& selector : pseudo_class.not_selector) {
|
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||||
if (matches(selector, element))
|
if (matches(selector, element))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -430,12 +430,16 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
|
||||||
pseudo_class_description = "Not";
|
pseudo_class_description = "Not";
|
||||||
break;
|
break;
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::Is:
|
||||||
|
pseudo_class_description = "Is";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.appendff(" pseudo_class={}", pseudo_class_description);
|
builder.appendff(" pseudo_class={}", pseudo_class_description);
|
||||||
if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Not) {
|
if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Not
|
||||||
|
|| pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Is) {
|
||||||
builder.append("([");
|
builder.append("([");
|
||||||
for (auto& selector : pseudo_class.not_selector)
|
for (auto& selector : pseudo_class.argument_selector_list)
|
||||||
dump_selector(builder, selector);
|
dump_selector(builder, selector);
|
||||||
builder.append("])");
|
builder.append("])");
|
||||||
} else if ((pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild)
|
} else if ((pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue