Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  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.  
  8. #include <assert.h>
  9. #include <string.h>
  10.  
  11. #include <parserutils/utils/stack.h>
  12.  
  13. #include "stylesheet.h"
  14. #include "lex/lex.h"
  15. #include "parse/font_face.h"
  16. #include "parse/important.h"
  17. #include "parse/language.h"
  18. #include "parse/parse.h"
  19. #include "parse/propstrings.h"
  20. #include "parse/properties/properties.h"
  21. #include "parse/properties/utils.h"
  22.  
  23. #include "utils/parserutilserror.h"
  24. #include "utils/utils.h"
  25.  
  26. typedef struct context_entry {
  27.         css_parser_event type;          /**< Type of entry */
  28.         void *data;                     /**< Data for context */
  29. } context_entry;
  30.  
  31. /* Event handlers */
  32. static css_error language_handle_event(css_parser_event type,
  33.                 const parserutils_vector *tokens, void *pw);
  34. static css_error handleStartStylesheet(css_language *c,
  35.                 const parserutils_vector *vector);
  36. static css_error handleEndStylesheet(css_language *c,
  37.                 const parserutils_vector *vector);
  38. static css_error handleStartRuleset(css_language *c,
  39.                 const parserutils_vector *vector);
  40. static css_error handleEndRuleset(css_language *c,
  41.                 const parserutils_vector *vector);
  42. static css_error handleStartAtRule(css_language *c,
  43.                 const parserutils_vector *vector);
  44. static css_error handleEndAtRule(css_language *c,
  45.                 const parserutils_vector *vector);
  46. static css_error handleStartBlock(css_language *c,
  47.                 const parserutils_vector *vector);
  48. static css_error handleEndBlock(css_language *c,
  49.                 const parserutils_vector *vector);
  50. static css_error handleBlockContent(css_language *c,
  51.                 const parserutils_vector *vector);
  52. static css_error handleDeclaration(css_language *c,
  53.                 const parserutils_vector *vector);
  54.  
  55. /* At-rule parsing */
  56. static css_error parseMediaList(css_language *c,
  57.                 const parserutils_vector *vector, int *ctx,
  58.                 uint64_t *media);
  59. static css_error addNamespace(css_language *c,
  60.                 lwc_string *prefix, lwc_string *uri);
  61. static css_error lookupNamespace(css_language *c,
  62.                 lwc_string *prefix, lwc_string **uri);
  63.  
  64. /* Selector list parsing */
  65. static css_error parseClass(css_language *c,
  66.                 const parserutils_vector *vector, int *ctx,
  67.                 css_selector_detail *specific);
  68. static css_error parseAttrib(css_language *c,
  69.                 const parserutils_vector *vector, int *ctx,
  70.                 css_selector_detail *specific);
  71. static css_error parseNth(css_language *c,
  72.                 const parserutils_vector *vector, int *ctx,
  73.                 css_selector_detail_value *value);
  74. static css_error parsePseudo(css_language *c,
  75.                 const parserutils_vector *vector, int *ctx,
  76.                 bool in_not, css_selector_detail *specific);
  77. static css_error parseSpecific(css_language *c,
  78.                 const parserutils_vector *vector, int *ctx,
  79.                 bool in_not, css_selector_detail *specific);
  80. static css_error parseAppendSpecific(css_language *c,
  81.                 const parserutils_vector *vector, int *ctx,
  82.                 css_selector **parent);
  83. static css_error parseSelectorSpecifics(css_language *c,
  84.                 const parserutils_vector *vector, int *ctx,
  85.                 css_selector **parent);
  86. static css_error parseTypeSelector(css_language *c,
  87.                 const parserutils_vector *vector, int *ctx,
  88.                 css_qname *qname);
  89. static css_error parseSimpleSelector(css_language *c,
  90.                 const parserutils_vector *vector, int *ctx,
  91.                 css_selector **result);
  92. static css_error parseCombinator(css_language *c,
  93.                 const parserutils_vector *vector, int *ctx,
  94.                 css_combinator *result);
  95. static css_error parseSelector(css_language *c,
  96.                 const parserutils_vector *vector, int *ctx,
  97.                 css_selector **result);
  98. static css_error parseSelectorList(css_language *c,
  99.                 const parserutils_vector *vector, css_rule *rule);
  100.  
  101. /* Declaration parsing */
  102. static css_error parseProperty(css_language *c,
  103.                 const css_token *property, const parserutils_vector *vector,
  104.                 int *ctx, css_rule *rule);
  105.  
  106. /**
  107.  * Create a CSS language parser
  108.  *
  109.  * \param sheet     The stylesheet object to parse for
  110.  * \param parser    The core parser object to use
  111.  * \param alloc     Memory (de)allocation function
  112.  * \param pw        Pointer to client-specific private data
  113.  * \param language  Pointer to location to receive parser object
  114.  * \return CSS_OK on success,
  115.  *         CSS_BADPARM on bad parameters,
  116.  *         CSS_NOMEM on memory exhaustion
  117.  */
  118. css_error css__language_create(css_stylesheet *sheet, css_parser *parser,
  119.                 css_allocator_fn alloc, void *pw, void **language)
  120. {
  121.         css_language *c;
  122.         css_parser_optparams params;
  123.         parserutils_error perror;
  124.         css_error error;
  125.  
  126.         if (sheet == NULL || parser == NULL || alloc == NULL ||
  127.                         language == NULL)
  128.                 return CSS_BADPARM;
  129.  
  130.         c = alloc(NULL, sizeof(css_language), pw);
  131.         if (c == NULL)
  132.                 return CSS_NOMEM;
  133.  
  134.         perror = parserutils_stack_create(sizeof(context_entry),
  135.                         STACK_CHUNK, (parserutils_alloc) alloc, pw,
  136.                         &c->context);
  137.         if (perror != PARSERUTILS_OK) {
  138.                 alloc(c, 0, pw);
  139.                 return css_error_from_parserutils_error(perror);
  140.         }
  141.  
  142.         params.event_handler.handler = language_handle_event;
  143.         params.event_handler.pw = c;
  144.         error = css__parser_setopt(parser, CSS_PARSER_EVENT_HANDLER, &params);
  145.         if (error != CSS_OK) {
  146.                 parserutils_stack_destroy(c->context);
  147.                 alloc(c, 0, pw);
  148.                 return error;
  149.         }
  150.  
  151.         c->sheet = sheet;
  152.         c->state = CHARSET_PERMITTED;
  153.         c->default_namespace = NULL;
  154.         c->namespaces = NULL;
  155.         c->num_namespaces = 0;
  156.         c->strings = sheet->propstrings;
  157.         c->alloc = alloc;
  158.         c->pw = pw;
  159.  
  160.         *language = c;
  161.  
  162.         return CSS_OK;
  163. }
  164.  
  165. /**
  166.  * Destroy a CSS language parser
  167.  *
  168.  * \param language  The parser to destroy
  169.  * \return CSS_OK on success, appropriate error otherwise
  170.  */
  171. css_error css__language_destroy(css_language *language)
  172. {
  173.         uint32_t i;
  174.        
  175.         if (language == NULL)
  176.                 return CSS_BADPARM;
  177.  
  178.         if (language->default_namespace != NULL)
  179.                 lwc_string_unref(language->default_namespace);
  180.  
  181.         if (language->namespaces != NULL) {
  182.                 for (i = 0; i < language->num_namespaces; i++) {
  183.                         lwc_string_unref(language->namespaces[i].prefix);
  184.                         lwc_string_unref(language->namespaces[i].uri);
  185.                 }
  186.  
  187.                 language->alloc(language->namespaces, 0, language->pw);
  188.         }
  189.  
  190.         parserutils_stack_destroy(language->context);
  191.        
  192.         language->alloc(language, 0, language->pw);
  193.  
  194.         return CSS_OK;
  195. }
  196.  
  197. /**
  198.  * Handler for core parser events
  199.  *
  200.  * \param type    The event type
  201.  * \param tokens  Vector of tokens read since last event, or NULL
  202.  * \param pw      Pointer to handler context
  203.  * \return CSS_OK on success, CSS_INVALID to indicate parse error,
  204.  *         appropriate error otherwise.
  205.  */
  206. css_error language_handle_event(css_parser_event type,
  207.                 const parserutils_vector *tokens, void *pw)
  208. {
  209.         css_language *language = (css_language *) pw;
  210.  
  211.         switch (type) {
  212.         case CSS_PARSER_START_STYLESHEET:
  213.                 return handleStartStylesheet(language, tokens);
  214.         case CSS_PARSER_END_STYLESHEET:
  215.                 return handleEndStylesheet(language, tokens);
  216.         case CSS_PARSER_START_RULESET:
  217.                 return handleStartRuleset(language, tokens);
  218.         case CSS_PARSER_END_RULESET:
  219.                 return handleEndRuleset(language, tokens);
  220.         case CSS_PARSER_START_ATRULE:
  221.                 return handleStartAtRule(language, tokens);
  222.         case CSS_PARSER_END_ATRULE:
  223.                 return handleEndAtRule(language, tokens);
  224.         case CSS_PARSER_START_BLOCK:
  225.                 return handleStartBlock(language, tokens);
  226.         case CSS_PARSER_END_BLOCK:
  227.                 return handleEndBlock(language, tokens);
  228.         case CSS_PARSER_BLOCK_CONTENT:
  229.                 return handleBlockContent(language, tokens);
  230.         case CSS_PARSER_DECLARATION:
  231.                 return handleDeclaration(language, tokens);
  232.         }
  233.  
  234.         return CSS_OK;
  235. }
  236.  
  237. /******************************************************************************
  238.  * Parser stages                                                              *
  239.  ******************************************************************************/
  240.  
  241. css_error handleStartStylesheet(css_language *c,
  242.                 const parserutils_vector *vector)
  243. {
  244.         parserutils_error perror;
  245.         context_entry entry = { CSS_PARSER_START_STYLESHEET, NULL };
  246.  
  247.         UNUSED(vector);
  248.  
  249.         assert(c != NULL);
  250.  
  251.         perror = parserutils_stack_push(c->context, (void *) &entry);
  252.         if (perror != PARSERUTILS_OK) {
  253.                 return css_error_from_parserutils_error(perror);
  254.         }
  255.  
  256.         return CSS_OK;
  257. }
  258.  
  259. css_error handleEndStylesheet(css_language *c, const parserutils_vector *vector)
  260. {
  261.         parserutils_error perror;
  262.         context_entry *entry;
  263.  
  264.         UNUSED(vector);
  265.  
  266.         assert(c != NULL);
  267.  
  268.         entry = parserutils_stack_get_current(c->context);
  269.         if (entry == NULL || entry->type != CSS_PARSER_START_STYLESHEET)
  270.                 return CSS_INVALID;
  271.  
  272.         perror = parserutils_stack_pop(c->context, NULL);
  273.         if (perror != PARSERUTILS_OK) {
  274.                 return css_error_from_parserutils_error(perror);
  275.         }
  276.  
  277.         return CSS_OK;
  278. }
  279.  
  280. css_error handleStartRuleset(css_language *c, const parserutils_vector *vector)
  281. {
  282.         parserutils_error perror;
  283.         css_error error;
  284.         context_entry entry = { CSS_PARSER_START_RULESET, NULL };
  285.         context_entry *cur;
  286.         css_rule *parent_rule = NULL;
  287.         css_rule *rule = NULL;
  288.  
  289.         assert(c != NULL);
  290.  
  291.         /* Retrieve parent rule from stack, if any */
  292.         cur = parserutils_stack_get_current(c->context);
  293.         if (cur != NULL && cur->type != CSS_PARSER_START_STYLESHEET)
  294.                 parent_rule = cur->data;
  295.  
  296.         error = css__stylesheet_rule_create(c->sheet, CSS_RULE_SELECTOR, &rule);
  297.         if (error != CSS_OK)
  298.                 return error;
  299.  
  300.         if (vector != NULL) {
  301.                 /* Parse selectors, if there are any */
  302.                 error = parseSelectorList(c, vector, rule);
  303.                 if (error != CSS_OK) {
  304.                         css__stylesheet_rule_destroy(c->sheet, rule);
  305.                         return error;
  306.                 }
  307.         }
  308.  
  309.         entry.data = rule;
  310.  
  311.         perror = parserutils_stack_push(c->context, (void *) &entry);
  312.         if (perror != PARSERUTILS_OK) {
  313.                 css__stylesheet_rule_destroy(c->sheet, rule);
  314.                 return css_error_from_parserutils_error(perror);
  315.         }
  316.  
  317.         error = css__stylesheet_add_rule(c->sheet, rule, parent_rule);
  318.         if (error != CSS_OK) {
  319.                 parserutils_stack_pop(c->context, NULL);
  320.                 css__stylesheet_rule_destroy(c->sheet, rule);
  321.                 return error;
  322.         }
  323.  
  324.         /* Flag that we've had a valid rule, so @import/@namespace/@charset
  325.          * have no effect. */
  326.         c->state = HAD_RULE;
  327.  
  328.         /* Rule is now owned by the sheet, so no need to destroy it */
  329.  
  330.         return CSS_OK;
  331. }
  332.  
  333. css_error handleEndRuleset(css_language *c, const parserutils_vector *vector)
  334. {
  335.         parserutils_error perror;
  336.         context_entry *entry;
  337.  
  338.         UNUSED(vector);
  339.  
  340.         assert(c != NULL);
  341.  
  342.         entry = parserutils_stack_get_current(c->context);
  343.         if (entry == NULL || entry->type != CSS_PARSER_START_RULESET)
  344.                 return CSS_INVALID;
  345.  
  346.         perror = parserutils_stack_pop(c->context, NULL);
  347.         if (perror != PARSERUTILS_OK) {
  348.                 return css_error_from_parserutils_error(perror);
  349.         }
  350.  
  351.         return CSS_OK;
  352. }
  353.  
  354. css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
  355. {
  356.         parserutils_error perror;
  357.         context_entry entry = { CSS_PARSER_START_ATRULE, NULL };
  358.         const css_token *token = NULL;
  359.         const css_token *atkeyword = NULL;
  360.         int32_t ctx = 0;
  361.         bool match = false;
  362.         css_rule *rule;
  363.         css_error error;
  364.  
  365.         /* vector contains: ATKEYWORD ws any0 */
  366.  
  367.         assert(c != NULL);
  368.  
  369.         atkeyword = parserutils_vector_iterate(vector, &ctx);
  370.  
  371.         consumeWhitespace(vector, &ctx);
  372.  
  373.         /* We now have an ATKEYWORD and the context for the start of any0, if
  374.          * there is one */
  375.         assert(atkeyword != NULL && atkeyword->type == CSS_TOKEN_ATKEYWORD);
  376.  
  377.         if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[CHARSET],
  378.                         &match) == lwc_error_ok && match) {
  379.                 if (c->state == CHARSET_PERMITTED) {
  380.                         const css_token *charset;
  381.  
  382.                         /* any0 = STRING */
  383.                         if (ctx == 0)
  384.                                 return CSS_INVALID;
  385.  
  386.                         charset = parserutils_vector_iterate(vector, &ctx);
  387.                         if (charset == NULL ||
  388.                                         charset->type != CSS_TOKEN_STRING)
  389.                                 return CSS_INVALID;
  390.  
  391.                         token = parserutils_vector_iterate(vector, &ctx);
  392.                         if (token != NULL)
  393.                                 return CSS_INVALID;
  394.  
  395.                         error = css__stylesheet_rule_create(c->sheet,
  396.                                         CSS_RULE_CHARSET, &rule);
  397.                         if (error != CSS_OK)
  398.                                 return error;
  399.  
  400.                         error = css__stylesheet_rule_set_charset(c->sheet, rule,
  401.                                         charset->idata);
  402.                         if (error != CSS_OK) {
  403.                                 css__stylesheet_rule_destroy(c->sheet, rule);
  404.                                 return error;
  405.                         }
  406.  
  407.                         error = css__stylesheet_add_rule(c->sheet, rule, NULL);
  408.                         if (error != CSS_OK) {
  409.                                 css__stylesheet_rule_destroy(c->sheet, rule);
  410.                                 return error;
  411.                         }
  412.  
  413.                         /* Rule is now owned by the sheet,
  414.                          * so no need to destroy it */
  415.  
  416.                         c->state = IMPORT_PERMITTED;
  417.                 } else {
  418.                         return CSS_INVALID;
  419.                 }
  420.         } else if (lwc_string_caseless_isequal(atkeyword->idata,
  421.                         c->strings[LIBCSS_IMPORT], &match) == lwc_error_ok &&
  422.                         match) {
  423.                 if (c->state <= IMPORT_PERMITTED) {
  424.                         lwc_string *url;
  425.                         uint64_t media = 0;
  426.  
  427.                         /* any0 = (STRING | URI) ws
  428.                          *        (IDENT ws (',' ws IDENT ws)* )? */
  429.                         const css_token *uri =
  430.                                 parserutils_vector_iterate(vector, &ctx);
  431.                         if (uri == NULL || (uri->type != CSS_TOKEN_STRING &&
  432.                                         uri->type != CSS_TOKEN_URI))
  433.                                 return CSS_INVALID;
  434.  
  435.                         consumeWhitespace(vector, &ctx);
  436.  
  437.                         /* Parse media list */
  438.                         error = parseMediaList(c, vector, &ctx, &media);
  439.                         if (error != CSS_OK)
  440.                                 return error;
  441.  
  442.                         /* Create rule */
  443.                         error = css__stylesheet_rule_create(c->sheet,
  444.                                         CSS_RULE_IMPORT, &rule);
  445.                         if (error != CSS_OK)
  446.                                 return error;
  447.  
  448.                         /* Resolve import URI */
  449.                         error = c->sheet->resolve(c->sheet->resolve_pw,
  450.                                         c->sheet->url,
  451.                                         uri->idata, &url);
  452.                         if (error != CSS_OK) {
  453.                                 css__stylesheet_rule_destroy(c->sheet, rule);
  454.                                 return error;
  455.                         }
  456.  
  457.                         /* Inform rule of it */
  458.                         error = css__stylesheet_rule_set_nascent_import(c->sheet,
  459.                                         rule, url, media);
  460.                         if (error != CSS_OK) {
  461.                                 lwc_string_unref(url);
  462.                                 css__stylesheet_rule_destroy(c->sheet, rule);
  463.                                 return error;
  464.                         }
  465.  
  466.                         /* Inform client of need for import */
  467.                         if (c->sheet->import != NULL) {
  468.                                 error = c->sheet->import(c->sheet->import_pw,
  469.                                                 c->sheet, url, media);
  470.                                 if (error != CSS_OK) {
  471.                                         lwc_string_unref(url);
  472.                                         css__stylesheet_rule_destroy(c->sheet,
  473.                                                         rule);
  474.                                         return error;
  475.                                 }
  476.                         }
  477.  
  478.                         /* No longer care about url */
  479.                         lwc_string_unref(url);
  480.  
  481.                         /* Add rule to sheet */
  482.                         error = css__stylesheet_add_rule(c->sheet, rule, NULL);
  483.                         if (error != CSS_OK) {
  484.                                 css__stylesheet_rule_destroy(c->sheet, rule);
  485.                                 return error;
  486.                         }
  487.  
  488.                         /* Rule is now owned by the sheet,
  489.                          * so no need to destroy it */
  490.  
  491.                         c->state = IMPORT_PERMITTED;
  492.                 } else {
  493.                         return CSS_INVALID;
  494.                 }
  495.         } else if (lwc_string_caseless_isequal(atkeyword->idata,
  496.                         c->strings[NAMESPACE], &match) == lwc_error_ok &&
  497.                         match) {
  498.                 if (c->state <= NAMESPACE_PERMITTED) {
  499.                         lwc_string *prefix = NULL;
  500.  
  501.                         /* any0 = (IDENT ws)? (STRING | URI) ws */
  502.  
  503.                         token = parserutils_vector_iterate(vector, &ctx);
  504.                         if (token == NULL)
  505.                                 return CSS_INVALID;
  506.  
  507.                         if (token->type == CSS_TOKEN_IDENT) {
  508.                                 prefix = token->idata;
  509.  
  510.                                 consumeWhitespace(vector, &ctx);
  511.  
  512.                                 token = parserutils_vector_iterate(vector,
  513.                                                 &ctx);
  514.                         }
  515.  
  516.                         if (token == NULL || (token->type != CSS_TOKEN_STRING &&
  517.                                         token->type != CSS_TOKEN_URI)) {
  518.                                 return CSS_INVALID;
  519.                         }
  520.  
  521.                         consumeWhitespace(vector, &ctx);
  522.  
  523.                         error = addNamespace(c, prefix, token->idata);
  524.                         if (error != CSS_OK)
  525.                                 return error;
  526.  
  527.                         c->state = NAMESPACE_PERMITTED;
  528.                 } else {
  529.                         return CSS_INVALID;
  530.                 }
  531.         } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[MEDIA],
  532.                         &match) == lwc_error_ok && match) {
  533.                 uint64_t media = 0;
  534.  
  535.                 /* any0 = IDENT ws (',' ws IDENT ws)* */
  536.  
  537.                 error = parseMediaList(c, vector, &ctx, &media);
  538.                 if (error != CSS_OK)
  539.                         return error;
  540.  
  541.                 error = css__stylesheet_rule_create(c->sheet,
  542.                                 CSS_RULE_MEDIA, &rule);
  543.                 if (error != CSS_OK)
  544.                         return error;
  545.  
  546.                 error = css__stylesheet_rule_set_media(c->sheet, rule, media);
  547.                 if (error != CSS_OK) {
  548.                         css__stylesheet_rule_destroy(c->sheet, rule);
  549.                         return error;
  550.                 }
  551.  
  552.                 error = css__stylesheet_add_rule(c->sheet, rule, NULL);
  553.                 if (error != CSS_OK) {
  554.                         css__stylesheet_rule_destroy(c->sheet, rule);
  555.                         return error;
  556.                 }
  557.  
  558.                 /* Rule is now owned by the sheet,
  559.                  * so no need to destroy it */
  560.  
  561.                 c->state = HAD_RULE;
  562.         } else if (lwc_string_caseless_isequal(atkeyword->idata,
  563.                         c->strings[FONT_FACE], &match) == lwc_error_ok &&
  564.                         match) {
  565.                 error = css__stylesheet_rule_create(c->sheet,
  566.                                 CSS_RULE_FONT_FACE, &rule);
  567.                 if (error != CSS_OK)
  568.                         return error;
  569.                
  570.                 consumeWhitespace(vector, &ctx);
  571.  
  572.                 error = css__stylesheet_add_rule(c->sheet, rule, NULL);
  573.                 if (error != CSS_OK) {
  574.                         css__stylesheet_rule_destroy(c->sheet, rule);
  575.                         return error;
  576.                 }
  577.  
  578.                 /* Rule is now owned by the sheet,
  579.                  * so no need to destroy it */
  580.  
  581.                 c->state = HAD_RULE;
  582.         } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[PAGE],
  583.                         &match) == lwc_error_ok && match) {
  584.                 const css_token *token;
  585.  
  586.                 /* any0 = (':' IDENT)? ws */
  587.  
  588.                 error = css__stylesheet_rule_create(c->sheet,
  589.                                 CSS_RULE_PAGE, &rule);
  590.                 if (error != CSS_OK)
  591.                         return error;
  592.  
  593.                 consumeWhitespace(vector, &ctx);
  594.  
  595.                 token = parserutils_vector_peek(vector, ctx);
  596.                 if (token != NULL) {
  597.                         css_selector *sel = NULL;
  598.  
  599.                         error = parseSelector(c, vector, &ctx, &sel);
  600.                         if (error != CSS_OK) {
  601.                                 css__stylesheet_rule_destroy(c->sheet, rule);
  602.                                 return error;
  603.                         }
  604.  
  605.                         error = css__stylesheet_rule_set_page_selector(c->sheet,
  606.                                         rule, sel);
  607.                         if (error != CSS_OK) {
  608.                                 css__stylesheet_selector_destroy(c->sheet, sel);
  609.                                 css__stylesheet_rule_destroy(c->sheet, rule);
  610.                                 return error;
  611.                         }
  612.                 }
  613.  
  614.                 error = css__stylesheet_add_rule(c->sheet, rule, NULL);
  615.                 if (error != CSS_OK) {
  616.                         css__stylesheet_rule_destroy(c->sheet, rule);
  617.                         return error;
  618.                 }
  619.  
  620.                 /* Rule is now owned by the sheet,
  621.                  * so no need to destroy it */
  622.  
  623.                 c->state = HAD_RULE;
  624.         } else {
  625.                 return CSS_INVALID;
  626.         }
  627.  
  628.         entry.data = rule;
  629.  
  630.         perror = parserutils_stack_push(c->context, (void *) &entry);
  631.         if (perror != PARSERUTILS_OK) {
  632.                 return css_error_from_parserutils_error(perror);
  633.         }
  634.  
  635.         return CSS_OK;
  636. }
  637.  
  638. css_error handleEndAtRule(css_language *c, const parserutils_vector *vector)
  639. {
  640.         parserutils_error perror;
  641.         context_entry *entry;
  642.  
  643.         UNUSED(vector);
  644.  
  645.         assert(c != NULL);
  646.  
  647.         entry = parserutils_stack_get_current(c->context);
  648.         if (entry == NULL || entry->type != CSS_PARSER_START_ATRULE)
  649.                 return CSS_INVALID;
  650.  
  651.         perror = parserutils_stack_pop(c->context, NULL);
  652.         if (perror != PARSERUTILS_OK) {
  653.                 return css_error_from_parserutils_error(perror);
  654.         }
  655.  
  656.         return CSS_OK;
  657. }
  658.  
  659. css_error handleStartBlock(css_language *c, const parserutils_vector *vector)
  660. {
  661.         parserutils_error perror;
  662.         context_entry entry = { CSS_PARSER_START_BLOCK, NULL };
  663.         context_entry *cur;
  664.  
  665.         UNUSED(vector);
  666.  
  667.         /* If the current item on the stack isn't a block,
  668.          * then clone its data field. This ensures that the relevant rule
  669.          * is available when parsing the block contents. */
  670.         cur = parserutils_stack_get_current(c->context);
  671.         if (cur != NULL && cur->type != CSS_PARSER_START_BLOCK)
  672.                 entry.data = cur->data;
  673.  
  674.         perror = parserutils_stack_push(c->context, (void *) &entry);
  675.         if (perror != PARSERUTILS_OK) {
  676.                 return css_error_from_parserutils_error(perror);
  677.         }
  678.  
  679.         return CSS_OK;
  680. }
  681.  
  682. css_error handleEndBlock(css_language *c, const parserutils_vector *vector)
  683. {
  684.         parserutils_error perror;
  685.         context_entry *entry;
  686.         css_rule *rule;
  687.  
  688.         entry = parserutils_stack_get_current(c->context);
  689.         if (entry == NULL || entry->type != CSS_PARSER_START_BLOCK)
  690.                 return CSS_INVALID;
  691.  
  692.         rule = entry->data;
  693.  
  694.         perror = parserutils_stack_pop(c->context, NULL);
  695.         if (perror != PARSERUTILS_OK) {
  696.                 return css_error_from_parserutils_error(perror);
  697.         }
  698.  
  699.         /* If the block we just popped off the stack was associated with a
  700.          * non-block stack entry, and that entry is not a top-level statement,
  701.          * then report the end of that entry, too. */
  702.         if (rule != NULL && rule->ptype != CSS_RULE_PARENT_STYLESHEET) {
  703.                 if (rule->type == CSS_RULE_SELECTOR)
  704.                         return handleEndRuleset(c, vector);
  705.         }
  706.  
  707.         return CSS_OK;
  708. }
  709.  
  710. css_error handleBlockContent(css_language *c, const parserutils_vector *vector)
  711. {
  712.         context_entry *entry;
  713.         css_rule *rule;
  714.  
  715.         /* Block content comprises either declarations (if the current block is
  716.          * associated with @page, @font-face or a selector), or rulesets (if the
  717.          * current block is associated with @media). */
  718.  
  719.         entry = parserutils_stack_get_current(c->context);
  720.         if (entry == NULL || entry->data == NULL)
  721.                 return CSS_INVALID;
  722.  
  723.         rule = entry->data;
  724.         if (rule == NULL || (rule->type != CSS_RULE_SELECTOR &&
  725.                         rule->type != CSS_RULE_PAGE &&
  726.                         rule->type != CSS_RULE_MEDIA &&
  727.                         rule->type != CSS_RULE_FONT_FACE))
  728.                 return CSS_INVALID;
  729.  
  730.         if (rule->type == CSS_RULE_MEDIA) {
  731.                 /* Expect rulesets */
  732.                 return handleStartRuleset(c, vector);
  733.         } else {
  734.                 /* Expect declarations */
  735.                 return handleDeclaration(c, vector);
  736.         }
  737.  
  738.         return CSS_OK;
  739. }
  740.  
  741. css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
  742. {
  743.         css_error error;
  744.         const css_token *token, *ident;
  745.         int ctx = 0;
  746.         context_entry *entry;
  747.         css_rule *rule;
  748.  
  749.         /* Locations where declarations are permitted:
  750.          *
  751.          * + In @page
  752.          * + In @font-face
  753.          * + In ruleset
  754.          */
  755.         entry = parserutils_stack_get_current(c->context);
  756.         if (entry == NULL || entry->data == NULL)
  757.                 return CSS_INVALID;
  758.  
  759.         rule = entry->data;
  760.         if (rule == NULL || (rule->type != CSS_RULE_SELECTOR &&
  761.                                 rule->type != CSS_RULE_PAGE &&
  762.                                 rule->type != CSS_RULE_FONT_FACE))
  763.                 return CSS_INVALID;
  764.  
  765.         /* Strip any leading whitespace (can happen if in nested block) */
  766.         consumeWhitespace(vector, &ctx);
  767.  
  768.         /* IDENT ws ':' ws value
  769.          *
  770.          * In CSS 2.1, value is any1, so '{' or ATKEYWORD => parse error
  771.          */
  772.         ident = parserutils_vector_iterate(vector, &ctx);
  773.         if (ident == NULL || ident->type != CSS_TOKEN_IDENT)
  774.                 return CSS_INVALID;
  775.  
  776.         consumeWhitespace(vector, &ctx);
  777.  
  778.         token = parserutils_vector_iterate(vector, &ctx);
  779.         if (token == NULL || tokenIsChar(token, ':') == false)
  780.                 return CSS_INVALID;
  781.  
  782.         consumeWhitespace(vector, &ctx);
  783.  
  784.         if (rule->type == CSS_RULE_FONT_FACE) {
  785.                 css_rule_font_face * ff_rule = (css_rule_font_face *) rule;
  786.                 error = css__parse_font_descriptor(
  787.                                 c, ident, vector, &ctx, ff_rule);
  788.         } else {
  789.                 error = parseProperty(c, ident, vector, &ctx, rule);
  790.         }
  791.         if (error != CSS_OK)
  792.                 return error;
  793.  
  794.         return CSS_OK;
  795. }
  796.  
  797. /******************************************************************************
  798.  * At-rule parsing functions                                                  *
  799.  ******************************************************************************/
  800.  
  801. css_error parseMediaList(css_language *c,
  802.                 const parserutils_vector *vector, int *ctx,
  803.                 uint64_t *media)
  804. {
  805.         uint64_t ret = 0;
  806.         bool match = false;
  807.         const css_token *token;
  808.  
  809.         token = parserutils_vector_iterate(vector, ctx);
  810.  
  811.         while (token != NULL) {
  812.                 if (token->type != CSS_TOKEN_IDENT)
  813.                         return CSS_INVALID;
  814.  
  815.                 if (lwc_string_caseless_isequal(token->idata, c->strings[AURAL],
  816.                                 &match) == lwc_error_ok && match) {
  817.                         ret |= CSS_MEDIA_AURAL;
  818.                 } else if (lwc_string_caseless_isequal(
  819.                                 token->idata, c->strings[BRAILLE],
  820.                                 &match) == lwc_error_ok && match) {
  821.                         ret |= CSS_MEDIA_BRAILLE;
  822.                 } else if (lwc_string_caseless_isequal(
  823.                                 token->idata, c->strings[EMBOSSED],
  824.                                 &match) == lwc_error_ok && match) {
  825.                         ret |= CSS_MEDIA_EMBOSSED;
  826.                 } else if (lwc_string_caseless_isequal(
  827.                                 token->idata, c->strings[HANDHELD],
  828.                                 &match) == lwc_error_ok && match) {
  829.                         ret |= CSS_MEDIA_HANDHELD;
  830.                 } else if (lwc_string_caseless_isequal(
  831.                                 token->idata, c->strings[PRINT],
  832.                                 &match) == lwc_error_ok && match) {
  833.                         ret |= CSS_MEDIA_PRINT;
  834.                 } else if (lwc_string_caseless_isequal(
  835.                                 token->idata, c->strings[PROJECTION],
  836.                                 &match) == lwc_error_ok && match) {
  837.                         ret |= CSS_MEDIA_PROJECTION;
  838.                 } else if (lwc_string_caseless_isequal(
  839.                                 token->idata, c->strings[SCREEN],
  840.                                 &match) == lwc_error_ok && match) {
  841.                         ret |= CSS_MEDIA_SCREEN;
  842.                 } else if (lwc_string_caseless_isequal(
  843.                                 token->idata, c->strings[SPEECH],
  844.                                 &match) == lwc_error_ok && match) {
  845.                         ret |= CSS_MEDIA_SPEECH;
  846.                 } else if (lwc_string_caseless_isequal(
  847.                                 token->idata, c->strings[TTY],
  848.                                 &match) == lwc_error_ok && match) {
  849.                         ret |= CSS_MEDIA_TTY;
  850.                 } else if (lwc_string_caseless_isequal(
  851.                                 token->idata, c->strings[TV],
  852.                                 &match) == lwc_error_ok && match) {
  853.                         ret |= CSS_MEDIA_TV;
  854.                 } else if (lwc_string_caseless_isequal(
  855.                                 token->idata, c->strings[ALL],
  856.                                 &match) == lwc_error_ok && match) {
  857.                         ret |= CSS_MEDIA_ALL;
  858.                 } else
  859.                         return CSS_INVALID;
  860.  
  861.                 consumeWhitespace(vector, ctx);
  862.  
  863.                 token = parserutils_vector_iterate(vector, ctx);
  864.                 if (token != NULL && tokenIsChar(token, ',') == false)
  865.                         return CSS_INVALID;
  866.  
  867.                 consumeWhitespace(vector, ctx);
  868.         }
  869.  
  870.         /* If, after parsing the media list, we still have no media,
  871.          * then it must be ALL. */
  872.         if (ret == 0)
  873.                 ret = CSS_MEDIA_ALL;
  874.  
  875.         *media = ret;
  876.  
  877.         return CSS_OK;
  878. }
  879.  
  880. /**
  881.  * Add a namespace mapping
  882.  *
  883.  * \param c       Parsing context to add to
  884.  * \param prefix  Namespace prefix, or NULL for default namespace
  885.  * \param uri     Namespace URI
  886.  * \return CSS_OK on success, CSS_NOMEM on memory exhaustion.
  887.  */
  888. css_error addNamespace(css_language *c, lwc_string *prefix, lwc_string *uri)
  889. {
  890.         if (prefix == NULL) {
  891.                 /* Replace default namespace */
  892.                 if (c->default_namespace != NULL)
  893.                         lwc_string_unref(c->default_namespace);
  894.  
  895.                 /* Special case: if new namespace uri is "", use NULL */
  896.                 if (lwc_string_length(uri) == 0)
  897.                         c->default_namespace = NULL;
  898.                 else
  899.                         c->default_namespace = lwc_string_ref(uri);
  900.         } else {
  901.                 /* Replace, or add mapping */
  902.                 bool match;
  903.                 uint32_t idx;
  904.  
  905.                 for (idx = 0; idx < c->num_namespaces; idx++) {
  906.                         if (lwc_string_isequal(c->namespaces[idx].prefix,
  907.                                         prefix, &match) == lwc_error_ok &&
  908.                                         match)
  909.                                 break;
  910.                 }
  911.  
  912.                 if (idx == c->num_namespaces) {
  913.                         /* Not found, create a new mapping */
  914.                         css_namespace *ns = c->alloc(c->namespaces,
  915.                                         sizeof(css_namespace) *
  916.                                                 (c->num_namespaces + 1),
  917.                                         c->pw);
  918.  
  919.                         if (ns == NULL)
  920.                                 return CSS_NOMEM;
  921.  
  922.                         ns[idx].prefix = lwc_string_ref(prefix);
  923.                         ns[idx].uri = NULL;
  924.  
  925.                         c->namespaces = ns;
  926.                         c->num_namespaces++;
  927.                 }
  928.  
  929.                 /* Replace namespace URI */
  930.                 if (c->namespaces[idx].uri != NULL)
  931.                         lwc_string_unref(c->namespaces[idx].uri);
  932.  
  933.                 /* Special case: if new namespace uri is "", use NULL */
  934.                 if (lwc_string_length(uri) == 0)
  935.                         c->namespaces[idx].uri = NULL;
  936.                 else
  937.                         c->namespaces[idx].uri = lwc_string_ref(uri);
  938.         }
  939.  
  940.         return CSS_OK;
  941. }
  942.  
  943. /**
  944.  * Look up a namespace prefix
  945.  *
  946.  * \param c       Language parser context
  947.  * \param prefix  Namespace prefix to find, or NULL for none
  948.  * \param uri     Pointer to location to receive namespace URI
  949.  * \return CSS_OK on success, CSS_INVALID if prefix is not found
  950.  */
  951. css_error lookupNamespace(css_language *c, lwc_string *prefix, lwc_string **uri)
  952. {
  953.         uint32_t idx;
  954.         bool match;
  955.  
  956.         if (prefix == NULL) {
  957.                 *uri = NULL;
  958.         } else {
  959.                 for (idx = 0; idx < c->num_namespaces; idx++) {
  960.                         if (lwc_string_isequal(c->namespaces[idx].prefix,
  961.                                         prefix, &match) == lwc_error_ok &&
  962.                                         match)
  963.                                 break;
  964.                 }
  965.  
  966.                 if (idx == c->num_namespaces)
  967.                         return CSS_INVALID;
  968.  
  969.                 *uri = c->namespaces[idx].uri;
  970.         }
  971.  
  972.         return CSS_OK;
  973. }
  974.  
  975. /******************************************************************************
  976.  * Selector list parsing functions                                            *
  977.  ******************************************************************************/
  978.  
  979. css_error parseClass(css_language *c, const parserutils_vector *vector,
  980.                 int *ctx, css_selector_detail *specific)
  981. {
  982.         css_qname qname;
  983.         css_selector_detail_value detail_value;
  984.         const css_token *token;
  985.  
  986.         /* class     -> '.' IDENT */
  987.         token = parserutils_vector_iterate(vector, ctx);
  988.         if (token == NULL || tokenIsChar(token, '.') == false)
  989.                 return CSS_INVALID;
  990.  
  991.         token = parserutils_vector_iterate(vector, ctx);
  992.         if (token == NULL || token->type != CSS_TOKEN_IDENT)
  993.                 return CSS_INVALID;
  994.  
  995.         detail_value.string = NULL;
  996.  
  997.         qname.ns = NULL;
  998.         qname.name = token->idata;
  999.  
  1000.         return css__stylesheet_selector_detail_init(c->sheet,
  1001.                         CSS_SELECTOR_CLASS, &qname, detail_value,
  1002.                         CSS_SELECTOR_DETAIL_VALUE_STRING, false, specific);
  1003. }
  1004.  
  1005. css_error parseAttrib(css_language *c, const parserutils_vector *vector,
  1006.                 int *ctx, css_selector_detail *specific)
  1007. {
  1008.         css_qname qname;
  1009.         css_selector_detail_value detail_value;
  1010.         const css_token *token, *value = NULL;
  1011.         css_selector_type type = CSS_SELECTOR_ATTRIBUTE;
  1012.         css_error error;
  1013.         lwc_string *prefix = NULL;
  1014.  
  1015.         /* attrib    -> '[' ws namespace_prefix? IDENT ws [
  1016.          *                     [ '=' |
  1017.          *                       INCLUDES |
  1018.          *                       DASHMATCH |
  1019.          *                       PREFIXMATCH |
  1020.          *                       SUFFIXMATCH |
  1021.          *                       SUBSTRINGMATCH
  1022.          *                     ] ws
  1023.          *                     [ IDENT | STRING ] ws ]? ']'
  1024.          * namespace_prefix -> [ IDENT | '*' ]? '|'
  1025.          */
  1026.         token = parserutils_vector_iterate(vector, ctx);
  1027.         if (token == NULL || tokenIsChar(token, '[') == false)
  1028.                 return CSS_INVALID;
  1029.  
  1030.         consumeWhitespace(vector, ctx);
  1031.  
  1032.         token = parserutils_vector_iterate(vector, ctx);
  1033.         if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  1034.                         tokenIsChar(token, '*') == false &&
  1035.                         tokenIsChar(token, '|') == false))
  1036.                 return CSS_INVALID;
  1037.  
  1038.         if (tokenIsChar(token, '|')) {
  1039.                 token = parserutils_vector_iterate(vector, ctx);
  1040.         } else {
  1041.                 const css_token *temp;
  1042.  
  1043.                 temp = parserutils_vector_peek(vector, *ctx);
  1044.                 if (temp != NULL && tokenIsChar(temp, '|')) {
  1045.                         prefix = token->idata;
  1046.  
  1047.                         parserutils_vector_iterate(vector, ctx);
  1048.  
  1049.                         token = parserutils_vector_iterate(vector, ctx);
  1050.                 }
  1051.         }
  1052.  
  1053.         if (token == NULL || token->type != CSS_TOKEN_IDENT)
  1054.                 return CSS_INVALID;
  1055.  
  1056.         error = lookupNamespace(c, prefix, &qname.ns);
  1057.         if (error != CSS_OK)
  1058.                 return error;
  1059.  
  1060.         qname.name = token->idata;
  1061.  
  1062.         consumeWhitespace(vector, ctx);
  1063.  
  1064.         token = parserutils_vector_iterate(vector, ctx);
  1065.         if (token == NULL)
  1066.                 return CSS_INVALID;
  1067.  
  1068.         if (tokenIsChar(token, ']') == false) {
  1069.                 if (tokenIsChar(token, '='))
  1070.                         type = CSS_SELECTOR_ATTRIBUTE_EQUAL;
  1071.                 else if (token->type == CSS_TOKEN_INCLUDES)
  1072.                         type = CSS_SELECTOR_ATTRIBUTE_INCLUDES;
  1073.                 else if (token->type == CSS_TOKEN_DASHMATCH)
  1074.                         type = CSS_SELECTOR_ATTRIBUTE_DASHMATCH;
  1075.                 else if (token->type == CSS_TOKEN_PREFIXMATCH)
  1076.                         type = CSS_SELECTOR_ATTRIBUTE_PREFIX;
  1077.                 else if (token->type == CSS_TOKEN_SUFFIXMATCH)
  1078.                         type = CSS_SELECTOR_ATTRIBUTE_SUFFIX;
  1079.                 else if (token->type == CSS_TOKEN_SUBSTRINGMATCH)
  1080.                         type = CSS_SELECTOR_ATTRIBUTE_SUBSTRING;
  1081.                 else
  1082.                         return CSS_INVALID;
  1083.  
  1084.                 consumeWhitespace(vector, ctx);
  1085.  
  1086.                 token = parserutils_vector_iterate(vector, ctx);
  1087.                 if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  1088.                                 token->type != CSS_TOKEN_STRING))
  1089.                         return CSS_INVALID;
  1090.  
  1091.                 value = token;
  1092.  
  1093.                 consumeWhitespace(vector, ctx);
  1094.  
  1095.                 token = parserutils_vector_iterate(vector, ctx);
  1096.                 if (token == NULL || tokenIsChar(token, ']') == false)
  1097.                         return CSS_INVALID;
  1098.         }
  1099.  
  1100.         detail_value.string = value != NULL ? value->idata : NULL;
  1101.  
  1102.         return css__stylesheet_selector_detail_init(c->sheet, type,
  1103.                         &qname, detail_value,
  1104.                         CSS_SELECTOR_DETAIL_VALUE_STRING, false, specific);
  1105. }
  1106.  
  1107. css_error parseNth(css_language *c,
  1108.                 const parserutils_vector *vector, int *ctx,
  1109.                 css_selector_detail_value *value)
  1110. {
  1111.         const css_token *token;
  1112.         bool match;
  1113.  
  1114.         /* nth -> [ DIMENSION | IDENT ] ws [ [ CHAR ws ]? NUMBER ws ]?
  1115.          *        (e.g. DIMENSION: 2n-1, 2n- 1, 2n -1, 2n - 1)
  1116.          *        (e.g. IDENT: -n-1, -n- 1, -n -1, -n - 1)
  1117.          *     -> NUMBER ws
  1118.          *     -> IDENT(odd) ws
  1119.          *     -> IDENT(even) ws
  1120.          */
  1121.  
  1122.         token = parserutils_vector_iterate(vector, ctx);
  1123.         if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  1124.                         token->type != CSS_TOKEN_NUMBER &&
  1125.                         token->type != CSS_TOKEN_DIMENSION))
  1126.                 return CSS_INVALID;
  1127.  
  1128.         if (token->type == CSS_TOKEN_IDENT &&
  1129.                         lwc_string_caseless_isequal(token->idata,
  1130.                                 c->strings[ODD], &match) == lwc_error_ok &&
  1131.                         match) {
  1132.                 /* Odd */
  1133.                 value->nth.a = 2;
  1134.                 value->nth.b = 1;
  1135.         } else if (token->type == CSS_TOKEN_IDENT &&
  1136.                         lwc_string_caseless_isequal(token->idata,
  1137.                                 c->strings[EVEN], &match) == lwc_error_ok &&
  1138.                         match) {
  1139.                 /* Even */
  1140.                 value->nth.a = 2;
  1141.                 value->nth.b = 0;
  1142.         } else if (token->type == CSS_TOKEN_NUMBER) {
  1143.                 size_t consumed = 0;
  1144.                 css_fixed val = 0;
  1145.  
  1146.                 val = css__number_from_lwc_string(token->idata,
  1147.                                 true, &consumed);
  1148.                 if (consumed != lwc_string_length(token->idata))
  1149.                         return CSS_INVALID;
  1150.  
  1151.                 value->nth.a = 0;
  1152.                 value->nth.b = FIXTOINT(val);
  1153.         } else {
  1154.                 /* [ DIMENSION | IDENT ] ws [ [ CHAR ws ]? NUMBER ws ]?
  1155.                  *
  1156.                  * (e.g. DIMENSION: 2n-1, 2n- 1, 2n -1, 2n - 1)
  1157.                  * (e.g. IDENT: n, -n-1, -n- 1, -n -1, -n - 1)
  1158.                  */
  1159.                 size_t consumed = 0, len;
  1160.                 const char *data;
  1161.                 css_fixed a = 0, b = 0;
  1162.                 int sign = 1;
  1163.                 bool had_sign = false, had_b = false;
  1164.  
  1165.                 len = lwc_string_length(token->idata);
  1166.                 data = lwc_string_data(token->idata);
  1167.  
  1168.                 /* Compute a */
  1169.                 if (token->type == CSS_TOKEN_IDENT) {
  1170.                         if (len < 2) {
  1171.                                 if (data[0] != 'n' && data[0] != 'N')
  1172.                                         return CSS_INVALID;
  1173.  
  1174.                                 /* n */
  1175.                                 a = INTTOFIX(1);
  1176.  
  1177.                                 data += 1;
  1178.                                 len -= 1;
  1179.                         } else {
  1180.                                 if (data[0] != '-' ||
  1181.                                         (data[1] != 'n' && data[1] != 'N'))
  1182.                                         return CSS_INVALID;
  1183.  
  1184.                                 /* -n */
  1185.                                 a = INTTOFIX(-1);
  1186.  
  1187.                                 data += 2;
  1188.                                 len -= 2;
  1189.                         }
  1190.  
  1191.                         if (len > 0) {
  1192.                                 if (data[0] != '-')
  1193.                                         return CSS_INVALID;
  1194.  
  1195.                                 /* -n- */
  1196.                                 sign = -1;
  1197.                                 had_sign = true;
  1198.  
  1199.                                 if (len > 1) {
  1200.                                         /* Reject additional sign */
  1201.                                         if (data[1] == '-' || data[1] == '+')
  1202.                                                 return CSS_INVALID;
  1203.  
  1204.                                         /* -n-b */
  1205.                                         b = css__number_from_string(
  1206.                                                 (const uint8_t *) data + 1,
  1207.                                                 len - 1,
  1208.                                                 true,
  1209.                                                 &consumed);
  1210.                                         if (consumed != len - 1)
  1211.                                                 return CSS_INVALID;
  1212.  
  1213.                                         had_b = true;
  1214.                                 }
  1215.                         }
  1216.                 } else {
  1217.                         /* 2n */
  1218.                         a = css__number_from_lwc_string(token->idata,
  1219.                                         true, &consumed);
  1220.                         if (consumed == 0 || (data[consumed] != 'n' &&
  1221.                                         data[consumed] != 'N'))
  1222.                                 return CSS_INVALID;
  1223.  
  1224.                         if (len - (++consumed) > 0) {
  1225.                                 if (data[consumed] != '-')
  1226.                                         return CSS_INVALID;
  1227.  
  1228.                                 /* 2n- */
  1229.                                 sign = -1;
  1230.                                 had_sign = true;
  1231.  
  1232.                                 if (len - (++consumed) > 0) {
  1233.                                         size_t bstart;
  1234.  
  1235.                                         /* Reject additional sign */
  1236.                                         if (data[consumed] == '-' ||
  1237.                                                         data[consumed] == '+')
  1238.                                                 return CSS_INVALID;
  1239.  
  1240.                                         /* 2n-b */
  1241.                                         bstart = consumed;
  1242.  
  1243.                                         b = css__number_from_string(
  1244.                                                 (const uint8_t *) data + bstart,
  1245.                                                 len - bstart,
  1246.                                                 true,
  1247.                                                 &consumed);
  1248.                                         if (consumed != len - bstart)
  1249.                                                 return CSS_INVALID;
  1250.  
  1251.                                         had_b = true;
  1252.                                 }
  1253.                         }
  1254.                 }
  1255.  
  1256.                 if (had_b == false) {
  1257.                         consumeWhitespace(vector, ctx);
  1258.  
  1259.                         /* Look for optional b : [ [ CHAR ws ]? NUMBER ws ]? */
  1260.                         token = parserutils_vector_peek(vector, *ctx);
  1261.  
  1262.                         if (had_sign == false && token != NULL &&
  1263.                                         (tokenIsChar(token, '-') ||
  1264.                                         tokenIsChar(token, '+'))) {
  1265.                                 parserutils_vector_iterate(vector, ctx);
  1266.  
  1267.                                 had_sign = true;
  1268.  
  1269.                                 if (tokenIsChar(token, '-'))
  1270.                                         sign = -1;
  1271.  
  1272.                                 consumeWhitespace(vector, ctx);
  1273.  
  1274.                                 token = parserutils_vector_peek(vector, *ctx);
  1275.                         }
  1276.  
  1277.                         /* Expect NUMBER */
  1278.                         if (token != NULL && token->type == CSS_TOKEN_NUMBER) {
  1279.                                 parserutils_vector_iterate(vector, ctx);
  1280.  
  1281.                                 /* If we've already seen a sign, ensure one
  1282.                                  * does not occur at the start of this token
  1283.                                  */
  1284.                                 if (had_sign && lwc_string_length(
  1285.                                                 token->idata) > 0) {
  1286.                                         data = lwc_string_data(token->idata);
  1287.  
  1288.                                         if (data[0] == '-' || data[0] == '+')
  1289.                                                 return CSS_INVALID;
  1290.                                 }
  1291.  
  1292.                                 b = css__number_from_lwc_string(token->idata,
  1293.                                                 true, &consumed);
  1294.                                 if (consumed != lwc_string_length(token->idata))
  1295.                                         return CSS_INVALID;
  1296.                         }
  1297.                 }
  1298.  
  1299.                 value->nth.a = FIXTOINT(a);
  1300.                 value->nth.b = FIXTOINT(b) * sign;
  1301.         }
  1302.  
  1303.         consumeWhitespace(vector, ctx);
  1304.  
  1305.         return CSS_OK;
  1306. }
  1307.  
  1308. css_error parsePseudo(css_language *c, const parserutils_vector *vector,
  1309.                 int *ctx, bool in_not, css_selector_detail *specific)
  1310. {
  1311.         static const struct
  1312.         {
  1313.                 int index;
  1314.                 css_selector_type type;
  1315.         } pseudo_lut[] = {
  1316.                 { FIRST_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
  1317.                 { LINK, CSS_SELECTOR_PSEUDO_CLASS },
  1318.                 { VISITED, CSS_SELECTOR_PSEUDO_CLASS },
  1319.                 { HOVER, CSS_SELECTOR_PSEUDO_CLASS },
  1320.                 { ACTIVE, CSS_SELECTOR_PSEUDO_CLASS },
  1321.                 { FOCUS, CSS_SELECTOR_PSEUDO_CLASS },
  1322.                 { LANG, CSS_SELECTOR_PSEUDO_CLASS },
  1323.                 { LEFT, CSS_SELECTOR_PSEUDO_CLASS },
  1324.                 { RIGHT, CSS_SELECTOR_PSEUDO_CLASS },
  1325.                 { FIRST, CSS_SELECTOR_PSEUDO_CLASS },
  1326.                 { ROOT, CSS_SELECTOR_PSEUDO_CLASS },
  1327.                 { NTH_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
  1328.                 { NTH_LAST_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
  1329.                 { NTH_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
  1330.                 { NTH_LAST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
  1331.                 { LAST_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
  1332.                 { FIRST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
  1333.                 { LAST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
  1334.                 { ONLY_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
  1335.                 { ONLY_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
  1336.                 { EMPTY, CSS_SELECTOR_PSEUDO_CLASS },
  1337.                 { TARGET, CSS_SELECTOR_PSEUDO_CLASS },
  1338.                 { ENABLED, CSS_SELECTOR_PSEUDO_CLASS },
  1339.                 { DISABLED, CSS_SELECTOR_PSEUDO_CLASS },
  1340.                 { CHECKED, CSS_SELECTOR_PSEUDO_CLASS },
  1341.                 { NOT, CSS_SELECTOR_PSEUDO_CLASS },
  1342.  
  1343.                 { FIRST_LINE, CSS_SELECTOR_PSEUDO_ELEMENT },
  1344.                 { FIRST_LETTER, CSS_SELECTOR_PSEUDO_ELEMENT },
  1345.                 { BEFORE, CSS_SELECTOR_PSEUDO_ELEMENT },
  1346.                 { AFTER, CSS_SELECTOR_PSEUDO_ELEMENT }
  1347.         };
  1348.         css_selector_detail_value detail_value;
  1349.         css_selector_detail_value_type value_type =
  1350.                         CSS_SELECTOR_DETAIL_VALUE_STRING;
  1351.         css_qname qname;
  1352.         const css_token *token;
  1353.         bool match = false, require_element = false, negate = false;
  1354.         uint32_t lut_idx;
  1355.         css_selector_type type = CSS_SELECTOR_PSEUDO_CLASS;/* GCC's braindead */
  1356.         css_error error;
  1357.  
  1358.         /* pseudo    -> ':' ':'? [ IDENT | FUNCTION ws any1 ws ')' ] */
  1359.  
  1360.         detail_value.string = NULL;
  1361.  
  1362.         token = parserutils_vector_iterate(vector, ctx);
  1363.         if (token == NULL || tokenIsChar(token, ':') == false)
  1364.                 return CSS_INVALID;
  1365.  
  1366.         /* Optional second colon before pseudo element names */
  1367.         token = parserutils_vector_iterate(vector, ctx);
  1368.         if (token != NULL && tokenIsChar(token, ':')) {
  1369.                 /* If present, we require a pseudo element */
  1370.                 require_element = true;
  1371.  
  1372.                 /* Consume subsequent token */
  1373.                 token = parserutils_vector_iterate(vector, ctx);
  1374.         }
  1375.  
  1376.         /* Expect IDENT or FUNCTION */
  1377.         if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  1378.                         token->type != CSS_TOKEN_FUNCTION))
  1379.                 return CSS_INVALID;
  1380.  
  1381.         qname.ns = NULL;
  1382.         qname.name = token->idata;
  1383.  
  1384.         /* Search lut for selector type */
  1385.         for (lut_idx = 0; lut_idx < N_ELEMENTS(pseudo_lut); lut_idx++) {
  1386.                 if ((lwc_string_caseless_isequal(qname.name,
  1387.                                 c->strings[pseudo_lut[lut_idx].index],
  1388.                                 &match) == lwc_error_ok) && match) {
  1389.                         type = pseudo_lut[lut_idx].type;
  1390.                         break;
  1391.                 }
  1392.         }
  1393.  
  1394.         /* Not found: invalid */
  1395.         if (lut_idx == N_ELEMENTS(pseudo_lut))
  1396.                 return CSS_INVALID;
  1397.  
  1398.         /* Required a pseudo element, but didn't find one: invalid */
  1399.         if (require_element && type != CSS_SELECTOR_PSEUDO_ELEMENT)
  1400.                 return CSS_INVALID;
  1401.  
  1402.         /* :not() and pseudo elements are not permitted in :not() */
  1403.         if (in_not && (type == CSS_SELECTOR_PSEUDO_ELEMENT ||
  1404.                         pseudo_lut[lut_idx].index == NOT))
  1405.                 return CSS_INVALID;
  1406.  
  1407.         if (token->type == CSS_TOKEN_FUNCTION) {
  1408.                 int fun_type = pseudo_lut[lut_idx].index;
  1409.  
  1410.                 consumeWhitespace(vector, ctx);
  1411.  
  1412.                 if (fun_type == LANG) {
  1413.                         /* IDENT */
  1414.                         token = parserutils_vector_iterate(vector, ctx);
  1415.                         if (token == NULL || token->type != CSS_TOKEN_IDENT)
  1416.                                 return CSS_INVALID;
  1417.  
  1418.                         detail_value.string = token->idata;
  1419.                         value_type = CSS_SELECTOR_DETAIL_VALUE_STRING;
  1420.  
  1421.                         consumeWhitespace(vector, ctx);
  1422.                 } else if (fun_type == NTH_CHILD ||
  1423.                                 fun_type == NTH_LAST_CHILD ||
  1424.                                 fun_type == NTH_OF_TYPE ||
  1425.                                 fun_type == NTH_LAST_OF_TYPE) {
  1426.                         /* an + b */
  1427.                         error = parseNth(c, vector, ctx, &detail_value);
  1428.                         if (error != CSS_OK)
  1429.                                 return error;
  1430.  
  1431.                         value_type = CSS_SELECTOR_DETAIL_VALUE_NTH;
  1432.                 } else if (fun_type == NOT) {
  1433.                         /* type_selector | specific */
  1434.                         token = parserutils_vector_peek(vector, *ctx);
  1435.                         if (token == NULL)
  1436.                                 return CSS_INVALID;
  1437.  
  1438.                         if (token->type == CSS_TOKEN_IDENT ||
  1439.                                         tokenIsChar(token, '*') ||
  1440.                                         tokenIsChar(token, '|')) {
  1441.                                 /* Have type selector */
  1442.                                 error = parseTypeSelector(c, vector, ctx,
  1443.                                                 &qname);
  1444.                                 if (error != CSS_OK)
  1445.                                         return error;
  1446.  
  1447.                                 type = CSS_SELECTOR_ELEMENT;
  1448.  
  1449.                                 detail_value.string = NULL;
  1450.                                 value_type = CSS_SELECTOR_DETAIL_VALUE_STRING;
  1451.                         } else {
  1452.                                 /* specific */
  1453.                                 css_selector_detail det;
  1454.  
  1455.                                 error = parseSpecific(c, vector, ctx, true,
  1456.                                                 &det);
  1457.                                 if (error != CSS_OK)
  1458.                                         return error;
  1459.  
  1460.                                 qname = det.qname;
  1461.                                 type = det.type;
  1462.                                 detail_value = det.value;
  1463.                                 value_type = det.value_type;
  1464.                         }
  1465.  
  1466.                         negate = true;
  1467.  
  1468.                         consumeWhitespace(vector, ctx);
  1469.                 }
  1470.  
  1471.                 token = parserutils_vector_iterate(vector, ctx);
  1472.                 if (token == NULL || tokenIsChar(token, ')') == false)
  1473.                         return CSS_INVALID;
  1474.         }
  1475.  
  1476.         return css__stylesheet_selector_detail_init(c->sheet,
  1477.                         type, &qname, detail_value, value_type,
  1478.                         negate, specific);
  1479. }
  1480.  
  1481. css_error parseSpecific(css_language *c,
  1482.                 const parserutils_vector *vector, int *ctx,
  1483.                 bool in_not, css_selector_detail *specific)
  1484. {
  1485.         css_error error;
  1486.         const css_token *token;
  1487.  
  1488.         /* specific  -> [ HASH | class | attrib | pseudo ] */
  1489.  
  1490.         token = parserutils_vector_peek(vector, *ctx);
  1491.         if (token == NULL)
  1492.                 return CSS_INVALID;
  1493.  
  1494.         if (token->type == CSS_TOKEN_HASH) {
  1495.                 css_qname qname;
  1496.                 css_selector_detail_value detail_value;
  1497.  
  1498.                 detail_value.string = NULL;
  1499.  
  1500.                 qname.ns = NULL;
  1501.                 qname.name = token->idata;
  1502.  
  1503.                 error = css__stylesheet_selector_detail_init(c->sheet,
  1504.                                 CSS_SELECTOR_ID, &qname, detail_value,
  1505.                                 CSS_SELECTOR_DETAIL_VALUE_STRING, false,
  1506.                                 specific);
  1507.                 if (error != CSS_OK)
  1508.                         return error;
  1509.  
  1510.                 parserutils_vector_iterate(vector, ctx);
  1511.         } else if (tokenIsChar(token, '.')) {
  1512.                 error = parseClass(c, vector, ctx, specific);
  1513.                 if (error != CSS_OK)
  1514.                         return error;
  1515.         } else if (tokenIsChar(token, '[')) {
  1516.                 error = parseAttrib(c, vector, ctx, specific);
  1517.                 if (error != CSS_OK)
  1518.                         return error;
  1519.         } else if (tokenIsChar(token, ':')) {
  1520.                 error = parsePseudo(c, vector, ctx, in_not, specific);
  1521.                 if (error != CSS_OK)
  1522.                         return error;
  1523.         } else {
  1524.                 return CSS_INVALID;
  1525.         }
  1526.  
  1527.         return CSS_OK;
  1528. }
  1529.  
  1530. css_error parseAppendSpecific(css_language *c,
  1531.                 const parserutils_vector *vector, int *ctx,
  1532.                 css_selector **parent)
  1533. {
  1534.         css_error error;
  1535.         css_selector_detail specific;
  1536.  
  1537.         error = parseSpecific(c, vector, ctx, false, &specific);
  1538.         if (error != CSS_OK)
  1539.                 return error;
  1540.  
  1541.         return css__stylesheet_selector_append_specific(c->sheet, parent,
  1542.                         &specific);
  1543. }
  1544.  
  1545. css_error parseSelectorSpecifics(css_language *c,
  1546.                 const parserutils_vector *vector, int *ctx,
  1547.                 css_selector **parent)
  1548. {
  1549.         css_error error;
  1550.         const css_token *token;
  1551.  
  1552.         /* specifics -> specific* */
  1553.         while ((token = parserutils_vector_peek(vector, *ctx)) != NULL &&
  1554.                         token->type != CSS_TOKEN_S &&
  1555.                         tokenIsChar(token, '+') == false &&
  1556.                         tokenIsChar(token, '>') == false &&
  1557.                         tokenIsChar(token, '~') == false &&
  1558.                         tokenIsChar(token, ',') == false) {
  1559.                 error = parseAppendSpecific(c, vector, ctx, parent);
  1560.                 if (error != CSS_OK)
  1561.                         return error;
  1562.         }
  1563.  
  1564.         return CSS_OK;
  1565. }
  1566.  
  1567. css_error parseTypeSelector(css_language *c, const parserutils_vector *vector,
  1568.                 int *ctx, css_qname *qname)
  1569. {
  1570.         const css_token *token;
  1571.         css_error error;
  1572.         lwc_string *prefix = NULL;
  1573.  
  1574.         /* type_selector    -> namespace_prefix? element_name
  1575.          * namespace_prefix -> [ IDENT | '*' ]? '|'
  1576.          * element_name     -> IDENT | '*'
  1577.          */
  1578.  
  1579.         token = parserutils_vector_peek(vector, *ctx);
  1580.         if (token == NULL)
  1581.                 return CSS_INVALID;
  1582.  
  1583.         if (tokenIsChar(token, '|') == false) {
  1584.                 prefix = token->idata;
  1585.  
  1586.                 parserutils_vector_iterate(vector, ctx);
  1587.  
  1588.                 token = parserutils_vector_peek(vector, *ctx);
  1589.         }
  1590.  
  1591.         if (token != NULL && tokenIsChar(token, '|')) {
  1592.                 /* Have namespace prefix */
  1593.                 parserutils_vector_iterate(vector, ctx);
  1594.  
  1595.                 /* Expect element_name */
  1596.                 token = parserutils_vector_iterate(vector, ctx);
  1597.  
  1598.                 if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
  1599.                                 tokenIsChar(token, '*') == false)) {
  1600.                         return CSS_INVALID;
  1601.                 }
  1602.  
  1603.                 error = lookupNamespace(c, prefix, &qname->ns);
  1604.                 if (error != CSS_OK)
  1605.                         return error;
  1606.  
  1607.                 qname->name = token->idata;
  1608.         } else {
  1609.                 /* No namespace prefix */
  1610.                 if (c->default_namespace == NULL) {
  1611.                         qname->ns = c->strings[UNIVERSAL];
  1612.                 } else {
  1613.                         qname->ns = c->default_namespace;
  1614.                 }
  1615.  
  1616.                 qname->name = prefix;
  1617.         }
  1618.  
  1619.         return CSS_OK;
  1620. }
  1621.  
  1622. css_error parseSimpleSelector(css_language *c,
  1623.                 const parserutils_vector *vector, int *ctx,
  1624.                 css_selector **result)
  1625. {
  1626.         int orig_ctx = *ctx;
  1627.         css_error error;
  1628.         const css_token *token;
  1629.         css_selector *selector;
  1630.         css_qname qname;
  1631.  
  1632.         /* simple_selector  -> type_selector specifics
  1633.          *                  -> specific specifics
  1634.          */
  1635.  
  1636.         token = parserutils_vector_peek(vector, *ctx);
  1637.         if (token == NULL)
  1638.                 return CSS_INVALID;
  1639.  
  1640.         if (token->type == CSS_TOKEN_IDENT || tokenIsChar(token, '*') ||
  1641.                         tokenIsChar(token, '|')) {
  1642.                 /* Have type selector */
  1643.                 error = parseTypeSelector(c, vector, ctx, &qname);
  1644.                 if (error != CSS_OK) {
  1645.                         *ctx = orig_ctx;
  1646.                         return error;
  1647.                 }
  1648.  
  1649.                 error = css__stylesheet_selector_create(c->sheet,
  1650.                                 &qname, &selector);
  1651.                 if (error != CSS_OK) {
  1652.                         *ctx = orig_ctx;
  1653.                         return error;
  1654.                 }
  1655.         } else {
  1656.                 /* Universal selector */
  1657.                 if (c->default_namespace == NULL)
  1658.                         qname.ns = c->strings[UNIVERSAL];
  1659.                 else
  1660.                         qname.ns = c->default_namespace;
  1661.  
  1662.                 qname.name = c->strings[UNIVERSAL];
  1663.  
  1664.                 error = css__stylesheet_selector_create(c->sheet,
  1665.                                 &qname, &selector);
  1666.                 if (error != CSS_OK)
  1667.                         return error;
  1668.  
  1669.                 /* Ensure we have at least one specific selector */
  1670.                 error = parseAppendSpecific(c, vector, ctx, &selector);
  1671.                 if (error != CSS_OK) {
  1672.                         css__stylesheet_selector_destroy(c->sheet, selector);
  1673.                         return error;
  1674.                 }
  1675.         }
  1676.  
  1677.         error = parseSelectorSpecifics(c, vector, ctx, &selector);
  1678.         if (error != CSS_OK) {
  1679.                 css__stylesheet_selector_destroy(c->sheet, selector);
  1680.                 return error;
  1681.         }
  1682.  
  1683.         *result = selector;
  1684.  
  1685.         return CSS_OK;
  1686. }
  1687.  
  1688. css_error parseCombinator(css_language *c, const parserutils_vector *vector,
  1689.                 int *ctx, css_combinator *result)
  1690. {
  1691.         const css_token *token;
  1692.         css_combinator comb = CSS_COMBINATOR_NONE;
  1693.  
  1694.         /* combinator      -> ws '+' ws | ws '>' ws | ws '~' ws | ws1 */
  1695.  
  1696.         UNUSED(c);
  1697.  
  1698.         while ((token = parserutils_vector_peek(vector, *ctx)) != NULL) {
  1699.                 if (tokenIsChar(token, '+'))
  1700.                         comb = CSS_COMBINATOR_SIBLING;
  1701.                 else if (tokenIsChar(token, '>'))
  1702.                         comb = CSS_COMBINATOR_PARENT;
  1703.                 else if (tokenIsChar(token, '~'))
  1704.                         comb = CSS_COMBINATOR_GENERIC_SIBLING;
  1705.                 else if (token->type == CSS_TOKEN_S)
  1706.                         comb = CSS_COMBINATOR_ANCESTOR;
  1707.                 else
  1708.                         break;
  1709.  
  1710.                 parserutils_vector_iterate(vector, ctx);
  1711.  
  1712.                 /* If we've seen a '+', '>', or '~', we're done. */
  1713.                 if (comb != CSS_COMBINATOR_ANCESTOR)
  1714.                         break;
  1715.         }
  1716.  
  1717.         /* No valid combinator found */
  1718.         if (comb == CSS_COMBINATOR_NONE)
  1719.                 return CSS_INVALID;
  1720.  
  1721.         /* Consume any trailing whitespace */
  1722.         consumeWhitespace(vector, ctx);
  1723.  
  1724.         *result = comb;
  1725.  
  1726.         return CSS_OK;
  1727. }
  1728.  
  1729. css_error parseSelector(css_language *c, const parserutils_vector *vector,
  1730.                 int *ctx, css_selector **result)
  1731. {
  1732.         css_error error;
  1733.         const css_token *token = NULL;
  1734.         css_selector *selector = NULL;
  1735.  
  1736.         /* selector -> simple_selector [ combinator simple_selector ]* ws
  1737.          *
  1738.          * Note, however, that, as combinator can be wholly whitespace,
  1739.          * there's an ambiguity as to whether "ws" has been reached. We
  1740.          * resolve this by attempting to extract a combinator, then
  1741.          * recovering when we detect that we've reached the end of the
  1742.          * selector.
  1743.          */
  1744.  
  1745.         error = parseSimpleSelector(c, vector, ctx, &selector);
  1746.         if (error != CSS_OK)
  1747.                 return error;
  1748.         *result = selector;
  1749.  
  1750.         while ((token = parserutils_vector_peek(vector, *ctx)) != NULL &&
  1751.                         tokenIsChar(token, ',') == false) {
  1752.                 css_combinator comb = CSS_COMBINATOR_NONE;
  1753.                 css_selector *other = NULL;
  1754.  
  1755.                 error = parseCombinator(c, vector, ctx, &comb);
  1756.                 if (error != CSS_OK)
  1757.                         return error;
  1758.  
  1759.                 /* In the case of "html , body { ... }", the whitespace after
  1760.                  * "html" and "body" will be considered an ancestor combinator.
  1761.                  * This clearly is not the case, however. Therefore, as a
  1762.                  * special case, if we've got an ancestor combinator and there
  1763.                  * are no further tokens, or if the next token is a comma,
  1764.                  * we ignore the supposed combinator and continue. */
  1765.                 if (comb == CSS_COMBINATOR_ANCESTOR &&
  1766.                                 ((token = parserutils_vector_peek(vector,
  1767.                                         *ctx)) == NULL ||
  1768.                                 tokenIsChar(token, ',')))
  1769.                         continue;
  1770.  
  1771.                 error = parseSimpleSelector(c, vector, ctx, &other);
  1772.                 if (error != CSS_OK)
  1773.                         return error;
  1774.  
  1775.                 *result = other;
  1776.  
  1777.                 error = css__stylesheet_selector_combine(c->sheet,
  1778.                                 comb, selector, other);
  1779.                 if (error != CSS_OK) {
  1780.                         css__stylesheet_selector_destroy(c->sheet, selector);
  1781.                         return error;
  1782.                 }
  1783.  
  1784.                 selector = other;
  1785.         }
  1786.  
  1787.         return CSS_OK;
  1788. }
  1789.  
  1790. css_error parseSelectorList(css_language *c, const parserutils_vector *vector,
  1791.                 css_rule *rule)
  1792. {
  1793.         css_error error;
  1794.         const css_token *token = NULL;
  1795.         css_selector *selector = NULL;
  1796.         int ctx = 0;
  1797.  
  1798.         /* Strip any leading whitespace (can happen if in nested block) */
  1799.         consumeWhitespace(vector, &ctx);
  1800.  
  1801.         /* selector_list   -> selector [ ',' ws selector ]* */
  1802.  
  1803.         error = parseSelector(c, vector, &ctx, &selector);
  1804.         if (error != CSS_OK) {
  1805.                 if (selector != NULL)
  1806.                         css__stylesheet_selector_destroy(c->sheet, selector);
  1807.                 return error;
  1808.         }
  1809.  
  1810.         assert(selector != NULL);
  1811.  
  1812.         error = css__stylesheet_rule_add_selector(c->sheet, rule, selector);
  1813.         if (error != CSS_OK) {
  1814.                 css__stylesheet_selector_destroy(c->sheet, selector);
  1815.                 return error;
  1816.         }
  1817.  
  1818.         while (parserutils_vector_peek(vector, ctx) != NULL) {
  1819.                 token = parserutils_vector_iterate(vector, &ctx);
  1820.                 if (tokenIsChar(token, ',') == false)
  1821.                         return CSS_INVALID;
  1822.  
  1823.                 consumeWhitespace(vector, &ctx);
  1824.  
  1825.                 selector = NULL;
  1826.  
  1827.                 error = parseSelector(c, vector, &ctx, &selector);
  1828.                 if (error != CSS_OK) {
  1829.                         if (selector != NULL) {
  1830.                                 css__stylesheet_selector_destroy(c->sheet,
  1831.                                                 selector);
  1832.                         }
  1833.                         return error;
  1834.                 }
  1835.  
  1836.                 assert(selector != NULL);
  1837.  
  1838.                 error = css__stylesheet_rule_add_selector(c->sheet, rule,
  1839.                                 selector);
  1840.                 if (error != CSS_OK) {
  1841.                         css__stylesheet_selector_destroy(c->sheet, selector);
  1842.                         return error;
  1843.                 }
  1844.         }
  1845.  
  1846.         return CSS_OK;
  1847. }
  1848.  
  1849. /******************************************************************************
  1850.  * Property parsing functions                                                 *
  1851.  ******************************************************************************/
  1852.  
  1853. css_error parseProperty(css_language *c, const css_token *property,
  1854.                 const parserutils_vector *vector, int *ctx, css_rule *rule)
  1855. {
  1856.         css_error error;
  1857.         css_prop_handler handler = NULL;
  1858.         int i = 0;
  1859.         uint8_t flags = 0;
  1860.         css_style *style = NULL;
  1861.         const css_token *token;
  1862.  
  1863.         /* Find property index */
  1864.         /** \todo improve on this linear search */
  1865.         for (i = FIRST_PROP; i <= LAST_PROP; i++) {
  1866.                 bool match = false;
  1867.  
  1868.                 if (lwc_string_caseless_isequal(property->idata, c->strings[i],
  1869.                                 &match) == lwc_error_ok && match)
  1870.                         break;
  1871.         }
  1872.         if (i == LAST_PROP + 1)
  1873.                 return CSS_INVALID;
  1874.  
  1875.         /* Get handler */
  1876.         handler = property_handlers[i - FIRST_PROP];
  1877.         assert(handler != NULL);
  1878.  
  1879.         /* allocate style */
  1880.         error = css__stylesheet_style_create(c->sheet, &style);
  1881.         if (error != CSS_OK)
  1882.                 return error;
  1883.  
  1884.         assert (style != NULL);
  1885.  
  1886.         /* Call the handler */
  1887.         error = handler(c, vector, ctx, style);
  1888.         if (error != CSS_OK) {
  1889.                 css__stylesheet_style_destroy(style);
  1890.                 return error;
  1891.         }
  1892.  
  1893.         /* Determine if this declaration is important or not */
  1894.         error = css__parse_important(c, vector, ctx, &flags);
  1895.         if (error != CSS_OK) {
  1896.                 css__stylesheet_style_destroy(style);
  1897.                 return error;
  1898.         }
  1899.  
  1900.         /* Ensure that we've exhausted all the input */
  1901.         consumeWhitespace(vector, ctx);
  1902.         token = parserutils_vector_iterate(vector, ctx);
  1903.         if (token != NULL) {
  1904.                 /* Trailing junk, so discard declaration */
  1905.                 css__stylesheet_style_destroy(style);
  1906.                 return CSS_INVALID;
  1907.         }
  1908.  
  1909.         /* If it's important, then mark the style appropriately */
  1910.         if (flags != 0)
  1911.                 css__make_style_important(style);
  1912.  
  1913.         /* Append style to rule */
  1914.         error = css__stylesheet_rule_append_style(c->sheet, rule, style);
  1915.         if (error != CSS_OK) {
  1916.                 css__stylesheet_style_destroy(style);
  1917.                 return error;
  1918.         }
  1919.  
  1920.         /* Style owned or destroyed by stylesheet, so forget about it */
  1921.  
  1922.         return CSS_OK;
  1923. }
  1924.  
  1925.