1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 14:07:45 +00:00

LibWeb: Allow whitespace inside CSS attribute selectors

Noticed this while checking some MediaWiki-based sites. It's not
obvious, but the spec does allow this, by not mentioning it in this list
of places whitespace is forbidden:
https://www.w3.org/TR/selectors-4/#white-space
This commit is contained in:
Sam Atkins 2021-10-28 13:42:47 +01:00 committed by Andreas Kling
parent 691e4820ac
commit 6f1debaab0

View file

@ -387,15 +387,17 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
}; };
} else if (first_value.is_block() && first_value.block().is_square()) { } else if (first_value.is_block() && first_value.block().is_square()) {
auto& attribute_parts = first_value.block().values(); auto attribute_tokens = TokenStream { first_value.block().values() };
if (attribute_parts.is_empty()) { attribute_tokens.skip_whitespace();
if (!attribute_tokens.has_next_token()) {
dbgln_if(CSS_PARSER_DEBUG, "CSS attribute selector is empty!"); dbgln_if(CSS_PARSER_DEBUG, "CSS attribute selector is empty!");
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
} }
// FIXME: Handle namespace prefix for attribute name. // FIXME: Handle namespace prefix for attribute name.
auto& attribute_part = attribute_parts.first(); auto& attribute_part = attribute_tokens.next_token();
if (!attribute_part.is(Token::Type::Ident)) { if (!attribute_part.is(Token::Type::Ident)) {
dbgln_if(CSS_PARSER_DEBUG, "Expected ident for attribute name, got: '{}'", attribute_part.to_debug_string()); dbgln_if(CSS_PARSER_DEBUG, "Expected ident for attribute name, got: '{}'", attribute_part.to_debug_string());
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
@ -414,11 +416,11 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
} }
}; };
if (attribute_parts.size() == 1) attribute_tokens.skip_whitespace();
if (!attribute_tokens.has_next_token())
return simple_selector; return simple_selector;
size_t attribute_index = 1; auto& delim_part = attribute_tokens.next_token();
auto& delim_part = attribute_parts.at(attribute_index);
if (!delim_part.is(Token::Type::Delim)) { if (!delim_part.is(Token::Type::Delim)) {
dbgln_if(CSS_PARSER_DEBUG, "Expected a delim for attribute comparison, got: '{}'", delim_part.to_debug_string()); dbgln_if(CSS_PARSER_DEBUG, "Expected a delim for attribute comparison, got: '{}'", delim_part.to_debug_string());
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
@ -426,15 +428,13 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
if (delim_part.token().delim() == "="sv) { if (delim_part.token().delim() == "="sv) {
simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch; simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch;
attribute_index++;
} else { } else {
attribute_index++; if (!attribute_tokens.has_next_token()) {
if (attribute_index >= attribute_parts.size()) {
dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended part way through a match type."); dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended part way through a match type.");
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
} }
auto& delim_second_part = attribute_parts.at(attribute_index); auto& delim_second_part = attribute_tokens.next_token();
if (!(delim_second_part.is(Token::Type::Delim) && delim_second_part.token().delim() == "=")) { if (!(delim_second_part.is(Token::Type::Delim) && delim_second_part.token().delim() == "=")) {
dbgln_if(CSS_PARSER_DEBUG, "Expected a double delim for attribute comparison, got: '{}{}'", delim_part.to_debug_string(), delim_second_part.to_debug_string()); dbgln_if(CSS_PARSER_DEBUG, "Expected a double delim for attribute comparison, got: '{}{}'", delim_part.to_debug_string(), delim_second_part.to_debug_string());
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
@ -442,34 +442,34 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
if (delim_part.token().delim() == "~"sv) { if (delim_part.token().delim() == "~"sv) {
simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsWord; simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsWord;
attribute_index++;
} else if (delim_part.token().delim() == "*"sv) { } else if (delim_part.token().delim() == "*"sv) {
simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsString; simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsString;
attribute_index++;
} else if (delim_part.token().delim() == "|"sv) { } else if (delim_part.token().delim() == "|"sv) {
simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment; simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment;
attribute_index++;
} else if (delim_part.token().delim() == "^"sv) { } else if (delim_part.token().delim() == "^"sv) {
simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithString; simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithString;
attribute_index++;
} else if (delim_part.token().delim() == "$"sv) { } else if (delim_part.token().delim() == "$"sv) {
simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::EndsWithString; simple_selector.attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::EndsWithString;
attribute_index++; } else {
attribute_tokens.reconsume_current_input_token();
} }
} }
if (attribute_index >= attribute_parts.size()) { attribute_tokens.skip_whitespace();
if (!attribute_tokens.has_next_token()) {
dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended without a value to match."); dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended without a value to match.");
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
} }
auto& value_part = attribute_parts.at(attribute_index); auto& value_part = attribute_tokens.next_token();
if (!value_part.is(Token::Type::Ident) && !value_part.is(Token::Type::String)) { if (!value_part.is(Token::Type::Ident) && !value_part.is(Token::Type::String)) {
dbgln_if(CSS_PARSER_DEBUG, "Expected a string or ident for the value to match attribute against, got: '{}'", value_part.to_debug_string()); dbgln_if(CSS_PARSER_DEBUG, "Expected a string or ident for the value to match attribute against, got: '{}'", value_part.to_debug_string());
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
} }
simple_selector.attribute.value = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string(); simple_selector.attribute.value = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string();
attribute_tokens.skip_whitespace();
// FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case // FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
return simple_selector; return simple_selector;