1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 21:07:35 +00:00

LibWeb: Add 'PseudoClass' as a CSS SimpleSelector::Type

Same reasoning as the previous commit.
This commit is contained in:
Sam Atkins 2021-07-12 16:18:00 +01:00 committed by Andreas Kling
parent 96b2356cbb
commit 4af7d41879
6 changed files with 230 additions and 234 deletions

View file

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

View file

@ -270,10 +270,6 @@ Optional<Selector> Parser::parse_single_selector(TokenStream<T>& 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<Selector> Parser::parse_single_selector(TokenStream<T>& 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<Selector> Parser::parse_single_selector(TokenStream<T>& 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<Selector> Parser::parse_single_selector(TokenStream<T>& 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<StyleComponentValueRule>(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<StyleComponentValueRule>(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<Selector> Parser::parse_single_selector(TokenStream<T>& 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;
};

View file

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

View file

@ -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);
};
PseudoClass pseudo_class { PseudoClass::None };
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;
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 {

View file

@ -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:
switch (pseudo_class.type) {
case CSS::Selector::SimpleSelector::PseudoClass::Type::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;
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<DOM::Element>() || element.first_child_of_type<DOM::Text>())
return false;
break;
case CSS::Selector::SimpleSelector::PseudoClass::Root:
if (!is<HTML::HTMLElement>(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<DOM::Element>() || element.first_child_of_type<DOM::Text>());
case CSS::Selector::SimpleSelector::PseudoClass::Type::Root:
return is<HTML::HTMLElement>(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<DOM::Element>(); 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();
}

View file

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