SelectorParsing.cpp 42 KB

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