mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 13:57:35 +00:00
LibWeb: Add namespaces to Attribute selectors
For now, we parse these, but don't actually consider the namespace when matching them. `DOM::Element` does not (yet) store attribute namespaces so we can't check what they are.
This commit is contained in:
parent
1858f06881
commit
debf38ee9d
5 changed files with 51 additions and 36 deletions
|
@ -370,10 +370,9 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_attribute_simple_se
|
|||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
// FIXME: Handle namespace prefix for attribute name.
|
||||
auto const& attribute_part = attribute_tokens.next_token();
|
||||
if (!attribute_part.is(Token::Type::Ident)) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "Expected ident for attribute name, got: '{}'", attribute_part.to_debug_string());
|
||||
auto maybe_qualified_name = parse_selector_qualified_name(attribute_tokens, AllowWildcardName::No);
|
||||
if (!maybe_qualified_name.has_value()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "Expected qualified-name for attribute name, got: '{}'", attribute_tokens.peek_token().to_debug_string());
|
||||
return ParseError::SyntaxError;
|
||||
}
|
||||
|
||||
|
@ -386,7 +385,7 @@ Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_attribute_simple_se
|
|||
// they are converted to lowercase, so we do that here too. If we want to be
|
||||
// correct with XML later, we'll need to keep the original case and then do
|
||||
// a case-insensitive compare later.
|
||||
.name = FlyString::from_deprecated_fly_string(attribute_part.token().ident().to_lowercase_string()).release_value_but_fixme_should_propagate_errors(),
|
||||
.qualified_name = maybe_qualified_name.release_value(),
|
||||
.case_type = Selector::SimpleSelector::Attribute::CaseType::DefaultMatch,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -155,10 +155,15 @@ ErrorOr<String> Selector::SimpleSelector::serialize() const
|
|||
// 1. Append "[" (U+005B) to s.
|
||||
TRY(s.try_append('['));
|
||||
|
||||
// FIXME: 2. If the namespace prefix maps to a namespace that 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.
|
||||
// 2. If the namespace prefix maps to a namespace that 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 (attribute.qualified_name.namespace_type == QualifiedName::NamespaceType::Named) {
|
||||
TRY(serialize_an_identifier(s, attribute.qualified_name.namespace_));
|
||||
TRY(s.try_append('|'));
|
||||
}
|
||||
|
||||
// 3. Append the serialization of the attribute name as an identifier to s.
|
||||
TRY(serialize_an_identifier(s, attribute.name));
|
||||
TRY(serialize_an_identifier(s, attribute.qualified_name.name.name));
|
||||
|
||||
// 4. If there is an attribute value specified, append "=", "~=", "|=", "^=", "$=", or "*=" as appropriate (depending on the type of attribute selector),
|
||||
// followed by the serialization of the attribute value as a string, to s.
|
||||
|
|
|
@ -177,7 +177,7 @@ public:
|
|||
CaseInsensitiveMatch,
|
||||
};
|
||||
MatchType match_type;
|
||||
FlyString name {};
|
||||
QualifiedName qualified_name;
|
||||
String value {};
|
||||
CaseType case_type;
|
||||
};
|
||||
|
|
|
@ -120,11 +120,15 @@ static inline bool matches_indeterminate_pseudo_class(DOM::Element const& elemen
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline bool matches_attribute(CSS::Selector::SimpleSelector::Attribute const& attribute, DOM::Element const& element)
|
||||
static inline bool matches_attribute(CSS::Selector::SimpleSelector::Attribute const& attribute, [[maybe_unused]] Optional<CSS::CSSStyleSheet const&> style_sheet_for_rule, DOM::Element const& element)
|
||||
{
|
||||
// FIXME: Check the attribute's namespace, once we support that in DOM::Element!
|
||||
|
||||
auto attribute_name = attribute.qualified_name.name.name.to_deprecated_fly_string();
|
||||
|
||||
if (attribute.match_type == CSS::Selector::SimpleSelector::Attribute::MatchType::HasAttribute) {
|
||||
// Early way out in case of an attribute existence selector.
|
||||
return element.has_attribute(attribute.name.to_string().to_deprecated_string());
|
||||
return element.has_attribute(attribute_name);
|
||||
}
|
||||
|
||||
auto const case_insensitive_match = (attribute.case_type == CSS::Selector::SimpleSelector::Attribute::CaseType::CaseInsensitiveMatch);
|
||||
|
@ -135,14 +139,14 @@ static inline bool matches_attribute(CSS::Selector::SimpleSelector::Attribute co
|
|||
switch (attribute.match_type) {
|
||||
case CSS::Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch:
|
||||
return case_insensitive_match
|
||||
? Infra::is_ascii_case_insensitive_match(element.attribute(attribute.name.to_string().to_deprecated_string()), attribute.value)
|
||||
: element.attribute(attribute.name.to_string().to_deprecated_string()) == attribute.value.to_deprecated_string();
|
||||
? Infra::is_ascii_case_insensitive_match(element.attribute(attribute_name), attribute.value)
|
||||
: element.attribute(attribute_name) == attribute.value.to_deprecated_string();
|
||||
case CSS::Selector::SimpleSelector::Attribute::MatchType::ContainsWord: {
|
||||
if (attribute.value.is_empty()) {
|
||||
// This selector is always false is match value is empty.
|
||||
return false;
|
||||
}
|
||||
auto const view = element.attribute(attribute.name.to_string().to_deprecated_string()).split_view(' ');
|
||||
auto const view = element.attribute(attribute_name).split_view(' ');
|
||||
auto const size = view.size();
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
auto const value = view.at(i);
|
||||
|
@ -156,9 +160,9 @@ static inline bool matches_attribute(CSS::Selector::SimpleSelector::Attribute co
|
|||
}
|
||||
case CSS::Selector::SimpleSelector::Attribute::MatchType::ContainsString:
|
||||
return !attribute.value.is_empty()
|
||||
&& element.attribute(attribute.name.to_string().to_deprecated_string()).contains(attribute.value, case_sensitivity);
|
||||
&& element.attribute(attribute_name).contains(attribute.value, case_sensitivity);
|
||||
case CSS::Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment: {
|
||||
auto const element_attr_value = element.attribute(attribute.name.to_string().to_deprecated_string());
|
||||
auto const element_attr_value = element.attribute(attribute_name);
|
||||
if (element_attr_value.is_empty()) {
|
||||
// If the attribute value on element is empty, the selector is true
|
||||
// if the match value is also empty and false otherwise.
|
||||
|
@ -174,10 +178,10 @@ static inline bool matches_attribute(CSS::Selector::SimpleSelector::Attribute co
|
|||
}
|
||||
case CSS::Selector::SimpleSelector::Attribute::MatchType::StartsWithString:
|
||||
return !attribute.value.is_empty()
|
||||
&& element.attribute(attribute.name.to_string().to_deprecated_string()).starts_with(attribute.value, case_sensitivity);
|
||||
&& element.attribute(attribute_name).starts_with(attribute.value, case_sensitivity);
|
||||
case CSS::Selector::SimpleSelector::Attribute::MatchType::EndsWithString:
|
||||
return !attribute.value.is_empty()
|
||||
&& element.attribute(attribute.name.to_string().to_deprecated_string()).ends_with(attribute.value, case_sensitivity);
|
||||
&& element.attribute(attribute_name).ends_with(attribute.value, case_sensitivity);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -476,7 +480,7 @@ static inline bool matches(CSS::Selector::SimpleSelector const& component, Optio
|
|||
case CSS::Selector::SimpleSelector::Type::Class:
|
||||
return element.has_class(component.name());
|
||||
case CSS::Selector::SimpleSelector::Type::Attribute:
|
||||
return matches_attribute(component.attribute(), element);
|
||||
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);
|
||||
case CSS::Selector::SimpleSelector::Type::PseudoElement:
|
||||
|
|
|
@ -386,6 +386,26 @@ void dump_selector(CSS::Selector const& selector)
|
|||
dbgln("{}", builder.string_view());
|
||||
}
|
||||
|
||||
static void dump_qualified_name(StringBuilder& builder, CSS::Selector::SimpleSelector::QualifiedName const& 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);
|
||||
}
|
||||
|
||||
void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
||||
{
|
||||
builder.append(" CSS::Selector:\n"sv);
|
||||
|
@ -446,27 +466,12 @@ 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>()) {
|
||||
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);
|
||||
dump_qualified_name(builder, simple_selector.qualified_name());
|
||||
}
|
||||
|
||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass) {
|
||||
|
@ -681,7 +686,9 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
|
|||
break;
|
||||
}
|
||||
|
||||
builder.appendff(" [{}, name='{}', value='{}']", attribute_match_type_description, attribute.name, attribute.value);
|
||||
builder.appendff(" [{}, ", attribute_match_type_description);
|
||||
dump_qualified_name(builder, attribute.qualified_name);
|
||||
builder.appendff(", value='{}']", attribute.value);
|
||||
}
|
||||
|
||||
if (i != relative_selector.simple_selectors.size() - 1)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue