From 4af7d418795233e9457c12ab4fd674b445d35346 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Mon, 12 Jul 2021 16:18:00 +0100 Subject: LibWeb: Add 'PseudoClass' as a CSS SimpleSelector::Type Same reasoning as the previous commit. --- .../LibWeb/CSS/Parser/DeprecatedCSSParser.cpp | 51 ++++---- Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 68 +++++------ Userland/Libraries/LibWeb/CSS/Selector.cpp | 2 + Userland/Libraries/LibWeb/CSS/Selector.h | 73 +++++------ Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp | 135 +++++++++------------ Userland/Libraries/LibWeb/Dump.cpp | 135 +++++++++++---------- 6 files changed, 230 insertions(+), 234 deletions(-) (limited to 'Userland/Libraries') diff --git a/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp index 1670570061..513d6d6bc7 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp @@ -442,12 +442,15 @@ public: simple_selector.type = CSS::Selector::SimpleSelector::Type::TagName; } else if (peek() == '[') { simple_selector.type = CSS::Selector::SimpleSelector::Type::Attribute; + } else if (peek() == ':') { + simple_selector.type = CSS::Selector::SimpleSelector::Type::PseudoClass; } else { simple_selector.type = CSS::Selector::SimpleSelector::Type::Universal; } if ((simple_selector.type != CSS::Selector::SimpleSelector::Type::Universal) - && (simple_selector.type != CSS::Selector::SimpleSelector::Type::Attribute)) { + && (simple_selector.type != CSS::Selector::SimpleSelector::Type::Attribute) + && (simple_selector.type != CSS::Selector::SimpleSelector::Type::PseudoClass)) { while (is_valid_selector_char(peek())) buffer.append(consume_one()); @@ -515,7 +518,7 @@ public: return {}; } - if (peek() == ':') { + if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) { // FIXME: Implement pseudo elements. [[maybe_unused]] bool is_pseudo_element = false; consume_one(); @@ -559,49 +562,51 @@ public: if (is_pseudo_element) return {}; + auto& pseudo_class = simple_selector.pseudo_class; + if (pseudo_name.equals_ignoring_case("link")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Link; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Link; } else if (pseudo_name.equals_ignoring_case("visited")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Visited; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Visited; } else if (pseudo_name.equals_ignoring_case("active")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Active; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Active; } else if (pseudo_name.equals_ignoring_case("hover")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Hover; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Hover; } else if (pseudo_name.equals_ignoring_case("focus")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Focus; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Focus; } else if (pseudo_name.equals_ignoring_case("first-child")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::FirstChild; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::FirstChild; } else if (pseudo_name.equals_ignoring_case("last-child")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::LastChild; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::LastChild; } else if (pseudo_name.equals_ignoring_case("only-child")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::OnlyChild; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyChild; } else if (pseudo_name.equals_ignoring_case("empty")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Empty; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Empty; } else if (pseudo_name.equals_ignoring_case("root")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Root; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Root; } else if (pseudo_name.equals_ignoring_case("first-of-type")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::FirstOfType; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType; } else if (pseudo_name.equals_ignoring_case("last-of-type")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::LastOfType; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType; } else if (pseudo_name.starts_with("nth-child", CaseSensitivity::CaseInsensitive)) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::NthChild; - simple_selector.nth_child_pattern = CSS::Selector::SimpleSelector::NthChildPattern::parse(capture_selector_args(pseudo_name)); + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild; + pseudo_class.nth_child_pattern = CSS::Selector::SimpleSelector::NthChildPattern::parse(capture_selector_args(pseudo_name)); } else if (pseudo_name.starts_with("nth-last-child", CaseSensitivity::CaseInsensitive)) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::NthLastChild; - simple_selector.nth_child_pattern = CSS::Selector::SimpleSelector::NthChildPattern::parse(capture_selector_args(pseudo_name)); + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild; + pseudo_class.nth_child_pattern = CSS::Selector::SimpleSelector::NthChildPattern::parse(capture_selector_args(pseudo_name)); } else if (pseudo_name.equals_ignoring_case("before")) { simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::Before; } else if (pseudo_name.equals_ignoring_case("after")) { simple_selector.pseudo_element = CSS::Selector::SimpleSelector::PseudoElement::After; } else if (pseudo_name.equals_ignoring_case("disabled")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Disabled; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled; } else if (pseudo_name.equals_ignoring_case("enabled")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Enabled; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Enabled; } else if (pseudo_name.equals_ignoring_case("checked")) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Checked; + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Checked; } else if (pseudo_name.starts_with("not", CaseSensitivity::CaseInsensitive)) { - simple_selector.pseudo_class = CSS::Selector::SimpleSelector::PseudoClass::Not; - simple_selector.not_selector = capture_selector_args(pseudo_name); + pseudo_class.type = CSS::Selector::SimpleSelector::PseudoClass::Type::Not; + pseudo_class.not_selector = capture_selector_args(pseudo_name); } else { dbgln("Unknown pseudo class: '{}'", pseudo_name); return {}; diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 1af0849898..8c03bf3da0 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -270,10 +270,6 @@ Optional Parser::parse_single_selector(TokenStream& tokens, bool is } else if (current_value.is(Token::Type::Ident)) { simple_selector.type = Selector::SimpleSelector::Type::TagName; simple_selector.value = current_value.token().ident().to_lowercase_string(); - } else if ((current_value.is(Token::Type::Delim) && current_value.token().delim() == ":")) { - // FIXME: This is a temporary hack until we make the Selector::SimpleSelector::Type changes. - simple_selector.type = Selector::SimpleSelector::Type::Universal; - tokens.reconsume_current_input_token(); } else if (current_value.is_block() && current_value.block().is_square()) { simple_selector.type = Selector::SimpleSelector::Type::Attribute; @@ -353,17 +349,7 @@ Optional Parser::parse_single_selector(TokenStream& tokens, bool is attribute.value = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string(); // FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case - } else { - dbgln("Invalid simple selector!"); - return {}; - } - - current_value = tokens.next_token(); - if (check_for_eof_or_whitespace(current_value)) - return simple_selector; - - // FIXME: Pseudo-class selectors want to be their own Selector::SimpleSelector::Type according to the spec. - if (current_value.is(Token::Type::Colon)) { + } else if (current_value.is(Token::Type::Colon)) { bool is_pseudo = false; current_value = tokens.next_token(); @@ -382,46 +368,49 @@ Optional Parser::parse_single_selector(TokenStream& tokens, bool is if (is_pseudo) return {}; + auto& pseudo_class = simple_selector.pseudo_class; + current_value = tokens.next_token(); if (check_for_eof_or_whitespace(current_value)) - return simple_selector; + return {}; + simple_selector.type = Selector::SimpleSelector::Type::PseudoClass; if (current_value.is(Token::Type::Ident)) { auto pseudo_name = ((Token)current_value).ident(); if (pseudo_name.equals_ignoring_case("link")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Link; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Link; } else if (pseudo_name.equals_ignoring_case("visited")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Visited; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Visited; } else if (pseudo_name.equals_ignoring_case("active")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Active; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Active; } else if (pseudo_name.equals_ignoring_case("hover")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Hover; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Hover; } else if (pseudo_name.equals_ignoring_case("focus")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Focus; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Focus; } else if (pseudo_name.equals_ignoring_case("first-child")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::FirstChild; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::FirstChild; } else if (pseudo_name.equals_ignoring_case("last-child")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::LastChild; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::LastChild; } else if (pseudo_name.equals_ignoring_case("only-child")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::OnlyChild; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::OnlyChild; } else if (pseudo_name.equals_ignoring_case("empty")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Empty; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Empty; } else if (pseudo_name.equals_ignoring_case("root")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Root; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Root; } else if (pseudo_name.equals_ignoring_case("first-of-type")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::FirstOfType; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::FirstOfType; } else if (pseudo_name.equals_ignoring_case("last-of-type")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::LastOfType; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::LastOfType; } else if (pseudo_name.equals_ignoring_case("before")) { simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::Before; } else if (pseudo_name.equals_ignoring_case("after")) { simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::After; } else if (pseudo_name.equals_ignoring_case("disabled")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Disabled; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Disabled; } else if (pseudo_name.equals_ignoring_case("enabled")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Enabled; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Enabled; } else if (pseudo_name.equals_ignoring_case("checked")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Checked; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Checked; } else { dbgln("Unknown pseudo class: '{}'", pseudo_name); return simple_selector; @@ -429,28 +418,28 @@ Optional Parser::parse_single_selector(TokenStream& tokens, bool is } else if (current_value.is(Token::Type::Function)) { auto& pseudo_function = current_value.function(); if (pseudo_function.name().equals_ignoring_case("nth-child")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::NthChild; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild; auto function_values = TokenStream(pseudo_function.values()); auto nth_child_pattern = parse_nth_child_pattern(function_values); if (nth_child_pattern.has_value()) { - simple_selector.nth_child_pattern = nth_child_pattern.value(); + pseudo_class.nth_child_pattern = nth_child_pattern.value(); } else { dbgln("Invalid nth-child format"); return {}; } } else if (pseudo_function.name().equals_ignoring_case("nth-last-child")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::NthLastChild; + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild; auto function_values = TokenStream(pseudo_function.values()); auto nth_child_pattern = parse_nth_child_pattern(function_values); if (nth_child_pattern.has_value()) { - simple_selector.nth_child_pattern = nth_child_pattern.value(); + pseudo_class.nth_child_pattern = nth_child_pattern.value(); } else { dbgln("Invalid nth-child format"); return {}; } } else if (pseudo_function.name().equals_ignoring_case("not")) { - simple_selector.pseudo_class = Selector::SimpleSelector::PseudoClass::Not; - simple_selector.not_selector = pseudo_function.values_as_string(); + pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not; + pseudo_class.not_selector = pseudo_function.values_as_string(); } else { dbgln("Unknown pseudo class: '{}'()", pseudo_function.name()); return {}; @@ -459,10 +448,11 @@ Optional Parser::parse_single_selector(TokenStream& tokens, bool is dbgln("Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", current_value.to_debug_string()); return {}; } + } else { + dbgln("Invalid simple selector!"); + return {}; } - tokens.reconsume_current_input_token(); - return simple_selector; }; diff --git a/Userland/Libraries/LibWeb/CSS/Selector.cpp b/Userland/Libraries/LibWeb/CSS/Selector.cpp index 896534daf4..aac8481329 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.cpp +++ b/Userland/Libraries/LibWeb/CSS/Selector.cpp @@ -49,6 +49,8 @@ u32 Selector::specificity() const Selector::SimpleSelector::NthChildPattern Selector::SimpleSelector::NthChildPattern::parse(StringView const& args) { + // FIXME: Remove this when the DeprecatedCSSParser is gone. + // The new Parser::parse_nth_child_pattern() does the same as this, using Tokens. CSS::Selector::SimpleSelector::NthChildPattern pattern; if (args.equals_ignoring_case("odd")) { pattern.step_size = 2; diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h index ab19ce156d..2ad7177719 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.h +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -23,31 +23,49 @@ public: Id, Class, Attribute, + PseudoClass, }; Type type { Type::Invalid }; - enum class PseudoClass { - None, - Link, - Visited, - Hover, - Focus, - FirstChild, - LastChild, - OnlyChild, - Empty, - Root, - FirstOfType, - LastOfType, - NthChild, - NthLastChild, - Disabled, - Enabled, - Checked, - Not, - Active, + struct NthChildPattern { + int step_size = 0; + int offset = 0; + + static NthChildPattern parse(StringView const& args); + }; + + struct PseudoClass { + enum class Type { + None, + Link, + Visited, + Hover, + Focus, + FirstChild, + LastChild, + OnlyChild, + Empty, + Root, + FirstOfType, + LastOfType, + NthChild, + NthLastChild, + Disabled, + Enabled, + Checked, + Not, + Active, + }; + Type type { Type::None }; + + // FIXME: We don't need this field on every single SimpleSelector, but it's also annoying to malloc it somewhere. + // Only used when "pseudo_class" is "NthChild" or "NthLastChild". + NthChildPattern nth_child_pattern; + + // FIXME: This wants to be a Selector, rather than parsing it each time it is used. + String not_selector {}; }; - PseudoClass pseudo_class { PseudoClass::None }; + PseudoClass pseudo_class; enum class PseudoElement { None, @@ -74,19 +92,6 @@ public: String value; }; Attribute attribute; - - struct NthChildPattern { - int step_size = 0; - int offset = 0; - - static NthChildPattern parse(StringView const& args); - }; - - // FIXME: We don't need this field on every single SimpleSelector, but it's also annoying to malloc it somewhere. - // Only used when "pseudo_class" is "NthChild" or "NthLastChild". - NthChildPattern nth_child_pattern; - // FIXME: This wants to be a Selector, rather than parsing it each time it is used. - String not_selector {}; }; struct ComplexSelector { diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp index 2bfeb77b3d..0296aba1d6 100644 --- a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -57,103 +57,75 @@ static bool matches_attribute(CSS::Selector::SimpleSelector::Attribute const& at return false; } -static bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element const& element) +static bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, DOM::Element const& element) { - switch (component.pseudo_element) { - case CSS::Selector::SimpleSelector::PseudoElement::None: - break; - default: - // FIXME: Implement pseudo-elements. - return false; - } - - switch (component.pseudo_class) { - case CSS::Selector::SimpleSelector::PseudoClass::None: - break; - case CSS::Selector::SimpleSelector::PseudoClass::Link: - if (!element.is_link()) - return false; + switch (pseudo_class.type) { + case CSS::Selector::SimpleSelector::PseudoClass::Type::None: break; - case CSS::Selector::SimpleSelector::PseudoClass::Visited: + case CSS::Selector::SimpleSelector::PseudoClass::Type::Link: + return element.is_link(); + case CSS::Selector::SimpleSelector::PseudoClass::Type::Visited: // FIXME: Maybe match this selector sometimes? return false; - case CSS::Selector::SimpleSelector::PseudoClass::Active: - if (!element.is_active()) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Hover: - if (!matches_hover_pseudo_class(element)) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Focus: - if (!element.is_focused()) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::FirstChild: - if (element.previous_element_sibling()) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::LastChild: - if (element.next_element_sibling()) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::OnlyChild: - if (element.previous_element_sibling() || element.next_element_sibling()) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Empty: - if (element.first_child_of_type() || element.first_child_of_type()) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Root: - if (!is(element)) - return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::FirstOfType: + case CSS::Selector::SimpleSelector::PseudoClass::Type::Active: + return element.is_active(); + case CSS::Selector::SimpleSelector::PseudoClass::Type::Hover: + return matches_hover_pseudo_class(element); + case CSS::Selector::SimpleSelector::PseudoClass::Type::Focus: + return element.is_focused(); + case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstChild: + return !element.previous_element_sibling(); + case CSS::Selector::SimpleSelector::PseudoClass::Type::LastChild: + return !element.next_element_sibling(); + case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyChild: + return !(element.previous_element_sibling() || element.next_element_sibling()); + case CSS::Selector::SimpleSelector::PseudoClass::Type::Empty: + return !(element.first_child_of_type() || element.first_child_of_type()); + case CSS::Selector::SimpleSelector::PseudoClass::Type::Root: + return is(element); + case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType: for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) { if (sibling->tag_name() == element.tag_name()) return false; } - break; - case CSS::Selector::SimpleSelector::PseudoClass::LastOfType: + return true; + case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType: for (auto* sibling = element.next_element_sibling(); sibling; sibling = sibling->next_element_sibling()) { if (sibling->tag_name() == element.tag_name()) return false; } - break; - case CSS::Selector::SimpleSelector::PseudoClass::Disabled: + return true; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled: if (!element.tag_name().equals_ignoring_case(HTML::TagNames::input)) return false; if (!element.has_attribute("disabled")) return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Enabled: + return true; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Enabled: if (!element.tag_name().equals_ignoring_case(HTML::TagNames::input)) return false; if (element.has_attribute("disabled")) return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Checked: + return true; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked: if (!element.tag_name().equals_ignoring_case(HTML::TagNames::input)) return false; if (!element.has_attribute("checked")) return false; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Not: { - if (component.not_selector.is_empty()) + return true; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Not: { + if (pseudo_class.not_selector.is_empty()) return false; - auto not_selector = Web::parse_selector(CSS::DeprecatedParsingContext(element), component.not_selector); + auto not_selector = Web::parse_selector(CSS::DeprecatedParsingContext(element), pseudo_class.not_selector); if (!not_selector.has_value()) return false; auto not_matches = matches(not_selector.value(), element); - if (not_matches) - return false; - break; + return !not_matches; } - case CSS::Selector::SimpleSelector::PseudoClass::NthChild: - case CSS::Selector::SimpleSelector::PseudoClass::NthLastChild: - auto const step_size = component.nth_child_pattern.step_size; - auto const offset = component.nth_child_pattern.offset; + case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild: + case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild: + auto const step_size = pseudo_class.nth_child_pattern.step_size; + auto const offset = pseudo_class.nth_child_pattern.offset; if (step_size == 0 && offset == 0) return false; // "If both a and b are equal to zero, the pseudo-class represents no element in the document tree." @@ -162,7 +134,7 @@ static bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element return false; int index = 1; - if (component.pseudo_class == CSS::Selector::SimpleSelector::PseudoClass::NthChild) { + if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild) { for (auto* child = parent->first_child_of_type(); child && child != &element; child = child->next_element_sibling()) ++index; } else { @@ -172,16 +144,10 @@ static bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element if (step_size < 0) { // When "step_size" is negative, selector represents first "offset" elements in document tree. - if (offset <= 0 || index > offset) - return false; - else - break; + return !(offset <= 0 || index > offset); } else if (step_size == 1) { // When "step_size == 1", selector represents last "offset" elements in document tree. - if (offset < 0 || index < offset) - return false; - else - break; + return !(offset < 0 || index < offset); } // Like "a % b", but handles negative integers correctly. @@ -201,7 +167,20 @@ static bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element } else if (canonical_modulo(index - offset, step_size) != 0) { return false; } + return true; + } + + return false; +} + +static bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element const& element) +{ + switch (component.pseudo_element) { + case CSS::Selector::SimpleSelector::PseudoElement::None: break; + default: + // FIXME: Implement pseudo-elements. + return false; } switch (component.type) { @@ -215,6 +194,8 @@ static bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element return component.value == element.local_name(); case CSS::Selector::SimpleSelector::Type::Attribute: return matches_attribute(component.attribute, element); + case CSS::Selector::SimpleSelector::Type::PseudoClass: + return matches_pseudo_class(component.pseudo_class, element); default: VERIFY_NOT_REACHED(); } diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp index 802a91e5f6..bcbb61ba02 100644 --- a/Userland/Libraries/LibWeb/Dump.cpp +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -317,72 +317,85 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector) case CSS::Selector::SimpleSelector::Type::Attribute: type_description = "Attribute"; break; - } - - char const* pseudo_class_description = ""; - switch (simple_selector.pseudo_class) { - case CSS::Selector::SimpleSelector::PseudoClass::Link: - pseudo_class_description = "Link"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Visited: - pseudo_class_description = "Visited"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Active: - pseudo_class_description = "Active"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::None: - pseudo_class_description = "None"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Root: - pseudo_class_description = "Root"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::FirstOfType: - pseudo_class_description = "FirstOfType"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::LastOfType: - pseudo_class_description = "LastOfType"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::NthChild: - pseudo_class_description = "NthChild"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::NthLastChild: - pseudo_class_description = "NthLastChild"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Focus: - pseudo_class_description = "Focus"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Empty: - pseudo_class_description = "Empty"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Hover: - pseudo_class_description = "Hover"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::LastChild: - pseudo_class_description = "LastChild"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::FirstChild: - pseudo_class_description = "FirstChild"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::OnlyChild: - pseudo_class_description = "OnlyChild"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Disabled: - pseudo_class_description = "Disabled"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Enabled: - pseudo_class_description = "Enabled"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Checked: - pseudo_class_description = "Checked"; - break; - case CSS::Selector::SimpleSelector::PseudoClass::Not: - pseudo_class_description = "Not"; + case CSS::Selector::SimpleSelector::Type::PseudoClass: + type_description = "PseudoClass"; break; } builder.appendff("{}:{}", type_description, simple_selector.value); - if (simple_selector.pseudo_class != CSS::Selector::SimpleSelector::PseudoClass::None) + + if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) { + auto const& pseudo_class = simple_selector.pseudo_class; + + char const* pseudo_class_description = ""; + switch (pseudo_class.type) { + case CSS::Selector::SimpleSelector::PseudoClass::Type::Link: + pseudo_class_description = "Link"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Visited: + pseudo_class_description = "Visited"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Active: + pseudo_class_description = "Active"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::None: + pseudo_class_description = "None"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Root: + pseudo_class_description = "Root"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType: + pseudo_class_description = "FirstOfType"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType: + pseudo_class_description = "LastOfType"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild: + pseudo_class_description = "NthChild"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild: + pseudo_class_description = "NthLastChild"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Focus: + pseudo_class_description = "Focus"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Empty: + pseudo_class_description = "Empty"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Hover: + pseudo_class_description = "Hover"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::LastChild: + pseudo_class_description = "LastChild"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstChild: + pseudo_class_description = "FirstChild"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyChild: + pseudo_class_description = "OnlyChild"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled: + pseudo_class_description = "Disabled"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Enabled: + pseudo_class_description = "Enabled"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked: + pseudo_class_description = "Checked"; + break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Not: + pseudo_class_description = "Not"; + break; + } + builder.appendff(" pseudo_class={}", pseudo_class_description); + if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Not) { + builder.appendff("({})", pseudo_class.not_selector); + } else if ((pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild) + || (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild)) { + builder.appendff("(step={}, offset={})", pseudo_class.nth_child_pattern.step_size, pseudo_class.nth_child_pattern.offset); + } + } if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Attribute) { char const* attribute_match_type_description = ""; -- cgit v1.2.3