From 8cae79cc8de78c510f25a4f86ac4da00f5640590 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Mon, 12 Jul 2021 16:34:18 +0100 Subject: [PATCH] LibWeb: Add 'PseudoElement' as a CSS SimpleSelector::Type Same reasoning again! This is the last one. While I was at it, I added the two remaining CSS2.2 pseudo-elements, ::first-line and ::first-letter. All 4 are handled in the new CSS parser, including with the compatibility single-colon syntax. I have not added support to the old parser. --- .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 38 +++++++++++++++++-- Userland/Libraries/LibWeb/CSS/Selector.h | 3 ++ .../Libraries/LibWeb/CSS/SelectorEngine.cpp | 11 ++---- Userland/Libraries/LibWeb/Dump.cpp | 26 +++++++++++++ 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 8c03bf3da0..bbf5e49cfb 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -363,10 +363,24 @@ Optional Parser::parse_single_selector(TokenStream& tokens, bool is return {}; } - // Ignore for now, otherwise we produce a "false positive" selector - // and apply styles to the element itself, not its pseudo element - if (is_pseudo) - return {}; + if (is_pseudo) { + auto pseudo_name = ((Token)current_value).ident(); + simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; + + 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("first-line")) { + simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLine; + } else if (pseudo_name.equals_ignoring_case("first-letter")) { + simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLetter; + } else { + return {}; + } + + return simple_selector; + } auto& pseudo_class = simple_selector.pseudo_class; @@ -411,6 +425,22 @@ Optional Parser::parse_single_selector(TokenStream& tokens, bool is pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Enabled; } else if (pseudo_name.equals_ignoring_case("checked")) { pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Checked; + } else if (pseudo_name.equals_ignoring_case("before")) { + // Single-colon syntax allowed for compatibility. https://www.w3.org/TR/selectors/#pseudo-element-syntax + simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; + simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::Before; + } else if (pseudo_name.equals_ignoring_case("after")) { + // See :before + simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; + simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::After; + } else if (pseudo_name.equals_ignoring_case("first-line")) { + // See :before + simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; + simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLine; + } else if (pseudo_name.equals_ignoring_case("first-letter")) { + // See :before + simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; + simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLetter; } else { dbgln("Unknown pseudo class: '{}'", pseudo_name); return simple_selector; diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h index 2ad7177719..d9cb4c710b 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.h +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -24,6 +24,7 @@ public: Class, Attribute, PseudoClass, + PseudoElement, }; Type type { Type::Invalid }; @@ -71,6 +72,8 @@ public: None, Before, After, + FirstLine, + FirstLetter, }; PseudoElement pseudo_element { PseudoElement::None }; diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp index 0296aba1d6..f94d8c425d 100644 --- a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -175,14 +175,6 @@ static bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass cons 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) { case CSS::Selector::SimpleSelector::Type::Universal: return true; @@ -196,6 +188,9 @@ static bool matches(CSS::Selector::SimpleSelector const& component, DOM::Element return matches_attribute(component.attribute, element); case CSS::Selector::SimpleSelector::Type::PseudoClass: return matches_pseudo_class(component.pseudo_class, element); + case CSS::Selector::SimpleSelector::Type::PseudoElement: + // FIXME: Implement pseudo-elements. + return false; default: VERIFY_NOT_REACHED(); } diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp index bcbb61ba02..1bb330dca6 100644 --- a/Userland/Libraries/LibWeb/Dump.cpp +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -320,6 +320,9 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector) case CSS::Selector::SimpleSelector::Type::PseudoClass: type_description = "PseudoClass"; break; + case CSS::Selector::SimpleSelector::Type::PseudoElement: + type_description = "PseudoElement"; + break; } builder.appendff("{}:{}", type_description, simple_selector.value); @@ -397,6 +400,29 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector) } } + if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) { + char const* pseudo_element_description = ""; + switch (simple_selector.pseudo_element) { + case CSS::Selector::SimpleSelector::PseudoElement::None: + pseudo_element_description = "None"; + break; + case CSS::Selector::SimpleSelector::PseudoElement::Before: + pseudo_element_description = "before"; + break; + case CSS::Selector::SimpleSelector::PseudoElement::After: + pseudo_element_description = "after"; + break; + case CSS::Selector::SimpleSelector::PseudoElement::FirstLine: + pseudo_element_description = "first-line"; + break; + case CSS::Selector::SimpleSelector::PseudoElement::FirstLetter: + pseudo_element_description = "first-letter"; + break; + } + + builder.appendff(" pseudo_element={}", pseudo_element_description); + } + if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Attribute) { char const* attribute_match_type_description = "";