mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 12:37:43 +00:00
LibWeb: Implement :nth-of-type and :nth-last-of-type selectors :^)
This commit is contained in:
parent
2e23cce557
commit
a57128467a
5 changed files with 57 additions and 18 deletions
|
@ -585,6 +585,18 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
|
||||||
|
|
||||||
} else if (pseudo_class_token.is_function()) {
|
} else if (pseudo_class_token.is_function()) {
|
||||||
|
|
||||||
|
auto parse_nth_child_pattern = [this](auto& simple_selector, auto& pseudo_function) -> bool {
|
||||||
|
auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values());
|
||||||
|
auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values);
|
||||||
|
if (nth_child_pattern.has_value()) {
|
||||||
|
simple_selector.pseudo_class.nth_child_pattern = nth_child_pattern.value();
|
||||||
|
} else {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid format for {}", pseudo_function.name());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
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("not")) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not;
|
||||||
|
@ -597,24 +609,20 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
|
||||||
simple_selector.pseudo_class.not_selector = not_selector.release_value();
|
simple_selector.pseudo_class.not_selector = not_selector.release_value();
|
||||||
} else if (pseudo_function.name().equals_ignoring_case("nth-child")) {
|
} else if (pseudo_function.name().equals_ignoring_case("nth-child")) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild;
|
||||||
auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values());
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values);
|
|
||||||
if (nth_child_pattern.has_value()) {
|
|
||||||
simple_selector.pseudo_class.nth_child_pattern = nth_child_pattern.value();
|
|
||||||
} else {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid nth-child format");
|
|
||||||
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")) {
|
||||||
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild;
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild;
|
||||||
auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values());
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values);
|
return ParsingResult::SyntaxError;
|
||||||
if (nth_child_pattern.has_value()) {
|
} else if (pseudo_function.name().equals_ignoring_case("nth-of-type")) {
|
||||||
simple_selector.pseudo_class.nth_child_pattern = nth_child_pattern.value();
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthOfType;
|
||||||
} else {
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid nth-child format");
|
return ParsingResult::SyntaxError;
|
||||||
|
} else if (pseudo_function.name().equals_ignoring_case("nth-last-of-type")) {
|
||||||
|
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastOfType;
|
||||||
|
if (!parse_nth_child_pattern(simple_selector, pseudo_function))
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
|
dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
|
||||||
return ParsingResult::SyntaxError;
|
return ParsingResult::SyntaxError;
|
||||||
|
|
|
@ -307,6 +307,10 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty
|
||||||
return "last-of-type"sv;
|
return "last-of-type"sv;
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
|
case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
|
||||||
return "only-of-type"sv;
|
return "only-of-type"sv;
|
||||||
|
case Selector::SimpleSelector::PseudoClass::Type::NthOfType:
|
||||||
|
return "nth-of-type"sv;
|
||||||
|
case Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
|
||||||
|
return "nth-last-of-type"sv;
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Disabled:
|
case Selector::SimpleSelector::PseudoClass::Type::Disabled:
|
||||||
return "disabled"sv;
|
return "disabled"sv;
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Enabled:
|
case Selector::SimpleSelector::PseudoClass::Type::Enabled:
|
||||||
|
|
|
@ -62,13 +62,15 @@ public:
|
||||||
FirstChild,
|
FirstChild,
|
||||||
LastChild,
|
LastChild,
|
||||||
OnlyChild,
|
OnlyChild,
|
||||||
|
NthChild,
|
||||||
|
NthLastChild,
|
||||||
Empty,
|
Empty,
|
||||||
Root,
|
Root,
|
||||||
FirstOfType,
|
FirstOfType,
|
||||||
LastOfType,
|
LastOfType,
|
||||||
OnlyOfType,
|
OnlyOfType,
|
||||||
NthChild,
|
NthOfType,
|
||||||
NthLastChild,
|
NthLastOfType,
|
||||||
Disabled,
|
Disabled,
|
||||||
Enabled,
|
Enabled,
|
||||||
Checked,
|
Checked,
|
||||||
|
|
|
@ -142,6 +142,8 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
return true;
|
return true;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType:
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
|
||||||
auto const step_size = pseudo_class.nth_child_pattern.step_size;
|
auto const step_size = pseudo_class.nth_child_pattern.step_size;
|
||||||
auto const offset = pseudo_class.nth_child_pattern.offset;
|
auto const offset = pseudo_class.nth_child_pattern.offset;
|
||||||
if (step_size == 0 && offset == 0)
|
if (step_size == 0 && offset == 0)
|
||||||
|
@ -152,12 +154,29 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int index = 1;
|
int index = 1;
|
||||||
if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild) {
|
switch (pseudo_class.type) {
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild: {
|
||||||
for (auto* child = parent->first_child_of_type<DOM::Element>(); child && child != &element; child = child->next_element_sibling())
|
for (auto* child = parent->first_child_of_type<DOM::Element>(); child && child != &element; child = child->next_element_sibling())
|
||||||
++index;
|
++index;
|
||||||
} else {
|
break;
|
||||||
|
}
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild: {
|
||||||
for (auto* child = parent->last_child_of_type<DOM::Element>(); child && child != &element; child = child->previous_element_sibling())
|
for (auto* child = parent->last_child_of_type<DOM::Element>(); child && child != &element; child = child->previous_element_sibling())
|
||||||
++index;
|
++index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType: {
|
||||||
|
for (auto* child = previous_sibling_with_same_tag_name(element); child; child = previous_sibling_with_same_tag_name(*child))
|
||||||
|
++index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType: {
|
||||||
|
for (auto* child = next_sibling_with_same_tag_name(element); child; child = next_sibling_with_same_tag_name(*child))
|
||||||
|
++index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step_size < 0) {
|
if (step_size < 0) {
|
||||||
|
|
|
@ -385,6 +385,12 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
|
||||||
pseudo_class_description = "OnlyOfType";
|
pseudo_class_description = "OnlyOfType";
|
||||||
break;
|
break;
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType:
|
||||||
|
pseudo_class_description = "NthOfType";
|
||||||
|
break;
|
||||||
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
|
||||||
|
pseudo_class_description = "NthLastOfType";
|
||||||
|
break;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
||||||
pseudo_class_description = "NthChild";
|
pseudo_class_description = "NthChild";
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue