mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:07:34 +00:00
LibWeb: Use generated PseudoClass data
Everything should behave the same as before.
This commit is contained in:
parent
f76c614a84
commit
b314a115ca
5 changed files with 179 additions and 466 deletions
|
@ -31,6 +31,7 @@
|
||||||
#include <LibWeb/CSS/Parser/Function.h>
|
#include <LibWeb/CSS/Parser/Function.h>
|
||||||
#include <LibWeb/CSS/Parser/Parser.h>
|
#include <LibWeb/CSS/Parser/Parser.h>
|
||||||
#include <LibWeb/CSS/Parser/Rule.h>
|
#include <LibWeb/CSS/Parser/Rule.h>
|
||||||
|
#include <LibWeb/CSS/PseudoClass.h>
|
||||||
#include <LibWeb/CSS/Selector.h>
|
#include <LibWeb/CSS/Selector.h>
|
||||||
#include <LibWeb/CSS/StyleValue.h>
|
#include <LibWeb/CSS/StyleValue.h>
|
||||||
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
|
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
|
||||||
|
@ -532,71 +533,17 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
auto make_pseudo_class_selector = [](auto pseudo_class) {
|
auto make_pseudo_class_selector = [](auto pseudo_class) {
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||||
.value = Selector::SimpleSelector::PseudoClass {
|
.value = Selector::SimpleSelector::PseudoClassSelector { .type = pseudo_class }
|
||||||
.type = pseudo_class }
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("active"sv))
|
if (auto pseudo_class = pseudo_class_from_string(pseudo_name); pseudo_class.has_value()) {
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Active);
|
if (!pseudo_class_metadata(pseudo_class.value()).is_valid_as_identifier) {
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("buffering"sv))
|
dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is only valid as a function", pseudo_name);
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Buffering);
|
return ParseError::SyntaxError;
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("checked"sv))
|
}
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Checked);
|
return make_pseudo_class_selector(pseudo_class.value());
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("indeterminate"sv))
|
}
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Indeterminate);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("defined"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Defined);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("disabled"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Disabled);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("empty"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Empty);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("enabled"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Enabled);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("first-child"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FirstChild);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("first-of-type"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FirstOfType);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("focus"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Focus);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("focus-visible"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FocusVisible);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("focus-within"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FocusWithin);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("hover"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Hover);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("last-child"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::LastChild);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("last-of-type"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::LastOfType);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("link"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Link);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("muted"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Muted);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("only-child"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::OnlyChild);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("only-of-type"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::OnlyOfType);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("playing"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Playing);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("paused"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Paused);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("root"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Root);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("seeking"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Seeking);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("stalled"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Stalled);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("target"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Target);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("host"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Host);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("visited"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Visited);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("volume-locked"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::VolumeLocked);
|
|
||||||
if (pseudo_name.equals_ignoring_ascii_case("scope"sv))
|
|
||||||
return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Scope);
|
|
||||||
|
|
||||||
// Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
|
// Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
|
||||||
// https://www.w3.org/TR/selectors/#pseudo-element-syntax
|
// https://www.w3.org/TR/selectors/#pseudo-element-syntax
|
||||||
|
@ -632,7 +579,7 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
if (!tokens.has_next_token()) {
|
if (!tokens.has_next_token()) {
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||||
.value = Selector::SimpleSelector::PseudoClass {
|
.value = Selector::SimpleSelector::PseudoClassSelector {
|
||||||
.type = pseudo_class,
|
.type = pseudo_class,
|
||||||
.nth_child_pattern = nth_child_pattern.release_value() }
|
.nth_child_pattern = nth_child_pattern.release_value() }
|
||||||
};
|
};
|
||||||
|
@ -655,7 +602,7 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||||
.value = Selector::SimpleSelector::PseudoClass {
|
.value = Selector::SimpleSelector::PseudoClassSelector {
|
||||||
.type = pseudo_class,
|
.type = pseudo_class,
|
||||||
.nth_child_pattern = nth_child_pattern.release_value(),
|
.nth_child_pattern = nth_child_pattern.release_value(),
|
||||||
.argument_selector_list = move(selector_list) }
|
.argument_selector_list = move(selector_list) }
|
||||||
|
@ -663,69 +610,67 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
||||||
};
|
};
|
||||||
|
|
||||||
auto const& pseudo_function = pseudo_class_token.function();
|
auto const& pseudo_function = pseudo_class_token.function();
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("is"sv)
|
auto maybe_pseudo_class = pseudo_class_from_string(pseudo_function.name());
|
||||||
|| pseudo_function.name().equals_ignoring_ascii_case("where"sv)) {
|
if (!maybe_pseudo_class.has_value()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
auto pseudo_class = maybe_pseudo_class.value();
|
||||||
|
auto metadata = pseudo_class_metadata(pseudo_class);
|
||||||
|
|
||||||
|
if (!metadata.is_valid_as_function) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is not valid as a function", pseudo_function.name());
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pseudo_function.values().is_empty()) {
|
||||||
|
dbgln_if(CSS_PARSER_DEBUG, "Empty :{}() selector", pseudo_function.name());
|
||||||
|
return ParseError::SyntaxError;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (metadata.parameter_type) {
|
||||||
|
case PseudoClassMetadata::ParameterType::ANPlusB:
|
||||||
|
return parse_nth_child_selector(pseudo_class, pseudo_function.values(), false);
|
||||||
|
case PseudoClassMetadata::ParameterType::ANPlusBOf:
|
||||||
|
return parse_nth_child_selector(pseudo_class, pseudo_function.values(), true);
|
||||||
|
case PseudoClassMetadata::ParameterType::ForgivingSelectorList: {
|
||||||
auto function_token_stream = TokenStream(pseudo_function.values());
|
auto function_token_stream = TokenStream(pseudo_function.values());
|
||||||
// NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list.
|
// NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list.
|
||||||
auto argument_selector_list = MUST(parse_a_selector_list(function_token_stream, SelectorType::Standalone, SelectorParsingMode::Forgiving));
|
auto argument_selector_list = MUST(parse_a_selector_list(function_token_stream, SelectorType::Standalone, SelectorParsingMode::Forgiving));
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||||
.value = Selector::SimpleSelector::PseudoClass {
|
.value = Selector::SimpleSelector::PseudoClassSelector {
|
||||||
.type = pseudo_function.name().equals_ignoring_ascii_case("is"sv)
|
.type = pseudo_class,
|
||||||
? Selector::SimpleSelector::PseudoClass::Type::Is
|
|
||||||
: Selector::SimpleSelector::PseudoClass::Type::Where,
|
|
||||||
.argument_selector_list = move(argument_selector_list) }
|
.argument_selector_list = move(argument_selector_list) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("not"sv)) {
|
case PseudoClassMetadata::ParameterType::LanguageRanges: {
|
||||||
auto function_token_stream = TokenStream(pseudo_function.values());
|
|
||||||
auto not_selector = TRY(parse_a_selector_list(function_token_stream, SelectorType::Standalone));
|
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
|
||||||
.value = Selector::SimpleSelector::PseudoClass {
|
|
||||||
.type = Selector::SimpleSelector::PseudoClass::Type::Not,
|
|
||||||
.argument_selector_list = move(not_selector) }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("host"sv)) {
|
|
||||||
auto function_token_stream = TokenStream(pseudo_function.values());
|
|
||||||
auto host_selector = TRY(parse_a_selector_list(function_token_stream, SelectorType::Standalone));
|
|
||||||
|
|
||||||
return Selector::SimpleSelector {
|
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
|
||||||
.value = Selector::SimpleSelector::PseudoClass {
|
|
||||||
.type = Selector::SimpleSelector::PseudoClass::Type::Host,
|
|
||||||
.argument_selector_list = move(host_selector) }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("lang"sv)) {
|
|
||||||
if (pseudo_function.values().is_empty()) {
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Empty :lang() selector");
|
|
||||||
return ParseError::SyntaxError;
|
|
||||||
}
|
|
||||||
// FIXME: Support multiple, comma-separated, language ranges.
|
// FIXME: Support multiple, comma-separated, language ranges.
|
||||||
Vector<FlyString> languages;
|
Vector<FlyString> languages;
|
||||||
languages.append(pseudo_function.values().first().token().to_string().release_value_but_fixme_should_propagate_errors());
|
languages.append(pseudo_function.values().first().token().to_string().release_value_but_fixme_should_propagate_errors());
|
||||||
return Selector::SimpleSelector {
|
return Selector::SimpleSelector {
|
||||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||||
.value = Selector::SimpleSelector::PseudoClass {
|
.value = Selector::SimpleSelector::PseudoClassSelector {
|
||||||
.type = Selector::SimpleSelector::PseudoClass::Type::Lang,
|
.type = pseudo_class,
|
||||||
.languages = move(languages) }
|
.languages = move(languages) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("nth-child"sv))
|
case PseudoClassMetadata::ParameterType::SelectorList: {
|
||||||
return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthChild, pseudo_function.values(), true);
|
auto function_token_stream = TokenStream(pseudo_function.values());
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("nth-last-child"sv))
|
auto not_selector = TRY(parse_a_selector_list(function_token_stream, SelectorType::Standalone));
|
||||||
return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthLastChild, pseudo_function.values(), true);
|
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("nth-of-type"sv))
|
|
||||||
return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthOfType, pseudo_function.values(), false);
|
|
||||||
if (pseudo_function.name().equals_ignoring_ascii_case("nth-last-of-type"sv))
|
|
||||||
return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthLastOfType, pseudo_function.values(), false);
|
|
||||||
|
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
|
return Selector::SimpleSelector {
|
||||||
return ParseError::SyntaxError;
|
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||||
|
.value = Selector::SimpleSelector::PseudoClassSelector {
|
||||||
|
.type = pseudo_class,
|
||||||
|
.argument_selector_list = move(not_selector) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case PseudoClassMetadata::ParameterType::None:
|
||||||
|
// `None` means this is not a function-type pseudo-class, so this state should be impossible.
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string());
|
dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string());
|
||||||
return ParseError::SyntaxError;
|
return ParseError::SyntaxError;
|
||||||
|
|
|
@ -72,15 +72,15 @@ u32 Selector::specificity() const
|
||||||
case SimpleSelector::Type::PseudoClass: {
|
case SimpleSelector::Type::PseudoClass: {
|
||||||
auto& pseudo_class = simple_selector.pseudo_class();
|
auto& pseudo_class = simple_selector.pseudo_class();
|
||||||
switch (pseudo_class.type) {
|
switch (pseudo_class.type) {
|
||||||
case SimpleSelector::PseudoClass::Type::Is:
|
case PseudoClass::Is:
|
||||||
case SimpleSelector::PseudoClass::Type::Not: {
|
case PseudoClass::Not: {
|
||||||
// The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the
|
// The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the
|
||||||
// specificity of the most specific complex selector in its selector list argument.
|
// specificity of the most specific complex selector in its selector list argument.
|
||||||
count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list);
|
count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SimpleSelector::PseudoClass::Type::NthChild:
|
case PseudoClass::NthChild:
|
||||||
case SimpleSelector::PseudoClass::Type::NthLastChild: {
|
case PseudoClass::NthLastChild: {
|
||||||
// Analogously, the specificity of an :nth-child() or :nth-last-child() selector
|
// Analogously, the specificity of an :nth-child() or :nth-last-child() selector
|
||||||
// is the specificity of the pseudo class itself (counting as one pseudo-class selector)
|
// is the specificity of the pseudo class itself (counting as one pseudo-class selector)
|
||||||
// plus the specificity of the most specific complex selector in its selector list argument (if any).
|
// plus the specificity of the most specific complex selector in its selector list argument (if any).
|
||||||
|
@ -88,7 +88,7 @@ u32 Selector::specificity() const
|
||||||
count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list);
|
count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SimpleSelector::PseudoClass::Type::Where:
|
case PseudoClass::Where:
|
||||||
// The specificity of a :where() pseudo-class is replaced by zero.
|
// The specificity of a :where() pseudo-class is replaced by zero.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -229,66 +229,66 @@ ErrorOr<String> Selector::SimpleSelector::serialize() const
|
||||||
auto& pseudo_class = this->pseudo_class();
|
auto& pseudo_class = this->pseudo_class();
|
||||||
|
|
||||||
switch (pseudo_class.type) {
|
switch (pseudo_class.type) {
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Link:
|
case PseudoClass::Link:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Visited:
|
case PseudoClass::Visited:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Hover:
|
case PseudoClass::Hover:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Focus:
|
case PseudoClass::Focus:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FocusVisible:
|
case PseudoClass::FocusVisible:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FocusWithin:
|
case PseudoClass::FocusWithin:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FirstChild:
|
case PseudoClass::FirstChild:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::LastChild:
|
case PseudoClass::LastChild:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::OnlyChild:
|
case PseudoClass::OnlyChild:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Empty:
|
case PseudoClass::Empty:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Root:
|
case PseudoClass::Root:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Host:
|
case PseudoClass::Host:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
|
case PseudoClass::FirstOfType:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::LastOfType:
|
case PseudoClass::LastOfType:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
|
case PseudoClass::OnlyOfType:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Disabled:
|
case PseudoClass::Disabled:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Enabled:
|
case PseudoClass::Enabled:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Checked:
|
case PseudoClass::Checked:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Indeterminate:
|
case PseudoClass::Indeterminate:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Active:
|
case PseudoClass::Active:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Scope:
|
case PseudoClass::Scope:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Defined:
|
case PseudoClass::Defined:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Playing:
|
case PseudoClass::Playing:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Paused:
|
case PseudoClass::Paused:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Seeking:
|
case PseudoClass::Seeking:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Muted:
|
case PseudoClass::Muted:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::VolumeLocked:
|
case PseudoClass::VolumeLocked:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Buffering:
|
case PseudoClass::Buffering:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Stalled:
|
case PseudoClass::Stalled:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Target:
|
case PseudoClass::Target:
|
||||||
// If the pseudo-class does not accept arguments append ":" (U+003A), followed by the name of the pseudo-class, to s.
|
// If the pseudo-class does not accept arguments append ":" (U+003A), followed by the name of the pseudo-class, to s.
|
||||||
TRY(s.try_append(':'));
|
TRY(s.try_append(':'));
|
||||||
TRY(s.try_append(pseudo_class_name(pseudo_class.type)));
|
TRY(s.try_append(pseudo_class_name(pseudo_class.type)));
|
||||||
break;
|
break;
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
case PseudoClass::NthChild:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
case PseudoClass::NthLastChild:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthOfType:
|
case PseudoClass::NthOfType:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
|
case PseudoClass::NthLastOfType:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Not:
|
case PseudoClass::Not:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Is:
|
case PseudoClass::Is:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Where:
|
case PseudoClass::Where:
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Lang:
|
case PseudoClass::Lang:
|
||||||
// Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028),
|
// Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028),
|
||||||
// followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s.
|
// followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s.
|
||||||
TRY(s.try_append(':'));
|
TRY(s.try_append(':'));
|
||||||
TRY(s.try_append(pseudo_class_name(pseudo_class.type)));
|
TRY(s.try_append(pseudo_class_name(pseudo_class.type)));
|
||||||
TRY(s.try_append('('));
|
TRY(s.try_append('('));
|
||||||
if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthChild
|
if (pseudo_class.type == PseudoClass::NthChild
|
||||||
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastChild
|
|| pseudo_class.type == PseudoClass::NthLastChild
|
||||||
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthOfType
|
|| pseudo_class.type == PseudoClass::NthOfType
|
||||||
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastOfType) {
|
|| pseudo_class.type == PseudoClass::NthLastOfType) {
|
||||||
// The result of serializing the value using the rules to serialize an <an+b> value.
|
// The result of serializing the value using the rules to serialize an <an+b> value.
|
||||||
TRY(s.try_append(TRY(pseudo_class.nth_child_pattern.serialize())));
|
TRY(s.try_append(TRY(pseudo_class.nth_child_pattern.serialize())));
|
||||||
} else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Not
|
} else if (pseudo_class.type == PseudoClass::Not
|
||||||
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Is
|
|| pseudo_class.type == PseudoClass::Is
|
||||||
|| pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Where) {
|
|| pseudo_class.type == PseudoClass::Where) {
|
||||||
// The result of serializing the value using the rules for serializing a group of selectors.
|
// The result of serializing the value using the rules for serializing a group of selectors.
|
||||||
// NOTE: `:is()` and `:where()` aren't in the spec for this yet, but it should be!
|
// NOTE: `:is()` and `:where()` aren't in the spec for this yet, but it should be!
|
||||||
TRY(s.try_append(TRY(serialize_a_group_of_selectors(pseudo_class.argument_selector_list))));
|
TRY(s.try_append(TRY(serialize_a_group_of_selectors(pseudo_class.argument_selector_list))));
|
||||||
} else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Lang) {
|
} else if (pseudo_class.type == PseudoClass::Lang) {
|
||||||
// The serialization of a comma-separated list of each argument’s serialization as a string, preserving relative order.
|
// The serialization of a comma-separated list of each argument’s serialization as a string, preserving relative order.
|
||||||
s.join(", "sv, pseudo_class.languages);
|
s.join(", "sv, pseudo_class.languages);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <AK/RefCounted.h>
|
#include <AK/RefCounted.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibWeb/CSS/PseudoClass.h>
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
|
@ -84,48 +85,8 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PseudoClass {
|
struct PseudoClassSelector {
|
||||||
enum class Type {
|
PseudoClass type;
|
||||||
Link,
|
|
||||||
Visited,
|
|
||||||
Hover,
|
|
||||||
Focus,
|
|
||||||
FocusVisible,
|
|
||||||
FocusWithin,
|
|
||||||
FirstChild,
|
|
||||||
LastChild,
|
|
||||||
OnlyChild,
|
|
||||||
NthChild,
|
|
||||||
NthLastChild,
|
|
||||||
Empty,
|
|
||||||
Root,
|
|
||||||
Host,
|
|
||||||
FirstOfType,
|
|
||||||
LastOfType,
|
|
||||||
OnlyOfType,
|
|
||||||
NthOfType,
|
|
||||||
NthLastOfType,
|
|
||||||
Disabled,
|
|
||||||
Enabled,
|
|
||||||
Checked,
|
|
||||||
Indeterminate,
|
|
||||||
Is,
|
|
||||||
Not,
|
|
||||||
Where,
|
|
||||||
Active,
|
|
||||||
Lang,
|
|
||||||
Scope,
|
|
||||||
Defined,
|
|
||||||
Playing,
|
|
||||||
Paused,
|
|
||||||
Seeking,
|
|
||||||
Muted,
|
|
||||||
VolumeLocked,
|
|
||||||
Buffering,
|
|
||||||
Stalled,
|
|
||||||
Target,
|
|
||||||
};
|
|
||||||
Type type;
|
|
||||||
|
|
||||||
// FIXME: We don't need this field on every single SimpleSelector, but it's also annoying to malloc it somewhere.
|
// 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".
|
// Only used when "pseudo_class" is "NthChild" or "NthLastChild".
|
||||||
|
@ -184,12 +145,12 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
Variant<Empty, Attribute, PseudoClass, PseudoElement, Name, QualifiedName> value {};
|
Variant<Empty, Attribute, PseudoClassSelector, PseudoElement, Name, QualifiedName> value {};
|
||||||
|
|
||||||
Attribute const& attribute() const { return value.get<Attribute>(); }
|
Attribute const& attribute() const { return value.get<Attribute>(); }
|
||||||
Attribute& attribute() { return value.get<Attribute>(); }
|
Attribute& attribute() { return value.get<Attribute>(); }
|
||||||
PseudoClass const& pseudo_class() const { return value.get<PseudoClass>(); }
|
PseudoClassSelector const& pseudo_class() const { return value.get<PseudoClassSelector>(); }
|
||||||
PseudoClass& pseudo_class() { return value.get<PseudoClass>(); }
|
PseudoClassSelector& pseudo_class() { return value.get<PseudoClassSelector>(); }
|
||||||
PseudoElement const& pseudo_element() const { return value.get<PseudoElement>(); }
|
PseudoElement const& pseudo_element() const { return value.get<PseudoElement>(); }
|
||||||
PseudoElement& pseudo_element() { return value.get<PseudoElement>(); }
|
PseudoElement& pseudo_element() { return value.get<PseudoElement>(); }
|
||||||
|
|
||||||
|
@ -268,89 +229,6 @@ constexpr StringView pseudo_element_name(Selector::PseudoElement pseudo_element)
|
||||||
|
|
||||||
Optional<Selector::PseudoElement> pseudo_element_from_string(StringView);
|
Optional<Selector::PseudoElement> pseudo_element_from_string(StringView);
|
||||||
|
|
||||||
constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Type pseudo_class)
|
|
||||||
{
|
|
||||||
switch (pseudo_class) {
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Link:
|
|
||||||
return "link"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Visited:
|
|
||||||
return "visited"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Hover:
|
|
||||||
return "hover"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Focus:
|
|
||||||
return "focus"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FocusVisible:
|
|
||||||
return "focus-visible"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FocusWithin:
|
|
||||||
return "focus-within"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FirstChild:
|
|
||||||
return "first-child"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::LastChild:
|
|
||||||
return "last-child"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::OnlyChild:
|
|
||||||
return "only-child"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Empty:
|
|
||||||
return "empty"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Root:
|
|
||||||
return "root"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Host:
|
|
||||||
return "host"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
|
|
||||||
return "first-of-type"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::LastOfType:
|
|
||||||
return "last-of-type"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
|
|
||||||
return "only-of-type"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthOfType:
|
|
||||||
return "nth-of-type"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
|
|
||||||
return "nth-last-of-type"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Disabled:
|
|
||||||
return "disabled"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Enabled:
|
|
||||||
return "enabled"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Checked:
|
|
||||||
return "checked"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Indeterminate:
|
|
||||||
return "indeterminate"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Active:
|
|
||||||
return "active"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
|
||||||
return "nth-child"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
|
||||||
return "nth-last-child"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Is:
|
|
||||||
return "is"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Not:
|
|
||||||
return "not"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Where:
|
|
||||||
return "where"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Lang:
|
|
||||||
return "lang"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Scope:
|
|
||||||
return "scope"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Defined:
|
|
||||||
return "defined"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Playing:
|
|
||||||
return "playing"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Paused:
|
|
||||||
return "paused"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Seeking:
|
|
||||||
return "seeking"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Muted:
|
|
||||||
return "muted"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::VolumeLocked:
|
|
||||||
return "volume-locked"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Buffering:
|
|
||||||
return "buffering"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Stalled:
|
|
||||||
return "stalled"sv;
|
|
||||||
case Selector::SimpleSelector::PseudoClass::Type::Target:
|
|
||||||
return "target"sv;
|
|
||||||
}
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<String> serialize_a_group_of_selectors(Vector<NonnullRefPtr<Selector>> const& selectors);
|
ErrorOr<String> serialize_a_group_of_selectors(Vector<NonnullRefPtr<Selector>> const& selectors);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,34 +207,34 @@ static inline DOM::Element const* next_sibling_with_same_tag_name(DOM::Element c
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
|
static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClassSelector const& pseudo_class, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
|
||||||
{
|
{
|
||||||
switch (pseudo_class.type) {
|
switch (pseudo_class.type) {
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Link:
|
case CSS::PseudoClass::Link:
|
||||||
return matches_link_pseudo_class(element);
|
return matches_link_pseudo_class(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Visited:
|
case CSS::PseudoClass::Visited:
|
||||||
// FIXME: Maybe match this selector sometimes?
|
// FIXME: Maybe match this selector sometimes?
|
||||||
return false;
|
return false;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Active:
|
case CSS::PseudoClass::Active:
|
||||||
return element.is_active();
|
return element.is_active();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Hover:
|
case CSS::PseudoClass::Hover:
|
||||||
return matches_hover_pseudo_class(element);
|
return matches_hover_pseudo_class(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Focus:
|
case CSS::PseudoClass::Focus:
|
||||||
return element.is_focused();
|
return element.is_focused();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::FocusVisible:
|
case CSS::PseudoClass::FocusVisible:
|
||||||
// FIXME: We should only apply this when a visible focus is useful. Decide when that is!
|
// FIXME: We should only apply this when a visible focus is useful. Decide when that is!
|
||||||
return element.is_focused();
|
return element.is_focused();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::FocusWithin: {
|
case CSS::PseudoClass::FocusWithin: {
|
||||||
auto* focused_element = element.document().focused_element();
|
auto* focused_element = element.document().focused_element();
|
||||||
return focused_element && element.is_inclusive_ancestor_of(*focused_element);
|
return focused_element && element.is_inclusive_ancestor_of(*focused_element);
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstChild:
|
case CSS::PseudoClass::FirstChild:
|
||||||
return !element.previous_element_sibling();
|
return !element.previous_element_sibling();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::LastChild:
|
case CSS::PseudoClass::LastChild:
|
||||||
return !element.next_element_sibling();
|
return !element.next_element_sibling();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyChild:
|
case CSS::PseudoClass::OnlyChild:
|
||||||
return !(element.previous_element_sibling() || element.next_element_sibling());
|
return !(element.previous_element_sibling() || element.next_element_sibling());
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Empty: {
|
case CSS::PseudoClass::Empty: {
|
||||||
if (!element.has_children())
|
if (!element.has_children())
|
||||||
return true;
|
return true;
|
||||||
if (element.first_child_of_type<DOM::Element>())
|
if (element.first_child_of_type<DOM::Element>())
|
||||||
|
@ -251,53 +251,53 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
});
|
});
|
||||||
return !has_nonempty_text_child;
|
return !has_nonempty_text_child;
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Root:
|
case CSS::PseudoClass::Root:
|
||||||
return is<HTML::HTMLHtmlElement>(element);
|
return is<HTML::HTMLHtmlElement>(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Host:
|
case CSS::PseudoClass::Host:
|
||||||
// FIXME: Implement :host selector.
|
// FIXME: Implement :host selector.
|
||||||
return false;
|
return false;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Scope:
|
case CSS::PseudoClass::Scope:
|
||||||
return scope ? &element == scope : is<HTML::HTMLHtmlElement>(element);
|
return scope ? &element == scope : is<HTML::HTMLHtmlElement>(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
|
case CSS::PseudoClass::FirstOfType:
|
||||||
return !previous_sibling_with_same_tag_name(element);
|
return !previous_sibling_with_same_tag_name(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType:
|
case CSS::PseudoClass::LastOfType:
|
||||||
return !next_sibling_with_same_tag_name(element);
|
return !next_sibling_with_same_tag_name(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
|
case CSS::PseudoClass::OnlyOfType:
|
||||||
return !previous_sibling_with_same_tag_name(element) && !next_sibling_with_same_tag_name(element);
|
return !previous_sibling_with_same_tag_name(element) && !next_sibling_with_same_tag_name(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Lang:
|
case CSS::PseudoClass::Lang:
|
||||||
return matches_lang_pseudo_class(element, pseudo_class.languages);
|
return matches_lang_pseudo_class(element, pseudo_class.languages);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled:
|
case CSS::PseudoClass::Disabled:
|
||||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-disabled
|
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-disabled
|
||||||
// The :disabled pseudo-class must match any element that is actually disabled.
|
// The :disabled pseudo-class must match any element that is actually disabled.
|
||||||
return element.is_actually_disabled();
|
return element.is_actually_disabled();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Enabled:
|
case CSS::PseudoClass::Enabled:
|
||||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-enabled
|
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-enabled
|
||||||
// The :enabled pseudo-class must match any button, input, select, textarea, optgroup, option, fieldset element, or form-associated custom element that is not actually disabled.
|
// The :enabled pseudo-class must match any button, input, select, textarea, optgroup, option, fieldset element, or form-associated custom element that is not actually disabled.
|
||||||
return (is<HTML::HTMLButtonElement>(element) || is<HTML::HTMLInputElement>(element) || is<HTML::HTMLSelectElement>(element) || is<HTML::HTMLTextAreaElement>(element) || is<HTML::HTMLOptGroupElement>(element) || is<HTML::HTMLOptionElement>(element) || is<HTML::HTMLFieldSetElement>(element))
|
return (is<HTML::HTMLButtonElement>(element) || is<HTML::HTMLInputElement>(element) || is<HTML::HTMLSelectElement>(element) || is<HTML::HTMLTextAreaElement>(element) || is<HTML::HTMLOptGroupElement>(element) || is<HTML::HTMLOptionElement>(element) || is<HTML::HTMLFieldSetElement>(element))
|
||||||
&& !element.is_actually_disabled();
|
&& !element.is_actually_disabled();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked:
|
case CSS::PseudoClass::Checked:
|
||||||
return matches_checked_pseudo_class(element);
|
return matches_checked_pseudo_class(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Indeterminate:
|
case CSS::PseudoClass::Indeterminate:
|
||||||
return matches_indeterminate_pseudo_class(element);
|
return matches_indeterminate_pseudo_class(element);
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Defined:
|
case CSS::PseudoClass::Defined:
|
||||||
return element.is_defined();
|
return element.is_defined();
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Is:
|
case CSS::PseudoClass::Is:
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Where:
|
case CSS::PseudoClass::Where:
|
||||||
for (auto& selector : pseudo_class.argument_selector_list) {
|
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||||
if (matches(selector, style_sheet_for_rule, element))
|
if (matches(selector, style_sheet_for_rule, element))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
|
case CSS::PseudoClass::Not:
|
||||||
for (auto& selector : pseudo_class.argument_selector_list) {
|
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||||
if (matches(selector, style_sheet_for_rule, element))
|
if (matches(selector, style_sheet_for_rule, element))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
|
case CSS::PseudoClass::NthChild:
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild:
|
case CSS::PseudoClass::NthLastChild:
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType:
|
case CSS::PseudoClass::NthOfType:
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType: {
|
case CSS::PseudoClass::NthLastOfType: {
|
||||||
auto const step_size = pseudo_class.nth_child_pattern.step_size;
|
auto const step_size = pseudo_class.nth_child_pattern.step_size;
|
||||||
auto const offset = pseudo_class.nth_child_pattern.offset;
|
auto const offset = pseudo_class.nth_child_pattern.offset;
|
||||||
if (step_size == 0 && offset == 0)
|
if (step_size == 0 && offset == 0)
|
||||||
|
@ -320,7 +320,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
|
|
||||||
int index = 1;
|
int index = 1;
|
||||||
switch (pseudo_class.type) {
|
switch (pseudo_class.type) {
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild: {
|
case CSS::PseudoClass::NthChild: {
|
||||||
if (!matches_selector_list(pseudo_class.argument_selector_list, element))
|
if (!matches_selector_list(pseudo_class.argument_selector_list, element))
|
||||||
return false;
|
return false;
|
||||||
for (auto* child = parent->first_child_of_type<DOM::Element>(); child && child != &element; child = child->next_element_sibling()) {
|
for (auto* child = parent->first_child_of_type<DOM::Element>(); child && child != &element; child = child->next_element_sibling()) {
|
||||||
|
@ -329,7 +329,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild: {
|
case CSS::PseudoClass::NthLastChild: {
|
||||||
if (!matches_selector_list(pseudo_class.argument_selector_list, element))
|
if (!matches_selector_list(pseudo_class.argument_selector_list, element))
|
||||||
return false;
|
return false;
|
||||||
for (auto* child = parent->last_child_of_type<DOM::Element>(); child && child != &element; child = child->previous_element_sibling()) {
|
for (auto* child = parent->last_child_of_type<DOM::Element>(); child && child != &element; child = child->previous_element_sibling()) {
|
||||||
|
@ -338,12 +338,12 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType: {
|
case CSS::PseudoClass::NthOfType: {
|
||||||
for (auto* child = previous_sibling_with_same_tag_name(element); child; child = previous_sibling_with_same_tag_name(*child))
|
for (auto* child = previous_sibling_with_same_tag_name(element); child; child = previous_sibling_with_same_tag_name(*child))
|
||||||
++index;
|
++index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType: {
|
case CSS::PseudoClass::NthLastOfType: {
|
||||||
for (auto* child = next_sibling_with_same_tag_name(element); child; child = next_sibling_with_same_tag_name(*child))
|
for (auto* child = next_sibling_with_same_tag_name(element); child; child = next_sibling_with_same_tag_name(*child))
|
||||||
++index;
|
++index;
|
||||||
break;
|
break;
|
||||||
|
@ -384,48 +384,48 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
||||||
// Otherwise, we start at "offset" and count forwards.
|
// Otherwise, we start at "offset" and count forwards.
|
||||||
return index >= offset && canonical_modulo(index - offset, step_size) == 0;
|
return index >= offset && canonical_modulo(index - offset, step_size) == 0;
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Playing: {
|
case CSS::PseudoClass::Playing: {
|
||||||
if (!is<HTML::HTMLMediaElement>(element))
|
if (!is<HTML::HTMLMediaElement>(element))
|
||||||
return false;
|
return false;
|
||||||
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
||||||
return !media_element.paused();
|
return !media_element.paused();
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Paused: {
|
case CSS::PseudoClass::Paused: {
|
||||||
if (!is<HTML::HTMLMediaElement>(element))
|
if (!is<HTML::HTMLMediaElement>(element))
|
||||||
return false;
|
return false;
|
||||||
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
||||||
return media_element.paused();
|
return media_element.paused();
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Seeking: {
|
case CSS::PseudoClass::Seeking: {
|
||||||
if (!is<HTML::HTMLMediaElement>(element))
|
if (!is<HTML::HTMLMediaElement>(element))
|
||||||
return false;
|
return false;
|
||||||
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
||||||
return media_element.seeking();
|
return media_element.seeking();
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Muted: {
|
case CSS::PseudoClass::Muted: {
|
||||||
if (!is<HTML::HTMLMediaElement>(element))
|
if (!is<HTML::HTMLMediaElement>(element))
|
||||||
return false;
|
return false;
|
||||||
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
||||||
return media_element.muted();
|
return media_element.muted();
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::VolumeLocked: {
|
case CSS::PseudoClass::VolumeLocked: {
|
||||||
// FIXME: Currently we don't allow the user to specify an override volume, so this is always false.
|
// FIXME: Currently we don't allow the user to specify an override volume, so this is always false.
|
||||||
// Once we do, implement this!
|
// Once we do, implement this!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Buffering: {
|
case CSS::PseudoClass::Buffering: {
|
||||||
if (!is<HTML::HTMLMediaElement>(element))
|
if (!is<HTML::HTMLMediaElement>(element))
|
||||||
return false;
|
return false;
|
||||||
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
||||||
return media_element.blocked();
|
return media_element.blocked();
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Stalled: {
|
case CSS::PseudoClass::Stalled: {
|
||||||
if (!is<HTML::HTMLMediaElement>(element))
|
if (!is<HTML::HTMLMediaElement>(element))
|
||||||
return false;
|
return false;
|
||||||
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
auto const& media_element = static_cast<HTML::HTMLMediaElement const&>(element);
|
||||||
return media_element.stalled();
|
return media_element.stalled();
|
||||||
}
|
}
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Target:
|
case CSS::PseudoClass::Target:
|
||||||
return element.is_target();
|
return element.is_target();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <LibWeb/CSS/CSSStyleSheet.h>
|
#include <LibWeb/CSS/CSSStyleSheet.h>
|
||||||
#include <LibWeb/CSS/CSSSupportsRule.h>
|
#include <LibWeb/CSS/CSSSupportsRule.h>
|
||||||
#include <LibWeb/CSS/PropertyID.h>
|
#include <LibWeb/CSS/PropertyID.h>
|
||||||
|
#include <LibWeb/CSS/PseudoClass.h>
|
||||||
#include <LibWeb/DOM/Comment.h>
|
#include <LibWeb/DOM/Comment.h>
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/Element.h>
|
#include <LibWeb/DOM/Element.h>
|
||||||
|
@ -458,7 +459,7 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
||||||
type_description = "Attribute";
|
type_description = "Attribute";
|
||||||
break;
|
break;
|
||||||
case CSS::Selector::SimpleSelector::Type::PseudoClass:
|
case CSS::Selector::SimpleSelector::Type::PseudoClass:
|
||||||
type_description = "PseudoClass";
|
type_description = "PseudoClassSelector";
|
||||||
break;
|
break;
|
||||||
case CSS::Selector::SimpleSelector::Type::PseudoElement:
|
case CSS::Selector::SimpleSelector::Type::PseudoElement:
|
||||||
type_description = "PseudoElement";
|
type_description = "PseudoElement";
|
||||||
|
@ -477,141 +478,14 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
||||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
|
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
|
||||||
auto const& pseudo_class = simple_selector.pseudo_class();
|
auto const& pseudo_class = simple_selector.pseudo_class();
|
||||||
|
|
||||||
char const* pseudo_class_description = "";
|
builder.appendff(" pseudo_class={}", CSS::pseudo_class_name(pseudo_class.type));
|
||||||
switch (pseudo_class.type) {
|
auto pseudo_class_metadata = CSS::pseudo_class_metadata(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::Root:
|
|
||||||
pseudo_class_description = "Root";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Host:
|
|
||||||
pseudo_class_description = "Host";
|
|
||||||
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::OnlyOfType:
|
|
||||||
pseudo_class_description = "OnlyOfType";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType:
|
|
||||||
pseudo_class_description = "NthOfType";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType:
|
|
||||||
pseudo_class_description = "NthLastOfType";
|
|
||||||
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::FocusVisible:
|
|
||||||
pseudo_class_description = "FocusVisible";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::FocusWithin:
|
|
||||||
pseudo_class_description = "FocusWithin";
|
|
||||||
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::Indeterminate:
|
|
||||||
pseudo_class_description = "Indeterminate";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
|
|
||||||
pseudo_class_description = "Not";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Is:
|
|
||||||
pseudo_class_description = "Is";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Where:
|
|
||||||
pseudo_class_description = "Where";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Lang:
|
|
||||||
pseudo_class_description = "Lang";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Scope:
|
|
||||||
pseudo_class_description = "Scope";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Defined:
|
|
||||||
pseudo_class_description = "Defined";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Playing:
|
|
||||||
pseudo_class_description = "Playing";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Paused:
|
|
||||||
pseudo_class_description = "Paused";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Seeking:
|
|
||||||
pseudo_class_description = "Seeking";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Muted:
|
|
||||||
pseudo_class_description = "Muted";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::VolumeLocked:
|
|
||||||
pseudo_class_description = "VolumeLocked";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Buffering:
|
|
||||||
pseudo_class_description = "Buffering";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Stalled:
|
|
||||||
pseudo_class_description = "Stalled";
|
|
||||||
break;
|
|
||||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Target:
|
|
||||||
pseudo_class_description = "Target";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.appendff(" pseudo_class={}", pseudo_class_description);
|
switch (pseudo_class_metadata.parameter_type) {
|
||||||
if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Lang) {
|
case CSS::PseudoClassMetadata::ParameterType::None:
|
||||||
builder.append('(');
|
break;
|
||||||
builder.join(',', pseudo_class.languages);
|
case CSS::PseudoClassMetadata::ParameterType::ANPlusB:
|
||||||
builder.append(')');
|
case CSS::PseudoClassMetadata::ParameterType::ANPlusBOf: {
|
||||||
} else if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Not
|
|
||||||
|| pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Host
|
|
||||||
|| pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Is
|
|
||||||
|| pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Where) {
|
|
||||||
builder.append("(["sv);
|
|
||||||
for (auto& selector : pseudo_class.argument_selector_list)
|
|
||||||
dump_selector(builder, selector);
|
|
||||||
builder.append("])"sv);
|
|
||||||
} else if ((pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild)
|
|
||||||
|| (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastChild)
|
|
||||||
|| (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthOfType)
|
|
||||||
|| (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthLastOfType)) {
|
|
||||||
builder.appendff("(step={}, offset={}", pseudo_class.nth_child_pattern.step_size, pseudo_class.nth_child_pattern.offset);
|
builder.appendff("(step={}, offset={}", pseudo_class.nth_child_pattern.step_size, pseudo_class.nth_child_pattern.offset);
|
||||||
if (!pseudo_class.argument_selector_list.is_empty()) {
|
if (!pseudo_class.argument_selector_list.is_empty()) {
|
||||||
builder.append(", selectors=["sv);
|
builder.append(", selectors=["sv);
|
||||||
|
@ -620,6 +494,22 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
||||||
builder.append("]"sv);
|
builder.append("]"sv);
|
||||||
}
|
}
|
||||||
builder.append(")"sv);
|
builder.append(")"sv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CSS::PseudoClassMetadata::ParameterType::ForgivingSelectorList:
|
||||||
|
case CSS::PseudoClassMetadata::ParameterType::SelectorList: {
|
||||||
|
builder.append("(["sv);
|
||||||
|
for (auto& selector : pseudo_class.argument_selector_list)
|
||||||
|
dump_selector(builder, selector);
|
||||||
|
builder.append("])"sv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CSS::PseudoClassMetadata::ParameterType::LanguageRanges: {
|
||||||
|
builder.append('(');
|
||||||
|
builder.join(',', pseudo_class.languages);
|
||||||
|
builder.append(')');
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue