PageRenderTime 60ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/peek-build/src/netdepends/libcss/src/parse/language.c

https://bitbucket.org/C0deMaver1ck/peeklinux
C | 1301 lines | 914 code | 242 blank | 145 comment | 436 complexity | daf8d671d66ca1bf4a5e5bc95d36f4ea MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  1. /*
  2. * This file is part of LibCSS.
  3. * Licensed under the MIT License,
  4. * http://www.opensource.org/licenses/mit-license.php
  5. * Copyright 2008 John-Mark Bell <jmb@netsurf-browser.org>
  6. */
  7. #include <assert.h>
  8. #include <string.h>
  9. #include <parserutils/utils/stack.h>
  10. #include "stylesheet.h"
  11. #include "lex/lex.h"
  12. #include "parse/important.h"
  13. #include "parse/language.h"
  14. #include "parse/parse.h"
  15. #include "parse/propstrings.h"
  16. #include "parse/properties/properties.h"
  17. #include "parse/properties/utils.h"
  18. #include "utils/parserutilserror.h"
  19. #include "utils/utils.h"
  20. typedef struct context_entry {
  21. css_parser_event type; /**< Type of entry */
  22. void *data; /**< Data for context */
  23. } context_entry;
  24. /* Event handlers */
  25. static css_error language_handle_event(css_parser_event type,
  26. const parserutils_vector *tokens, void *pw);
  27. static css_error handleStartStylesheet(css_language *c,
  28. const parserutils_vector *vector);
  29. static css_error handleEndStylesheet(css_language *c,
  30. const parserutils_vector *vector);
  31. static css_error handleStartRuleset(css_language *c,
  32. const parserutils_vector *vector);
  33. static css_error handleEndRuleset(css_language *c,
  34. const parserutils_vector *vector);
  35. static css_error handleStartAtRule(css_language *c,
  36. const parserutils_vector *vector);
  37. static css_error handleEndAtRule(css_language *c,
  38. const parserutils_vector *vector);
  39. static css_error handleStartBlock(css_language *c,
  40. const parserutils_vector *vector);
  41. static css_error handleEndBlock(css_language *c,
  42. const parserutils_vector *vector);
  43. static css_error handleBlockContent(css_language *c,
  44. const parserutils_vector *vector);
  45. static css_error handleDeclaration(css_language *c,
  46. const parserutils_vector *vector);
  47. /* At-rule parsing */
  48. static css_error parseMediaList(css_language *c,
  49. const parserutils_vector *vector, int *ctx,
  50. uint64_t *media);
  51. /* Selector list parsing */
  52. static css_error parseClass(css_language *c,
  53. const parserutils_vector *vector, int *ctx,
  54. css_selector_detail *specific);
  55. static css_error parseAttrib(css_language *c,
  56. const parserutils_vector *vector, int *ctx,
  57. css_selector_detail *specific);
  58. static css_error parsePseudo(css_language *c,
  59. const parserutils_vector *vector, int *ctx,
  60. css_selector_detail *specific);
  61. static css_error parseSpecific(css_language *c,
  62. const parserutils_vector *vector, int *ctx,
  63. css_selector **parent);
  64. static css_error parseSelectorSpecifics(css_language *c,
  65. const parserutils_vector *vector, int *ctx,
  66. css_selector **parent);
  67. static css_error parseSimpleSelector(css_language *c,
  68. const parserutils_vector *vector, int *ctx,
  69. css_selector **result);
  70. static css_error parseCombinator(css_language *c,
  71. const parserutils_vector *vector, int *ctx,
  72. css_combinator *result);
  73. static css_error parseSelector(css_language *c,
  74. const parserutils_vector *vector, int *ctx,
  75. css_selector **result);
  76. static css_error parseSelectorList(css_language *c,
  77. const parserutils_vector *vector, css_rule *rule);
  78. /* Declaration parsing */
  79. static css_error parseProperty(css_language *c,
  80. const css_token *property, const parserutils_vector *vector,
  81. int *ctx, css_rule *rule);
  82. /**
  83. * Create a CSS language parser
  84. *
  85. * \param sheet The stylesheet object to parse for
  86. * \param parser The core parser object to use
  87. * \param alloc Memory (de)allocation function
  88. * \param pw Pointer to client-specific private data
  89. * \param language Pointer to location to receive parser object
  90. * \return CSS_OK on success,
  91. * CSS_BADPARM on bad parameters,
  92. * CSS_NOMEM on memory exhaustion
  93. */
  94. css_error css_language_create(css_stylesheet *sheet, css_parser *parser,
  95. css_allocator_fn alloc, void *pw, void **language)
  96. {
  97. css_language *c;
  98. css_parser_optparams params;
  99. parserutils_error perror;
  100. lwc_error lerror;
  101. css_error error;
  102. int i;
  103. if (sheet == NULL || parser == NULL || alloc == NULL ||
  104. language == NULL)
  105. return CSS_BADPARM;
  106. c = alloc(NULL, sizeof(css_language), pw);
  107. if (c == NULL)
  108. return CSS_NOMEM;
  109. perror = parserutils_stack_create(sizeof(context_entry),
  110. STACK_CHUNK, (parserutils_alloc) alloc, pw,
  111. &c->context);
  112. if (perror != PARSERUTILS_OK) {
  113. alloc(c, 0, pw);
  114. return css_error_from_parserutils_error(perror);
  115. }
  116. /* Intern all known strings */
  117. for (i = 0; i < LAST_KNOWN; i++) {
  118. lerror = lwc_intern_string(stringmap[i].data,
  119. stringmap[i].len,
  120. &(c->strings[i]));
  121. if (lerror != lwc_error_ok) {
  122. parserutils_stack_destroy(c->context);
  123. alloc(c, 0, pw);
  124. return CSS_NOMEM;
  125. }
  126. }
  127. params.event_handler.handler = language_handle_event;
  128. params.event_handler.pw = c;
  129. error = css_parser_setopt(parser, CSS_PARSER_EVENT_HANDLER, &params);
  130. if (error != CSS_OK) {
  131. parserutils_stack_destroy(c->context);
  132. alloc(c, 0, pw);
  133. return error;
  134. }
  135. c->sheet = sheet;
  136. c->state = BEFORE_CHARSET;
  137. c->alloc = alloc;
  138. c->pw = pw;
  139. *language = c;
  140. return CSS_OK;
  141. }
  142. /**
  143. * Destroy a CSS language parser
  144. *
  145. * \param language The parser to destroy
  146. * \return CSS_OK on success, appropriate error otherwise
  147. */
  148. css_error css_language_destroy(css_language *language)
  149. {
  150. int i;
  151. if (language == NULL)
  152. return CSS_BADPARM;
  153. parserutils_stack_destroy(language->context);
  154. for (i = 0; i < LAST_KNOWN; ++i) {
  155. lwc_string_unref(language->strings[i]);
  156. }
  157. language->alloc(language, 0, language->pw);
  158. return CSS_OK;
  159. }
  160. /**
  161. * Handler for core parser events
  162. *
  163. * \param type The event type
  164. * \param tokens Vector of tokens read since last event, or NULL
  165. * \param pw Pointer to handler context
  166. * \return CSS_OK on success, CSS_INVALID to indicate parse error,
  167. * appropriate error otherwise.
  168. */
  169. css_error language_handle_event(css_parser_event type,
  170. const parserutils_vector *tokens, void *pw)
  171. {
  172. css_language *language = (css_language *) pw;
  173. switch (type) {
  174. case CSS_PARSER_START_STYLESHEET:
  175. return handleStartStylesheet(language, tokens);
  176. case CSS_PARSER_END_STYLESHEET:
  177. return handleEndStylesheet(language, tokens);
  178. case CSS_PARSER_START_RULESET:
  179. return handleStartRuleset(language, tokens);
  180. case CSS_PARSER_END_RULESET:
  181. return handleEndRuleset(language, tokens);
  182. case CSS_PARSER_START_ATRULE:
  183. return handleStartAtRule(language, tokens);
  184. case CSS_PARSER_END_ATRULE:
  185. return handleEndAtRule(language, tokens);
  186. case CSS_PARSER_START_BLOCK:
  187. return handleStartBlock(language, tokens);
  188. case CSS_PARSER_END_BLOCK:
  189. return handleEndBlock(language, tokens);
  190. case CSS_PARSER_BLOCK_CONTENT:
  191. return handleBlockContent(language, tokens);
  192. case CSS_PARSER_DECLARATION:
  193. return handleDeclaration(language, tokens);
  194. }
  195. return CSS_OK;
  196. }
  197. /******************************************************************************
  198. * Parser stages *
  199. ******************************************************************************/
  200. css_error handleStartStylesheet(css_language *c,
  201. const parserutils_vector *vector)
  202. {
  203. parserutils_error perror;
  204. context_entry entry = { CSS_PARSER_START_STYLESHEET, NULL };
  205. UNUSED(vector);
  206. assert(c != NULL);
  207. perror = parserutils_stack_push(c->context, (void *) &entry);
  208. if (perror != PARSERUTILS_OK) {
  209. return css_error_from_parserutils_error(perror);
  210. }
  211. return CSS_OK;
  212. }
  213. css_error handleEndStylesheet(css_language *c, const parserutils_vector *vector)
  214. {
  215. parserutils_error perror;
  216. context_entry *entry;
  217. UNUSED(vector);
  218. assert(c != NULL);
  219. entry = parserutils_stack_get_current(c->context);
  220. if (entry == NULL || entry->type != CSS_PARSER_START_STYLESHEET)
  221. return CSS_INVALID;
  222. perror = parserutils_stack_pop(c->context, NULL);
  223. if (perror != PARSERUTILS_OK) {
  224. return css_error_from_parserutils_error(perror);
  225. }
  226. return CSS_OK;
  227. }
  228. css_error handleStartRuleset(css_language *c, const parserutils_vector *vector)
  229. {
  230. parserutils_error perror;
  231. css_error error;
  232. context_entry entry = { CSS_PARSER_START_RULESET, NULL };
  233. context_entry *cur;
  234. css_rule *parent_rule = NULL;
  235. css_rule *rule = NULL;
  236. assert(c != NULL);
  237. /* Retrieve parent rule from stack, if any */
  238. cur = parserutils_stack_get_current(c->context);
  239. if (cur != NULL && cur->type != CSS_PARSER_START_STYLESHEET)
  240. parent_rule = cur->data;
  241. error = css_stylesheet_rule_create(c->sheet, CSS_RULE_SELECTOR, &rule);
  242. if (error != CSS_OK)
  243. return error;
  244. if (vector != NULL) {
  245. /* Parse selectors, if there are any */
  246. error = parseSelectorList(c, vector, rule);
  247. if (error != CSS_OK) {
  248. css_stylesheet_rule_destroy(c->sheet, rule);
  249. return error;
  250. }
  251. }
  252. entry.data = rule;
  253. perror = parserutils_stack_push(c->context, (void *) &entry);
  254. if (perror != PARSERUTILS_OK) {
  255. css_stylesheet_rule_destroy(c->sheet, rule);
  256. return css_error_from_parserutils_error(perror);
  257. }
  258. error = css_stylesheet_add_rule(c->sheet, rule, parent_rule);
  259. if (error != CSS_OK) {
  260. parserutils_stack_pop(c->context, NULL);
  261. css_stylesheet_rule_destroy(c->sheet, rule);
  262. return error;
  263. }
  264. /* Flag that we've had a valid rule, so @import/@charset have
  265. * no effect. */
  266. c->state = HAD_RULE;
  267. /* Rule is now owned by the sheet, so no need to destroy it */
  268. return CSS_OK;
  269. }
  270. css_error handleEndRuleset(css_language *c, const parserutils_vector *vector)
  271. {
  272. parserutils_error perror;
  273. context_entry *entry;
  274. UNUSED(vector);
  275. assert(c != NULL);
  276. entry = parserutils_stack_get_current(c->context);
  277. if (entry == NULL || entry->type != CSS_PARSER_START_RULESET)
  278. return CSS_INVALID;
  279. perror = parserutils_stack_pop(c->context, NULL);
  280. if (perror != PARSERUTILS_OK) {
  281. return css_error_from_parserutils_error(perror);
  282. }
  283. return CSS_OK;
  284. }
  285. css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
  286. {
  287. parserutils_error perror;
  288. context_entry entry = { CSS_PARSER_START_ATRULE, NULL };
  289. const css_token *token = NULL;
  290. const css_token *atkeyword = NULL;
  291. int32_t ctx = 0;
  292. bool match = false;
  293. css_rule *rule;
  294. css_error error;
  295. /* vector contains: ATKEYWORD ws any0 */
  296. assert(c != NULL);
  297. atkeyword = parserutils_vector_iterate(vector, &ctx);
  298. consumeWhitespace(vector, &ctx);
  299. /* We now have an ATKEYWORD and the context for the start of any0, if
  300. * there is one */
  301. assert(atkeyword != NULL && atkeyword->type == CSS_TOKEN_ATKEYWORD);
  302. if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[CHARSET],
  303. &match) == lwc_error_ok && match) {
  304. if (c->state == BEFORE_CHARSET) {
  305. const css_token *charset;
  306. /* any0 = STRING */
  307. if (ctx == 0)
  308. return CSS_INVALID;
  309. charset = parserutils_vector_iterate(vector, &ctx);
  310. if (charset == NULL ||
  311. charset->type != CSS_TOKEN_STRING)
  312. return CSS_INVALID;
  313. token = parserutils_vector_iterate(vector, &ctx);
  314. if (token != NULL)
  315. return CSS_INVALID;
  316. error = css_stylesheet_rule_create(c->sheet,
  317. CSS_RULE_CHARSET, &rule);
  318. if (error != CSS_OK)
  319. return error;
  320. error = css_stylesheet_rule_set_charset(c->sheet, rule,
  321. charset->idata);
  322. if (error != CSS_OK) {
  323. css_stylesheet_rule_destroy(c->sheet, rule);
  324. return error;
  325. }
  326. error = css_stylesheet_add_rule(c->sheet, rule, NULL);
  327. if (error != CSS_OK) {
  328. css_stylesheet_rule_destroy(c->sheet, rule);
  329. return error;
  330. }
  331. /* Rule is now owned by the sheet,
  332. * so no need to destroy it */
  333. c->state = BEFORE_RULES;
  334. } else {
  335. return CSS_INVALID;
  336. }
  337. } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[IMPORT],
  338. &match) == lwc_error_ok && match) {
  339. if (c->state != HAD_RULE) {
  340. lwc_string *url;
  341. uint64_t media = 0;
  342. /* any0 = (STRING | URI) ws
  343. * (IDENT ws (',' ws IDENT ws)* )? */
  344. const css_token *uri =
  345. parserutils_vector_iterate(vector, &ctx);
  346. if (uri == NULL || (uri->type != CSS_TOKEN_STRING &&
  347. uri->type != CSS_TOKEN_URI))
  348. return CSS_INVALID;
  349. consumeWhitespace(vector, &ctx);
  350. /* Parse media list */
  351. error = parseMediaList(c, vector, &ctx, &media);
  352. if (error != CSS_OK)
  353. return error;
  354. /* Create rule */
  355. error = css_stylesheet_rule_create(c->sheet,
  356. CSS_RULE_IMPORT, &rule);
  357. if (error != CSS_OK)
  358. return error;
  359. /* Resolve import URI */
  360. error = c->sheet->resolve(c->sheet->resolve_pw,
  361. c->sheet->url,
  362. uri->idata, &url);
  363. if (error != CSS_OK) {
  364. css_stylesheet_rule_destroy(c->sheet, rule);
  365. return error;
  366. }
  367. /* Inform rule of it */
  368. error = css_stylesheet_rule_set_nascent_import(c->sheet,
  369. rule, url, media);
  370. if (error != CSS_OK) {
  371. lwc_string_unref(url);
  372. css_stylesheet_rule_destroy(c->sheet, rule);
  373. return error;
  374. }
  375. /* No longer care about url */
  376. lwc_string_unref(url);
  377. /* Add rule to sheet */
  378. error = css_stylesheet_add_rule(c->sheet, rule, NULL);
  379. if (error != CSS_OK) {
  380. css_stylesheet_rule_destroy(c->sheet, rule);
  381. return error;
  382. }
  383. /* Rule is now owned by the sheet,
  384. * so no need to destroy it */
  385. c->state = BEFORE_RULES;
  386. } else {
  387. return CSS_INVALID;
  388. }
  389. } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[MEDIA],
  390. &match) == lwc_error_ok && match) {
  391. uint64_t media = 0;
  392. /* any0 = IDENT ws (',' ws IDENT ws)* */
  393. error = parseMediaList(c, vector, &ctx, &media);
  394. if (error != CSS_OK)
  395. return error;
  396. error = css_stylesheet_rule_create(c->sheet,
  397. CSS_RULE_MEDIA, &rule);
  398. if (error != CSS_OK)
  399. return error;
  400. error = css_stylesheet_rule_set_media(c->sheet, rule, media);
  401. if (error != CSS_OK) {
  402. css_stylesheet_rule_destroy(c->sheet, rule);
  403. return error;
  404. }
  405. error = css_stylesheet_add_rule(c->sheet, rule, NULL);
  406. if (error != CSS_OK) {
  407. css_stylesheet_rule_destroy(c->sheet, rule);
  408. return error;
  409. }
  410. /* Rule is now owned by the sheet,
  411. * so no need to destroy it */
  412. c->state = HAD_RULE;
  413. } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[PAGE],
  414. &match) == lwc_error_ok && match) {
  415. const css_token *token;
  416. /* any0 = (':' IDENT)? ws */
  417. error = css_stylesheet_rule_create(c->sheet,
  418. CSS_RULE_PAGE, &rule);
  419. if (error != CSS_OK)
  420. return error;
  421. consumeWhitespace(vector, &ctx);
  422. token = parserutils_vector_peek(vector, ctx);
  423. if (token != NULL) {
  424. css_selector *sel = NULL;
  425. error = parseSelector(c, vector, &ctx, &sel);
  426. if (error != CSS_OK) {
  427. css_stylesheet_rule_destroy(c->sheet, rule);
  428. return error;
  429. }
  430. error = css_stylesheet_rule_set_page_selector(c->sheet,
  431. rule, sel);
  432. if (error != CSS_OK) {
  433. css_stylesheet_selector_destroy(c->sheet, sel);
  434. css_stylesheet_rule_destroy(c->sheet, rule);
  435. return error;
  436. }
  437. }
  438. error = css_stylesheet_add_rule(c->sheet, rule, NULL);
  439. if (error != CSS_OK) {
  440. css_stylesheet_rule_destroy(c->sheet, rule);
  441. return error;
  442. }
  443. /* Rule is now owned by the sheet,
  444. * so no need to destroy it */
  445. c->state = HAD_RULE;
  446. } else {
  447. return CSS_INVALID;
  448. }
  449. entry.data = rule;
  450. perror = parserutils_stack_push(c->context, (void *) &entry);
  451. if (perror != PARSERUTILS_OK) {
  452. return css_error_from_parserutils_error(perror);
  453. }
  454. return CSS_OK;
  455. }
  456. css_error handleEndAtRule(css_language *c, const parserutils_vector *vector)
  457. {
  458. parserutils_error perror;
  459. context_entry *entry;
  460. UNUSED(vector);
  461. assert(c != NULL);
  462. entry = parserutils_stack_get_current(c->context);
  463. if (entry == NULL || entry->type != CSS_PARSER_START_ATRULE)
  464. return CSS_INVALID;
  465. perror = parserutils_stack_pop(c->context, NULL);
  466. if (perror != PARSERUTILS_OK) {
  467. return css_error_from_parserutils_error(perror);
  468. }
  469. return CSS_OK;
  470. }
  471. css_error handleStartBlock(css_language *c, const parserutils_vector *vector)
  472. {
  473. parserutils_error perror;
  474. context_entry entry = { CSS_PARSER_START_BLOCK, NULL };
  475. context_entry *cur;
  476. UNUSED(vector);
  477. /* If the current item on the stack isn't a block,
  478. * then clone its data field. This ensures that the relevant rule
  479. * is available when parsing the block contents. */
  480. cur = parserutils_stack_get_current(c->context);
  481. if (cur != NULL && cur->type != CSS_PARSER_START_BLOCK)
  482. entry.data = cur->data;
  483. perror = parserutils_stack_push(c->context, (void *) &entry);
  484. if (perror != PARSERUTILS_OK) {
  485. return css_error_from_parserutils_error(perror);
  486. }
  487. return CSS_OK;
  488. }
  489. css_error handleEndBlock(css_language *c, const parserutils_vector *vector)
  490. {
  491. parserutils_error perror;
  492. context_entry *entry;
  493. css_rule *rule;
  494. UNUSED(vector);
  495. entry = parserutils_stack_get_current(c->context);
  496. if (entry == NULL || entry->type != CSS_PARSER_START_BLOCK)
  497. return CSS_INVALID;
  498. rule = entry->data;
  499. perror = parserutils_stack_pop(c->context, NULL);
  500. if (perror != PARSERUTILS_OK) {
  501. return css_error_from_parserutils_error(perror);
  502. }
  503. /* If the block we just popped off the stack was associated with a
  504. * non-block stack entry, and that entry is not a top-level statement,
  505. * then report the end of that entry, too. */
  506. if (rule != NULL && rule->ptype != CSS_RULE_PARENT_STYLESHEET) {
  507. if (rule->type == CSS_RULE_SELECTOR)
  508. return handleEndRuleset(c, vector);
  509. }
  510. return CSS_OK;
  511. }
  512. css_error handleBlockContent(css_language *c, const parserutils_vector *vector)
  513. {
  514. context_entry *entry;
  515. css_rule *rule;
  516. /* In CSS 2.1, block content comprises either declarations (if the
  517. * current block is associated with @page or a selector), or rulesets
  518. * (if the current block is associated with @media). */
  519. entry = parserutils_stack_get_current(c->context);
  520. if (entry == NULL || entry->data == NULL)
  521. return CSS_INVALID;
  522. rule = entry->data;
  523. if (rule == NULL || (rule->type != CSS_RULE_SELECTOR &&
  524. rule->type != CSS_RULE_PAGE &&
  525. rule->type != CSS_RULE_MEDIA))
  526. return CSS_INVALID;
  527. if (rule->type == CSS_RULE_MEDIA) {
  528. /* Expect rulesets */
  529. return handleStartRuleset(c, vector);
  530. } else {
  531. /* Expect declarations */
  532. return handleDeclaration(c, vector);
  533. }
  534. return CSS_OK;
  535. }
  536. css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
  537. {
  538. css_error error;
  539. const css_token *token, *ident;
  540. int ctx = 0;
  541. context_entry *entry;
  542. css_rule *rule;
  543. /* Locations where declarations are permitted:
  544. *
  545. * + In @page
  546. * + In ruleset
  547. */
  548. entry = parserutils_stack_get_current(c->context);
  549. if (entry == NULL || entry->data == NULL)
  550. return CSS_INVALID;
  551. rule = entry->data;
  552. if (rule == NULL || (rule->type != CSS_RULE_SELECTOR &&
  553. rule->type != CSS_RULE_PAGE))
  554. return CSS_INVALID;
  555. /* Strip any leading whitespace (can happen if in nested block) */
  556. consumeWhitespace(vector, &ctx);
  557. /* IDENT ws ':' ws value
  558. *
  559. * In CSS 2.1, value is any1, so '{' or ATKEYWORD => parse error
  560. */
  561. ident = parserutils_vector_iterate(vector, &ctx);
  562. if (ident == NULL || ident->type != CSS_TOKEN_IDENT)
  563. return CSS_INVALID;
  564. consumeWhitespace(vector, &ctx);
  565. token = parserutils_vector_iterate(vector, &ctx);
  566. if (token == NULL || tokenIsChar(token, ':') == false)
  567. return CSS_INVALID;
  568. consumeWhitespace(vector, &ctx);
  569. error = parseProperty(c, ident, vector, &ctx, rule);
  570. if (error != CSS_OK)
  571. return error;
  572. return CSS_OK;
  573. }
  574. /******************************************************************************
  575. * At-rule parsing functions *
  576. ******************************************************************************/
  577. css_error parseMediaList(css_language *c,
  578. const parserutils_vector *vector, int *ctx,
  579. uint64_t *media)
  580. {
  581. uint64_t ret = 0;
  582. bool match = false;
  583. const css_token *token;
  584. token = parserutils_vector_iterate(vector, ctx);
  585. while (token != NULL) {
  586. if (token->type != CSS_TOKEN_IDENT)
  587. return CSS_INVALID;
  588. if (lwc_string_caseless_isequal(token->idata, c->strings[AURAL],
  589. &match) == lwc_error_ok && match) {
  590. ret |= CSS_MEDIA_AURAL;
  591. } else if (lwc_string_caseless_isequal(
  592. token->idata, c->strings[BRAILLE],
  593. &match) == lwc_error_ok && match) {
  594. ret |= CSS_MEDIA_BRAILLE;
  595. } else if (lwc_string_caseless_isequal(
  596. token->idata, c->strings[EMBOSSED],
  597. &match) == lwc_error_ok && match) {
  598. ret |= CSS_MEDIA_EMBOSSED;
  599. } else if (lwc_string_caseless_isequal(
  600. token->idata, c->strings[HANDHELD],
  601. &match) == lwc_error_ok && match) {
  602. ret |= CSS_MEDIA_HANDHELD;
  603. } else if (lwc_string_caseless_isequal(
  604. token->idata, c->strings[PRINT],
  605. &match) == lwc_error_ok && match) {
  606. ret |= CSS_MEDIA_PRINT;
  607. } else if (lwc_string_caseless_isequal(
  608. token->idata, c->strings[PROJECTION],
  609. &match) == lwc_error_ok && match) {
  610. ret |= CSS_MEDIA_PROJECTION;
  611. } else if (lwc_string_caseless_isequal(
  612. token->idata, c->strings[SCREEN],
  613. &match) == lwc_error_ok && match) {
  614. ret |= CSS_MEDIA_SCREEN;
  615. } else if (lwc_string_caseless_isequal(
  616. token->idata, c->strings[SPEECH],
  617. &match) == lwc_error_ok && match) {
  618. ret |= CSS_MEDIA_SPEECH;
  619. } else if (lwc_string_caseless_isequal(
  620. token->idata, c->strings[TTY],
  621. &match) == lwc_error_ok && match) {
  622. ret |= CSS_MEDIA_TTY;
  623. } else if (lwc_string_caseless_isequal(
  624. token->idata, c->strings[TV],
  625. &match) == lwc_error_ok && match) {
  626. ret |= CSS_MEDIA_TV;
  627. } else if (lwc_string_caseless_isequal(
  628. token->idata, c->strings[ALL],
  629. &match) == lwc_error_ok && match) {
  630. ret |= CSS_MEDIA_ALL;
  631. } else
  632. return CSS_INVALID;
  633. consumeWhitespace(vector, ctx);
  634. token = parserutils_vector_iterate(vector, ctx);
  635. if (token != NULL && tokenIsChar(token, ',') == false)
  636. return CSS_INVALID;
  637. consumeWhitespace(vector, ctx);
  638. }
  639. /* If, after parsing the media list, we still have no media,
  640. * then it must be ALL. */
  641. if (ret == 0)
  642. ret = CSS_MEDIA_ALL;
  643. *media = ret;
  644. return CSS_OK;
  645. }
  646. /******************************************************************************
  647. * Selector list parsing functions *
  648. ******************************************************************************/
  649. css_error parseClass(css_language *c, const parserutils_vector *vector,
  650. int *ctx, css_selector_detail *specific)
  651. {
  652. const css_token *token;
  653. /* class -> '.' IDENT */
  654. token = parserutils_vector_iterate(vector, ctx);
  655. if (token == NULL || tokenIsChar(token, '.') == false)
  656. return CSS_INVALID;
  657. token = parserutils_vector_iterate(vector, ctx);
  658. if (token == NULL || token->type != CSS_TOKEN_IDENT)
  659. return CSS_INVALID;
  660. return css_stylesheet_selector_detail_init(c->sheet,
  661. CSS_SELECTOR_CLASS, token->idata, NULL, specific);
  662. }
  663. css_error parseAttrib(css_language *c, const parserutils_vector *vector,
  664. int *ctx, css_selector_detail *specific)
  665. {
  666. const css_token *token, *name, *value = NULL;
  667. css_selector_type type = CSS_SELECTOR_ATTRIBUTE;
  668. /* attrib -> '[' ws IDENT ws [
  669. * [ '=' | INCLUDES | DASHMATCH ] ws
  670. * [ IDENT | STRING ] ws ]? ']'
  671. */
  672. token = parserutils_vector_iterate(vector, ctx);
  673. if (token == NULL || tokenIsChar(token, '[') == false)
  674. return CSS_INVALID;
  675. consumeWhitespace(vector, ctx);
  676. token = parserutils_vector_iterate(vector, ctx);
  677. if (token == NULL || token->type != CSS_TOKEN_IDENT)
  678. return CSS_INVALID;
  679. name = token;
  680. consumeWhitespace(vector, ctx);
  681. token = parserutils_vector_iterate(vector, ctx);
  682. if (token == NULL)
  683. return CSS_INVALID;
  684. if (tokenIsChar(token, ']') == false) {
  685. if (tokenIsChar(token, '='))
  686. type = CSS_SELECTOR_ATTRIBUTE_EQUAL;
  687. else if (token->type == CSS_TOKEN_INCLUDES)
  688. type = CSS_SELECTOR_ATTRIBUTE_INCLUDES;
  689. else if (token->type == CSS_TOKEN_DASHMATCH)
  690. type = CSS_SELECTOR_ATTRIBUTE_DASHMATCH;
  691. else
  692. return CSS_INVALID;
  693. consumeWhitespace(vector, ctx);
  694. token = parserutils_vector_iterate(vector, ctx);
  695. if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  696. token->type != CSS_TOKEN_STRING))
  697. return CSS_INVALID;
  698. value = token;
  699. consumeWhitespace(vector, ctx);
  700. token = parserutils_vector_iterate(vector, ctx);
  701. if (token == NULL || tokenIsChar(token, ']') == false)
  702. return CSS_INVALID;
  703. }
  704. return css_stylesheet_selector_detail_init(c->sheet, type,
  705. name->idata, value != NULL ? value->idata : NULL,
  706. specific);
  707. }
  708. css_error parsePseudo(css_language *c, const parserutils_vector *vector,
  709. int *ctx, css_selector_detail *specific)
  710. {
  711. const css_token *token, *name, *value = NULL;
  712. bool match = false;
  713. css_selector_type type;
  714. /* pseudo -> ':' [ IDENT | FUNCTION ws IDENT? ws ')' ] */
  715. token = parserutils_vector_iterate(vector, ctx);
  716. if (token == NULL || tokenIsChar(token, ':') == false)
  717. return CSS_INVALID;
  718. token = parserutils_vector_iterate(vector, ctx);
  719. if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  720. token->type != CSS_TOKEN_FUNCTION))
  721. return CSS_INVALID;
  722. name = token;
  723. if (token->type == CSS_TOKEN_FUNCTION) {
  724. consumeWhitespace(vector, ctx);
  725. token = parserutils_vector_iterate(vector, ctx);
  726. if (token != NULL && token->type == CSS_TOKEN_IDENT) {
  727. value = token;
  728. consumeWhitespace(vector, ctx);
  729. token = parserutils_vector_iterate(vector, ctx);
  730. }
  731. if (token == NULL || tokenIsChar(token, ')') == false)
  732. return CSS_INVALID;
  733. }
  734. if ((lwc_string_caseless_isequal(
  735. name->idata, c->strings[FIRST_CHILD],
  736. &match) == lwc_error_ok && match) ||
  737. (lwc_string_caseless_isequal(
  738. name->idata, c->strings[LINK],
  739. &match) == lwc_error_ok && match) ||
  740. (lwc_string_caseless_isequal(
  741. name->idata, c->strings[VISITED],
  742. &match) == lwc_error_ok && match) ||
  743. (lwc_string_caseless_isequal(
  744. name->idata, c->strings[HOVER],
  745. &match) == lwc_error_ok && match) ||
  746. (lwc_string_caseless_isequal(
  747. name->idata, c->strings[ACTIVE],
  748. &match) == lwc_error_ok && match) ||
  749. (lwc_string_caseless_isequal(
  750. name->idata, c->strings[FOCUS],
  751. &match) == lwc_error_ok && match) ||
  752. (lwc_string_caseless_isequal(
  753. name->idata, c->strings[LANG],
  754. &match) == lwc_error_ok && match) ||
  755. (lwc_string_caseless_isequal(
  756. name->idata, c->strings[LEFT],
  757. &match) == lwc_error_ok && match) ||
  758. (lwc_string_caseless_isequal(
  759. name->idata, c->strings[RIGHT],
  760. &match) == lwc_error_ok && match) ||
  761. (lwc_string_caseless_isequal(
  762. name->idata, c->strings[FIRST],
  763. &match) == lwc_error_ok && match))
  764. type = CSS_SELECTOR_PSEUDO_CLASS;
  765. else if ((lwc_string_caseless_isequal(
  766. name->idata, c->strings[FIRST_LINE],
  767. &match) == lwc_error_ok && match) ||
  768. (lwc_string_caseless_isequal(
  769. name->idata, c->strings[FIRST_LETTER],
  770. &match) == lwc_error_ok && match) ||
  771. (lwc_string_caseless_isequal(
  772. name->idata, c->strings[BEFORE],
  773. &match) == lwc_error_ok && match) ||
  774. (lwc_string_caseless_isequal(
  775. name->idata, c->strings[AFTER],
  776. &match) == lwc_error_ok && match))
  777. type = CSS_SELECTOR_PSEUDO_ELEMENT;
  778. else
  779. return CSS_INVALID;
  780. return css_stylesheet_selector_detail_init(c->sheet,
  781. type, name->idata, value != NULL ? value->idata : NULL,
  782. specific);
  783. }
  784. css_error parseSpecific(css_language *c,
  785. const parserutils_vector *vector, int *ctx,
  786. css_selector **parent)
  787. {
  788. css_error error;
  789. const css_token *token;
  790. css_selector_detail specific;
  791. /* specific -> [ HASH | class | attrib | pseudo ] */
  792. token = parserutils_vector_peek(vector, *ctx);
  793. if (token == NULL)
  794. return CSS_INVALID;
  795. if (token->type == CSS_TOKEN_HASH) {
  796. error = css_stylesheet_selector_detail_init(c->sheet,
  797. CSS_SELECTOR_ID, token->idata, NULL, &specific);
  798. if (error != CSS_OK)
  799. return error;
  800. parserutils_vector_iterate(vector, ctx);
  801. } else if (tokenIsChar(token, '.')) {
  802. error = parseClass(c, vector, ctx, &specific);
  803. if (error != CSS_OK)
  804. return error;
  805. } else if (tokenIsChar(token, '[')) {
  806. error = parseAttrib(c, vector, ctx, &specific);
  807. if (error != CSS_OK)
  808. return error;
  809. } else if (tokenIsChar(token, ':')) {
  810. error = parsePseudo(c, vector, ctx, &specific);
  811. if (error != CSS_OK)
  812. return error;
  813. } else {
  814. return CSS_INVALID;
  815. }
  816. return css_stylesheet_selector_append_specific(c->sheet, parent,
  817. &specific);
  818. }
  819. css_error parseSelectorSpecifics(css_language *c,
  820. const parserutils_vector *vector, int *ctx,
  821. css_selector **parent)
  822. {
  823. css_error error;
  824. const css_token *token;
  825. /* specifics -> specific* */
  826. while ((token = parserutils_vector_peek(vector, *ctx)) != NULL &&
  827. token->type != CSS_TOKEN_S &&
  828. tokenIsChar(token, '+') == false &&
  829. tokenIsChar(token, '>') == false &&
  830. tokenIsChar(token, ',') == false) {
  831. error = parseSpecific(c, vector, ctx, parent);
  832. if (error != CSS_OK)
  833. return error;
  834. }
  835. return CSS_OK;
  836. }
  837. css_error parseSimpleSelector(css_language *c,
  838. const parserutils_vector *vector, int *ctx,
  839. css_selector **result)
  840. {
  841. css_error error;
  842. const css_token *token;
  843. css_selector *selector;
  844. /* simple_selector -> element_name specifics
  845. * -> specific specifics
  846. * element_name -> IDENT | '*'
  847. */
  848. token = parserutils_vector_peek(vector, *ctx);
  849. if (token == NULL)
  850. return CSS_INVALID;
  851. if (token->type == CSS_TOKEN_IDENT || tokenIsChar(token, '*')) {
  852. /* Have element name */
  853. error = css_stylesheet_selector_create(c->sheet,
  854. token->idata, &selector);
  855. if (error != CSS_OK)
  856. return error;
  857. parserutils_vector_iterate(vector, ctx);
  858. } else {
  859. /* Universal selector */
  860. error = css_stylesheet_selector_create(c->sheet,
  861. c->strings[UNIVERSAL], &selector);
  862. if (error != CSS_OK)
  863. return error;
  864. /* Ensure we have at least one specific selector */
  865. error = parseSpecific(c, vector, ctx, &selector);
  866. if (error != CSS_OK) {
  867. css_stylesheet_selector_destroy(c->sheet, selector);
  868. return error;
  869. }
  870. }
  871. error = parseSelectorSpecifics(c, vector, ctx, &selector);
  872. if (error != CSS_OK) {
  873. css_stylesheet_selector_destroy(c->sheet, selector);
  874. return error;
  875. }
  876. *result = selector;
  877. return CSS_OK;
  878. }
  879. css_error parseCombinator(css_language *c, const parserutils_vector *vector,
  880. int *ctx, css_combinator *result)
  881. {
  882. const css_token *token;
  883. css_combinator comb = CSS_COMBINATOR_NONE;
  884. /* combinator -> ws '+' ws | ws '>' ws | ws1 */
  885. UNUSED(c);
  886. while ((token = parserutils_vector_peek(vector, *ctx)) != NULL) {
  887. if (tokenIsChar(token, '+'))
  888. comb = CSS_COMBINATOR_SIBLING;
  889. else if (tokenIsChar(token, '>'))
  890. comb = CSS_COMBINATOR_PARENT;
  891. else if (token->type == CSS_TOKEN_S)
  892. comb = CSS_COMBINATOR_ANCESTOR;
  893. else
  894. break;
  895. parserutils_vector_iterate(vector, ctx);
  896. /* If we've seen a '+' or '>', we're done. */
  897. if (comb != CSS_COMBINATOR_ANCESTOR)
  898. break;
  899. }
  900. /* No valid combinator found */
  901. if (comb == CSS_COMBINATOR_NONE)
  902. return CSS_INVALID;
  903. /* Consume any trailing whitespace */
  904. consumeWhitespace(vector, ctx);
  905. *result = comb;
  906. return CSS_OK;
  907. }
  908. css_error parseSelector(css_language *c, const parserutils_vector *vector,
  909. int *ctx, css_selector **result)
  910. {
  911. css_error error;
  912. const css_token *token = NULL;
  913. css_selector *selector = NULL;
  914. /* selector -> simple_selector [ combinator simple_selector ]* ws
  915. *
  916. * Note, however, that, as combinator can be wholly whitespace,
  917. * there's an ambiguity as to whether "ws" has been reached. We
  918. * resolve this by attempting to extract a combinator, then
  919. * recovering when we detect that we've reached the end of the
  920. * selector.
  921. */
  922. error = parseSimpleSelector(c, vector, ctx, &selector);
  923. if (error != CSS_OK)
  924. return error;
  925. *result = selector;
  926. while ((token = parserutils_vector_peek(vector, *ctx)) != NULL &&
  927. tokenIsChar(token, ',') == false) {
  928. css_combinator comb = CSS_COMBINATOR_NONE;
  929. css_selector *other = NULL;
  930. error = parseCombinator(c, vector, ctx, &comb);
  931. if (error != CSS_OK)
  932. return error;
  933. /* In the case of "html , body { ... }", the whitespace after
  934. * "html" and "body" will be considered an ancestor combinator.
  935. * This clearly is not the case, however. Therefore, as a
  936. * special case, if we've got an ancestor combinator and there
  937. * are no further tokens, or if the next token is a comma,
  938. * we ignore the supposed combinator and continue. */
  939. if (comb == CSS_COMBINATOR_ANCESTOR &&
  940. ((token = parserutils_vector_peek(vector,
  941. *ctx)) == NULL ||
  942. tokenIsChar(token, ',')))
  943. continue;
  944. error = parseSimpleSelector(c, vector, ctx, &other);
  945. if (error != CSS_OK)
  946. return error;
  947. *result = other;
  948. error = css_stylesheet_selector_combine(c->sheet,
  949. comb, selector, other);
  950. if (error != CSS_OK) {
  951. css_stylesheet_selector_destroy(c->sheet, selector);
  952. return error;
  953. }
  954. selector = other;
  955. }
  956. return CSS_OK;
  957. }
  958. css_error parseSelectorList(css_language *c, const parserutils_vector *vector,
  959. css_rule *rule)
  960. {
  961. css_error error;
  962. const css_token *token = NULL;
  963. css_selector *selector = NULL;
  964. int ctx = 0;
  965. /* Strip any leading whitespace (can happen if in nested block) */
  966. consumeWhitespace(vector, &ctx);
  967. /* selector_list -> selector [ ',' ws selector ]* */
  968. error = parseSelector(c, vector, &ctx, &selector);
  969. if (error != CSS_OK) {
  970. if (selector != NULL)
  971. css_stylesheet_selector_destroy(c->sheet, selector);
  972. return error;
  973. }
  974. assert(selector != NULL);
  975. error = css_stylesheet_rule_add_selector(c->sheet, rule, selector);
  976. if (error != CSS_OK) {
  977. css_stylesheet_selector_destroy(c->sheet, selector);
  978. return error;
  979. }
  980. while (parserutils_vector_peek(vector, ctx) != NULL) {
  981. token = parserutils_vector_iterate(vector, &ctx);
  982. if (tokenIsChar(token, ',') == false)
  983. return CSS_INVALID;
  984. consumeWhitespace(vector, &ctx);
  985. selector = NULL;
  986. error = parseSelector(c, vector, &ctx, &selector);
  987. if (error != CSS_OK) {
  988. if (selector != NULL) {
  989. css_stylesheet_selector_destroy(c->sheet,
  990. selector);
  991. }
  992. return error;
  993. }
  994. assert(selector != NULL);
  995. error = css_stylesheet_rule_add_selector(c->sheet, rule,
  996. selector);
  997. if (error != CSS_OK) {
  998. css_stylesheet_selector_destroy(c->sheet, selector);
  999. return error;
  1000. }
  1001. }
  1002. return CSS_OK;
  1003. }
  1004. /******************************************************************************
  1005. * Property parsing functions *
  1006. ******************************************************************************/
  1007. css_error parseProperty(css_language *c, const css_token *property,
  1008. const parserutils_vector *vector, int *ctx, css_rule *rule)
  1009. {
  1010. css_error error;
  1011. css_prop_handler handler = NULL;
  1012. int i = 0;
  1013. uint8_t flags = 0;
  1014. css_style *style = NULL;
  1015. const css_token *token;
  1016. /* Find property index */
  1017. /** \todo improve on this linear search */
  1018. for (i = FIRST_PROP; i <= LAST_PROP; i++) {
  1019. bool match = false;
  1020. if (lwc_string_caseless_isequal(property->idata, c->strings[i],
  1021. &match) == lwc_error_ok && match)
  1022. break;
  1023. }
  1024. if (i == LAST_PROP + 1)
  1025. return CSS_INVALID;
  1026. /* Get handler */
  1027. handler = property_handlers[i - FIRST_PROP];
  1028. assert(handler != NULL);
  1029. /* Call it */
  1030. error = handler(c, vector, ctx, &style);
  1031. if (error != CSS_OK)
  1032. return error;
  1033. assert (style != NULL);
  1034. /* Determine if this declaration is important or not */
  1035. error = parse_important(c, vector, ctx, &flags);
  1036. if (error != CSS_OK) {
  1037. css_stylesheet_style_destroy(c->sheet, style, false);
  1038. return error;
  1039. }
  1040. /* Ensure that we've exhausted all the input */
  1041. consumeWhitespace(vector, ctx);
  1042. token = parserutils_vector_iterate(vector, ctx);
  1043. if (token != NULL) {
  1044. /* Trailing junk, so discard declaration */
  1045. css_stylesheet_style_destroy(c->sheet, style, false);
  1046. return CSS_INVALID;
  1047. }
  1048. /* If it's important, then mark the style appropriately */
  1049. if (flags != 0)
  1050. make_style_important(style);
  1051. /* Append style to rule */
  1052. error = css_stylesheet_rule_append_style(c->sheet, rule, style);
  1053. if (error != CSS_OK) {
  1054. css_stylesheet_style_destroy(c->sheet, style, false);
  1055. return error;
  1056. }
  1057. /* Style owned or destroyed by stylesheet, so forget about it */
  1058. return CSS_OK;
  1059. }