summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@gmail.com>2021-07-12 16:18:00 +0100
committerAndreas Kling <kling@serenityos.org>2021-07-14 13:31:00 +0200
commit4af7d418795233e9457c12ab4fd674b445d35346 (patch)
tree01b335491d778644fcab4e85c23851727c7b77aa /Userland
parent96b2356cbb187424ca3864fb1ca1095275d8ea61 (diff)
downloadserenity-4af7d418795233e9457c12ab4fd674b445d35346.zip
LibWeb: Add 'PseudoClass' as a CSS SimpleSelector::Type
Same reasoning as the previous commit.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp51
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp68
-rw-r--r--Userland/Libraries/LibWeb/CSS/Selector.cpp2
-rw-r--r--Userland/Libraries/LibWeb/CSS/Selector.h73
-rw-r--r--Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp135
-rw-r--r--Userland/Libraries/LibWeb/Dump.cpp135
6 files changed, 230 insertions, 234 deletions
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<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;
};
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<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();
}
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 = "";