summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@gmail.com>2021-07-12 16:34:18 +0100
committerAndreas Kling <kling@serenityos.org>2021-07-14 13:31:00 +0200
commit8cae79cc8de78c510f25a4f86ac4da00f5640590 (patch)
treeaadb26fa32cb8694f86b8d38b6b73bac8d5487f7
parent4af7d418795233e9457c12ab4fd674b445d35346 (diff)
downloadserenity-8cae79cc8de78c510f25a4f86ac4da00f5640590.zip
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.
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp38
-rw-r--r--Userland/Libraries/LibWeb/CSS/Selector.h3
-rw-r--r--Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp11
-rw-r--r--Userland/Libraries/LibWeb/Dump.cpp26
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<Selector> Parser::parse_single_selector(TokenStream<T>& 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<Selector> Parser::parse_single_selector(TokenStream<T>& 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 = "";