SelectorParsing.cpp 42 KB

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