SelectorParsing.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. /*
  2. * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2020-2021, the SerenityOS developers.
  4. * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
  5. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  6. * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
  7. *
  8. * SPDX-License-Identifier: BSD-2-Clause
  9. */
  10. #include <AK/Debug.h>
  11. #include <LibWeb/CSS/Parser/Parser.h>
  12. namespace Web::CSS::Parser {
  13. Optional<SelectorList> Parser::parse_as_selector(SelectorParsingMode parsing_mode)
  14. {
  15. auto selector_list = parse_a_selector_list(m_token_stream, SelectorType::Standalone, parsing_mode);
  16. if (!selector_list.is_error())
  17. return selector_list.release_value();
  18. return {};
  19. }
  20. Optional<SelectorList> Parser::parse_as_relative_selector(SelectorParsingMode parsing_mode)
  21. {
  22. auto selector_list = parse_a_selector_list(m_token_stream, SelectorType::Relative, parsing_mode);
  23. if (!selector_list.is_error())
  24. return selector_list.release_value();
  25. return {};
  26. }
  27. template<typename T>
  28. Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<T>& tokens, SelectorType mode, SelectorParsingMode parsing_mode)
  29. {
  30. auto comma_separated_lists = parse_a_comma_separated_list_of_component_values(tokens);
  31. Vector<NonnullRefPtr<Selector>> selectors;
  32. for (auto& selector_parts : comma_separated_lists) {
  33. auto stream = TokenStream(selector_parts);
  34. auto selector = parse_complex_selector(stream, mode);
  35. if (selector.is_error()) {
  36. if (parsing_mode == SelectorParsingMode::Forgiving)
  37. continue;
  38. return selector.error();
  39. }
  40. selectors.append(selector.release_value());
  41. }
  42. if (selectors.is_empty() && parsing_mode != SelectorParsingMode::Forgiving)
  43. return ParseError::SyntaxError;
  44. return selectors;
  45. }
  46. template Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<ComponentValue>&, SelectorType, SelectorParsingMode);
  47. template Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<Token>&, SelectorType, SelectorParsingMode);
  48. Parser::ParseErrorOr<NonnullRefPtr<Selector>> Parser::parse_complex_selector(TokenStream<ComponentValue>& tokens, SelectorType mode)
  49. {
  50. Vector<Selector::CompoundSelector> compound_selectors;
  51. auto first_selector = TRY(parse_compound_selector(tokens));
  52. if (!first_selector.has_value())
  53. return ParseError::SyntaxError;
  54. if (mode == SelectorType::Standalone) {
  55. if (first_selector->combinator != Selector::Combinator::Descendant)
  56. return ParseError::SyntaxError;
  57. first_selector->combinator = Selector::Combinator::None;
  58. }
  59. compound_selectors.append(first_selector.release_value());
  60. while (tokens.has_next_token()) {
  61. auto compound_selector = TRY(parse_compound_selector(tokens));
  62. if (!compound_selector.has_value())
  63. break;
  64. compound_selectors.append(compound_selector.release_value());
  65. }
  66. if (compound_selectors.is_empty())
  67. return ParseError::SyntaxError;
  68. return Selector::create(move(compound_selectors));
  69. }
  70. Parser::ParseErrorOr<Optional<Selector::CompoundSelector>> Parser::parse_compound_selector(TokenStream<ComponentValue>& tokens)
  71. {
  72. tokens.skip_whitespace();
  73. auto combinator = parse_selector_combinator(tokens).value_or(Selector::Combinator::Descendant);
  74. tokens.skip_whitespace();
  75. Vector<Selector::SimpleSelector> simple_selectors;
  76. while (tokens.has_next_token()) {
  77. auto component = TRY(parse_simple_selector(tokens));
  78. if (!component.has_value())
  79. break;
  80. simple_selectors.append(component.release_value());
  81. }
  82. if (simple_selectors.is_empty())
  83. return Optional<Selector::CompoundSelector> {};
  84. return Selector::CompoundSelector { combinator, move(simple_selectors) };
  85. }
  86. Optional<Selector::Combinator> Parser::parse_selector_combinator(TokenStream<ComponentValue>& tokens)
  87. {
  88. auto const& current_value = tokens.next_token();
  89. if (current_value.is(Token::Type::Delim)) {
  90. switch (current_value.token().delim()) {
  91. case '>':
  92. return Selector::Combinator::ImmediateChild;
  93. case '+':
  94. return Selector::Combinator::NextSibling;
  95. case '~':
  96. return Selector::Combinator::SubsequentSibling;
  97. case '|': {
  98. auto const& next = tokens.peek_token();
  99. if (next.is(Token::Type::EndOfFile))
  100. return {};
  101. if (next.is_delim('|')) {
  102. tokens.next_token();
  103. return Selector::Combinator::Column;
  104. }
  105. }
  106. }
  107. }
  108. tokens.reconsume_current_input_token();
  109. return {};
  110. }
  111. Optional<Selector::SimpleSelector::QualifiedName> Parser::parse_selector_qualified_name(TokenStream<ComponentValue>& tokens, AllowWildcardName allow_wildcard_name)
  112. {
  113. auto is_name = [](ComponentValue const& token) {
  114. return token.is_delim('*') || token.is(Token::Type::Ident);
  115. };
  116. auto get_name = [](ComponentValue const& token) {
  117. if (token.is_delim('*'))
  118. return "*"_fly_string;
  119. return token.token().ident();
  120. };
  121. // There are 3 possibilities here:
  122. // (Where <name> and <namespace> are either an <ident> or a `*` delim)
  123. // 1) `|<name>`
  124. // 2) `<namespace>|<name>`
  125. // 3) `<name>`
  126. // Whitespace is forbidden between any of these parts. https://www.w3.org/TR/selectors-4/#white-space
  127. auto transaction = tokens.begin_transaction();
  128. auto first_token = tokens.next_token();
  129. if (first_token.is_delim('|')) {
  130. // Case 1: `|<name>`
  131. if (is_name(tokens.peek_token())) {
  132. auto name_token = tokens.next_token();
  133. if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*'))
  134. return {};
  135. transaction.commit();
  136. return Selector::SimpleSelector::QualifiedName {
  137. .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::None,
  138. .name = get_name(name_token),
  139. };
  140. }
  141. return {};
  142. }
  143. if (!is_name(first_token))
  144. return {};
  145. if (tokens.peek_token().is_delim('|') && is_name(tokens.peek_token(1))) {
  146. // Case 2: `<namespace>|<name>`
  147. (void)tokens.next_token(); // `|`
  148. auto namespace_ = get_name(first_token);
  149. auto name = get_name(tokens.next_token());
  150. if (allow_wildcard_name == AllowWildcardName::No && name == "*"sv)
  151. return {};
  152. auto namespace_type = namespace_ == "*"sv
  153. ? Selector::SimpleSelector::QualifiedName::NamespaceType::Any
  154. : Selector::SimpleSelector::QualifiedName::NamespaceType::Named;
  155. transaction.commit();
  156. return Selector::SimpleSelector::QualifiedName {
  157. .namespace_type = namespace_type,
  158. .namespace_ = namespace_,
  159. .name = name,
  160. };
  161. }
  162. // Case 3: `<name>`
  163. auto& name_token = first_token;
  164. if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*'))
  165. return {};
  166. transaction.commit();
  167. return Selector::SimpleSelector::QualifiedName {
  168. .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::Default,
  169. .name = get_name(name_token),
  170. };
  171. }
  172. Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_attribute_simple_selector(ComponentValue const& first_value)
  173. {
  174. auto attribute_tokens = TokenStream { first_value.block().values() };
  175. attribute_tokens.skip_whitespace();
  176. if (!attribute_tokens.has_next_token()) {
  177. dbgln_if(CSS_PARSER_DEBUG, "CSS attribute selector is empty!");
  178. return ParseError::SyntaxError;
  179. }
  180. auto maybe_qualified_name = parse_selector_qualified_name(attribute_tokens, AllowWildcardName::No);
  181. if (!maybe_qualified_name.has_value()) {
  182. dbgln_if(CSS_PARSER_DEBUG, "Expected qualified-name for attribute name, got: '{}'", attribute_tokens.peek_token().to_debug_string());
  183. return ParseError::SyntaxError;
  184. }
  185. Selector::SimpleSelector simple_selector {
  186. .type = Selector::SimpleSelector::Type::Attribute,
  187. .value = Selector::SimpleSelector::Attribute {
  188. .match_type = Selector::SimpleSelector::Attribute::MatchType::HasAttribute,
  189. // FIXME: Case-sensitivity is defined by the document language.
  190. // HTML is insensitive with attribute names, and our code generally assumes
  191. // they are converted to lowercase, so we do that here too. If we want to be
  192. // correct with XML later, we'll need to keep the original case and then do
  193. // a case-insensitive compare later.
  194. .qualified_name = maybe_qualified_name.release_value(),
  195. .case_type = Selector::SimpleSelector::Attribute::CaseType::DefaultMatch,
  196. }
  197. };
  198. attribute_tokens.skip_whitespace();
  199. if (!attribute_tokens.has_next_token())
  200. return simple_selector;
  201. auto const& delim_part = attribute_tokens.next_token();
  202. if (!delim_part.is(Token::Type::Delim)) {
  203. dbgln_if(CSS_PARSER_DEBUG, "Expected a delim for attribute comparison, got: '{}'", delim_part.to_debug_string());
  204. return ParseError::SyntaxError;
  205. }
  206. if (delim_part.token().delim() == '=') {
  207. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch;
  208. } else {
  209. if (!attribute_tokens.has_next_token()) {
  210. dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended part way through a match type.");
  211. return ParseError::SyntaxError;
  212. }
  213. auto const& delim_second_part = attribute_tokens.next_token();
  214. if (!delim_second_part.is_delim('=')) {
  215. dbgln_if(CSS_PARSER_DEBUG, "Expected a double delim for attribute comparison, got: '{}{}'", delim_part.to_debug_string(), delim_second_part.to_debug_string());
  216. return ParseError::SyntaxError;
  217. }
  218. switch (delim_part.token().delim()) {
  219. case '~':
  220. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsWord;
  221. break;
  222. case '*':
  223. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsString;
  224. break;
  225. case '|':
  226. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment;
  227. break;
  228. case '^':
  229. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithString;
  230. break;
  231. case '$':
  232. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::EndsWithString;
  233. break;
  234. default:
  235. attribute_tokens.reconsume_current_input_token();
  236. }
  237. }
  238. attribute_tokens.skip_whitespace();
  239. if (!attribute_tokens.has_next_token()) {
  240. dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended without a value to match.");
  241. return ParseError::SyntaxError;
  242. }
  243. auto const& value_part = attribute_tokens.next_token();
  244. if (!value_part.is(Token::Type::Ident) && !value_part.is(Token::Type::String)) {
  245. dbgln_if(CSS_PARSER_DEBUG, "Expected a string or ident for the value to match attribute against, got: '{}'", value_part.to_debug_string());
  246. return ParseError::SyntaxError;
  247. }
  248. auto const& value_string = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string();
  249. simple_selector.attribute().value = value_string.to_string();
  250. attribute_tokens.skip_whitespace();
  251. // Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
  252. if (attribute_tokens.has_next_token()) {
  253. auto const& case_sensitivity_part = attribute_tokens.next_token();
  254. if (case_sensitivity_part.is(Token::Type::Ident)) {
  255. auto case_sensitivity = case_sensitivity_part.token().ident();
  256. if (case_sensitivity.equals_ignoring_ascii_case("i"sv)) {
  257. simple_selector.attribute().case_type = Selector::SimpleSelector::Attribute::CaseType::CaseInsensitiveMatch;
  258. } else if (case_sensitivity.equals_ignoring_ascii_case("s"sv)) {
  259. simple_selector.attribute().case_type = Selector::SimpleSelector::Attribute::CaseType::CaseSensitiveMatch;
  260. } else {
  261. dbgln_if(CSS_PARSER_DEBUG, "Expected a \"i\" or \"s\" attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string());
  262. return ParseError::SyntaxError;
  263. }
  264. } else {
  265. dbgln_if(CSS_PARSER_DEBUG, "Expected an attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string());
  266. return ParseError::SyntaxError;
  267. }
  268. }
  269. if (attribute_tokens.has_next_token()) {
  270. dbgln_if(CSS_PARSER_DEBUG, "Was not expecting anything else inside attribute selector.");
  271. return ParseError::SyntaxError;
  272. }
  273. return simple_selector;
  274. }
  275. Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selector(TokenStream<ComponentValue>& tokens)
  276. {
  277. auto peek_token_ends_selector = [&]() -> bool {
  278. auto const& value = tokens.peek_token();
  279. return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
  280. };
  281. if (peek_token_ends_selector())
  282. return ParseError::SyntaxError;
  283. bool is_pseudo = false;
  284. if (tokens.peek_token().is(Token::Type::Colon)) {
  285. is_pseudo = true;
  286. tokens.next_token();
  287. if (peek_token_ends_selector())
  288. return ParseError::SyntaxError;
  289. }
  290. if (is_pseudo) {
  291. auto const& name_token = tokens.next_token();
  292. if (!name_token.is(Token::Type::Ident)) {
  293. dbgln_if(CSS_PARSER_DEBUG, "Expected an ident for pseudo-element, got: '{}'", name_token.to_debug_string());
  294. return ParseError::SyntaxError;
  295. }
  296. auto pseudo_name = name_token.token().ident();
  297. auto pseudo_element = pseudo_element_from_string(pseudo_name);
  298. // Note: We allow the "ignored" -webkit prefix here for -webkit-progress-bar/-webkit-progress-bar
  299. if (!pseudo_element.has_value() && has_ignored_vendor_prefix(pseudo_name))
  300. return ParseError::IncludesIgnoredVendorPrefix;
  301. if (!pseudo_element.has_value()) {
  302. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-element: '::{}'", pseudo_name);
  303. return ParseError::SyntaxError;
  304. }
  305. return Selector::SimpleSelector {
  306. .type = Selector::SimpleSelector::Type::PseudoElement,
  307. .value = pseudo_element.value()
  308. };
  309. }
  310. if (peek_token_ends_selector())
  311. return ParseError::SyntaxError;
  312. auto const& pseudo_class_token = tokens.next_token();
  313. if (pseudo_class_token.is(Token::Type::Ident)) {
  314. auto pseudo_name = pseudo_class_token.token().ident();
  315. if (has_ignored_vendor_prefix(pseudo_name))
  316. return ParseError::IncludesIgnoredVendorPrefix;
  317. auto make_pseudo_class_selector = [](auto pseudo_class) {
  318. return Selector::SimpleSelector {
  319. .type = Selector::SimpleSelector::Type::PseudoClass,
  320. .value = Selector::SimpleSelector::PseudoClassSelector { .type = pseudo_class }
  321. };
  322. };
  323. if (auto pseudo_class = pseudo_class_from_string(pseudo_name); pseudo_class.has_value()) {
  324. if (!pseudo_class_metadata(pseudo_class.value()).is_valid_as_identifier) {
  325. dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is only valid as a function", pseudo_name);
  326. return ParseError::SyntaxError;
  327. }
  328. return make_pseudo_class_selector(pseudo_class.value());
  329. }
  330. // Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
  331. // https://www.w3.org/TR/selectors/#pseudo-element-syntax
  332. if (auto pseudo_element = pseudo_element_from_string(pseudo_name); pseudo_element.has_value()) {
  333. switch (pseudo_element.value()) {
  334. case Selector::PseudoElement::After:
  335. case Selector::PseudoElement::Before:
  336. case Selector::PseudoElement::FirstLetter:
  337. case Selector::PseudoElement::FirstLine:
  338. return Selector::SimpleSelector {
  339. .type = Selector::SimpleSelector::Type::PseudoElement,
  340. .value = pseudo_element.value()
  341. };
  342. default:
  343. break;
  344. }
  345. }
  346. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class: ':{}'", pseudo_name);
  347. return ParseError::SyntaxError;
  348. }
  349. if (pseudo_class_token.is_function()) {
  350. auto parse_nth_child_selector = [this](auto pseudo_class, Vector<ComponentValue> const& function_values, bool allow_of = false) -> ParseErrorOr<Selector::SimpleSelector> {
  351. auto tokens = TokenStream<ComponentValue>(function_values);
  352. auto nth_child_pattern = parse_a_n_plus_b_pattern(tokens);
  353. if (!nth_child_pattern.has_value()) {
  354. dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid An+B format for {}", pseudo_class_name(pseudo_class));
  355. return ParseError::SyntaxError;
  356. }
  357. tokens.skip_whitespace();
  358. if (!tokens.has_next_token()) {
  359. return Selector::SimpleSelector {
  360. .type = Selector::SimpleSelector::Type::PseudoClass,
  361. .value = Selector::SimpleSelector::PseudoClassSelector {
  362. .type = pseudo_class,
  363. .nth_child_pattern = nth_child_pattern.release_value() }
  364. };
  365. }
  366. if (!allow_of)
  367. return ParseError::SyntaxError;
  368. // Parse the `of <selector-list>` syntax
  369. auto const& maybe_of = tokens.next_token();
  370. if (!maybe_of.is_ident("of"sv))
  371. return ParseError::SyntaxError;
  372. tokens.skip_whitespace();
  373. auto selector_list = TRY(parse_a_selector_list(tokens, SelectorType::Standalone));
  374. tokens.skip_whitespace();
  375. if (tokens.has_next_token())
  376. return ParseError::SyntaxError;
  377. return Selector::SimpleSelector {
  378. .type = Selector::SimpleSelector::Type::PseudoClass,
  379. .value = Selector::SimpleSelector::PseudoClassSelector {
  380. .type = pseudo_class,
  381. .nth_child_pattern = nth_child_pattern.release_value(),
  382. .argument_selector_list = move(selector_list) }
  383. };
  384. };
  385. auto const& pseudo_function = pseudo_class_token.function();
  386. auto maybe_pseudo_class = pseudo_class_from_string(pseudo_function.name());
  387. if (!maybe_pseudo_class.has_value()) {
  388. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name());
  389. return ParseError::SyntaxError;
  390. }
  391. auto pseudo_class = maybe_pseudo_class.value();
  392. auto metadata = pseudo_class_metadata(pseudo_class);
  393. if (!metadata.is_valid_as_function) {
  394. dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is not valid as a function", pseudo_function.name());
  395. return ParseError::SyntaxError;
  396. }
  397. if (pseudo_function.values().is_empty()) {
  398. dbgln_if(CSS_PARSER_DEBUG, "Empty :{}() selector", pseudo_function.name());
  399. return ParseError::SyntaxError;
  400. }
  401. switch (metadata.parameter_type) {
  402. case PseudoClassMetadata::ParameterType::ANPlusB:
  403. return parse_nth_child_selector(pseudo_class, pseudo_function.values(), false);
  404. case PseudoClassMetadata::ParameterType::ANPlusBOf:
  405. return parse_nth_child_selector(pseudo_class, pseudo_function.values(), true);
  406. case PseudoClassMetadata::ParameterType::CompoundSelector: {
  407. auto function_token_stream = TokenStream(pseudo_function.values());
  408. auto compound_selector_or_error = parse_compound_selector(function_token_stream);
  409. if (compound_selector_or_error.is_error() || !compound_selector_or_error.value().has_value()) {
  410. dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as a compound selector", pseudo_function.name());
  411. return ParseError::SyntaxError;
  412. }
  413. Vector compound_selectors { compound_selector_or_error.release_value().release_value() };
  414. auto selector = Selector::create(move(compound_selectors));
  415. return Selector::SimpleSelector {
  416. .type = Selector::SimpleSelector::Type::PseudoClass,
  417. .value = Selector::SimpleSelector::PseudoClassSelector {
  418. .type = pseudo_class,
  419. .argument_selector_list = { move(selector) } }
  420. };
  421. }
  422. case PseudoClassMetadata::ParameterType::ForgivingSelectorList: {
  423. auto function_token_stream = TokenStream(pseudo_function.values());
  424. // NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list.
  425. auto argument_selector_list = MUST(parse_a_selector_list(function_token_stream, SelectorType::Standalone, SelectorParsingMode::Forgiving));
  426. return Selector::SimpleSelector {
  427. .type = Selector::SimpleSelector::Type::PseudoClass,
  428. .value = Selector::SimpleSelector::PseudoClassSelector {
  429. .type = pseudo_class,
  430. .argument_selector_list = move(argument_selector_list) }
  431. };
  432. }
  433. case PseudoClassMetadata::ParameterType::Ident: {
  434. auto function_token_stream = TokenStream(pseudo_function.values());
  435. function_token_stream.skip_whitespace();
  436. auto maybe_ident_token = function_token_stream.next_token();
  437. function_token_stream.skip_whitespace();
  438. if (!maybe_ident_token.is(Token::Type::Ident) || function_token_stream.has_next_token()) {
  439. dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as an ident: not an ident", pseudo_function.name());
  440. return ParseError::SyntaxError;
  441. }
  442. auto maybe_ident = value_id_from_string(maybe_ident_token.token().ident());
  443. if (!maybe_ident.has_value()) {
  444. dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as an ident: unrecognized ident", pseudo_function.name());
  445. return ParseError::SyntaxError;
  446. }
  447. return Selector::SimpleSelector {
  448. .type = Selector::SimpleSelector::Type::PseudoClass,
  449. .value = Selector::SimpleSelector::PseudoClassSelector {
  450. .type = pseudo_class,
  451. .identifier = maybe_ident.value() }
  452. };
  453. }
  454. case PseudoClassMetadata::ParameterType::LanguageRanges: {
  455. Vector<FlyString> languages;
  456. auto function_token_stream = TokenStream(pseudo_function.values());
  457. auto language_token_lists = parse_a_comma_separated_list_of_component_values(function_token_stream);
  458. for (auto language_token_list : language_token_lists) {
  459. auto language_token_stream = TokenStream(language_token_list);
  460. language_token_stream.skip_whitespace();
  461. auto language_token = language_token_stream.next_token();
  462. if (!(language_token.is(Token::Type::Ident) || language_token.is(Token::Type::String))) {
  463. dbgln_if(CSS_PARSER_DEBUG, "Invalid language range in :{}() - not a string/ident", pseudo_function.name());
  464. return ParseError::SyntaxError;
  465. }
  466. auto language_string = language_token.is(Token::Type::String) ? language_token.token().string() : language_token.token().ident();
  467. languages.append(language_string);
  468. language_token_stream.skip_whitespace();
  469. if (language_token_stream.has_next_token()) {
  470. dbgln_if(CSS_PARSER_DEBUG, "Invalid language range in :{}() - trailing tokens", pseudo_function.name());
  471. return ParseError::SyntaxError;
  472. }
  473. }
  474. return Selector::SimpleSelector {
  475. .type = Selector::SimpleSelector::Type::PseudoClass,
  476. .value = Selector::SimpleSelector::PseudoClassSelector {
  477. .type = pseudo_class,
  478. .languages = move(languages) }
  479. };
  480. }
  481. case PseudoClassMetadata::ParameterType::SelectorList: {
  482. auto function_token_stream = TokenStream(pseudo_function.values());
  483. auto not_selector = TRY(parse_a_selector_list(function_token_stream, SelectorType::Standalone));
  484. return Selector::SimpleSelector {
  485. .type = Selector::SimpleSelector::Type::PseudoClass,
  486. .value = Selector::SimpleSelector::PseudoClassSelector {
  487. .type = pseudo_class,
  488. .argument_selector_list = move(not_selector) }
  489. };
  490. }
  491. case PseudoClassMetadata::ParameterType::None:
  492. // `None` means this is not a function-type pseudo-class, so this state should be impossible.
  493. VERIFY_NOT_REACHED();
  494. }
  495. }
  496. dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string());
  497. return ParseError::SyntaxError;
  498. }
  499. Parser::ParseErrorOr<Optional<Selector::SimpleSelector>> Parser::parse_simple_selector(TokenStream<ComponentValue>& tokens)
  500. {
  501. auto peek_token_ends_selector = [&]() -> bool {
  502. auto const& value = tokens.peek_token();
  503. return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
  504. };
  505. if (peek_token_ends_selector())
  506. return Optional<Selector::SimpleSelector> {};
  507. // Handle universal and tag-name types together, since both can be namespaced
  508. if (auto qualified_name = parse_selector_qualified_name(tokens, AllowWildcardName::Yes); qualified_name.has_value()) {
  509. if (qualified_name->name.name == "*"sv) {
  510. return Selector::SimpleSelector {
  511. .type = Selector::SimpleSelector::Type::Universal,
  512. .value = qualified_name.release_value(),
  513. };
  514. }
  515. return Selector::SimpleSelector {
  516. .type = Selector::SimpleSelector::Type::TagName,
  517. .value = qualified_name.release_value(),
  518. };
  519. }
  520. auto const& first_value = tokens.next_token();
  521. if (first_value.is(Token::Type::Delim)) {
  522. u32 delim = first_value.token().delim();
  523. switch (delim) {
  524. case '*':
  525. // Handled already
  526. VERIFY_NOT_REACHED();
  527. case '.': {
  528. if (peek_token_ends_selector())
  529. return ParseError::SyntaxError;
  530. auto const& class_name_value = tokens.next_token();
  531. if (!class_name_value.is(Token::Type::Ident)) {
  532. dbgln_if(CSS_PARSER_DEBUG, "Expected an ident after '.', got: {}", class_name_value.to_debug_string());
  533. return ParseError::SyntaxError;
  534. }
  535. return Selector::SimpleSelector {
  536. .type = Selector::SimpleSelector::Type::Class,
  537. .value = Selector::SimpleSelector::Name { class_name_value.token().ident() }
  538. };
  539. }
  540. case '>':
  541. case '+':
  542. case '~':
  543. case '|':
  544. // Whitespace is not required between the compound-selector and a combinator.
  545. // So, if we see a combinator, return that this compound-selector is done, instead of a syntax error.
  546. tokens.reconsume_current_input_token();
  547. return Optional<Selector::SimpleSelector> {};
  548. default:
  549. dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!");
  550. return ParseError::SyntaxError;
  551. }
  552. }
  553. if (first_value.is(Token::Type::Hash)) {
  554. if (first_value.token().hash_type() != Token::HashType::Id) {
  555. dbgln_if(CSS_PARSER_DEBUG, "Selector contains hash token that is not an id: {}", first_value.to_debug_string());
  556. return ParseError::SyntaxError;
  557. }
  558. return Selector::SimpleSelector {
  559. .type = Selector::SimpleSelector::Type::Id,
  560. .value = Selector::SimpleSelector::Name { first_value.token().hash_value() }
  561. };
  562. }
  563. if (first_value.is_block() && first_value.block().is_square())
  564. return TRY(parse_attribute_simple_selector(first_value));
  565. if (first_value.is(Token::Type::Colon))
  566. return TRY(parse_pseudo_simple_selector(tokens));
  567. dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!");
  568. return ParseError::SyntaxError;
  569. }
  570. Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_pattern(TokenStream<ComponentValue>& values)
  571. {
  572. auto transaction = values.begin_transaction();
  573. auto syntax_error = [&]() -> Optional<Selector::SimpleSelector::ANPlusBPattern> {
  574. if constexpr (CSS_PARSER_DEBUG) {
  575. dbgln_if(CSS_PARSER_DEBUG, "Invalid An+B value:");
  576. values.dump_all_tokens();
  577. }
  578. return {};
  579. };
  580. auto is_sign = [](ComponentValue const& value) -> bool {
  581. return value.is(Token::Type::Delim) && (value.token().delim() == '+' || value.token().delim() == '-');
  582. };
  583. auto is_n_dimension = [](ComponentValue const& value) -> bool {
  584. if (!value.is(Token::Type::Dimension))
  585. return false;
  586. if (!value.token().number().is_integer())
  587. return false;
  588. if (!value.token().dimension_unit().equals_ignoring_ascii_case("n"sv))
  589. return false;
  590. return true;
  591. };
  592. auto is_ndash_dimension = [](ComponentValue const& value) -> bool {
  593. if (!value.is(Token::Type::Dimension))
  594. return false;
  595. if (!value.token().number().is_integer())
  596. return false;
  597. if (!value.token().dimension_unit().equals_ignoring_ascii_case("n-"sv))
  598. return false;
  599. return true;
  600. };
  601. auto is_ndashdigit_dimension = [](ComponentValue const& value) -> bool {
  602. if (!value.is(Token::Type::Dimension))
  603. return false;
  604. if (!value.token().number().is_integer())
  605. return false;
  606. auto dimension_unit = value.token().dimension_unit();
  607. if (!dimension_unit.starts_with_bytes("n-"sv, CaseSensitivity::CaseInsensitive))
  608. return false;
  609. for (size_t i = 2; i < dimension_unit.bytes_as_string_view().length(); ++i) {
  610. if (!is_ascii_digit(dimension_unit.bytes_as_string_view()[i]))
  611. return false;
  612. }
  613. return true;
  614. };
  615. auto is_ndashdigit_ident = [](ComponentValue const& value) -> bool {
  616. if (!value.is(Token::Type::Ident))
  617. return false;
  618. auto ident = value.token().ident();
  619. if (!ident.starts_with_bytes("n-"sv, CaseSensitivity::CaseInsensitive))
  620. return false;
  621. for (size_t i = 2; i < ident.bytes_as_string_view().length(); ++i) {
  622. if (!is_ascii_digit(ident.bytes_as_string_view()[i]))
  623. return false;
  624. }
  625. return true;
  626. };
  627. auto is_dashndashdigit_ident = [](ComponentValue const& value) -> bool {
  628. if (!value.is(Token::Type::Ident))
  629. return false;
  630. auto ident = value.token().ident();
  631. if (!ident.starts_with_bytes("-n-"sv, CaseSensitivity::CaseInsensitive))
  632. return false;
  633. if (ident.bytes_as_string_view().length() == 3)
  634. return false;
  635. for (size_t i = 3; i < ident.bytes_as_string_view().length(); ++i) {
  636. if (!is_ascii_digit(ident.bytes_as_string_view()[i]))
  637. return false;
  638. }
  639. return true;
  640. };
  641. auto is_integer = [](ComponentValue const& value) -> bool {
  642. return value.is(Token::Type::Number) && value.token().number().is_integer();
  643. };
  644. auto is_signed_integer = [](ComponentValue const& value) -> bool {
  645. return value.is(Token::Type::Number) && value.token().number().is_integer_with_explicit_sign();
  646. };
  647. auto is_signless_integer = [](ComponentValue const& value) -> bool {
  648. return value.is(Token::Type::Number) && !value.token().number().is_integer_with_explicit_sign();
  649. };
  650. // https://www.w3.org/TR/css-syntax-3/#the-anb-type
  651. // Unfortunately these can't be in the same order as in the spec.
  652. values.skip_whitespace();
  653. auto const& first_value = values.next_token();
  654. // odd | even
  655. if (first_value.is(Token::Type::Ident)) {
  656. auto ident = first_value.token().ident();
  657. if (ident.equals_ignoring_ascii_case("odd"sv)) {
  658. transaction.commit();
  659. return Selector::SimpleSelector::ANPlusBPattern { 2, 1 };
  660. }
  661. if (ident.equals_ignoring_ascii_case("even"sv)) {
  662. transaction.commit();
  663. return Selector::SimpleSelector::ANPlusBPattern { 2, 0 };
  664. }
  665. }
  666. // <integer>
  667. if (is_integer(first_value)) {
  668. int b = first_value.token().to_integer();
  669. transaction.commit();
  670. return Selector::SimpleSelector::ANPlusBPattern { 0, b };
  671. }
  672. // <n-dimension>
  673. // <n-dimension> <signed-integer>
  674. // <n-dimension> ['+' | '-'] <signless-integer>
  675. if (is_n_dimension(first_value)) {
  676. int a = first_value.token().dimension_value_int();
  677. values.skip_whitespace();
  678. // <n-dimension> <signed-integer>
  679. if (is_signed_integer(values.peek_token())) {
  680. int b = values.next_token().token().to_integer();
  681. transaction.commit();
  682. return Selector::SimpleSelector::ANPlusBPattern { a, b };
  683. }
  684. // <n-dimension> ['+' | '-'] <signless-integer>
  685. {
  686. auto child_transaction = transaction.create_child();
  687. auto const& second_value = values.next_token();
  688. values.skip_whitespace();
  689. auto const& third_value = values.next_token();
  690. if (is_sign(second_value) && is_signless_integer(third_value)) {
  691. int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
  692. child_transaction.commit();
  693. return Selector::SimpleSelector::ANPlusBPattern { a, b };
  694. }
  695. }
  696. // <n-dimension>
  697. transaction.commit();
  698. return Selector::SimpleSelector::ANPlusBPattern { a, 0 };
  699. }
  700. // <ndash-dimension> <signless-integer>
  701. if (is_ndash_dimension(first_value)) {
  702. values.skip_whitespace();
  703. auto const& second_value = values.next_token();
  704. if (is_signless_integer(second_value)) {
  705. int a = first_value.token().dimension_value_int();
  706. int b = -second_value.token().to_integer();
  707. transaction.commit();
  708. return Selector::SimpleSelector::ANPlusBPattern { a, b };
  709. }
  710. return syntax_error();
  711. }
  712. // <ndashdigit-dimension>
  713. if (is_ndashdigit_dimension(first_value)) {
  714. auto const& dimension = first_value.token();
  715. int a = dimension.dimension_value_int();
  716. auto maybe_b = dimension.dimension_unit().bytes_as_string_view().substring_view(1).to_int();
  717. if (maybe_b.has_value()) {
  718. transaction.commit();
  719. return Selector::SimpleSelector::ANPlusBPattern { a, maybe_b.value() };
  720. }
  721. return syntax_error();
  722. }
  723. // <dashndashdigit-ident>
  724. if (is_dashndashdigit_ident(first_value)) {
  725. auto maybe_b = first_value.token().ident().bytes_as_string_view().substring_view(2).to_int();
  726. if (maybe_b.has_value()) {
  727. transaction.commit();
  728. return Selector::SimpleSelector::ANPlusBPattern { -1, maybe_b.value() };
  729. }
  730. return syntax_error();
  731. }
  732. // -n
  733. // -n <signed-integer>
  734. // -n ['+' | '-'] <signless-integer>
  735. if (first_value.is_ident("-n"sv)) {
  736. values.skip_whitespace();
  737. // -n <signed-integer>
  738. if (is_signed_integer(values.peek_token())) {
  739. int b = values.next_token().token().to_integer();
  740. transaction.commit();
  741. return Selector::SimpleSelector::ANPlusBPattern { -1, b };
  742. }
  743. // -n ['+' | '-'] <signless-integer>
  744. {
  745. auto child_transaction = transaction.create_child();
  746. auto const& second_value = values.next_token();
  747. values.skip_whitespace();
  748. auto const& third_value = values.next_token();
  749. if (is_sign(second_value) && is_signless_integer(third_value)) {
  750. int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
  751. child_transaction.commit();
  752. return Selector::SimpleSelector::ANPlusBPattern { -1, b };
  753. }
  754. }
  755. // -n
  756. transaction.commit();
  757. return Selector::SimpleSelector::ANPlusBPattern { -1, 0 };
  758. }
  759. // -n- <signless-integer>
  760. if (first_value.is_ident("-n-"sv)) {
  761. values.skip_whitespace();
  762. auto const& second_value = values.next_token();
  763. if (is_signless_integer(second_value)) {
  764. int b = -second_value.token().to_integer();
  765. transaction.commit();
  766. return Selector::SimpleSelector::ANPlusBPattern { -1, b };
  767. }
  768. return syntax_error();
  769. }
  770. // All that's left now are these:
  771. // '+'?† n
  772. // '+'?† n <signed-integer>
  773. // '+'?† n ['+' | '-'] <signless-integer>
  774. // '+'?† n- <signless-integer>
  775. // '+'?† <ndashdigit-ident>
  776. // In all of these cases, the + is optional, and has no effect.
  777. // So, we just skip the +, and carry on.
  778. if (!first_value.is_delim('+')) {
  779. values.reconsume_current_input_token();
  780. // We do *not* skip whitespace here.
  781. }
  782. auto const& first_after_plus = values.next_token();
  783. // '+'?† n
  784. // '+'?† n <signed-integer>
  785. // '+'?† n ['+' | '-'] <signless-integer>
  786. if (first_after_plus.is_ident("n"sv)) {
  787. values.skip_whitespace();
  788. // '+'?† n <signed-integer>
  789. if (is_signed_integer(values.peek_token())) {
  790. int b = values.next_token().token().to_integer();
  791. transaction.commit();
  792. return Selector::SimpleSelector::ANPlusBPattern { 1, b };
  793. }
  794. // '+'?† n ['+' | '-'] <signless-integer>
  795. {
  796. auto child_transaction = transaction.create_child();
  797. auto const& second_value = values.next_token();
  798. values.skip_whitespace();
  799. auto const& third_value = values.next_token();
  800. if (is_sign(second_value) && is_signless_integer(third_value)) {
  801. int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
  802. child_transaction.commit();
  803. return Selector::SimpleSelector::ANPlusBPattern { 1, b };
  804. }
  805. }
  806. // '+'?† n
  807. transaction.commit();
  808. return Selector::SimpleSelector::ANPlusBPattern { 1, 0 };
  809. }
  810. // '+'?† n- <signless-integer>
  811. if (first_after_plus.is_ident("n-"sv)) {
  812. values.skip_whitespace();
  813. auto const& second_value = values.next_token();
  814. if (is_signless_integer(second_value)) {
  815. int b = -second_value.token().to_integer();
  816. transaction.commit();
  817. return Selector::SimpleSelector::ANPlusBPattern { 1, b };
  818. }
  819. return syntax_error();
  820. }
  821. // '+'?† <ndashdigit-ident>
  822. if (is_ndashdigit_ident(first_after_plus)) {
  823. auto maybe_b = first_after_plus.token().ident().bytes_as_string_view().substring_view(1).to_int();
  824. if (maybe_b.has_value()) {
  825. transaction.commit();
  826. return Selector::SimpleSelector::ANPlusBPattern { 1, maybe_b.value() };
  827. }
  828. return syntax_error();
  829. }
  830. return syntax_error();
  831. }
  832. }