LibWeb: Implement the :has()
pseudo-class
See https://drafts.csswg.org/selectors-4/#relational.
This commit is contained in:
parent
be1075e673
commit
f63a945ba0
Notes:
sideshowbarker
2024-07-17 02:22:23 +09:00
Author: https://github.com/dzfrias Commit: https://github.com/LadybirdBrowser/ladybird/commit/f63a945ba0 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/613 Reviewed-by: https://github.com/AtkinsSJ ✅
21 changed files with 203 additions and 17 deletions
|
@ -72,6 +72,7 @@ struct PseudoClassMetadata {
|
|||
ANPlusBOf,
|
||||
CompoundSelector,
|
||||
ForgivingSelectorList,
|
||||
ForgivingRelativeSelectorList,
|
||||
Ident,
|
||||
LanguageRanges,
|
||||
SelectorList,
|
||||
|
@ -167,6 +168,8 @@ PseudoClassMetadata pseudo_class_metadata(PseudoClass pseudo_class)
|
|||
parameter_type = "CompoundSelector"_string;
|
||||
} else if (argument_string == "<forgiving-selector-list>"sv) {
|
||||
parameter_type = "ForgivingSelectorList"_string;
|
||||
} else if (argument_string == "<forgiving-relative-selector-list>"sv) {
|
||||
parameter_type = "ForgivingRelativeSelectorList"_string;
|
||||
} else if (argument_string == "<ident>"sv) {
|
||||
parameter_type = "Ident"_string;
|
||||
} else if (argument_string == "<language-ranges>"sv) {
|
||||
|
|
11
Tests/LibWeb/Ref/css-has-compound.html
Normal file
11
Tests/LibWeb/Ref/css-has-compound.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<link rel="match" href="reference/css-has-compound.html" />
|
||||
<style>
|
||||
a:has(span.nice > em) {
|
||||
color: orange;
|
||||
}
|
||||
</style>
|
||||
<a href="https://example.com"><em><span class="nice"><em>Link</em></span></em></a>
|
||||
<a href="https://example.com"><em>Link</em></a>
|
||||
<a href="https://example.com"><em><span class="hello"><em>Link</em></span></em></a>
|
||||
<a href="https://example.com"><em><span class="nice">Link</span></em></a>
|
9
Tests/LibWeb/Ref/css-has-descendant.html
Normal file
9
Tests/LibWeb/Ref/css-has-descendant.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!doctype html>
|
||||
<link rel="match" href="reference/css-has-descendant.html" />
|
||||
<style>
|
||||
a:has(span) {
|
||||
color: orange;
|
||||
}
|
||||
</style>
|
||||
<a href="https://example.com"><em><span>Link</span></em></a>
|
||||
<a href="https://example.com"><em>Link</em></a>
|
9
Tests/LibWeb/Ref/css-has-direct-child.html
Normal file
9
Tests/LibWeb/Ref/css-has-direct-child.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!doctype html>
|
||||
<link rel="match" href="reference/css-has-direct-child.html" />
|
||||
<style>
|
||||
a:has(> span) {
|
||||
color: orange;
|
||||
}
|
||||
</style>
|
||||
<a href="https://example.com"><span>Link</span></a>
|
||||
<a href="https://example.com"><em><span>Link</span></em></a>
|
11
Tests/LibWeb/Ref/css-has-next-sibling.html
Normal file
11
Tests/LibWeb/Ref/css-has-next-sibling.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<link rel="match" href="reference/css-has-next-sibling.html" />
|
||||
<style>
|
||||
a:has(+ span) {
|
||||
color: orange;
|
||||
}
|
||||
</style>
|
||||
<a href="https://example.com">Link</a><span>Hello!</span>
|
||||
<a href="https://example.com">Link</a><em>Hello!</em>
|
||||
<a href="https://example.com">Link</a><em>Hello</em><span>world!</span>
|
||||
<a href="https://example.com">Link</a>
|
19
Tests/LibWeb/Ref/css-has-subsequent-sibling.html
Normal file
19
Tests/LibWeb/Ref/css-has-subsequent-sibling.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
<!doctype html>
|
||||
<link rel="match" href="reference/css-has-subsequent-sibling.html" />
|
||||
<style>
|
||||
a:has(~ span) {
|
||||
color: orange;
|
||||
}
|
||||
</style>
|
||||
<div>
|
||||
<a href="https://example.com">Link</a><span>Hello!</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://example.com">Link</a><em>Hello!</em>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://example.com">Link</a><em>Hello</em><span>world!</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://example.com">Link</a>
|
||||
</div>
|
8
Tests/LibWeb/Ref/css-nested-has.html
Normal file
8
Tests/LibWeb/Ref/css-nested-has.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!doctype html>
|
||||
<link rel="match" href="reference/css-nested-has.html" />
|
||||
<style>
|
||||
a:has(span:has(strong)) {
|
||||
color: orange;
|
||||
}
|
||||
</style>
|
||||
<a href="https://example.com"><em><span><strong>Link</strong></span></em></a>
|
12
Tests/LibWeb/Ref/css-pseudo-element-in-has.html
Normal file
12
Tests/LibWeb/Ref/css-pseudo-element-in-has.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!doctype html>
|
||||
<link rel="match" href="reference/css-pseudo-element-in-has.html" />
|
||||
<style>
|
||||
span::after {
|
||||
content: "bar";
|
||||
color: red;
|
||||
}
|
||||
a:has(::after) {
|
||||
color: orange;
|
||||
}
|
||||
</style>
|
||||
<a href="https://example.com"><span>foo</span></a>
|
4
Tests/LibWeb/Ref/reference/css-has-compound.html
Normal file
4
Tests/LibWeb/Ref/reference/css-has-compound.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<a href="https://example.com" style="color: orange"><em><span class="nice"><em>Link</em></span></em></a>
|
||||
<a href="https://example.com"><em>Link</em></a>
|
||||
<a href="https://example.com"><em><span class="hello"><em>Link</em></span></em></a>
|
||||
<a href="https://example.com"><em><span class="nice">Link</span></em></a>
|
2
Tests/LibWeb/Ref/reference/css-has-descendant.html
Normal file
2
Tests/LibWeb/Ref/reference/css-has-descendant.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
<a href="https://example.com" style="color: orange"><em><span class="nice"><em>Link</em></span></em></a>
|
||||
<a href="https://example.com"><em>Link</em></a>
|
2
Tests/LibWeb/Ref/reference/css-has-direct-child.html
Normal file
2
Tests/LibWeb/Ref/reference/css-has-direct-child.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
<a href="https://example.com" style="color: orange"><span>Link</span></a>
|
||||
<a href="https://example.com"><em><span>Link</span></em></a>
|
4
Tests/LibWeb/Ref/reference/css-has-next-sibling.html
Normal file
4
Tests/LibWeb/Ref/reference/css-has-next-sibling.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<a href="https://example.com" style="color: orange">Link</a><span>Hello!</span>
|
||||
<a href="https://example.com">Link</a><em>Hello!</em>
|
||||
<a href="https://example.com">Link</a><em>Hello</em><span>world!</span>
|
||||
<a href="https://example.com">Link</a>
|
12
Tests/LibWeb/Ref/reference/css-has-subsequent-sibling.html
Normal file
12
Tests/LibWeb/Ref/reference/css-has-subsequent-sibling.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div>
|
||||
<a href="https://example.com" style="color: orange">Link</a><span>Hello!</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://example.com">Link</a><em>Hello!</em>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://example.com" style="color: orange">Link</a><em>Hello</em><span>world!</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://example.com">Link</a>
|
||||
</div>
|
1
Tests/LibWeb/Ref/reference/css-nested-has.html
Normal file
1
Tests/LibWeb/Ref/reference/css-nested-has.html
Normal file
|
@ -0,0 +1 @@
|
|||
<a href="https://example.com"><em><span><strong>Link</strong></span></em></a>
|
|
@ -0,0 +1 @@
|
|||
<a href="https://example.com"><span>foo<span style="color: red">bar</span></span></a>
|
|
@ -516,10 +516,14 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selec
|
|||
.argument_selector_list = { move(selector) } }
|
||||
};
|
||||
}
|
||||
case PseudoClassMetadata::ParameterType::ForgivingRelativeSelectorList:
|
||||
case PseudoClassMetadata::ParameterType::ForgivingSelectorList: {
|
||||
auto function_token_stream = TokenStream(pseudo_function.values());
|
||||
auto selector_type = metadata.parameter_type == PseudoClassMetadata::ParameterType::ForgivingSelectorList
|
||||
? SelectorType::Standalone
|
||||
: SelectorType::Relative;
|
||||
// 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, selector_type, SelectorParsingMode::Forgiving));
|
||||
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::PseudoClass,
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
"focus-within": {
|
||||
"argument": ""
|
||||
},
|
||||
"has": {
|
||||
"argument": "<forgiving-relative-selector-list>"
|
||||
},
|
||||
"host": {
|
||||
"argument": "<compound-selector>?"
|
||||
},
|
||||
|
|
|
@ -119,6 +119,7 @@ u32 Selector::specificity() const
|
|||
case SimpleSelector::Type::PseudoClass: {
|
||||
auto& pseudo_class = simple_selector.pseudo_class();
|
||||
switch (pseudo_class.type) {
|
||||
case PseudoClass::Has:
|
||||
case PseudoClass::Is:
|
||||
case PseudoClass::Not: {
|
||||
// The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the
|
||||
|
|
|
@ -63,6 +63,56 @@ static inline bool matches_lang_pseudo_class(DOM::Element const& element, Vector
|
|||
return false;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/selectors-4/#relational
|
||||
static inline bool matches_has_pseudo_class(CSS::Selector const& selector, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& anchor)
|
||||
{
|
||||
switch (selector.compound_selectors()[0].combinator) {
|
||||
// Shouldn't be possible because we've parsed relative selectors, which always have a combinator, implicitly or explicitly.
|
||||
case CSS::Selector::Combinator::None:
|
||||
VERIFY_NOT_REACHED();
|
||||
case CSS::Selector::Combinator::Descendant: {
|
||||
bool has = false;
|
||||
anchor.for_each_in_subtree([&](auto const& descendant) {
|
||||
if (!descendant.is_element())
|
||||
return TraversalDecision::Continue;
|
||||
auto const& descendant_element = static_cast<DOM::Element const&>(descendant);
|
||||
if (matches(selector, style_sheet_for_rule, descendant_element, {}, {}, SelectorKind::Relative)) {
|
||||
has = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
return has;
|
||||
}
|
||||
case CSS::Selector::Combinator::ImmediateChild: {
|
||||
bool has = false;
|
||||
anchor.for_each_child([&](DOM::Node const& child) {
|
||||
if (!child.is_element())
|
||||
return IterationDecision::Continue;
|
||||
auto const& child_element = static_cast<DOM::Element const&>(child);
|
||||
if (matches(selector, style_sheet_for_rule, child_element, {}, {}, SelectorKind::Relative)) {
|
||||
has = true;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return has;
|
||||
}
|
||||
case CSS::Selector::Combinator::NextSibling:
|
||||
return anchor.next_element_sibling() != nullptr && matches(selector, style_sheet_for_rule, *anchor.next_element_sibling(), {}, {}, SelectorKind::Relative);
|
||||
case CSS::Selector::Combinator::SubsequentSibling: {
|
||||
for (auto* sibling = anchor.next_element_sibling(); sibling; sibling = sibling->next_element_sibling()) {
|
||||
if (matches(selector, style_sheet_for_rule, *sibling, {}, {}, SelectorKind::Relative))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case CSS::Selector::Combinator::Column:
|
||||
TODO();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-link
|
||||
static inline bool matches_link_pseudo_class(DOM::Element const& element)
|
||||
{
|
||||
|
@ -268,7 +318,7 @@ static bool matches_open_state_pseudo_class(DOM::Element const& element, bool op
|
|||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
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, SelectorKind selector_kind)
|
||||
{
|
||||
switch (pseudo_class.type) {
|
||||
case CSS::PseudoClass::Link:
|
||||
|
@ -359,6 +409,16 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
return matches_indeterminate_pseudo_class(element);
|
||||
case CSS::PseudoClass::Defined:
|
||||
return element.is_defined();
|
||||
case CSS::PseudoClass::Has:
|
||||
// :has() cannot be nested in a :has()
|
||||
if (selector_kind == SelectorKind::Relative)
|
||||
return false;
|
||||
// These selectors should be relative selectors (https://drafts.csswg.org/selectors-4/#relative-selector)
|
||||
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||
if (matches_has_pseudo_class(selector, style_sheet_for_rule, element))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case CSS::PseudoClass::Is:
|
||||
case CSS::PseudoClass::Where:
|
||||
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||
|
@ -588,7 +648,7 @@ static ALWAYS_INLINE bool matches_namespace(
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static inline bool matches(CSS::Selector::SimpleSelector const& component, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
|
||||
static inline bool matches(CSS::Selector::SimpleSelector const& component, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope, SelectorKind selector_kind)
|
||||
{
|
||||
switch (component.type) {
|
||||
case CSS::Selector::SimpleSelector::Type::Universal:
|
||||
|
@ -615,7 +675,7 @@ static inline bool matches(CSS::Selector::SimpleSelector const& component, Optio
|
|||
case CSS::Selector::SimpleSelector::Type::Attribute:
|
||||
return matches_attribute(component.attribute(), style_sheet_for_rule, element);
|
||||
case CSS::Selector::SimpleSelector::Type::PseudoClass:
|
||||
return matches_pseudo_class(component.pseudo_class(), style_sheet_for_rule, element, scope);
|
||||
return matches_pseudo_class(component.pseudo_class(), style_sheet_for_rule, element, scope, selector_kind);
|
||||
case CSS::Selector::SimpleSelector::Type::PseudoElement:
|
||||
// Pseudo-element matching/not-matching is handled in the top level matches().
|
||||
return true;
|
||||
|
@ -624,22 +684,26 @@ static inline bool matches(CSS::Selector::SimpleSelector const& component, Optio
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool matches(CSS::Selector const& selector, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, int component_list_index, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
|
||||
static inline bool matches(CSS::Selector const& selector, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, int component_list_index, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope, SelectorKind selector_kind)
|
||||
{
|
||||
auto& relative_selector = selector.compound_selectors()[component_list_index];
|
||||
for (auto& simple_selector : relative_selector.simple_selectors) {
|
||||
if (!matches(simple_selector, style_sheet_for_rule, element, scope))
|
||||
auto& compound_selector = selector.compound_selectors()[component_list_index];
|
||||
for (auto& simple_selector : compound_selector.simple_selectors) {
|
||||
if (!matches(simple_selector, style_sheet_for_rule, element, scope, selector_kind))
|
||||
return false;
|
||||
}
|
||||
switch (relative_selector.combinator) {
|
||||
// Always matches because we assume that element is already relative to its anchor
|
||||
if (selector_kind == SelectorKind::Relative && component_list_index == 0)
|
||||
return true;
|
||||
switch (compound_selector.combinator) {
|
||||
case CSS::Selector::Combinator::None:
|
||||
VERIFY(selector_kind != SelectorKind::Relative);
|
||||
return true;
|
||||
case CSS::Selector::Combinator::Descendant:
|
||||
VERIFY(component_list_index != 0);
|
||||
for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) {
|
||||
if (!is<DOM::Element>(*ancestor))
|
||||
continue;
|
||||
if (matches(selector, style_sheet_for_rule, component_list_index - 1, static_cast<DOM::Element const&>(*ancestor), scope))
|
||||
if (matches(selector, style_sheet_for_rule, component_list_index - 1, static_cast<DOM::Element const&>(*ancestor), scope, selector_kind))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -647,16 +711,16 @@ static inline bool matches(CSS::Selector const& selector, Optional<CSS::CSSStyle
|
|||
VERIFY(component_list_index != 0);
|
||||
if (!element.parent() || !is<DOM::Element>(*element.parent()))
|
||||
return false;
|
||||
return matches(selector, style_sheet_for_rule, component_list_index - 1, static_cast<DOM::Element const&>(*element.parent()), scope);
|
||||
return matches(selector, style_sheet_for_rule, component_list_index - 1, static_cast<DOM::Element const&>(*element.parent()), scope, selector_kind);
|
||||
case CSS::Selector::Combinator::NextSibling:
|
||||
VERIFY(component_list_index != 0);
|
||||
if (auto* sibling = element.previous_element_sibling())
|
||||
return matches(selector, style_sheet_for_rule, component_list_index - 1, *sibling, scope);
|
||||
return matches(selector, style_sheet_for_rule, component_list_index - 1, *sibling, scope, selector_kind);
|
||||
return false;
|
||||
case CSS::Selector::Combinator::SubsequentSibling:
|
||||
VERIFY(component_list_index != 0);
|
||||
for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
|
||||
if (matches(selector, style_sheet_for_rule, component_list_index - 1, *sibling, scope))
|
||||
if (matches(selector, style_sheet_for_rule, component_list_index - 1, *sibling, scope, selector_kind))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -666,14 +730,14 @@ static inline bool matches(CSS::Selector const& selector, Optional<CSS::CSSStyle
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool matches(CSS::Selector const& selector, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, JS::GCPtr<DOM::ParentNode const> scope)
|
||||
bool matches(CSS::Selector const& selector, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, JS::GCPtr<DOM::ParentNode const> scope, SelectorKind selector_kind)
|
||||
{
|
||||
VERIFY(!selector.compound_selectors().is_empty());
|
||||
if (pseudo_element.has_value() && selector.pseudo_element().has_value() && selector.pseudo_element().value().type() != pseudo_element)
|
||||
return false;
|
||||
if (!pseudo_element.has_value() && selector.pseudo_element().has_value())
|
||||
return false;
|
||||
return matches(selector, style_sheet_for_rule, selector.compound_selectors().size() - 1, element, scope);
|
||||
return matches(selector, style_sheet_for_rule, selector.compound_selectors().size() - 1, element, scope, selector_kind);
|
||||
}
|
||||
|
||||
static bool fast_matches_simple_selector(CSS::Selector::SimpleSelector const& simple_selector, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element)
|
||||
|
@ -696,7 +760,7 @@ static bool fast_matches_simple_selector(CSS::Selector::SimpleSelector const& si
|
|||
case CSS::Selector::SimpleSelector::Type::Attribute:
|
||||
return matches_attribute(simple_selector.attribute(), style_sheet_for_rule, element);
|
||||
case CSS::Selector::SimpleSelector::Type::PseudoClass:
|
||||
return matches_pseudo_class(simple_selector.pseudo_class(), style_sheet_for_rule, element, nullptr);
|
||||
return matches_pseudo_class(simple_selector.pseudo_class(), style_sheet_for_rule, element, nullptr, SelectorKind::Normal);
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
|
|
@ -11,7 +11,12 @@
|
|||
|
||||
namespace Web::SelectorEngine {
|
||||
|
||||
bool matches(CSS::Selector const&, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const&, Optional<CSS::Selector::PseudoElement::Type> = {}, JS::GCPtr<DOM::ParentNode const> scope = {});
|
||||
enum class SelectorKind {
|
||||
Normal,
|
||||
Relative,
|
||||
};
|
||||
|
||||
bool matches(CSS::Selector const&, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const&, Optional<CSS::Selector::PseudoElement::Type> = {}, JS::GCPtr<DOM::ParentNode const> scope = {}, SelectorKind selector_kind = SelectorKind::Normal);
|
||||
|
||||
[[nodiscard]] bool fast_matches(CSS::Selector const&, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const&);
|
||||
[[nodiscard]] bool can_use_fast_matches(CSS::Selector const&);
|
||||
|
|
|
@ -527,6 +527,7 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
|||
}
|
||||
case CSS::PseudoClassMetadata::ParameterType::CompoundSelector:
|
||||
case CSS::PseudoClassMetadata::ParameterType::ForgivingSelectorList:
|
||||
case CSS::PseudoClassMetadata::ParameterType::ForgivingRelativeSelectorList:
|
||||
case CSS::PseudoClassMetadata::ParameterType::SelectorList: {
|
||||
builder.append("(["sv);
|
||||
for (auto& selector : pseudo_class.argument_selector_list)
|
||||
|
|
Loading…
Add table
Reference in a new issue