mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
LibWeb: Add namespaces to Universal and TagName selectors
This commit is contained in:
parent
6c2ed0f51b
commit
1858f06881
Notes:
sideshowbarker
2024-07-17 05:09:48 +09:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/SerenityOS/serenity/commit/1858f06881 Pull-request: https://github.com/SerenityOS/serenity/pull/20429
13 changed files with 245 additions and 49 deletions
|
@ -0,0 +1,41 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x207.3125 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,8) content-size 784x161.65625 children: inline
|
||||
line 0 width: 413.453125, height: 161.65625, bottom: 161.65625, baseline: 152
|
||||
frag 0 from SVGSVGBox start: 0, length: 0, rect: [9,9 300x150]
|
||||
frag 1 from TextNode start: 0, length: 1, rect: [310,146 8x17.46875]
|
||||
" "
|
||||
frag 2 from TextNode start: 0, length: 5, rect: [320,126 99.453125x43.671875]
|
||||
"Hello"
|
||||
SVGSVGBox <svg> at (9,9) content-size 300x150 [SVG] children: inline
|
||||
InlineNode <a>
|
||||
SVGTextBox <text> at (9,9) content-size 0x0 children: inline
|
||||
TextNode <#text>
|
||||
TextNode <#text>
|
||||
InlineNode <math>
|
||||
InlineNode <a>
|
||||
TextNode <#text>
|
||||
TextNode <#text>
|
||||
BlockContainer <div> at (9,170.65625) content-size 782x43.65625 children: inline
|
||||
line 0 width: 101.453125, height: 43.65625, bottom: 43.65625, baseline: 33.828125
|
||||
frag 0 from TextNode start: 0, length: 5, rect: [10,170.65625 99.453125x43.671875]
|
||||
"Hello"
|
||||
InlineNode <a>
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,215.3125) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
||||
|
||||
PaintableWithLines (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x207.3125]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,8 784x161.65625] overflow: [8,8 784x161.671875]
|
||||
SVGSVGPaintable (SVGSVGBox<svg>) [8,8 302x152]
|
||||
TextPaintable (TextNode<#text>)
|
||||
InlinePaintable (InlineNode<math>)
|
||||
InlinePaintable (InlineNode<a>)
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>) [8,169.65625 784x45.65625] overflow: [9,170.65625 782x43.671875]
|
||||
InlinePaintable (InlineNode<A>)
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,215.3125 784x0]
|
|
@ -0,0 +1,22 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x324 children: not-inline
|
||||
SVGSVGBox <svg> at (18,18) content-size 100x100 [SVG] children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,128) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <math> at (9,129) content-size 100x100 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,230) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div> at (9,231) content-size 100x100 children: not-inline
|
||||
BlockContainer <(anonymous)> at (8,332) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
||||
|
||||
PaintableWithLines (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x324]
|
||||
SVGSVGPaintable (SVGSVGBox<svg>) [8,8 120x120]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,128 784x0]
|
||||
PaintableWithLines (BlockContainer<math>) [8,128 102x102]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,230 784x0]
|
||||
PaintableWithLines (BlockContainer<DIV>) [8,230 102x102]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,332 784x0]
|
|
@ -0,0 +1,23 @@
|
|||
<style>
|
||||
@namespace s "http://www.w3.org/2000/svg";
|
||||
|
||||
body * {
|
||||
border: 1px solid black;
|
||||
}
|
||||
a {
|
||||
width: 100px;
|
||||
}
|
||||
|a {
|
||||
width: 200px !important;
|
||||
}
|
||||
*|a {
|
||||
height: 100px;
|
||||
font-size: 40px;
|
||||
}
|
||||
s|a {
|
||||
font-size: 80px;
|
||||
}
|
||||
</style>
|
||||
<svg><a><text x="20" y="80">Hello</text></a></svg>
|
||||
<math><a>Hello</a></math>
|
||||
<div><a>Hello</a></div>
|
|
@ -0,0 +1,21 @@
|
|||
<style>
|
||||
@namespace s "http://www.w3.org/2000/svg";
|
||||
|
||||
body * {
|
||||
display: block;
|
||||
width: 100px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
body |* {
|
||||
width: 200px !important;
|
||||
}
|
||||
body *|* {
|
||||
height: 100px;
|
||||
}
|
||||
body s|* {
|
||||
border-width: 10px;
|
||||
}
|
||||
</style>
|
||||
<svg></svg>
|
||||
<math></math>
|
||||
<div></div>
|
|
@ -740,15 +740,28 @@ Parser::ParseErrorOr<Optional<Selector::SimpleSelector>> Parser::parse_simple_se
|
|||
if (peek_token_ends_selector())
|
||||
return Optional<Selector::SimpleSelector> {};
|
||||
|
||||
// Handle universal and tag-name types together, since both can be namespaced
|
||||
if (auto qualified_name = parse_selector_qualified_name(tokens, AllowWildcardName::Yes); qualified_name.has_value()) {
|
||||
if (qualified_name->name.name == "*"sv) {
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::Universal,
|
||||
.value = qualified_name.release_value(),
|
||||
};
|
||||
}
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::TagName,
|
||||
.value = qualified_name.release_value(),
|
||||
};
|
||||
}
|
||||
|
||||
auto const& first_value = tokens.next_token();
|
||||
|
||||
if (first_value.is(Token::Type::Delim)) {
|
||||
u32 delim = first_value.token().delim();
|
||||
switch (delim) {
|
||||
case '*':
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::Universal
|
||||
};
|
||||
// Handled already
|
||||
VERIFY_NOT_REACHED();
|
||||
case '.': {
|
||||
if (peek_token_ends_selector())
|
||||
return ParseError::SyntaxError;
|
||||
|
@ -787,13 +800,7 @@ Parser::ParseErrorOr<Optional<Selector::SimpleSelector>> Parser::parse_simple_se
|
|||
.value = Selector::SimpleSelector::Name { FlyString::from_utf8(first_value.token().hash_value()).release_value_but_fixme_should_propagate_errors() }
|
||||
};
|
||||
}
|
||||
if (first_value.is(Token::Type::Ident)) {
|
||||
return Selector::SimpleSelector {
|
||||
.type = Selector::SimpleSelector::Type::TagName,
|
||||
// FIXME: XML requires case-sensitivity for identifiers, while HTML does not. As such, this should be reworked if XML support is added.
|
||||
.value = Selector::SimpleSelector::Name { FlyString::from_deprecated_fly_string(first_value.token().ident().to_lowercase_string()).release_value_but_fixme_should_propagate_errors() }
|
||||
};
|
||||
}
|
||||
|
||||
if (first_value.is_block() && first_value.block().is_square())
|
||||
return TRY(parse_attribute_simple_selector(first_value));
|
||||
|
||||
|
|
|
@ -124,17 +124,31 @@ ErrorOr<String> Selector::SimpleSelector::serialize() const
|
|||
StringBuilder s;
|
||||
switch (type) {
|
||||
case Selector::SimpleSelector::Type::TagName:
|
||||
case Selector::SimpleSelector::Type::Universal:
|
||||
// FIXME: 1. If the namespace prefix maps to a namespace that is not the default namespace and is not the null namespace (not in a namespace) append the serialization of the namespace prefix as an identifier, followed by a "|" (U+007C) to s.
|
||||
// FIXME: 2. If the namespace prefix maps to a namespace that is the null namespace (not in a namespace) append "|" (U+007C) to s.
|
||||
// 3. If this is a type selector append the serialization of the element name as an identifier to s.
|
||||
if (type == Selector::SimpleSelector::Type::TagName) {
|
||||
TRY(serialize_an_identifier(s, name()));
|
||||
case Selector::SimpleSelector::Type::Universal: {
|
||||
auto qualified_name = this->qualified_name();
|
||||
// 1. If the namespace prefix maps to a namespace that is not the default namespace and is not the null
|
||||
// namespace (not in a namespace) append the serialization of the namespace prefix as an identifier,
|
||||
// followed by a "|" (U+007C) to s.
|
||||
if (qualified_name.namespace_type == QualifiedName::NamespaceType::Named) {
|
||||
TRY(serialize_an_identifier(s, qualified_name.namespace_));
|
||||
TRY(s.try_append('|'));
|
||||
}
|
||||
|
||||
// 2. If the namespace prefix maps to a namespace that is the null namespace (not in a namespace)
|
||||
// append "|" (U+007C) to s.
|
||||
if (qualified_name.namespace_type == QualifiedName::NamespaceType::None)
|
||||
TRY(s.try_append('|'));
|
||||
|
||||
// 3. If this is a type selector append the serialization of the element name as an identifier to s.
|
||||
if (type == Selector::SimpleSelector::Type::TagName)
|
||||
TRY(serialize_an_identifier(s, qualified_name.name.name));
|
||||
|
||||
// 4. If this is a universal selector append "*" (U+002A) to s.
|
||||
if (type == Selector::SimpleSelector::Type::Universal)
|
||||
TRY(s.try_append('*'));
|
||||
|
||||
break;
|
||||
}
|
||||
case Selector::SimpleSelector::Type::Attribute: {
|
||||
auto& attribute = this->attribute();
|
||||
|
||||
|
@ -300,11 +314,23 @@ ErrorOr<String> Selector::serialize() const
|
|||
&& compound_selector.simple_selectors.first().type == Selector::SimpleSelector::Type::Universal) {
|
||||
TRY(s.try_append(TRY(compound_selector.simple_selectors.first().serialize())));
|
||||
}
|
||||
// 2. Otherwise, for each simple selector in the compound selectors...
|
||||
// FIXME: ...that is not a universal selector of which the namespace prefix maps to a namespace that is not the default namespace...
|
||||
// ...serialize the simple selector and append the result to s.
|
||||
// 2. Otherwise, for each simple selector in the compound selectors that is not a universal selector
|
||||
// of which the namespace prefix maps to a namespace that is not the default namespace
|
||||
// serialize the simple selector and append the result to s.
|
||||
else {
|
||||
for (auto& simple_selector : compound_selector.simple_selectors) {
|
||||
if (simple_selector.type == SimpleSelector::Type::Universal) {
|
||||
auto qualified_name = simple_selector.qualified_name();
|
||||
if (qualified_name.namespace_type == SimpleSelector::QualifiedName::NamespaceType::Default)
|
||||
continue;
|
||||
// FIXME: I *think* if we have a namespace prefix that happens to equal the same as the default namespace,
|
||||
// we also should skip it. But we don't have access to that here. eg:
|
||||
// <style>
|
||||
// @namespace "http://example";
|
||||
// @namespace foo "http://example";
|
||||
// foo|*.bar { } /* This would skip the `foo|*` when serializing. */
|
||||
// </style>
|
||||
}
|
||||
TRY(s.try_append(TRY(simple_selector.serialize())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ public:
|
|||
};
|
||||
|
||||
Type type;
|
||||
Variant<Empty, Attribute, PseudoClass, PseudoElement, Name> value {};
|
||||
Variant<Empty, Attribute, PseudoClass, PseudoElement, Name, QualifiedName> value {};
|
||||
|
||||
Attribute const& attribute() const { return value.get<Attribute>(); }
|
||||
Attribute& attribute() { return value.get<Attribute>(); }
|
||||
|
@ -196,6 +196,8 @@ public:
|
|||
FlyString& name() { return value.get<Name>().name; }
|
||||
FlyString const& lowercase_name() const { return value.get<Name>().lowercase_name; }
|
||||
FlyString& lowercase_name() { return value.get<Name>().lowercase_name; }
|
||||
QualifiedName const& qualified_name() const { return value.get<QualifiedName>(); }
|
||||
QualifiedName& qualified_name() { return value.get<QualifiedName>(); }
|
||||
|
||||
ErrorOr<String> serialize() const;
|
||||
};
|
||||
|
|
|
@ -203,7 +203,7 @@ static inline DOM::Element const* next_sibling_with_same_tag_name(DOM::Element c
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, DOM::Element const& element, JS::GCPtr<DOM::ParentNode const> scope)
|
||||
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)
|
||||
{
|
||||
switch (pseudo_class.type) {
|
||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Link:
|
||||
|
@ -280,13 +280,13 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Is:
|
||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Where:
|
||||
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||
if (matches(selector, element))
|
||||
if (matches(selector, style_sheet_for_rule, element))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case CSS::Selector::SimpleSelector::PseudoClass::Type::Not:
|
||||
for (auto& selector : pseudo_class.argument_selector_list) {
|
||||
if (matches(selector, element))
|
||||
if (matches(selector, style_sheet_for_rule, element))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -303,11 +303,11 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
if (!parent)
|
||||
return false;
|
||||
|
||||
auto matches_selector_list = [](CSS::SelectorList const& list, DOM::Element const& element) {
|
||||
auto matches_selector_list = [&style_sheet_for_rule](CSS::SelectorList const& list, DOM::Element const& element) {
|
||||
if (list.is_empty())
|
||||
return true;
|
||||
for (auto const& child_selector : list) {
|
||||
if (matches(child_selector, element)) {
|
||||
if (matches(child_selector, style_sheet_for_rule, element)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -426,24 +426,59 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline bool matches(CSS::Selector::SimpleSelector const& component, 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)
|
||||
{
|
||||
switch (component.type) {
|
||||
case CSS::Selector::SimpleSelector::Type::Universal:
|
||||
return true;
|
||||
case CSS::Selector::SimpleSelector::Type::TagName: {
|
||||
auto qualified_name = component.qualified_name();
|
||||
|
||||
// Reject if the tag name doesn't match
|
||||
if (component.type == CSS::Selector::SimpleSelector::Type::TagName) {
|
||||
// See https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors
|
||||
if (element.document().document_type() == DOM::Document::Type::HTML) {
|
||||
if (qualified_name.name.lowercase_name != element.local_name().view())
|
||||
return false;
|
||||
} else if (!Infra::is_ascii_case_insensitive_match(qualified_name.name.name, element.local_name())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Match the namespace
|
||||
switch (qualified_name.namespace_type) {
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Default:
|
||||
// "if no default namespace has been declared for selectors, this is equivalent to *|E."
|
||||
if (!style_sheet_for_rule.has_value() || !style_sheet_for_rule->default_namespace().has_value())
|
||||
return true;
|
||||
// "Otherwise it is equivalent to ns|E where ns is the default namespace."
|
||||
return element.namespace_() == style_sheet_for_rule->default_namespace();
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::None:
|
||||
// "elements with name E without a namespace"
|
||||
return element.namespace_().is_empty();
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Any:
|
||||
// "elements with name E in any namespace, including those without a namespace"
|
||||
return true;
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Named:
|
||||
// "elements with name E in namespace ns"
|
||||
// Unrecognized namespace prefixes are invalid, so don't match.
|
||||
// (We can't detect this at parse time, since a namespace rule may be inserted later.)
|
||||
// So, if we don't have a context to look up namespaces from, we fail to match.
|
||||
if (!style_sheet_for_rule.has_value())
|
||||
return false;
|
||||
|
||||
auto selector_namespace = style_sheet_for_rule->namespace_uri(qualified_name.namespace_);
|
||||
return selector_namespace.has_value() && selector_namespace.value() == element.namespace_();
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
case CSS::Selector::SimpleSelector::Type::Id:
|
||||
return component.name() == element.attribute(HTML::AttributeNames::id).view();
|
||||
case CSS::Selector::SimpleSelector::Type::Class:
|
||||
return element.has_class(component.name());
|
||||
case CSS::Selector::SimpleSelector::Type::TagName:
|
||||
// See https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors
|
||||
if (element.document().document_type() == DOM::Document::Type::HTML)
|
||||
return component.lowercase_name() == element.local_name().view();
|
||||
return Infra::is_ascii_case_insensitive_match(component.name(), 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, scope);
|
||||
return matches_pseudo_class(component.pseudo_class(), style_sheet_for_rule, element, scope);
|
||||
case CSS::Selector::SimpleSelector::Type::PseudoElement:
|
||||
// Pseudo-element matching/not-matching is handled in the top level matches().
|
||||
return true;
|
||||
|
@ -452,11 +487,11 @@ static inline bool matches(CSS::Selector::SimpleSelector const& component, DOM::
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool matches(CSS::Selector const& selector, 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)
|
||||
{
|
||||
auto& relative_selector = selector.compound_selectors()[component_list_index];
|
||||
for (auto& simple_selector : relative_selector.simple_selectors) {
|
||||
if (!matches(simple_selector, element, scope))
|
||||
if (!matches(simple_selector, style_sheet_for_rule, element, scope))
|
||||
return false;
|
||||
}
|
||||
switch (relative_selector.combinator) {
|
||||
|
@ -467,7 +502,7 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind
|
|||
for (auto* ancestor = element.parent(); ancestor; ancestor = ancestor->parent()) {
|
||||
if (!is<DOM::Element>(*ancestor))
|
||||
continue;
|
||||
if (matches(selector, 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))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -475,16 +510,16 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind
|
|||
VERIFY(component_list_index != 0);
|
||||
if (!element.parent() || !is<DOM::Element>(*element.parent()))
|
||||
return false;
|
||||
return matches(selector, 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);
|
||||
case CSS::Selector::Combinator::NextSibling:
|
||||
VERIFY(component_list_index != 0);
|
||||
if (auto* sibling = element.previous_element_sibling())
|
||||
return matches(selector, component_list_index - 1, *sibling, scope);
|
||||
return matches(selector, style_sheet_for_rule, component_list_index - 1, *sibling, scope);
|
||||
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, component_list_index - 1, *sibling, scope))
|
||||
if (matches(selector, style_sheet_for_rule, component_list_index - 1, *sibling, scope))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -494,14 +529,14 @@ static inline bool matches(CSS::Selector const& selector, int component_list_ind
|
|||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
bool matches(CSS::Selector const& selector, DOM::Element const& element, Optional<CSS::Selector::PseudoElement> 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> pseudo_element, JS::GCPtr<DOM::ParentNode const> scope)
|
||||
{
|
||||
VERIFY(!selector.compound_selectors().is_empty());
|
||||
if (pseudo_element.has_value() && selector.pseudo_element() != pseudo_element)
|
||||
return false;
|
||||
if (!pseudo_element.has_value() && selector.pseudo_element().has_value())
|
||||
return false;
|
||||
return matches(selector, selector.compound_selectors().size() - 1, element, scope);
|
||||
return matches(selector, style_sheet_for_rule, selector.compound_selectors().size() - 1, element, scope);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
|
||||
namespace Web::SelectorEngine {
|
||||
|
||||
bool matches(CSS::Selector const&, DOM::Element const&, Optional<CSS::Selector::PseudoElement> = {}, JS::GCPtr<DOM::ParentNode const> scope = {});
|
||||
bool matches(CSS::Selector const&, Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const&, Optional<CSS::Selector::PseudoElement> = {}, JS::GCPtr<DOM::ParentNode const> scope = {});
|
||||
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
|
|||
matching_rules.ensure_capacity(rules_to_run.size());
|
||||
for (auto const& rule_to_run : rules_to_run) {
|
||||
auto const& selector = rule_to_run.rule->selectors()[rule_to_run.selector_index];
|
||||
if (SelectorEngine::matches(selector, element, pseudo_element))
|
||||
if (SelectorEngine::matches(selector, *rule_to_run.sheet, element, pseudo_element))
|
||||
matching_rules.append(rule_to_run);
|
||||
}
|
||||
return matching_rules;
|
||||
|
@ -2614,7 +2614,7 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
|
|||
break;
|
||||
}
|
||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::TagName) {
|
||||
rule_cache->rules_by_tag_name.ensure(simple_selector.name()).append(move(matching_rule));
|
||||
rule_cache->rules_by_tag_name.ensure(simple_selector.qualified_name().name.lowercase_name).append(move(matching_rule));
|
||||
++num_tag_name_rules;
|
||||
added_to_bucket = true;
|
||||
break;
|
||||
|
|
|
@ -562,7 +562,7 @@ WebIDL::ExceptionOr<bool> Element::matches(StringView selectors) const
|
|||
// 3. If the result of match a selector against an element, using s, this, and scoping root this, returns success, then return true; otherwise, return false.
|
||||
auto sel = maybe_selectors.value();
|
||||
for (auto& s : sel) {
|
||||
if (SelectorEngine::matches(s, *this, {}, static_cast<ParentNode const*>(this)))
|
||||
if (SelectorEngine::matches(s, {}, *this, {}, static_cast<ParentNode const*>(this)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -581,7 +581,7 @@ WebIDL::ExceptionOr<DOM::Element const*> Element::closest(StringView selectors)
|
|||
auto matches_selectors = [this](CSS::SelectorList const& selector_list, Element const* element) {
|
||||
// 4. For each element in elements, if match a selector against an element, using s, element, and scoping root this, returns success, return element.
|
||||
for (auto& selector : selector_list) {
|
||||
if (!SelectorEngine::matches(selector, *element, {}, this))
|
||||
if (!SelectorEngine::matches(selector, {}, *element, {}, this))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -39,7 +39,7 @@ WebIDL::ExceptionOr<JS::GCPtr<Element>> ParentNode::query_selector(StringView se
|
|||
// FIXME: This should be shadow-including. https://drafts.csswg.org/selectors-4/#match-a-selector-against-a-tree
|
||||
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||
for (auto& selector : selectors) {
|
||||
if (SelectorEngine::matches(selector, element, {}, this)) {
|
||||
if (SelectorEngine::matches(selector, {}, element, {}, this)) {
|
||||
result = &element;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<NodeList>> ParentNode::query_selector_all(S
|
|||
// FIXME: This should be shadow-including. https://drafts.csswg.org/selectors-4/#match-a-selector-against-a-tree
|
||||
for_each_in_subtree_of_type<Element>([&](auto& element) {
|
||||
for (auto& selector : selectors) {
|
||||
if (SelectorEngine::matches(selector, element, {}, this)) {
|
||||
if (SelectorEngine::matches(selector, {}, element, {}, this)) {
|
||||
elements.append(&element);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -447,8 +447,27 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
|||
|
||||
builder.appendff("{}:", type_description);
|
||||
// FIXME: This is goofy
|
||||
if (simple_selector.value.has<CSS::Selector::SimpleSelector::Name>())
|
||||
if (simple_selector.value.has<CSS::Selector::SimpleSelector::Name>()) {
|
||||
builder.append(simple_selector.name());
|
||||
} else if (simple_selector.value.has<CSS::Selector::SimpleSelector::QualifiedName>()) {
|
||||
auto qualified_name = simple_selector.qualified_name();
|
||||
StringView namespace_type;
|
||||
switch (qualified_name.namespace_type) {
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Default:
|
||||
namespace_type = "Default"sv;
|
||||
break;
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::None:
|
||||
namespace_type = "None"sv;
|
||||
break;
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Any:
|
||||
namespace_type = "Any"sv;
|
||||
break;
|
||||
case CSS::Selector::SimpleSelector::QualifiedName::NamespaceType::Named:
|
||||
namespace_type = "Named"sv;
|
||||
break;
|
||||
}
|
||||
builder.appendff(" [NamespaceType={}, Namespace='{}', Name='{}']", namespace_type, qualified_name.namespace_, qualified_name.name.name);
|
||||
}
|
||||
|
||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
|
||||
auto const& pseudo_class = simple_selector.pseudo_class();
|
||||
|
|
Loading…
Reference in a new issue