1
Fork 0
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:
Sam Atkins 2022-02-26 13:53:13 +00:00 committed by Andreas Kling
parent 2e23cce557
commit a57128467a
5 changed files with 57 additions and 18 deletions

View file

@ -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;

View file

@ -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:

View file

@ -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,

View file

@ -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) {

View file

@ -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;