mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 01:47:34 +00:00
LibWeb: Add 'Attribute' as a CSS SimpleSelector::Type
Previously, SimpleSelectors optionally had Attribute-selector data as well as their main type. Now, they're either one or the other, which better matches the spec, and makes parsing and matching more straightforward.
This commit is contained in:
parent
dadcb46344
commit
96b2356cbb
5 changed files with 136 additions and 139 deletions
|
@ -424,29 +424,31 @@ public:
|
|||
if (!peek() || peek() == '{' || peek() == ',' || is_combinator(peek()))
|
||||
return {};
|
||||
|
||||
CSS::Selector::SimpleSelector::Type type;
|
||||
CSS::Selector::SimpleSelector simple_selector;
|
||||
|
||||
if (peek() == '*') {
|
||||
type = CSS::Selector::SimpleSelector::Type::Universal;
|
||||
simple_selector.type = CSS::Selector::SimpleSelector::Type::Universal;
|
||||
consume_one();
|
||||
CSS::Selector::SimpleSelector result;
|
||||
result.type = type;
|
||||
return result;
|
||||
return simple_selector;
|
||||
}
|
||||
|
||||
if (peek() == '.') {
|
||||
type = CSS::Selector::SimpleSelector::Type::Class;
|
||||
simple_selector.type = CSS::Selector::SimpleSelector::Type::Class;
|
||||
consume_one();
|
||||
} else if (peek() == '#') {
|
||||
type = CSS::Selector::SimpleSelector::Type::Id;
|
||||
simple_selector.type = CSS::Selector::SimpleSelector::Type::Id;
|
||||
consume_one();
|
||||
} else if (isalpha(peek())) {
|
||||
type = CSS::Selector::SimpleSelector::Type::TagName;
|
||||
simple_selector.type = CSS::Selector::SimpleSelector::Type::TagName;
|
||||
} else if (peek() == '[') {
|
||||
simple_selector.type = CSS::Selector::SimpleSelector::Type::Attribute;
|
||||
} else {
|
||||
type = CSS::Selector::SimpleSelector::Type::Universal;
|
||||
simple_selector.type = CSS::Selector::SimpleSelector::Type::Universal;
|
||||
}
|
||||
|
||||
if (type != CSS::Selector::SimpleSelector::Type::Universal) {
|
||||
if ((simple_selector.type != CSS::Selector::SimpleSelector::Type::Universal)
|
||||
&& (simple_selector.type != CSS::Selector::SimpleSelector::Type::Attribute)) {
|
||||
|
||||
while (is_valid_selector_char(peek()))
|
||||
buffer.append(consume_one());
|
||||
PARSE_VERIFY(!buffer.is_empty());
|
||||
|
@ -454,18 +456,16 @@ public:
|
|||
|
||||
auto value = String::copy(buffer);
|
||||
|
||||
if (type == CSS::Selector::SimpleSelector::Type::TagName) {
|
||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::TagName) {
|
||||
// Some stylesheets use uppercase tag names, so here's a hack to just lowercase them internally.
|
||||
value = value.to_lowercase();
|
||||
}
|
||||
|
||||
CSS::Selector::SimpleSelector simple_selector;
|
||||
simple_selector.type = type;
|
||||
simple_selector.value = value;
|
||||
buffer.clear();
|
||||
|
||||
if (peek() == '[') {
|
||||
CSS::Selector::SimpleSelector::AttributeMatchType attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::HasAttribute;
|
||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Attribute) {
|
||||
CSS::Selector::SimpleSelector::Attribute::MatchType attribute_match_type = CSS::Selector::SimpleSelector::Attribute::MatchType::HasAttribute;
|
||||
String attribute_name;
|
||||
String attribute_value;
|
||||
bool in_value = false;
|
||||
|
@ -475,10 +475,10 @@ public:
|
|||
char ch = consume_one();
|
||||
if (ch == '=' || (ch == '~' && peek() == '=')) {
|
||||
if (ch == '=') {
|
||||
attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::ExactValueMatch;
|
||||
attribute_match_type = CSS::Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch;
|
||||
} else if (ch == '~') {
|
||||
consume_one();
|
||||
attribute_match_type = CSS::Selector::SimpleSelector::AttributeMatchType::ContainsWord;
|
||||
attribute_match_type = CSS::Selector::SimpleSelector::Attribute::MatchType::ContainsWord;
|
||||
}
|
||||
attribute_name = String::copy(buffer);
|
||||
buffer.clear();
|
||||
|
@ -503,9 +503,9 @@ public:
|
|||
else
|
||||
attribute_name = String::copy(buffer);
|
||||
buffer.clear();
|
||||
simple_selector.attribute_match_type = attribute_match_type;
|
||||
simple_selector.attribute_name = attribute_name;
|
||||
simple_selector.attribute_value = attribute_value;
|
||||
simple_selector.attribute.match_type = attribute_match_type;
|
||||
simple_selector.attribute.name = attribute_name;
|
||||
simple_selector.attribute.value = attribute_value;
|
||||
if (expected_end_of_attribute_selector != ']') {
|
||||
if (!consume_specific(expected_end_of_attribute_selector))
|
||||
return {};
|
||||
|
|
|
@ -243,26 +243,18 @@ Optional<Selector> Parser::parse_single_selector(TokenStream<T>& tokens, bool is
|
|||
if (check_for_eof_or_whitespace(current_value))
|
||||
return {};
|
||||
|
||||
Selector::SimpleSelector::Type type;
|
||||
String value;
|
||||
Selector::SimpleSelector simple_selector;
|
||||
// FIXME: Handle namespace prefixes.
|
||||
|
||||
if (current_value.is(Token::Type::Delim) && ((Token)current_value).delim() == "*") {
|
||||
|
||||
// FIXME: Handle selectors like `*.foo`.
|
||||
type = Selector::SimpleSelector::Type::Universal;
|
||||
Selector::SimpleSelector result;
|
||||
result.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (current_value.is(Token::Type::Hash)) {
|
||||
simple_selector.type = Selector::SimpleSelector::Type::Universal;
|
||||
} else if (current_value.is(Token::Type::Hash)) {
|
||||
if (((Token)current_value).m_hash_type != Token::HashType::Id) {
|
||||
dbgln("Selector contains hash token that is not an id: {}", current_value.to_debug_string());
|
||||
return {};
|
||||
}
|
||||
type = Selector::SimpleSelector::Type::Id;
|
||||
value = ((Token)current_value).m_value.to_string();
|
||||
simple_selector.type = Selector::SimpleSelector::Type::Id;
|
||||
simple_selector.value = ((Token)current_value).m_value.to_string();
|
||||
} else if (current_value.is(Token::Type::Delim) && ((Token)current_value).delim() == ".") {
|
||||
current_value = tokens.next_token();
|
||||
if (check_for_eof_or_whitespace(current_value))
|
||||
|
@ -273,33 +265,19 @@ Optional<Selector> Parser::parse_single_selector(TokenStream<T>& tokens, bool is
|
|||
return {};
|
||||
}
|
||||
|
||||
type = Selector::SimpleSelector::Type::Class;
|
||||
value = current_value.token().ident().to_lowercase_string();
|
||||
} else if (current_value.is(Token::Type::Delim) && current_value.token().delim() == "*") {
|
||||
type = Selector::SimpleSelector::Type::Universal;
|
||||
simple_selector.type = Selector::SimpleSelector::Type::Class;
|
||||
simple_selector.value = current_value.token().ident().to_lowercase_string();
|
||||
} else if (current_value.is(Token::Type::Ident)) {
|
||||
type = Selector::SimpleSelector::Type::TagName;
|
||||
value = current_value.token().ident().to_lowercase_string();
|
||||
} else if ((current_value.is(Token::Type::Delim) && current_value.token().delim() == ":")
|
||||
|| (current_value.is_block() && current_value.block().is_square())) {
|
||||
simple_selector.type = Selector::SimpleSelector::Type::TagName;
|
||||
simple_selector.value = current_value.token().ident().to_lowercase_string();
|
||||
} else if ((current_value.is(Token::Type::Delim) && current_value.token().delim() == ":")) {
|
||||
// FIXME: This is a temporary hack until we make the Selector::SimpleSelector::Type changes.
|
||||
type = Selector::SimpleSelector::Type::Universal;
|
||||
simple_selector.type = Selector::SimpleSelector::Type::Universal;
|
||||
tokens.reconsume_current_input_token();
|
||||
} else {
|
||||
dbgln("Invalid simple selector!");
|
||||
return {};
|
||||
}
|
||||
} else if (current_value.is_block() && current_value.block().is_square()) {
|
||||
simple_selector.type = Selector::SimpleSelector::Type::Attribute;
|
||||
|
||||
Selector::SimpleSelector simple_selector;
|
||||
simple_selector.type = type;
|
||||
simple_selector.value = value;
|
||||
|
||||
current_value = tokens.next_token();
|
||||
if (check_for_eof_or_whitespace(current_value))
|
||||
return simple_selector;
|
||||
|
||||
// FIXME: Attribute selectors want to be their own Selector::SimpleSelector::Type according to the spec.
|
||||
if (current_value.is_block() && current_value.block().is_square()) {
|
||||
auto& attribute = simple_selector.attribute;
|
||||
|
||||
Vector<StyleComponentValueRule> const& attribute_parts = current_value.block().values();
|
||||
|
||||
|
@ -315,8 +293,8 @@ Optional<Selector> Parser::parse_single_selector(TokenStream<T>& tokens, bool is
|
|||
return {};
|
||||
}
|
||||
|
||||
simple_selector.attribute_match_type = Selector::SimpleSelector::AttributeMatchType::HasAttribute;
|
||||
simple_selector.attribute_name = attribute_part.token().ident();
|
||||
attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::HasAttribute;
|
||||
attribute.name = attribute_part.token().ident();
|
||||
|
||||
if (attribute_parts.size() == 1)
|
||||
return simple_selector;
|
||||
|
@ -329,7 +307,7 @@ Optional<Selector> Parser::parse_single_selector(TokenStream<T>& tokens, bool is
|
|||
}
|
||||
|
||||
if (delim_part.token().delim() == "=") {
|
||||
simple_selector.attribute_match_type = Selector::SimpleSelector::AttributeMatchType::ExactValueMatch;
|
||||
attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch;
|
||||
attribute_index++;
|
||||
} else {
|
||||
attribute_index++;
|
||||
|
@ -345,19 +323,19 @@ Optional<Selector> Parser::parse_single_selector(TokenStream<T>& tokens, bool is
|
|||
}
|
||||
|
||||
if (delim_part.token().delim() == "~") {
|
||||
simple_selector.attribute_match_type = Selector::SimpleSelector::AttributeMatchType::ContainsWord;
|
||||
attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsWord;
|
||||
attribute_index++;
|
||||
} else if (delim_part.token().delim() == "*") {
|
||||
simple_selector.attribute_match_type = Selector::SimpleSelector::AttributeMatchType::ContainsString;
|
||||
attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsString;
|
||||
attribute_index++;
|
||||
} else if (delim_part.token().delim() == "|") {
|
||||
simple_selector.attribute_match_type = Selector::SimpleSelector::AttributeMatchType::StartsWithSegment;
|
||||
attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment;
|
||||
attribute_index++;
|
||||
} else if (delim_part.token().delim() == "^") {
|
||||
simple_selector.attribute_match_type = Selector::SimpleSelector::AttributeMatchType::StartsWithString;
|
||||
attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithString;
|
||||
attribute_index++;
|
||||
} else if (delim_part.token().delim() == "$") {
|
||||
simple_selector.attribute_match_type = Selector::SimpleSelector::AttributeMatchType::EndsWithString;
|
||||
attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::EndsWithString;
|
||||
attribute_index++;
|
||||
}
|
||||
}
|
||||
|
@ -372,12 +350,18 @@ Optional<Selector> Parser::parse_single_selector(TokenStream<T>& tokens, bool is
|
|||
dbgln("Expected a string or ident for the value to match attribute against, got: '{}'", value_part.to_debug_string());
|
||||
return {};
|
||||
}
|
||||
simple_selector.attribute_value = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string();
|
||||
attribute.value = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string();
|
||||
|
||||
// FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
|
||||
return simple_selector;
|
||||
} else {
|
||||
dbgln("Invalid simple selector!");
|
||||
return {};
|
||||
}
|
||||
|
||||
current_value = tokens.next_token();
|
||||
if (check_for_eof_or_whitespace(current_value))
|
||||
return simple_selector;
|
||||
|
||||
// FIXME: Pseudo-class selectors want to be their own Selector::SimpleSelector::Type according to the spec.
|
||||
if (current_value.is(Token::Type::Colon)) {
|
||||
bool is_pseudo = false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue