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. #include <stdarg.h>
  11.  
  12. #include "stylesheet.h"
  13. #include "bytecode/bytecode.h"
  14. #include "parse/language.h"
  15. #include "utils/parserutilserror.h"
  16. #include "utils/utils.h"
  17. #include "select/dispatch.h"
  18. #include "select/font_face.h"
  19.  
  20. static css_error _add_selectors(css_stylesheet *sheet, css_rule *rule);
  21. static css_error _remove_selectors(css_stylesheet *sheet, css_rule *rule);
  22. static size_t _rule_size(const css_rule *rule);
  23.  
  24. /**
  25.  * Add a string to a stylesheet's string vector.
  26.  *
  27.  * \param sheet The stylesheet to add string to.
  28.  * \param string The string to add.
  29.  * \param string_number Pointer to location to receive string number.
  30.  * \return CSS_OK on success,
  31.  *         CSS_BADPARM on bad parameters,
  32.  *         CSS_NOMEM on memory exhaustion
  33.  *
  34.  * \post Ownership of \a string reference is passed to the stylesheet (even on failure)
  35.  * \note The returned string number is guaranteed to be non-zero
  36.  */
  37. css_error css__stylesheet_string_add(css_stylesheet *sheet, lwc_string *string, uint32_t *string_number)
  38. {
  39.         uint32_t new_string_number; /* The string number count */
  40.  
  41.         /* search for the string in the existing vector */
  42.         for (new_string_number = 0;
  43.              new_string_number < sheet->string_vector_c;
  44.              new_string_number++) {
  45.                 lwc_error res;
  46.                 bool isequal;
  47.                 res = lwc_string_isequal(string,
  48.                                          sheet->string_vector[new_string_number],
  49.                                          &isequal);
  50.  
  51.                 if (res != lwc_error_ok) {
  52.                         lwc_string_unref(string);                      
  53.                         return css_error_from_lwc_error(res);
  54.                 }
  55.  
  56.                 if (isequal) {
  57.                         lwc_string_unref(string);                      
  58.                         *string_number = (new_string_number + 1);
  59.                         return CSS_OK;
  60.                 }
  61.                  
  62.         }
  63.  
  64.         /* string does not exist in current vector, add a new one */
  65.  
  66.         if (sheet->string_vector_c >= sheet->string_vector_l) {
  67.                 /* additional storage must be allocated to deal with
  68.                  * this request.
  69.                  */
  70.                 lwc_string **new_vector;
  71.                 uint32_t new_vector_len;
  72.  
  73.                 new_vector_len = sheet->string_vector_l + 256;
  74.                 new_vector = sheet->alloc(sheet->string_vector, new_vector_len * sizeof(lwc_string *), sheet->pw);
  75.  
  76.                 if (new_vector == NULL) {
  77.                         lwc_string_unref(string);                      
  78.                         return CSS_NOMEM;
  79.                 }
  80.                 sheet->string_vector = new_vector;
  81.                 sheet->string_vector_l = new_vector_len;
  82.         }
  83.  
  84.         sheet->string_vector_c++;
  85.         sheet->string_vector[new_string_number] = string;
  86.         *string_number = (new_string_number + 1);
  87.  
  88.         return CSS_OK;
  89. }
  90.  
  91. /**
  92.  * Get a string from a stylesheet's string vector.
  93.  *
  94.  * \param sheet The stylesheet to retrive string from.
  95.  * \param string_number The string number to retrive.
  96.  * \param string Pointer to location to receive string.
  97.  * \return CSS_OK on success,
  98.  *         CSS_BADPARM on bad parameters,
  99.  */
  100. css_error css__stylesheet_string_get(css_stylesheet *sheet, uint32_t string_number, lwc_string **string)
  101. {
  102.         /* External string numbers = index into vector + 1 */
  103.         string_number--;
  104.  
  105.         if (string_number > sheet->string_vector_c) {
  106.                 return CSS_BADPARM;
  107.         }
  108.  
  109.         *string = sheet->string_vector[string_number];
  110.         return CSS_OK;
  111. }
  112.  
  113. /**
  114.  * Create a stylesheet
  115.  *
  116.  * \param params      Stylesheet parameters
  117.  * \param alloc       Memory (de)allocation function
  118.  * \param alloc_pw    Client private data for alloc
  119.  * \param stylesheet  Pointer to location to receive stylesheet
  120.  * \return CSS_OK on success,
  121.  *         CSS_BADPARM on bad parameters,
  122.  *         CSS_NOMEM on memory exhaustion
  123.  */
  124. css_error css_stylesheet_create(const css_stylesheet_params *params,
  125.                 css_allocator_fn alloc, void *alloc_pw,
  126.                 css_stylesheet **stylesheet)
  127. {
  128.         css_parser_optparams optparams;
  129.         css_error error;
  130.         css_stylesheet *sheet;
  131.         size_t len;
  132.  
  133.         if (params == NULL || params->params_version !=
  134.                                 CSS_STYLESHEET_PARAMS_VERSION_1 ||
  135.                         params->url == NULL || alloc == NULL ||
  136.                         params->resolve == NULL || stylesheet == NULL)
  137.                 return CSS_BADPARM;
  138.  
  139.         sheet = alloc(NULL, sizeof(css_stylesheet), alloc_pw);
  140.         if (sheet == NULL)
  141.                 return CSS_NOMEM;
  142.  
  143.         memset(sheet, 0, sizeof(css_stylesheet));
  144.  
  145.         error = css__propstrings_get(&sheet->propstrings);
  146.         if (error != CSS_OK) {
  147.                 alloc(sheet, 0, alloc_pw);
  148.                 return error;
  149.         }
  150.        
  151.         sheet->inline_style = params->inline_style;
  152.  
  153.         if (params->inline_style) {
  154.                 error = css__parser_create_for_inline_style(params->charset,
  155.                         params->charset != NULL
  156.                                 ? CSS_CHARSET_DICTATED : CSS_CHARSET_DEFAULT,
  157.                         alloc, alloc_pw, &sheet->parser);
  158.         } else {
  159.                 error = css__parser_create(params->charset,
  160.                         params->charset != NULL
  161.                                 ? CSS_CHARSET_DICTATED : CSS_CHARSET_DEFAULT,
  162.                         alloc, alloc_pw, &sheet->parser);
  163.         }
  164.  
  165.         if (error != CSS_OK) {
  166.                 css__propstrings_unref();
  167.                 alloc(sheet, 0, alloc_pw);
  168.                 return error;
  169.         }
  170.  
  171.         sheet->quirks_allowed = params->allow_quirks;
  172.  
  173.         if (params->allow_quirks) {
  174.                 optparams.quirks = true;
  175.  
  176.                 error = css__parser_setopt(sheet->parser, CSS_PARSER_QUIRKS,
  177.                                 &optparams);
  178.                 if (error != CSS_OK) {
  179.                         css__parser_destroy(sheet->parser);
  180.                         css__propstrings_unref();
  181.                         alloc(sheet, 0, alloc_pw);
  182.                         return error;
  183.                 }
  184.         }
  185.  
  186.         sheet->level = params->level;
  187.         error = css__language_create(sheet, sheet->parser, alloc, alloc_pw,
  188.                         &sheet->parser_frontend);
  189.         if (error != CSS_OK) {
  190.                 css__parser_destroy(sheet->parser);
  191.                 css__propstrings_unref();
  192.                 alloc(sheet, 0, alloc_pw);
  193.                 return error;
  194.         }
  195.  
  196.         error = css__selector_hash_create(alloc, alloc_pw,
  197.                         &sheet->selectors);
  198.         if (error != CSS_OK) {
  199.                 css__language_destroy(sheet->parser_frontend);
  200.                 css__parser_destroy(sheet->parser);
  201.                 css__propstrings_unref();
  202.                 alloc(sheet, 0, alloc_pw);
  203.                 return error;
  204.         }
  205.  
  206.         len = strlen(params->url) + 1;
  207.         sheet->url = alloc(NULL, len, alloc_pw);
  208.         if (sheet->url == NULL) {
  209.                 css__selector_hash_destroy(sheet->selectors);
  210.                 css__language_destroy(sheet->parser_frontend);
  211.                 css__parser_destroy(sheet->parser);
  212.                 css__propstrings_unref();
  213.                 alloc(sheet, 0, alloc_pw);
  214.                 return CSS_NOMEM;
  215.         }
  216.         memcpy(sheet->url, params->url, len);
  217.  
  218.         if (params->title != NULL) {
  219.                 len = strlen(params->title) + 1;
  220.                 sheet->title = alloc(NULL, len, alloc_pw);
  221.                 if (sheet->title == NULL) {
  222.                         alloc(sheet->url, 0, alloc_pw);
  223.                         css__selector_hash_destroy(sheet->selectors);
  224.                         css__language_destroy(sheet->parser_frontend);
  225.                         css__parser_destroy(sheet->parser);
  226.                         css__propstrings_unref();
  227.                         alloc(sheet, 0, alloc_pw);
  228.                         return CSS_NOMEM;
  229.                 }
  230.                 memcpy(sheet->title, params->title, len);
  231.         }
  232.  
  233.         sheet->resolve = params->resolve;
  234.         sheet->resolve_pw = params->resolve_pw;
  235.  
  236.         sheet->import = params->import;
  237.         sheet->import_pw = params->import_pw;
  238.  
  239.         sheet->color = params->color;
  240.         sheet->color_pw = params->color_pw;
  241.  
  242.         sheet->font = params->font;
  243.         sheet->font_pw = params->font_pw;
  244.  
  245.         sheet->alloc = alloc;
  246.         sheet->pw = alloc_pw;
  247.  
  248.         sheet->size = sizeof(css_stylesheet) + strlen(sheet->url);
  249.         if (sheet->title != NULL)
  250.                 sheet->size += strlen(sheet->title);
  251.  
  252.         *stylesheet = sheet;
  253.  
  254.         return CSS_OK;
  255. }
  256.  
  257. /**
  258.  * Destroy a stylesheet
  259.  *
  260.  * \param sheet  The stylesheet to destroy
  261.  * \return CSS_OK on success, appropriate error otherwise
  262.  */
  263. css_error css_stylesheet_destroy(css_stylesheet *sheet)
  264. {
  265.         uint32_t index;
  266.         css_rule *r, *s;
  267.  
  268.         if (sheet == NULL)
  269.                 return CSS_BADPARM;
  270.        
  271.         if (sheet->title != NULL)
  272.                 sheet->alloc(sheet->title, 0, sheet->pw);
  273.  
  274.         sheet->alloc(sheet->url, 0, sheet->pw);
  275.        
  276.         for (r = sheet->rule_list; r != NULL; r = s) {
  277.                 s = r->next;
  278.  
  279.                 /* Detach from list */
  280.                 r->parent = NULL;
  281.                 r->prev = NULL;
  282.                 r->next = NULL;
  283.  
  284.                 css__stylesheet_rule_destroy(sheet, r);
  285.         }
  286.  
  287.         css__selector_hash_destroy(sheet->selectors);
  288.  
  289.         /* These three may have been destroyed when parsing completed */
  290.         if (sheet->parser_frontend != NULL)
  291.                 css__language_destroy(sheet->parser_frontend);
  292.  
  293.         if (sheet->parser != NULL)
  294.                 css__parser_destroy(sheet->parser);
  295.  
  296.         if (sheet->cached_style != NULL)
  297.                 css__stylesheet_style_destroy(sheet->cached_style);
  298.  
  299.         /* destroy string vector */
  300.         for (index = 0;
  301.              index < sheet->string_vector_c;
  302.              index++) {
  303.                 lwc_string_unref(sheet->string_vector[index]);         
  304.         }
  305.  
  306.         if (sheet->string_vector != NULL)
  307.                 sheet->alloc(sheet->string_vector, 0, sheet->pw);
  308.  
  309.         css__propstrings_unref();
  310.        
  311.         sheet->alloc(sheet, 0, sheet->pw);
  312.  
  313.         return CSS_OK;
  314. }
  315.  
  316. /**
  317.  * Append source data to a stylesheet
  318.  *
  319.  * \param sheet  The stylesheet to append data to
  320.  * \param data   Pointer to data to append
  321.  * \param len    Length, in bytes, of data to append
  322.  * \return CSS_OK on success, appropriate error otherwise
  323.  */
  324. css_error css_stylesheet_append_data(css_stylesheet *sheet,
  325.                 const uint8_t *data, size_t len)
  326. {
  327.         if (sheet == NULL || data == NULL)
  328.                 return CSS_BADPARM;
  329.  
  330.         if (sheet->parser == NULL)
  331.                 return CSS_INVALID;
  332.  
  333.         return css__parser_parse_chunk(sheet->parser, data, len);
  334. }
  335.  
  336. /**
  337.  * Flag that the last of a stylesheet's data has been seen
  338.  *
  339.  * \param sheet  The stylesheet in question
  340.  * \return CSS_OK on success,
  341.  *         CSS_IMPORTS_PENDING if there are imports pending,
  342.  *         appropriate error otherwise
  343.  */
  344. css_error css_stylesheet_data_done(css_stylesheet *sheet)
  345. {
  346.         const css_rule *r;
  347.         css_error error;
  348.  
  349.         if (sheet == NULL)
  350.                 return CSS_BADPARM;
  351.  
  352.         if (sheet->parser == NULL)
  353.                 return CSS_INVALID;
  354.  
  355.         error = css__parser_completed(sheet->parser);
  356.         if (error != CSS_OK)
  357.                 return error;
  358.  
  359.         /* Destroy the parser, as it's no longer needed */
  360.         css__language_destroy(sheet->parser_frontend);
  361.         css__parser_destroy(sheet->parser);
  362.  
  363.         sheet->parser_frontend = NULL;
  364.         sheet->parser = NULL;
  365.        
  366.         /* If we have a cached style, drop it as we're done parsing. */
  367.         if (sheet->cached_style != NULL) {
  368.                 css__stylesheet_style_destroy(sheet->cached_style);
  369.                 sheet->cached_style = NULL;
  370.         }
  371.  
  372.         /* Determine if there are any pending imports */
  373.         for (r = sheet->rule_list; r != NULL; r = r->next) {
  374.                 const css_rule_import *i = (const css_rule_import *) r;
  375.  
  376.                 if (r->type != CSS_RULE_UNKNOWN &&
  377.                                 r->type != CSS_RULE_CHARSET &&
  378.                                 r->type != CSS_RULE_IMPORT)
  379.                         break;
  380.  
  381.                 if (r->type == CSS_RULE_IMPORT && i->sheet == NULL)
  382.                         return CSS_IMPORTS_PENDING;
  383.         }
  384.  
  385.         return CSS_OK;
  386. }
  387.  
  388. /**
  389.  * Retrieve the next pending import for the parent stylesheet
  390.  *
  391.  * \param parent  Parent stylesheet
  392.  * \param url     Pointer to object to be populated with details of URL of
  393.  *                imported stylesheet (potentially relative)
  394.  * \param media   Pointer to location to receive applicable media types for
  395.  *                imported sheet,
  396.  * \return CSS_OK on success,
  397.  *         CSS_INVALID if there are no pending imports remaining
  398.  *
  399.  * The client must resolve the absolute URL of the imported stylesheet,
  400.  * using the parent's URL as the base. It must then fetch the imported
  401.  * stylesheet, and parse it to completion, including fetching any stylesheets
  402.  * it may import. The resultant sheet must then be registered with the
  403.  * parent using css_stylesheet_register_import().
  404.  *
  405.  * The client must then call this function again, to determine if there
  406.  * are any further imports for the parent stylesheet, and, if so,
  407.  * process them as described above.
  408.  *
  409.  * If the client is unable to fetch an imported stylesheet, it must
  410.  * register an empty stylesheet with the parent in its place.
  411.  */
  412. css_error css_stylesheet_next_pending_import(css_stylesheet *parent,
  413.                 lwc_string **url, uint64_t *media)
  414. {
  415.         const css_rule *r;
  416.  
  417.         if (parent == NULL || url == NULL || media == NULL)
  418.                 return CSS_BADPARM;
  419.  
  420.         for (r = parent->rule_list; r != NULL; r = r->next) {
  421.                 const css_rule_import *i = (const css_rule_import *) r;
  422.  
  423.                 if (r->type != CSS_RULE_UNKNOWN &&
  424.                                 r->type != CSS_RULE_CHARSET &&
  425.                                 r->type != CSS_RULE_IMPORT)
  426.                         break;
  427.  
  428.                 if (r->type == CSS_RULE_IMPORT && i->sheet == NULL) {
  429.                         *url = lwc_string_ref(i->url);
  430.                         *media = i->media;
  431.  
  432.                         return CSS_OK;
  433.                 }
  434.         }
  435.  
  436.         return CSS_INVALID;
  437. }
  438.  
  439. /**
  440.  * Register an imported stylesheet with its parent
  441.  *
  442.  * \param parent  Parent stylesheet
  443.  * \param import  Imported sheet
  444.  * \return CSS_OK on success,
  445.  *         CSS_INVALID if there are no outstanding imports,
  446.  *         appropriate error otherwise.
  447.  *
  448.  * Ownership of the imported stylesheet is retained by the client.
  449.  */
  450. css_error css_stylesheet_register_import(css_stylesheet *parent,
  451.                 css_stylesheet *import)
  452. {
  453.         css_rule *r;
  454.  
  455.         if (parent == NULL || import == NULL)
  456.                 return CSS_BADPARM;
  457.  
  458.         for (r = parent->rule_list; r != NULL; r = r->next) {
  459.                 css_rule_import *i = (css_rule_import *) r;
  460.  
  461.                 if (r->type != CSS_RULE_UNKNOWN &&
  462.                                 r->type != CSS_RULE_CHARSET &&
  463.                                 r->type != CSS_RULE_IMPORT)
  464.                         break;
  465.  
  466.                 if (r->type == CSS_RULE_IMPORT && i->sheet == NULL) {
  467.                         i->sheet = import;
  468.  
  469.                         return CSS_OK;
  470.                 }
  471.         }
  472.  
  473.         return CSS_INVALID;
  474. }
  475.  
  476. /**
  477.  * Retrieve the language level of a stylesheet
  478.  *
  479.  * \param sheet  The stylesheet to retrieve the language level of
  480.  * \param level  Pointer to location to receive language level
  481.  * \return CSS_OK on success, appropriate error otherwise
  482.  */
  483. css_error css_stylesheet_get_language_level(css_stylesheet *sheet,
  484.                 css_language_level *level)
  485. {
  486.         if (sheet == NULL || level == NULL)
  487.                 return CSS_BADPARM;
  488.  
  489.         *level = sheet->level;
  490.  
  491.         return CSS_OK;
  492. }
  493.  
  494. /**
  495.  * Retrieve the URL associated with a stylesheet
  496.  *
  497.  * \param sheet  The stylesheet to retrieve the URL from
  498.  * \param url    Pointer to location to receive pointer to URL
  499.  * \return CSS_OK on success, appropriate error otherwise
  500.  */
  501. css_error css_stylesheet_get_url(css_stylesheet *sheet, const char **url)
  502. {
  503.         if (sheet == NULL || url == NULL)
  504.                 return CSS_BADPARM;
  505.  
  506.         *url = sheet->url;
  507.  
  508.         return CSS_OK;
  509. }
  510.  
  511. /**
  512.  * Retrieve the title associated with a stylesheet
  513.  *
  514.  * \param sheet  The stylesheet to retrieve the title from
  515.  * \param title  Pointer to location to receive pointer to title
  516.  * \return CSS_Ok on success, appropriate error otherwise
  517.  */
  518. css_error css_stylesheet_get_title(css_stylesheet *sheet, const char **title)
  519. {
  520.         if (sheet == NULL || title == NULL)
  521.                 return CSS_BADPARM;
  522.  
  523.         *title = sheet->title;
  524.  
  525.         return CSS_OK;
  526. }
  527.  
  528. /**
  529.  * Determine whether quirky parsing was permitted on a stylesheet
  530.  *
  531.  * \param sheet   The stylesheet to consider
  532.  * \param quirks  Pointer to location to receive quirkyness
  533.  * \return CSS_OK on success, appropriate error otherwise
  534.  */
  535. css_error css_stylesheet_quirks_allowed(css_stylesheet *sheet, bool *allowed)
  536. {
  537.         if (sheet == NULL || allowed == NULL)
  538.                 return CSS_BADPARM;
  539.  
  540.         *allowed = sheet->quirks_allowed;
  541.  
  542.         return CSS_OK;
  543. }
  544.  
  545.  
  546. /**
  547.  * Determine whether quirky parsing was used on a stylesheet
  548.  *
  549.  * \param sheet   The stylesheet to consider
  550.  * \param quirks  Pointer to location to receive quirkyness
  551.  * \return CSS_OK on success, appropriate error otherwise
  552.  */
  553. css_error css_stylesheet_used_quirks(css_stylesheet *sheet, bool *quirks)
  554. {
  555.         if (sheet == NULL || quirks == NULL)
  556.                 return CSS_BADPARM;
  557.  
  558.         *quirks = sheet->quirks_used;
  559.  
  560.         return CSS_OK;
  561. }
  562.  
  563. /**
  564.  * Get disabled status of a stylesheet
  565.  *
  566.  * \param sheet     The stylesheet to consider
  567.  * \param disabled  Pointer to location to receive disabled state
  568.  * \return CSS_OK on success, appropriate error otherwise
  569.  */
  570. css_error css_stylesheet_get_disabled(css_stylesheet *sheet, bool *disabled)
  571. {
  572.         if (sheet == NULL || disabled == NULL)
  573.                 return CSS_BADPARM;
  574.  
  575.         *disabled = sheet->disabled;
  576.  
  577.         return CSS_OK;
  578. }
  579.  
  580. /**
  581.  * Set a stylesheet's disabled state
  582.  *
  583.  * \param sheet     The stylesheet to modify
  584.  * \param disabled  The new disabled state
  585.  * \return CSS_OK on success, appropriate error otherwise
  586.  */
  587. css_error css_stylesheet_set_disabled(css_stylesheet *sheet, bool disabled)
  588. {
  589.         if (sheet == NULL)
  590.                 return CSS_BADPARM;
  591.  
  592.         sheet->disabled = disabled;
  593.  
  594.         /** \todo needs to trigger some event announcing styles have changed */
  595.  
  596.         return CSS_OK;
  597. }
  598.  
  599. /**
  600.  * Determine the memory-resident size of a stylesheet
  601.  *
  602.  * \param sheet  Sheet to consider
  603.  * \param size   Pointer to location to receive byte count
  604.  * \return CSS_OK on success.
  605.  *
  606.  * \note The returned size will not include the size of interned strings
  607.  *       or imported stylesheets.
  608.  */
  609. css_error css_stylesheet_size(css_stylesheet *sheet, size_t *size)
  610. {
  611.         size_t bytes = 0;
  612.         css_error error;
  613.  
  614.         if (sheet == NULL || size == NULL)
  615.                 return CSS_BADPARM;
  616.  
  617.         bytes = sheet->size;
  618.  
  619.         /* Selector hash */
  620.         if (sheet->selectors != NULL) {
  621.                 size_t hash_size;
  622.  
  623.                 error = css__selector_hash_size(sheet->selectors, &hash_size);
  624.                 if (error != CSS_OK)
  625.                         return error;
  626.  
  627.                 bytes += hash_size;
  628.         }
  629.        
  630.         *size = bytes;
  631.  
  632.         return CSS_OK;
  633. }
  634.  
  635. /******************************************************************************
  636.  * Library-private API below here                                             *
  637.  ******************************************************************************/
  638. /* Note, CSS_STYLE_DEFAULT_SIZE must be a power of 2 */
  639. /* With a test set of NetSurf's homepage, BBC news, wikipedia, CNN, Ars, Google and El-Reg,
  640.  * 16 seems to be a good medium between wastage and reallocs.
  641.  */
  642. #define CSS_STYLE_DEFAULT_SIZE 16
  643.  
  644. /**
  645.  * Create a style
  646.  *
  647.  * \param sheet  The stylesheet context
  648.  * \param len    The required length of the style
  649.  * \param style  Pointer to location to receive style
  650.  * \return CSS_OK on success,
  651.  *         CSS_BADPARM on bad parameters,
  652.  *         CSS_NOMEM on memory exhaustion
  653.  */
  654. css_error css__stylesheet_style_create(css_stylesheet *sheet, css_style **style)
  655. {
  656.         css_style *s;
  657.  
  658.         if (sheet == NULL)
  659.                 return CSS_BADPARM;
  660.        
  661.         if (sheet->cached_style != NULL) {
  662.                 *style = sheet->cached_style;
  663.                 sheet->cached_style = NULL;
  664.                 return CSS_OK;
  665.         }
  666.        
  667.         s = sheet->alloc(NULL, sizeof(css_style), sheet->pw);
  668.         if (s == NULL)
  669.                 return CSS_NOMEM;
  670.  
  671.         s->bytecode = sheet->alloc(NULL, sizeof(css_code_t) * CSS_STYLE_DEFAULT_SIZE, sheet->pw);
  672.  
  673.         if (s->bytecode == NULL) {
  674.                 sheet->alloc(s, 0, sheet->pw); /* do not leak */
  675.        
  676.                 return CSS_NOMEM;
  677.         }
  678.         s->allocated = CSS_STYLE_DEFAULT_SIZE;
  679.         s->used = 0;
  680.         s->sheet = sheet;
  681.  
  682.         *style = s;
  683.  
  684.         return CSS_OK;
  685. }
  686.  
  687. css_error css__stylesheet_merge_style(css_style *target, css_style *style)
  688. {
  689.         css_code_t *newcode;
  690.         uint32_t newcode_len;
  691.         css_stylesheet *sheet;
  692.  
  693.         if (target == NULL || style == NULL)
  694.                 return CSS_BADPARM;
  695.  
  696.         sheet = target->sheet;
  697.         newcode_len = target->used + style->used ;
  698.        
  699.         if (newcode_len > target->allocated) {
  700.                 newcode_len += CSS_STYLE_DEFAULT_SIZE - 1;
  701.                 newcode_len &= ~(CSS_STYLE_DEFAULT_SIZE - 1);
  702.                 newcode = sheet->alloc(target->bytecode, newcode_len * sizeof(css_code_t), sheet->pw);
  703.  
  704.                 if (newcode == NULL)
  705.                         return CSS_NOMEM;
  706.  
  707.                 target->bytecode = newcode;
  708.                 target->allocated = newcode_len;
  709.         }
  710.  
  711.         memcpy(target->bytecode + target->used, style->bytecode, style->used * sizeof(css_code_t));
  712.  
  713.         target->used += style->used;
  714.  
  715.         return CSS_OK;
  716.  
  717. }
  718.  
  719. /** append one or more css code entries to a style */
  720. css_error css__stylesheet_style_vappend(css_style *style, uint32_t style_count, ...)
  721. {
  722.         va_list ap;
  723.         css_error error = CSS_OK;
  724.         css_code_t css_code;
  725.  
  726.         va_start(ap, style_count);
  727.         while (style_count > 0) {
  728.                 css_code = va_arg(ap, css_code_t);
  729.                 error = css__stylesheet_style_append(style, css_code);
  730.                 if (error != CSS_OK)
  731.                         break;
  732.                 style_count--;
  733.         }
  734.         va_end(ap);
  735.         return error;
  736. }
  737.  
  738. /** append a css code entry to a style */
  739. css_error css__stylesheet_style_append(css_style *style, css_code_t css_code)
  740. {
  741.         css_stylesheet *sheet;
  742.  
  743.         if (style == NULL)
  744.                 return CSS_BADPARM;
  745.  
  746.         sheet = style->sheet;
  747.  
  748.         if (style->allocated == style->used) {
  749.                 /* space not available to append, extend allocation */
  750.                 css_code_t *newcode;
  751.                 uint32_t newcode_len = style->allocated * 2;
  752.                 newcode = sheet->alloc(style->bytecode, sizeof(css_code_t) * newcode_len, sheet->pw);
  753.                 if (newcode == NULL)
  754.                         return CSS_NOMEM;
  755.                 style->bytecode = newcode;
  756.                 style->allocated = newcode_len;
  757.         }
  758.  
  759.         style->bytecode[style->used] = css_code;
  760.         style->used++;
  761.  
  762.         return CSS_OK;
  763. }
  764.  
  765. /**
  766.  * Destroy a style
  767.  *
  768.  * \param sheet  The stylesheet context
  769.  * \param style  The style to destroy
  770.  * \return CSS_OK on success, appropriate error otherwise
  771.  */
  772. css_error css__stylesheet_style_destroy(css_style *style)
  773. {
  774.         css_stylesheet *sheet;
  775.        
  776.         if (style == NULL)
  777.                 return CSS_BADPARM;
  778.  
  779.         sheet = style->sheet;
  780.  
  781.         if (sheet->cached_style == NULL) {
  782.                 sheet->cached_style = style;
  783.                 style->used = 0;
  784.         } else if (sheet->cached_style->allocated < style->allocated) {
  785.                 sheet->alloc(sheet->cached_style->bytecode, 0, sheet->pw);
  786.                 sheet->alloc(sheet->cached_style, 0, sheet->pw);
  787.                 sheet->cached_style = style;
  788.                 style->used = 0;
  789.         } else {
  790.                 sheet->alloc(style->bytecode, 0, sheet->pw);
  791.                 sheet->alloc(style, 0, sheet->pw);
  792.         }
  793.        
  794.         return CSS_OK;
  795. }
  796.  
  797. /**
  798.  * Create an element selector
  799.  *
  800.  * \param sheet     The stylesheet context
  801.  * \param qname     Qualified name of selector
  802.  * \param selector  Pointer to location to receive selector object
  803.  * \return CSS_OK on success,
  804.  *         CSS_BADPARM on bad parameters,
  805.  *         CSS_NOMEM on memory exhaustion
  806.  */
  807. css_error css__stylesheet_selector_create(css_stylesheet *sheet,
  808.                 css_qname *qname, css_selector **selector)
  809. {
  810.         css_selector *sel;
  811.  
  812.         if (sheet == NULL || qname == NULL || qname->name == NULL ||
  813.                         selector == NULL)
  814.                 return CSS_BADPARM;
  815.  
  816.         sel = sheet->alloc(NULL, sizeof(css_selector), sheet->pw);
  817.         if (sel == NULL)
  818.                 return CSS_NOMEM;
  819.  
  820.         memset(sel, 0, sizeof(css_selector));
  821.  
  822.         sel->data.type = CSS_SELECTOR_ELEMENT;
  823.         if (qname->ns != NULL)
  824.                 sel->data.qname.ns = lwc_string_ref(qname->ns);
  825.         else
  826.                 sel->data.qname.ns = NULL;
  827.         sel->data.qname.name = lwc_string_ref(qname->name);
  828.         sel->data.value.string = NULL;
  829.         sel->data.value_type = CSS_SELECTOR_DETAIL_VALUE_STRING;
  830.  
  831.         if (sheet->inline_style) {
  832.                 sel->specificity = CSS_SPECIFICITY_A;
  833.         } else {
  834.                 /* Initial specificity -- 1 for an element, 0 for universal */
  835.                 if (lwc_string_length(qname->name) != 1 ||
  836.                                 lwc_string_data(qname->name)[0] != '*')
  837.                         sel->specificity = CSS_SPECIFICITY_D;
  838.                 else
  839.                         sel->specificity = 0;
  840.         }
  841.  
  842.         sel->data.comb = CSS_COMBINATOR_NONE;
  843.  
  844.         *selector = sel;
  845.  
  846.         return CSS_OK;
  847. }
  848.  
  849. /**
  850.  * Destroy a selector object
  851.  *
  852.  * \param sheet     The stylesheet context
  853.  * \param selector  The selector to destroy
  854.  * \return CSS_OK on success, appropriate error otherwise
  855.  */
  856. css_error css__stylesheet_selector_destroy(css_stylesheet *sheet,
  857.                 css_selector *selector)
  858. {
  859.         css_selector *c, *d;
  860.         css_selector_detail *detail;
  861.  
  862.         if (sheet == NULL || selector == NULL)
  863.                 return CSS_BADPARM;
  864.  
  865.         /* Must not be attached to a rule */
  866.         assert(selector->rule == NULL);
  867.  
  868.         /* Destroy combinator chain */
  869.         for (c = selector->combinator; c != NULL; c = d) {
  870.                 d = c->combinator;
  871.  
  872.                 for (detail = &c->data; detail;) {
  873.                         if (detail->qname.ns != NULL)
  874.                                 lwc_string_unref(detail->qname.ns);
  875.                         lwc_string_unref(detail->qname.name);
  876.  
  877.                         if (detail->value_type ==
  878.                                         CSS_SELECTOR_DETAIL_VALUE_STRING &&
  879.                                         detail->value.string != NULL) {
  880.                                 lwc_string_unref(detail->value.string);
  881.                         }
  882.  
  883.                         if (detail->next)
  884.                                 detail++;
  885.                         else
  886.                                 detail = NULL;
  887.                 }
  888.                
  889.                 sheet->alloc(c, 0, sheet->pw);
  890.         }
  891.        
  892.         for (detail = &selector->data; detail;) {
  893.                 if (detail->qname.ns != NULL)
  894.                         lwc_string_unref(detail->qname.ns);
  895.                 lwc_string_unref(detail->qname.name);
  896.  
  897.                 if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING &&
  898.                                 detail->value.string != NULL) {
  899.                         lwc_string_unref(detail->value.string);
  900.                 }
  901.  
  902.                 if (detail->next)
  903.                         detail++;
  904.                 else
  905.                         detail = NULL;
  906.         }
  907.                      
  908.        
  909.         /* Destroy this selector */
  910.         sheet->alloc(selector, 0, sheet->pw);
  911.  
  912.         return CSS_OK;
  913. }
  914.  
  915. /**
  916.  * Initialise a selector detail
  917.  *
  918.  * \param sheet       The stylesheet context
  919.  * \param type        The type of selector to create
  920.  * \param qname       Qualified name of selector
  921.  * \param value       Value of selector
  922.  * \param value_type  Type of \a value
  923.  * \param negate      Whether the detail match should be negated
  924.  * \param detail      Pointer to detail object to initialise
  925.  * \return CSS_OK on success,
  926.  *         CSS_BADPARM on bad parameters
  927.  *
  928.  * \note No strings are referenced at this point: they will be
  929.  *       referenced when appending the detail to a selector.
  930.  */
  931. css_error css__stylesheet_selector_detail_init(css_stylesheet *sheet,
  932.                 css_selector_type type, css_qname *qname,
  933.                 css_selector_detail_value value,
  934.                 css_selector_detail_value_type value_type,
  935.                 bool negate, css_selector_detail *detail)
  936. {
  937.         if (sheet == NULL || qname == NULL || qname->name == NULL ||
  938.                         detail == NULL)
  939.                 return CSS_BADPARM;
  940.  
  941.         memset(detail, 0, sizeof(css_selector_detail));
  942.  
  943.         detail->type = type;
  944.         detail->qname = *qname;
  945.         detail->value = value;
  946.         detail->value_type = value_type;
  947.         detail->negate = negate;
  948.  
  949.         return CSS_OK;
  950. }
  951.  
  952. /**
  953.  * Append a selector to the specifics chain of another selector
  954.  *
  955.  * \param sheet     The stylesheet context
  956.  * \param parent    Pointer to pointer to the parent selector (updated on exit)
  957.  * \param specific  The selector to append (copied)
  958.  * \return CSS_OK on success, appropriate error otherwise.
  959.  */
  960. css_error css__stylesheet_selector_append_specific(css_stylesheet *sheet,
  961.                 css_selector **parent, const css_selector_detail *detail)
  962. {
  963.         css_selector *temp;
  964.         css_selector_detail *d;
  965.         size_t num_details = 0;
  966.  
  967.         if (sheet == NULL || parent == NULL ||
  968.                         *parent == NULL || detail == NULL)
  969.                 return CSS_BADPARM;
  970.  
  971.         /** \todo this may want optimising -- counting blocks is O(n)
  972.          * In practice, however, n isn't likely to be large, so may be fine
  973.          */
  974.  
  975.         /* Count current number of detail blocks */
  976.         for (d = &(*parent)->data; d->next != 0; d++)
  977.                 num_details++;
  978.  
  979.         /* Grow selector by one detail block */
  980.         temp = sheet->alloc((*parent), sizeof(css_selector) +
  981.                         (num_details + 1) * sizeof(css_selector_detail),
  982.                         sheet->pw);
  983.         if (temp == NULL)
  984.                 return CSS_NOMEM;
  985.  
  986.         /* Copy detail into empty block */
  987.         *(d = &(&temp->data)[num_details + 1]) = *detail;
  988.         /* Flag that there's another block */
  989.         (&temp->data)[num_details].next = 1;
  990.        
  991.         /* Ref the strings */
  992.         if (detail->qname.ns != NULL)
  993.                 d->qname.ns = lwc_string_ref(detail->qname.ns);
  994.         d->qname.name = lwc_string_ref(detail->qname.name);
  995.         if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING &&
  996.                         detail->value.string != NULL)
  997.                 d->value.string = lwc_string_ref(detail->value.string);
  998.        
  999.         (*parent) = temp;
  1000.  
  1001.         /* Update parent's specificity */
  1002.         switch (detail->type) {
  1003.         case CSS_SELECTOR_CLASS:
  1004.         case CSS_SELECTOR_PSEUDO_CLASS:
  1005.         case CSS_SELECTOR_ATTRIBUTE:
  1006.         case CSS_SELECTOR_ATTRIBUTE_EQUAL:
  1007.         case CSS_SELECTOR_ATTRIBUTE_DASHMATCH:
  1008.         case CSS_SELECTOR_ATTRIBUTE_INCLUDES:
  1009.         case CSS_SELECTOR_ATTRIBUTE_PREFIX:
  1010.         case CSS_SELECTOR_ATTRIBUTE_SUFFIX:
  1011.         case CSS_SELECTOR_ATTRIBUTE_SUBSTRING:
  1012.                 (*parent)->specificity += CSS_SPECIFICITY_C;
  1013.                 break;
  1014.         case CSS_SELECTOR_ID:
  1015.                 (*parent)->specificity += CSS_SPECIFICITY_B;
  1016.                 break;
  1017.         case CSS_SELECTOR_PSEUDO_ELEMENT:
  1018.         case CSS_SELECTOR_ELEMENT:
  1019.                 (*parent)->specificity += CSS_SPECIFICITY_D;
  1020.                 break;
  1021.         }
  1022.  
  1023.         return CSS_OK;
  1024. }
  1025.  
  1026. /**
  1027.  * Combine a pair of selectors
  1028.  *
  1029.  * \param sheet  The stylesheet context
  1030.  * \param type   The combinator type
  1031.  * \param a      The first operand
  1032.  * \param b      The second operand
  1033.  * \return CSS_OK on success, appropriate error otherwise.
  1034.  *
  1035.  * For example, given A + B, the combinator field of B would point at A,
  1036.  * with a combinator type of CSS_COMBINATOR_SIBLING. Thus, given B, we can
  1037.  * find its combinator. It is not possible to find B given A.
  1038.  */
  1039. css_error css__stylesheet_selector_combine(css_stylesheet *sheet,
  1040.                 css_combinator type, css_selector *a, css_selector *b)
  1041. {
  1042.         const css_selector_detail *det;
  1043.  
  1044.         if (sheet == NULL || a == NULL || b == NULL)
  1045.                 return CSS_BADPARM;
  1046.  
  1047.         /* Ensure that there is no existing combinator on B */
  1048.         assert(b->combinator == NULL);
  1049.  
  1050.         /* A must not contain a pseudo element */
  1051.         for (det = &a->data; det != NULL; ) {
  1052.                 if (det->type == CSS_SELECTOR_PSEUDO_ELEMENT)
  1053.                         return CSS_INVALID;
  1054.  
  1055.                 det = (det->next != 0) ? det + 1 : NULL;
  1056.         }
  1057.  
  1058.         b->combinator = a;
  1059.         b->data.comb = type;
  1060.  
  1061.         /* And propagate A's specificity to B */
  1062.         b->specificity += a->specificity;
  1063.  
  1064.         return CSS_OK;
  1065. }
  1066.  
  1067. /**
  1068.  * Create a CSS rule
  1069.  *
  1070.  * \param sheet  The stylesheet context
  1071.  * \param type   The rule type
  1072.  * \param rule   Pointer to location to receive rule object
  1073.  * \return CSS_OK on success,
  1074.  *         CSS_BADPARM on bad parameters,
  1075.  *         CSS_NOMEM on memory exhaustion
  1076.  */
  1077. css_error css__stylesheet_rule_create(css_stylesheet *sheet, css_rule_type type,
  1078.                 css_rule **rule)
  1079. {
  1080.         css_rule *r;
  1081.         size_t required = 0;
  1082.  
  1083.         if (sheet == NULL || rule == NULL)
  1084.                 return CSS_BADPARM;
  1085.  
  1086.         switch (type) {
  1087.         case CSS_RULE_UNKNOWN:
  1088.                 required = sizeof(css_rule);
  1089.                 break;
  1090.         case CSS_RULE_SELECTOR:
  1091.                 required = sizeof(css_rule_selector);
  1092.                 break;
  1093.         case CSS_RULE_CHARSET:
  1094.                 required = sizeof(css_rule_charset);
  1095.                 break;
  1096.         case CSS_RULE_IMPORT:
  1097.                 required = sizeof(css_rule_import);
  1098.                 break;
  1099.         case CSS_RULE_MEDIA:
  1100.                 required = sizeof(css_rule_media);
  1101.                 break;
  1102.         case CSS_RULE_FONT_FACE:
  1103.                 required = sizeof(css_rule_font_face);
  1104.                 break;
  1105.         case CSS_RULE_PAGE:
  1106.                 required = sizeof(css_rule_page);
  1107.                 break;
  1108.         }
  1109.  
  1110.         r = sheet->alloc(NULL, required, sheet->pw);
  1111.         if (r == NULL)
  1112.                 return CSS_NOMEM;
  1113.  
  1114.         memset(r, 0, required);
  1115.  
  1116.         r->type = type;
  1117.  
  1118.         *rule = r;
  1119.  
  1120.         return CSS_OK;
  1121. }
  1122.  
  1123. /**
  1124.  * Destroy a CSS rule
  1125.  *
  1126.  * \param sheet  The stylesheet context
  1127.  * \param rule   The rule to destroy
  1128.  * \return CSS_OK on success, appropriate error otherwise
  1129.  */
  1130. css_error css__stylesheet_rule_destroy(css_stylesheet *sheet, css_rule *rule)
  1131. {
  1132.         if (sheet == NULL || rule == NULL)
  1133.                 return CSS_BADPARM;
  1134.  
  1135.         /* Must be detached from parent/siblings */
  1136.         assert(rule->parent == NULL && rule->next == NULL &&
  1137.                         rule->prev == NULL);
  1138.  
  1139.         /* Destroy type-specific contents */
  1140.         switch (rule->type) {
  1141.         case CSS_RULE_UNKNOWN:
  1142.                 break;
  1143.         case CSS_RULE_SELECTOR:
  1144.         {
  1145.                 css_rule_selector *s = (css_rule_selector *) rule;
  1146.                 uint32_t i;
  1147.  
  1148.                 for (i = 0; i < rule->items; i++) {
  1149.                         css_selector *sel = s->selectors[i];
  1150.  
  1151.                         /* Detach from rule */
  1152.                         sel->rule = NULL;
  1153.  
  1154.                         css__stylesheet_selector_destroy(sheet, sel);
  1155.                 }
  1156.  
  1157.                 if (s->selectors != NULL)
  1158.                         sheet->alloc(s->selectors, 0, sheet->pw);
  1159.  
  1160.                 if (s->style != NULL)
  1161.                         css__stylesheet_style_destroy(s->style);
  1162.         }
  1163.                 break;
  1164.         case CSS_RULE_CHARSET:
  1165.         {
  1166.                 css_rule_charset *charset = (css_rule_charset *) rule;
  1167.                 lwc_string_unref(charset->encoding);
  1168.         }
  1169.                 break;
  1170.         case CSS_RULE_IMPORT:
  1171.         {
  1172.                 css_rule_import *import = (css_rule_import *) rule;
  1173.                
  1174.                 lwc_string_unref(import->url);
  1175.                
  1176.                 /* Do not destroy imported sheet: it is owned by the client */
  1177.         }
  1178.                 break;
  1179.         case CSS_RULE_MEDIA:
  1180.         {
  1181.                 css_rule_media *media = (css_rule_media *) rule;
  1182.                 css_rule *c, *d;
  1183.  
  1184.                 for (c = media->first_child; c != NULL; c = d) {
  1185.                         d = c->next;
  1186.  
  1187.                         /* Detach from list */
  1188.                         c->parent = NULL;
  1189.                         c->prev = NULL;
  1190.                         c->next = NULL;
  1191.  
  1192.                         css__stylesheet_rule_destroy(sheet, c);
  1193.                 }
  1194.         }
  1195.                 break;
  1196.         case CSS_RULE_FONT_FACE:
  1197.         {
  1198.                 css_rule_font_face *font_face_r = (css_rule_font_face *) rule;
  1199.  
  1200.                 if (font_face_r->font_face != NULL)
  1201.                         css__font_face_destroy(font_face_r->font_face);
  1202.         }
  1203.                 break;
  1204.         case CSS_RULE_PAGE:
  1205.         {
  1206.                 css_rule_page *page = (css_rule_page *) rule;
  1207.  
  1208.                 if (page->selector != NULL) {
  1209.                         page->selector->rule = NULL;
  1210.                         css__stylesheet_selector_destroy(sheet, page->selector);
  1211.                 }
  1212.  
  1213.                 if (page->style != NULL)
  1214.                         css__stylesheet_style_destroy(page->style);
  1215.         }
  1216.                 break;
  1217.         }
  1218.  
  1219.         /* Destroy rule */
  1220.         sheet->alloc(rule, 0, sheet->pw);
  1221.  
  1222.         return CSS_OK;
  1223. }
  1224.  
  1225. /**
  1226.  * Add a selector to a CSS rule
  1227.  *
  1228.  * \param sheet     The stylesheet context
  1229.  * \param rule      The rule to add to (must be of type CSS_RULE_SELECTOR)
  1230.  * \param selector  The selector to add
  1231.  * \return CSS_OK on success, appropriate error otherwise
  1232.  */
  1233. css_error css__stylesheet_rule_add_selector(css_stylesheet *sheet,
  1234.                 css_rule *rule, css_selector *selector)
  1235. {
  1236.         css_rule_selector *r = (css_rule_selector *) rule;
  1237.         css_selector **sels;
  1238.  
  1239.         if (sheet == NULL || rule == NULL || selector == NULL)
  1240.                 return CSS_BADPARM;
  1241.  
  1242.         /* Ensure rule is a CSS_RULE_SELECTOR */
  1243.         assert(rule->type == CSS_RULE_SELECTOR);
  1244.  
  1245.         sels = sheet->alloc(r->selectors,
  1246.                         (r->base.items + 1) * sizeof(css_selector *),
  1247.                         sheet->pw);
  1248.         if (sels == NULL)
  1249.                 return CSS_NOMEM;
  1250.  
  1251.         /* Insert into rule's selector list */
  1252.         sels[r->base.items] = selector;
  1253.         r->base.items++;
  1254.         r->selectors = sels;
  1255.  
  1256.         /* Set selector's rule field */
  1257.         selector->rule = rule;
  1258.        
  1259.         return CSS_OK;
  1260. }
  1261.  
  1262.  
  1263. /**
  1264.  * Append a style to a CSS rule
  1265.  *
  1266.  * \param sheet  The stylesheet context
  1267.  * \param rule   The rule to add to (must be CSS_RULE_SELECTOR or CSS_RULE_PAGE)
  1268.  * \param style  The style to add
  1269.  * \return CSS_OK on success, appropriate error otherwise
  1270.  */
  1271. css_error css__stylesheet_rule_append_style(css_stylesheet *sheet,
  1272.                 css_rule *rule, css_style *style)
  1273. {
  1274.         css_style *current_style;
  1275.         css_error error;
  1276.  
  1277.         if (sheet == NULL || rule == NULL || style == NULL)
  1278.                 return CSS_BADPARM;
  1279.  
  1280.         assert(rule->type == CSS_RULE_SELECTOR || rule->type == CSS_RULE_PAGE);
  1281.  
  1282.         if (rule->type == CSS_RULE_SELECTOR)
  1283.                 current_style = ((css_rule_selector *) rule)->style;
  1284.         else
  1285.                 current_style = ((css_rule_page *) rule)->style;
  1286.  
  1287.         if (current_style != NULL) {
  1288.                 error = css__stylesheet_merge_style(current_style, style);
  1289.  
  1290.                 if (error != CSS_OK)
  1291.                         return error;
  1292.  
  1293.                 /* Done with style */
  1294.                 css__stylesheet_style_destroy(style);
  1295.         } else {
  1296.                 /* No current style, so use this one */
  1297.                 current_style = style;
  1298.  
  1299.                 /* Add to the sheet's size */
  1300.                 sheet->size += (style->used * sizeof(css_code_t));
  1301.         }
  1302.  
  1303.         if (rule->type == CSS_RULE_SELECTOR)
  1304.                 ((css_rule_selector *) rule)->style = current_style;
  1305.         else
  1306.                 ((css_rule_page *) rule)->style = current_style;
  1307.  
  1308.         return CSS_OK;
  1309. }
  1310.  
  1311. /**
  1312.  * Set the charset of a CSS rule
  1313.  *
  1314.  * \param sheet    The stylesheet context
  1315.  * \param rule     The rule to add to (must be of type CSS_RULE_CHARSET)
  1316.  * \param charset  The charset
  1317.  * \return CSS_OK on success, appropriate error otherwise
  1318.  */
  1319. css_error css__stylesheet_rule_set_charset(css_stylesheet *sheet,
  1320.                 css_rule *rule, lwc_string *charset)
  1321. {
  1322.         css_rule_charset *r = (css_rule_charset *) rule;
  1323.  
  1324.         if (sheet == NULL || rule == NULL || charset == NULL)
  1325.                 return CSS_BADPARM;
  1326.  
  1327.         /* Ensure rule is a CSS_RULE_CHARSET */
  1328.         assert(rule->type == CSS_RULE_CHARSET);
  1329.  
  1330.         /* Set rule's encoding field */
  1331.         r->encoding = lwc_string_ref(charset);
  1332.        
  1333.         return CSS_OK;
  1334. }
  1335.  
  1336.  
  1337. /**
  1338.  * Set the necessary data to import a stylesheet associated with a rule
  1339.  *
  1340.  * \param sheet   The stylesheet context
  1341.  * \param rule    The rule to add to (must be of type CSS_RULE_IMPORT)
  1342.  * \param url     The URL of the imported stylesheet
  1343.  * \param media   The applicable media types for the imported stylesheet
  1344.  * \return CSS_OK on success, appropriate error otherwise
  1345.  */
  1346. css_error css__stylesheet_rule_set_nascent_import(css_stylesheet *sheet,
  1347.                 css_rule *rule, lwc_string *url,
  1348.                 uint64_t media)
  1349. {
  1350.         css_rule_import *r = (css_rule_import *) rule;
  1351.  
  1352.         if (sheet == NULL || rule == NULL || url == NULL)
  1353.                 return CSS_BADPARM;
  1354.  
  1355.         /* Ensure rule is a CSS_RULE_IMPORT */
  1356.         assert(rule->type == CSS_RULE_IMPORT);
  1357.  
  1358.         /* Set the rule's sheet field */
  1359.         r->url = lwc_string_ref(url);
  1360.         r->media = media;
  1361.  
  1362.         return CSS_OK;
  1363. }
  1364.  
  1365. /**
  1366.  * Set the media of an @media rule
  1367.  *
  1368.  * \param sheet  The stylesheet context
  1369.  * \param rule   The rule to add to (must be of type CSS_RULE_MEDIA)
  1370.  * \param media  The applicable media types for the rule
  1371.  * \return CSS_OK on success, appropriate error otherwise
  1372.  */
  1373. css_error css__stylesheet_rule_set_media(css_stylesheet *sheet,
  1374.                 css_rule *rule, uint64_t media)
  1375. {
  1376.         css_rule_media *r = (css_rule_media *) rule;
  1377.  
  1378.         if (sheet == NULL || rule == NULL)
  1379.                 return CSS_BADPARM;
  1380.  
  1381.         /* Ensure rule is a CSS_RULE_MEDIA */
  1382.         assert(rule->type == CSS_RULE_MEDIA);
  1383.  
  1384.         /* Set the rule's media */
  1385.         r->media = media;
  1386.  
  1387.         return CSS_OK;
  1388. }
  1389.  
  1390. /**
  1391.  * Set an @page rule selector
  1392.  *
  1393.  * \param sheet     The stylesheet context
  1394.  * \param rule      The rule to add to (must be of type CSS_RULE_PAGE)
  1395.  * \param selector  The page selector
  1396.  * \return CSS_OK on success, appropriate error otherwise
  1397.  */
  1398. css_error css__stylesheet_rule_set_page_selector(css_stylesheet *sheet,
  1399.                 css_rule *rule, css_selector *selector)
  1400. {
  1401.         css_rule_page *r = (css_rule_page *) rule;
  1402.  
  1403.         if (sheet == NULL || rule == NULL || selector == NULL)
  1404.                 return CSS_BADPARM;
  1405.  
  1406.         /* Ensure rule is a CSS_RULE_PAGE */
  1407.         assert(rule->type == CSS_RULE_PAGE);
  1408.  
  1409.         /** \todo validate selector */
  1410.  
  1411.         /* Set the rule's selector */
  1412.         r->selector = selector;
  1413.  
  1414.         /* Set selector's rule field */
  1415.         selector->rule = rule;
  1416.  
  1417.         return CSS_OK;
  1418. }
  1419.  
  1420. /**
  1421.  * Add a rule to a stylesheet
  1422.  *
  1423.  * \param sheet   The stylesheet to add to
  1424.  * \param rule    The rule to add
  1425.  * \param parent  The parent rule, or NULL for a top-level rule
  1426.  * \return CSS_OK on success, appropriate error otherwise
  1427.  */
  1428. css_error css__stylesheet_add_rule(css_stylesheet *sheet, css_rule *rule,
  1429.                 css_rule *parent)
  1430. {
  1431.         css_error error;
  1432.  
  1433.         if (sheet == NULL || rule == NULL)
  1434.                 return CSS_BADPARM;
  1435.  
  1436.         /* Need to fill in rule's index field before adding selectors
  1437.          * because selector chains consider the rule index for sort order
  1438.          */
  1439.         rule->index = sheet->rule_count;
  1440.  
  1441.         /* Add any selectors to the hash */
  1442.         error = _add_selectors(sheet, rule);
  1443.         if (error != CSS_OK)
  1444.                 return error;
  1445.  
  1446.         /* Add to the sheet's size */
  1447.         sheet->size += _rule_size(rule);
  1448.  
  1449.         if (parent != NULL) {
  1450.                 css_rule_media *media = (css_rule_media *) parent;
  1451.  
  1452.                 /* Parent must be an @media rule, or NULL */
  1453.                 assert(parent->type == CSS_RULE_MEDIA);
  1454.  
  1455.                 /* Add rule to parent */
  1456.                 rule->ptype = CSS_RULE_PARENT_RULE;
  1457.                 rule->parent = parent;
  1458.                 sheet->rule_count++;
  1459.  
  1460.                 if (media->last_child == NULL) {
  1461.                         rule->prev = rule->next = NULL;
  1462.                         media->first_child = media->last_child = rule;
  1463.                 } else {
  1464.                         media->last_child->next = rule;
  1465.                         rule->prev = media->last_child;
  1466.                         rule->next = NULL;
  1467.                         media->last_child = rule;
  1468.                 }
  1469.         } else {
  1470.                 /* Add rule to sheet */
  1471.                 rule->ptype = CSS_RULE_PARENT_STYLESHEET;
  1472.                 rule->parent = sheet;
  1473.                 sheet->rule_count++;
  1474.  
  1475.                 if (sheet->last_rule == NULL) {
  1476.                         rule->prev = rule->next = NULL;
  1477.                         sheet->rule_list = sheet->last_rule = rule;
  1478.                 } else {
  1479.                         sheet->last_rule->next = rule;
  1480.                         rule->prev = sheet->last_rule;
  1481.                         rule->next = NULL;
  1482.                         sheet->last_rule = rule;
  1483.                 }
  1484.         }
  1485.  
  1486.         /** \todo needs to trigger some event announcing styles have changed */
  1487.  
  1488.         return CSS_OK;
  1489. }
  1490.  
  1491. /**
  1492.  * Remove a rule from a stylesheet
  1493.  *
  1494.  * \param sheet  The sheet to remove from
  1495.  * \param rule   The rule to remove
  1496.  * \return CSS_OK on success, appropriate error otherwise
  1497.  */
  1498. css_error css__stylesheet_remove_rule(css_stylesheet *sheet, css_rule *rule)
  1499. {
  1500.         css_error error;
  1501.  
  1502.         if (sheet == NULL || rule == NULL)
  1503.                 return CSS_BADPARM;
  1504.  
  1505.         error = _remove_selectors(sheet, rule);
  1506.         if (error != CSS_OK)
  1507.                 return error;
  1508.  
  1509.         /* Reduce sheet's size */
  1510.         sheet->size -= _rule_size(rule);
  1511.  
  1512.         if (rule->next == NULL)
  1513.                 sheet->last_rule = rule->prev;
  1514.         else
  1515.                 rule->next->prev = rule->prev;
  1516.  
  1517.         if (rule->prev == NULL)
  1518.                 sheet->rule_list = rule->next;
  1519.         else
  1520.                 rule->prev->next = rule->next;
  1521.  
  1522.         /* Invalidate linkage fields */
  1523.         rule->parent = NULL;
  1524.         rule->prev = NULL;
  1525.         rule->next = NULL;
  1526.  
  1527.         /**\ todo renumber subsequent rules? may not be necessary, as there's
  1528.          * only an expectation that rules which occur later in the stylesheet
  1529.          * have a higher index than those that appear earlier. There's no
  1530.          * guarantee that the number space is continuous. */
  1531.  
  1532.         /** \todo needs to trigger some event announcing styles have changed */
  1533.  
  1534.         return CSS_OK;
  1535. }
  1536.  
  1537. /******************************************************************************
  1538.  * Private API below here                                                     *
  1539.  ******************************************************************************/
  1540.  
  1541. /**
  1542.  * Add selectors in a rule to the hash
  1543.  *
  1544.  * \param sheet  Stylesheet containing hash
  1545.  * \param rule   Rule to consider
  1546.  * \return CSS_OK on success, appropriate error otherwise
  1547.  */
  1548. css_error _add_selectors(css_stylesheet *sheet, css_rule *rule)
  1549. {
  1550.         css_error error;
  1551.  
  1552.         if (sheet == NULL || rule == NULL)
  1553.                 return CSS_BADPARM;
  1554.  
  1555.         /* Rule must not be in sheet */
  1556.         assert(rule->parent == NULL);
  1557.  
  1558.         switch (rule->type) {
  1559.         case CSS_RULE_SELECTOR:
  1560.         {
  1561.                 css_rule_selector *s = (css_rule_selector *) rule;
  1562.                 int32_t i;
  1563.  
  1564.                 for (i = 0; i < rule->items; i++) {
  1565.                         css_selector *sel = s->selectors[i];
  1566.  
  1567.                         error = css__selector_hash_insert(sheet->selectors, sel);
  1568.                         if (error != CSS_OK) {
  1569.                                 /* Failed, revert our changes */
  1570.                                 for (i--; i >= 0; i--) {
  1571.                                         sel = s->selectors[i];
  1572.  
  1573.                                         /* Ignore errors */
  1574.                                         css__selector_hash_remove(
  1575.                                                         sheet->selectors, sel);
  1576.                                 }
  1577.                                
  1578.                                 return error;
  1579.                         }
  1580.                 }
  1581.         }
  1582.                 break;
  1583.         case CSS_RULE_MEDIA:
  1584.         {
  1585.                 css_rule_media *m = (css_rule_media *) rule;
  1586.                 css_rule *r;
  1587.  
  1588.                 for (r = m->first_child; r != NULL; r = r->next) {
  1589.                         error = _add_selectors(sheet, r);
  1590.                         if (error != CSS_OK) {
  1591.                                 /* Failed, revert our changes */
  1592.                                 for (r = r->prev; r != NULL; r = r->prev) {
  1593.                                         _remove_selectors(sheet, r);
  1594.                                 }
  1595.  
  1596.                                 return error;
  1597.                         }
  1598.                 }
  1599.         }
  1600.                 break;
  1601.         }
  1602.  
  1603.         return CSS_OK;
  1604. }
  1605.  
  1606. /**
  1607.  * Remove selectors in a rule from the hash
  1608.  *
  1609.  * \param sheet  Stylesheet containing hash
  1610.  * \param rule   Rule to consider
  1611.  * \return CSS_OK on success, appropriate error otherwise
  1612.  */
  1613. css_error _remove_selectors(css_stylesheet *sheet, css_rule *rule)
  1614. {
  1615.         css_error error;
  1616.  
  1617.         if (sheet == NULL || rule == NULL)
  1618.                 return CSS_BADPARM;
  1619.  
  1620.         switch (rule->type) {
  1621.         case CSS_RULE_SELECTOR:
  1622.         {
  1623.                 css_rule_selector *s = (css_rule_selector *) rule;
  1624.                 int32_t i;
  1625.  
  1626.                 for (i = 0; i < rule->items; i++) {
  1627.                         css_selector *sel = s->selectors[i];
  1628.  
  1629.                         error = css__selector_hash_remove(sheet->selectors, sel);
  1630.                         if (error != CSS_OK)
  1631.                                 return error;
  1632.                 }
  1633.         }
  1634.                 break;
  1635.         case CSS_RULE_MEDIA:
  1636.         {
  1637.                 css_rule_media *m = (css_rule_media *) rule;
  1638.                 css_rule *r;
  1639.  
  1640.                 for (r = m->first_child; r != NULL; r = r->next) {
  1641.                         error = _remove_selectors(sheet, r);
  1642.                         if (error != CSS_OK)
  1643.                                 return error;
  1644.                 }
  1645.         }
  1646.                 break;
  1647.         }
  1648.  
  1649.         return CSS_OK;
  1650. }
  1651.  
  1652. /**
  1653.  * Calculate the size of a rule
  1654.  *
  1655.  * \param r  Rule to consider
  1656.  * \return Size in bytes
  1657.  *
  1658.  * \note The returned size does not include interned strings.
  1659.  */
  1660. size_t _rule_size(const css_rule *r)
  1661. {
  1662.         size_t bytes = 0;
  1663.  
  1664.         if (r->type == CSS_RULE_SELECTOR) {
  1665.                 const css_rule_selector *rs = (const css_rule_selector *) r;
  1666.                 uint32_t i;
  1667.  
  1668.                 bytes += sizeof(css_rule_selector);
  1669.  
  1670.                 /* Process selector chains */
  1671.                 bytes += r->items * sizeof(css_selector *);
  1672.                 for (i = 0; i < r->items; i++) {
  1673.                         const css_selector *s = rs->selectors[i];
  1674.  
  1675.                         do {
  1676.                                 const css_selector_detail *d = &s->data;
  1677.  
  1678.                                 bytes += sizeof(css_selector);
  1679.  
  1680.                                 while (d->next) {
  1681.                                         bytes += sizeof(css_selector_detail);
  1682.                                         d++;
  1683.                                 }
  1684.  
  1685.                                 s = s->combinator;
  1686.                         } while (s != NULL);
  1687.                 }
  1688.  
  1689.                 if (rs->style != NULL)
  1690.                         bytes += (rs->style->used * sizeof(css_code_t));
  1691.         } else if (r->type == CSS_RULE_CHARSET) {
  1692.                 bytes += sizeof(css_rule_charset);
  1693.         } else if (r->type == CSS_RULE_IMPORT) {
  1694.                 bytes += sizeof(css_rule_import);
  1695.         } else if (r->type == CSS_RULE_MEDIA) {
  1696.                 const css_rule_media *rm = (const css_rule_media *) r;
  1697.                 const css_rule *c;
  1698.  
  1699.                 bytes += sizeof(css_rule_media);
  1700.  
  1701.                 /* Process children */
  1702.                 for (c = rm->first_child; c != NULL; c = c->next)
  1703.                         bytes += _rule_size(c);
  1704.         } else if (r->type == CSS_RULE_FONT_FACE) {
  1705.                 const css_rule_font_face *rf = (const css_rule_font_face *) r;
  1706.  
  1707.                 bytes += sizeof(css_rule_font_face);
  1708.  
  1709.                 if (rf->font_face != NULL)
  1710.                         bytes += sizeof(css_font_face);
  1711.         } else if (r->type == CSS_RULE_PAGE) {
  1712.                 const css_rule_page *rp = (const css_rule_page *) r;
  1713.                 const css_selector *s = rp->selector;
  1714.  
  1715.                 /* Process selector chain */
  1716.                 while (s != NULL) {
  1717.                         const css_selector_detail *d = &s->data;
  1718.  
  1719.                         bytes += sizeof(css_selector);
  1720.  
  1721.                         while (d->next) {
  1722.                                 bytes += sizeof(css_selector_detail);
  1723.                                 d++;
  1724.                         }
  1725.  
  1726.                         s = s->combinator;     
  1727.                 }
  1728.  
  1729.                 if (rp->style != NULL)
  1730.                         bytes += (rp->style->used * sizeof(css_code_t));
  1731.         }
  1732.  
  1733.         return bytes;
  1734. }
  1735.