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.
This commit is contained in:
Sam Atkins 2021-07-12 16:34:18 +01:00 committed by Andreas Kling
parent 4af7d41879
commit 8cae79cc8d
Notes: sideshowbarker 2024-07-18 09:04:18 +09:00
4 changed files with 66 additions and 12 deletions

View file

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

View file

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

View file

@ -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();
}

View file

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