mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 19:37:35 +00:00
LibWeb: Add 'PseudoClass' as a CSS SimpleSelector::Type
Same reasoning as the previous commit.
This commit is contained in:
parent
96b2356cbb
commit
4af7d41879
6 changed files with 230 additions and 234 deletions
|
@ -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 {};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -23,10 +23,19 @@ public:
|
|||
Id,
|
||||
Class,
|
||||
Attribute,
|
||||
PseudoClass,
|
||||
};
|
||||
Type type { Type::Invalid };
|
||||
|
||||
enum class PseudoClass {
|
||||
struct NthChildPattern {
|
||||
int step_size = 0;
|
||||
int offset = 0;
|
||||
|
||||
static NthChildPattern parse(StringView const& args);
|
||||
};
|
||||
|
||||
struct PseudoClass {
|
||||
enum class Type {
|
||||
None,
|
||||
Link,
|
||||
Visited,
|
||||
|
@ -47,7 +56,16 @@ public:
|
|||
Not,
|
||||
Active,
|
||||
};
|
||||
PseudoClass pseudo_class { PseudoClass::None };
|
||||
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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 = "";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue