Subversion Repositories Kolibri OS

Rev

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

  1. #include <ctype.h>
  2. #include <inttypes.h>
  3. #include <stdbool.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7.  
  8. #include <libcss/libcss.h>
  9. #include <libcss/computed.h>
  10. #include <libcss/select.h>
  11. #include <libcss/stylesheet.h>
  12.  
  13. #include "utils/utils.h"
  14.  
  15. #include "dump_computed.h"
  16. #include "testutils.h"
  17.  
  18. typedef struct attribute {
  19.         lwc_string *name;
  20.         lwc_string *value;
  21. } attribute;
  22.  
  23. typedef struct node {
  24.         lwc_string *name;
  25.  
  26.         uint32_t n_attrs;
  27.         attribute *attrs;
  28.  
  29.         struct node *parent;
  30.         struct node *next;
  31.         struct node *prev;
  32.         struct node *children;
  33.         struct node *last_child;
  34. } node;
  35.  
  36. typedef struct sheet_ctx {
  37.         css_stylesheet *sheet;
  38.         css_origin origin;
  39.         uint64_t media;
  40. } sheet_ctx;
  41.  
  42. typedef struct line_ctx {
  43.         size_t explen;
  44.         size_t expused;
  45.         char *exp;
  46.  
  47.         bool intree;
  48.         bool insheet;
  49.         bool inerrors;
  50.         bool inexp;
  51.  
  52.         node *tree;
  53.         node *current;
  54.         uint32_t depth;
  55.  
  56.         uint32_t n_sheets;
  57.         sheet_ctx *sheets;
  58.  
  59.         uint64_t media;
  60.         uint32_t pseudo_element;
  61.         node *target;
  62.        
  63.         lwc_string *attr_class;
  64.         lwc_string *attr_id;
  65. } line_ctx;
  66.  
  67. static bool handle_line(const char *data, size_t datalen, void *pw);
  68. static void css__parse_tree(line_ctx *ctx, const char *data, size_t len);
  69. static void css__parse_tree_data(line_ctx *ctx, const char *data, size_t len);
  70. static void css__parse_sheet(line_ctx *ctx, const char *data, size_t len);
  71. static void css__parse_media_list(const char **data, size_t *len, uint64_t *media);
  72. static void css__parse_pseudo_list(const char **data, size_t *len,
  73.                 uint32_t *element);
  74. static void css__parse_expected(line_ctx *ctx, const char *data, size_t len);
  75. static void run_test(line_ctx *ctx, const char *exp, size_t explen);
  76. static void destroy_tree(node *root);
  77.  
  78. static css_error node_name(void *pw, void *node,
  79.                 css_qname *qname);
  80. static css_error node_classes(void *pw, void *node,
  81.                 lwc_string ***classes, uint32_t *n_classes);
  82. static css_error node_id(void *pw, void *node,
  83.                 lwc_string **id);
  84. static css_error named_ancestor_node(void *pw, void *node,
  85.                 const css_qname *qname,
  86.                 void **ancestor);
  87. static css_error named_parent_node(void *pw, void *node,
  88.                 const css_qname *qname,
  89.                 void **parent);
  90. static css_error named_sibling_node(void *pw, void *node,
  91.                 const css_qname *qname,
  92.                 void **sibling);
  93. static css_error named_generic_sibling_node(void *pw, void *node,
  94.                 const css_qname *qname,
  95.                 void **sibling);
  96. static css_error parent_node(void *pw, void *node, void **parent);
  97. static css_error sibling_node(void *pw, void *node, void **sibling);
  98. static css_error node_has_name(void *pw, void *node,
  99.                 const css_qname *qname,
  100.                 bool *match);
  101. static css_error node_has_class(void *pw, void *node,
  102.                 lwc_string *name,
  103.                 bool *match);
  104. static css_error node_has_id(void *pw, void *node,
  105.                 lwc_string *name,
  106.                 bool *match);
  107. static css_error node_has_attribute(void *pw, void *node,
  108.                 const css_qname *qname,
  109.                 bool *match);
  110. static css_error node_has_attribute_equal(void *pw, void *node,
  111.                 const css_qname *qname,
  112.                 lwc_string *value,
  113.                 bool *match);
  114. static css_error node_has_attribute_dashmatch(void *pw, void *node,
  115.                 const css_qname *qname,
  116.                 lwc_string *value,
  117.                 bool *match);
  118. static css_error node_has_attribute_includes(void *pw, void *node,
  119.                 const css_qname *qname,
  120.                 lwc_string *value,
  121.                 bool *match);
  122. static css_error node_has_attribute_prefix(void *pw, void *node,
  123.                 const css_qname *qname,
  124.                 lwc_string *value,
  125.                 bool *match);
  126. static css_error node_has_attribute_suffix(void *pw, void *node,
  127.                 const css_qname *qname,
  128.                 lwc_string *value,
  129.                 bool *match);
  130. static css_error node_has_attribute_substring(void *pw, void *node,
  131.                 const css_qname *qname,
  132.                 lwc_string *value,
  133.                 bool *match);
  134. static css_error node_is_root(void *pw, void *node, bool *match);
  135. static css_error node_count_siblings(void *pw, void *node,
  136.                 bool same_name, bool after, int32_t *count);
  137. static css_error node_is_empty(void *pw, void *node, bool *match);
  138. static css_error node_is_link(void *pw, void *node, bool *match);
  139. static css_error node_is_visited(void *pw, void *node, bool *match);
  140. static css_error node_is_hover(void *pw, void *node, bool *match);
  141. static css_error node_is_active(void *pw, void *node, bool *match);
  142. static css_error node_is_focus(void *pw, void *node, bool *match);
  143. static css_error node_is_enabled(void *pw, void *node, bool *match);
  144. static css_error node_is_disabled(void *pw, void *node, bool *match);
  145. static css_error node_is_checked(void *pw, void *node, bool *match);
  146. static css_error node_is_target(void *pw, void *node, bool *match);
  147. static css_error node_is_lang(void *pw, void *node,
  148.                 lwc_string *lang, bool *match);
  149. static css_error node_presentational_hint(void *pw, void *node,
  150.                 uint32_t property, css_hint *hint);
  151. static css_error ua_default_for_property(void *pw, uint32_t property,
  152.                 css_hint *hint);
  153. static css_error compute_font_size(void *pw, const css_hint *parent,
  154.                 css_hint *size);
  155.  
  156. static css_select_handler select_handler = {
  157.         CSS_SELECT_HANDLER_VERSION_1,
  158.  
  159.         node_name,
  160.         node_classes,
  161.         node_id,
  162.         named_ancestor_node,
  163.         named_parent_node,
  164.         named_sibling_node,
  165.         named_generic_sibling_node,
  166.         parent_node,
  167.         sibling_node,
  168.         node_has_name,
  169.         node_has_class,
  170.         node_has_id,
  171.         node_has_attribute,
  172.         node_has_attribute_equal,
  173.         node_has_attribute_dashmatch,
  174.         node_has_attribute_includes,
  175.         node_has_attribute_prefix,
  176.         node_has_attribute_suffix,
  177.         node_has_attribute_substring,
  178.         node_is_root,
  179.         node_count_siblings,
  180.         node_is_empty,
  181.         node_is_link,
  182.         node_is_visited,
  183.         node_is_hover,
  184.         node_is_active,
  185.         node_is_focus,
  186.         node_is_enabled,
  187.         node_is_disabled,
  188.         node_is_checked,
  189.         node_is_target,
  190.         node_is_lang,
  191.         node_presentational_hint,
  192.         ua_default_for_property,
  193.         compute_font_size
  194. };
  195.  
  196. static void *myrealloc(void *data, size_t len, void *pw)
  197. {
  198.         UNUSED(pw);
  199.  
  200.         return realloc(data, len);
  201. }
  202.  
  203. static css_error resolve_url(void *pw,
  204.                 const char *base, lwc_string *rel, lwc_string **abs)
  205. {
  206.         UNUSED(pw);
  207.         UNUSED(base);
  208.  
  209.         /* About as useless as possible */
  210.         *abs = lwc_string_ref(rel);
  211.  
  212.         return CSS_OK;
  213. }
  214.  
  215. static bool fail_because_lwc_leaked = false;
  216.  
  217. static void
  218. printing_lwc_iterator(lwc_string *str, void *pw)
  219. {
  220.         UNUSED(pw);
  221.        
  222.         printf(" DICT: %*s\n", (int)(lwc_string_length(str)), lwc_string_data(str));
  223.         fail_because_lwc_leaked = true;
  224. }
  225.  
  226. int main(int argc, char **argv)
  227. {
  228.         line_ctx ctx;
  229.        
  230.         if (argc != 2) {
  231.                 printf("Usage: %s <filename>\n", argv[0]);
  232.                 return 1;
  233.         }
  234.  
  235.         memset(&ctx, 0, sizeof(ctx));
  236.  
  237.  
  238.         lwc_intern_string("class", SLEN("class"), &ctx.attr_class);
  239.         lwc_intern_string("id", SLEN("id"), &ctx.attr_id);
  240.        
  241.         assert(css__parse_testfile(argv[1], handle_line, &ctx) == true);
  242.        
  243.         /* and run final test */
  244.         if (ctx.tree != NULL)
  245.                 run_test(&ctx, ctx.exp, ctx.expused);
  246.  
  247.         free(ctx.exp);
  248.        
  249.         lwc_string_unref(ctx.attr_class);
  250.         lwc_string_unref(ctx.attr_id);
  251.        
  252.         lwc_iterate_strings(printing_lwc_iterator, NULL);
  253.        
  254.         assert(fail_because_lwc_leaked == false);
  255.        
  256.         printf("PASS\n");
  257.         return 0;
  258. }
  259.  
  260. bool handle_line(const char *data, size_t datalen, void *pw)
  261. {
  262.         line_ctx *ctx = (line_ctx *) pw;
  263.         css_error error;
  264.  
  265.         if (data[0] == '#') {
  266.                 if (ctx->intree) {
  267.                         if (strncasecmp(data+1, "errors", 6) == 0) {
  268.                                 ctx->intree = false;
  269.                                 ctx->insheet = false;
  270.                                 ctx->inerrors = true;
  271.                                 ctx->inexp = false;
  272.                         } else {
  273.                                 /* Assume start of stylesheet */
  274.                                 css__parse_sheet(ctx, data + 1, datalen - 1);
  275.  
  276.                                 ctx->intree = false;
  277.                                 ctx->insheet = true;
  278.                                 ctx->inerrors = false;
  279.                                 ctx->inexp = false;
  280.                         }
  281.                 } else if (ctx->insheet) {
  282.                         if (strncasecmp(data+1, "errors", 6) == 0) {
  283.                                 assert(css_stylesheet_data_done(
  284.                                         ctx->sheets[ctx->n_sheets - 1].sheet)
  285.                                         == CSS_OK);
  286.  
  287.                                 ctx->intree = false;
  288.                                 ctx->insheet = false;
  289.                                 ctx->inerrors = true;
  290.                                 ctx->inexp = false;
  291.                         } else if (strncasecmp(data+1, "ua", 2) == 0 ||
  292.                                         strncasecmp(data+1, "user", 4) == 0 ||
  293.                                         strncasecmp(data+1, "author", 6) == 0) {
  294.                                 assert(css_stylesheet_data_done(
  295.                                         ctx->sheets[ctx->n_sheets - 1].sheet)
  296.                                         == CSS_OK);
  297.  
  298.                                 css__parse_sheet(ctx, data + 1, datalen - 1);
  299.                         } else {
  300.                                 error = css_stylesheet_append_data(
  301.                                         ctx->sheets[ctx->n_sheets - 1].sheet,
  302.                                         (const uint8_t *) data,
  303.                                         datalen);
  304.                                 assert(error == CSS_OK ||
  305.                                                 error == CSS_NEEDDATA);
  306.                         }
  307.                 } else if (ctx->inerrors) {
  308.                         ctx->intree = false;
  309.                         ctx->insheet = false;
  310.                         ctx->inerrors = false;
  311.                         ctx->inexp = true;
  312.                 } else if (ctx->inexp) {
  313.                         /* This marks end of testcase, so run it */
  314.                         run_test(ctx, ctx->exp, ctx->expused);
  315.  
  316.                         ctx->expused = 0;
  317.  
  318.                         ctx->intree = false;
  319.                         ctx->insheet = false;
  320.                         ctx->inerrors = false;
  321.                         ctx->inexp = false;
  322.                 } else {
  323.                         /* Start state */
  324.                         if (strncasecmp(data+1, "tree", 4) == 0) {
  325.                                 css__parse_tree(ctx, data + 5, datalen - 5);
  326.  
  327.                                 ctx->intree = true;
  328.                                 ctx->insheet = false;
  329.                                 ctx->inerrors = false;
  330.                                 ctx->inexp = false;
  331.                         }
  332.                 }
  333.         } else {
  334.                 if (ctx->intree) {
  335.                         /* Not interested in the '|' */
  336.                         css__parse_tree_data(ctx, data + 1, datalen - 1);
  337.                 } else if (ctx->insheet) {
  338.                         error = css_stylesheet_append_data(
  339.                                         ctx->sheets[ctx->n_sheets - 1].sheet,
  340.                                         (const uint8_t *) data, datalen);
  341.                         assert(error == CSS_OK || error == CSS_NEEDDATA);
  342.                 } else if (ctx->inexp) {
  343.                         css__parse_expected(ctx, data, datalen);
  344.                 }
  345.         }
  346.  
  347.         return true;
  348. }
  349.  
  350. void css__parse_tree(line_ctx *ctx, const char *data, size_t len)
  351. {
  352.         const char *p = data;
  353.         const char *end = data + len;
  354.         size_t left;
  355.  
  356.         /* [ <media_list> <pseudo>? ] ? */
  357.  
  358.         ctx->media = CSS_MEDIA_ALL;
  359.         ctx->pseudo_element = CSS_PSEUDO_ELEMENT_NONE;
  360.  
  361.         /* Consume any leading whitespace */
  362.         while (p < end && isspace(*p))
  363.                 p++;
  364.  
  365.         if (p < end) {
  366.                 left = end - p;
  367.  
  368.                 css__parse_media_list(&p, &left, &ctx->media);
  369.  
  370.                 end = p + left;
  371.         }
  372.  
  373.         if (p < end) {
  374.                 left = end - p;
  375.  
  376.                 css__parse_pseudo_list(&p, &left, &ctx->pseudo_element);
  377.         }
  378. }
  379.  
  380. void css__parse_tree_data(line_ctx *ctx, const char *data, size_t len)
  381. {
  382.         const char *p = data;
  383.         const char *end = data + len;
  384.         const char *name = NULL;
  385.         const char *value = NULL;
  386.         size_t namelen = 0;
  387.         size_t valuelen = 0;
  388.         uint32_t depth = 0;
  389.         bool target = false;
  390.  
  391.         /* ' '{depth+1} [ <element> '*'? | <attr> ]
  392.          *
  393.          * <element> ::= [^=*[:space:]]+
  394.          * <attr>    ::= [^=*[:space:]]+ '=' [^[:space:]]*
  395.          */
  396.  
  397.         while (p < end && isspace(*p)) {
  398.                 depth++;
  399.                 p++;
  400.         }
  401.         depth--;
  402.  
  403.         /* Get element/attribute name */
  404.         name = p;
  405.         while (p < end && *p != '=' && *p != '*' && isspace(*p) == false) {
  406.                 namelen++;
  407.                 p++;
  408.         }
  409.  
  410.         /* Skip whitespace */
  411.         while (p < end && isspace(*p))
  412.                 p++;
  413.  
  414.         if (p < end && *p == '=') {
  415.                 /* Attribute value */
  416.                 p++;
  417.  
  418.                 value = p;
  419.  
  420.                 while (p < end && isspace(*p) == false) {
  421.                         valuelen++;
  422.                         p++;
  423.                 }
  424.         } else if (p < end && *p == '*') {
  425.                 /* Element is target node */
  426.                 target = true;
  427.         }
  428.  
  429.         if (value == NULL) {
  430.                 /* We have an element, so create it */
  431.                 node *n = malloc(sizeof(node));
  432.                 assert(n != NULL);
  433.  
  434.                 memset(n, 0, sizeof(node));
  435.                
  436.                 lwc_intern_string(name, namelen, &n->name);
  437.  
  438.                 /* Insert it into tree */
  439.                 if (ctx->tree == NULL) {
  440.                         ctx->tree = n;
  441.                 } else {
  442.                         assert(depth > 0);
  443.                         assert(depth <= ctx->depth + 1);
  444.  
  445.                         /* Find node to insert into */
  446.                         while (depth <= ctx->depth) {
  447.                                 ctx->depth--;
  448.                                 ctx->current = ctx->current->parent;
  449.                         }
  450.  
  451.                         /* Insert into current node */
  452.                         if (ctx->current->children == NULL) {
  453.                                 ctx->current->children = n;
  454.                                 ctx->current->last_child = n;
  455.                         } else {
  456.                                 ctx->current->last_child->next = n;
  457.                                 n->prev = ctx->current->last_child;
  458.  
  459.                                 ctx->current->last_child = n;
  460.                         }
  461.                         n->parent = ctx->current;
  462.                 }
  463.  
  464.                 ctx->current = n;
  465.                 ctx->depth = depth;
  466.  
  467.                 /* Mark the target, if it's us */
  468.                 if (target)
  469.                         ctx->target = n;
  470.         } else {
  471.                 /* New attribute */
  472.                 attribute *attr;
  473.  
  474.                 attribute *temp = realloc(ctx->current->attrs,
  475.                         (ctx->current->n_attrs + 1) * sizeof(attribute));
  476.                 assert(temp != NULL);
  477.  
  478.                 ctx->current->attrs = temp;
  479.  
  480.                 attr = &ctx->current->attrs[ctx->current->n_attrs];
  481.                
  482.                 lwc_intern_string(name, namelen, &attr->name);
  483.                 lwc_intern_string(value, valuelen, &attr->value);
  484.  
  485.                 ctx->current->n_attrs++;
  486.         }
  487. }
  488.  
  489. void css__parse_sheet(line_ctx *ctx, const char *data, size_t len)
  490. {
  491.         css_stylesheet_params params;
  492.         const char *p;
  493.         const char *end = data + len;
  494.         css_origin origin = CSS_ORIGIN_AUTHOR;
  495.         uint64_t media = CSS_MEDIA_ALL;
  496.         css_stylesheet *sheet;
  497.         sheet_ctx *temp;
  498.  
  499.         /* <origin> <media_list>? */
  500.  
  501.         /* Find end of origin */
  502.         for (p = data; p < end; p++) {
  503.                 if (isspace(*p))
  504.                         break;
  505.         }
  506.  
  507.         if (p - data == 6 && strncasecmp(data, "author", 6) == 0)
  508.                 origin = CSS_ORIGIN_AUTHOR;
  509.         else if (p - data == 4 && strncasecmp(data, "user", 4) == 0)
  510.                 origin = CSS_ORIGIN_USER;
  511.         else if (p - data == 2 && strncasecmp(data, "ua", 2) == 0)
  512.                 origin = CSS_ORIGIN_UA;
  513.         else
  514.                 assert(0 && "Unknown stylesheet origin");
  515.  
  516.         /* Skip any whitespace */
  517.         while (p < end && isspace(*p))
  518.                 p++;
  519.  
  520.         if (p < end) {
  521.                 size_t ignored = end - p;
  522.  
  523.                 css__parse_media_list(&p, &ignored, &media);
  524.         }
  525.  
  526.         params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
  527.         params.level = CSS_LEVEL_21;
  528.         params.charset = "UTF-8";
  529.         params.url = "foo";
  530.         params.title = "foo";
  531.         params.allow_quirks = false;
  532.         params.inline_style = false;
  533.         params.resolve = resolve_url;
  534.         params.resolve_pw = NULL;
  535.         params.import = NULL;
  536.         params.import_pw = NULL;
  537.         params.color = NULL;
  538.         params.color_pw = NULL;
  539.         params.font = NULL;
  540.         params.font_pw = NULL;
  541.  
  542.         /** \todo How are we going to handle @import? */
  543.         assert(css_stylesheet_create(&params, myrealloc, NULL,
  544.                         &sheet) == CSS_OK);
  545.  
  546.         /* Extend array of sheets and append new sheet to it */
  547.         temp = realloc(ctx->sheets,
  548.                         (ctx->n_sheets + 1) * sizeof(sheet_ctx));
  549.         assert(temp != NULL);
  550.  
  551.         ctx->sheets = temp;
  552.  
  553.         ctx->sheets[ctx->n_sheets].sheet = sheet;
  554.         ctx->sheets[ctx->n_sheets].origin = origin;
  555.         ctx->sheets[ctx->n_sheets].media = media;
  556.  
  557.         ctx->n_sheets++;
  558. }
  559.  
  560. void css__parse_media_list(const char **data, size_t *len, uint64_t *media)
  561. {
  562.         const char *p = *data;
  563.         const char *end = p + *len;
  564.         uint64_t result = 0;
  565.  
  566.         /* <medium> [ ',' <medium> ]* */
  567.  
  568.         while (p < end) {
  569.                 const char *start = p;
  570.  
  571.                 /* consume a medium */
  572.                 while (isspace(*p) == false && *p != ',')
  573.                         p++;
  574.  
  575.                 if (p - start == 10 &&
  576.                                 strncasecmp(start, "projection", 10) == 0)
  577.                         result |= CSS_MEDIA_PROJECTION;
  578.                 else if (p - start == 8 &&
  579.                                 strncasecmp(start, "handheld", 8) == 0)
  580.                         result |= CSS_MEDIA_HANDHELD;
  581.                 else if (p - start == 8 &&
  582.                                 strncasecmp(start, "embossed", 8) == 0)
  583.                         result |= CSS_MEDIA_EMBOSSED;
  584.                 else if (p - start == 7 &&
  585.                                 strncasecmp(start, "braille", 7) == 0)
  586.                         result |= CSS_MEDIA_BRAILLE;
  587.                 else if (p - start == 6 &&
  588.                                 strncasecmp(start, "speech", 6) == 0)
  589.                         result |= CSS_MEDIA_SPEECH;
  590.                 else if (p - start == 6 &&
  591.                                 strncasecmp(start, "screen", 6) == 0)
  592.                         result |= CSS_MEDIA_SCREEN;
  593.                 else if (p - start == 5 &&
  594.                                 strncasecmp(start, "print", 5) == 0)
  595.                         result |= CSS_MEDIA_PRINT;
  596.                 else if (p - start == 5 &&
  597.                                 strncasecmp(start, "aural", 5) == 0)
  598.                         result |= CSS_MEDIA_AURAL;
  599.                 else if (p - start == 3 &&
  600.                                 strncasecmp(start, "tty", 3) == 0)
  601.                         result |= CSS_MEDIA_TTY;
  602.                 else if (p - start == 3 &&
  603.                                 strncasecmp(start, "all", 3) == 0)
  604.                         result |= CSS_MEDIA_ALL;
  605.                 else if (p - start == 2 &&
  606.                                 strncasecmp(start, "tv", 2) == 0)
  607.                         result |= CSS_MEDIA_TV;
  608.                 else
  609.                         assert(0 && "Unknown media type");
  610.  
  611.                 /* Consume whitespace */
  612.                 while (p < end && isspace(*p))
  613.                         p++;
  614.  
  615.                 /* Stop if we've reached the end */
  616.                 if (p == end || *p != ',')
  617.                         break;
  618.  
  619.                 /* Consume comma */
  620.                 p++;
  621.  
  622.                 /* Consume whitespace */
  623.                 while (p < end && isspace(*p))
  624.                         p++;
  625.         }
  626.  
  627.         *media = result;
  628.  
  629.         *data = p;
  630.         *len = end - p;
  631. }
  632.  
  633. void css__parse_pseudo_list(const char **data, size_t *len, uint32_t *element)
  634. {
  635.         const char *p = *data;
  636.         const char *end = p + *len;
  637.  
  638.         /* <pseudo> [ ',' <pseudo> ]* */
  639.  
  640.         *element = CSS_PSEUDO_ELEMENT_NONE;
  641.  
  642.         while (p < end) {
  643.                 const char *start = p;
  644.  
  645.                 /* consume a pseudo */
  646.                 while (isspace(*p) == false && *p != ',')
  647.                         p++;
  648.  
  649.                 /* Pseudo elements */
  650.                 if (p - start == 12 &&
  651.                                 strncasecmp(start, "first-letter", 12) == 0)
  652.                         *element = CSS_PSEUDO_ELEMENT_FIRST_LETTER;
  653.                 else if (p - start == 10 &&
  654.                                 strncasecmp(start, "first-line", 10) == 0)
  655.                         *element = CSS_PSEUDO_ELEMENT_FIRST_LINE;
  656.                 else if (p - start == 6 &&
  657.                                 strncasecmp(start, "before", 6) == 0)
  658.                         *element = CSS_PSEUDO_ELEMENT_BEFORE;
  659.                 else if (p - start == 5 &&
  660.                                 strncasecmp(start, "after", 5) == 0)
  661.                         *element = CSS_PSEUDO_ELEMENT_AFTER;
  662.                 else
  663.                         assert(0 && "Unknown pseudo");
  664.  
  665.                 /* Consume whitespace */
  666.                 while (p < end && isspace(*p))
  667.                         p++;
  668.  
  669.                 /* Stop if we've reached the end */
  670.                 if (p == end || *p != ',')
  671.                         break;
  672.  
  673.                 /* Consume comma */
  674.                 p++;
  675.  
  676.                 /* Consume whitespace */
  677.                 while (p < end && isspace(*p))
  678.                         p++;
  679.         }
  680.  
  681.         *data = p;
  682.         *len = end - p;
  683. }
  684.  
  685. void css__parse_expected(line_ctx *ctx, const char *data, size_t len)
  686. {
  687.         while (ctx->expused + len >= ctx->explen) {
  688.                 size_t required = ctx->explen == 0 ? 64 : ctx->explen * 2;
  689.                 char *temp = realloc(ctx->exp, required);
  690.                 if (temp == NULL) {
  691.                         assert(0 && "No memory for expected output");
  692.                 }
  693.  
  694.                 ctx->exp = temp;
  695.                 ctx->explen = required;
  696.         }
  697.  
  698.         memcpy(ctx->exp + ctx->expused, data, len);
  699.  
  700.         ctx->expused += len;
  701. }
  702.  
  703. void run_test(line_ctx *ctx, const char *exp, size_t explen)
  704. {
  705.         css_select_ctx *select;
  706.         css_select_results *results;
  707.         uint32_t i;
  708.         char *buf;
  709.         size_t buflen;
  710.         static int testnum;
  711.  
  712.         UNUSED(exp);
  713.  
  714.         buf = malloc(8192);
  715.         if (buf == NULL) {
  716.                 assert(0 && "No memory for result data");
  717.         }
  718.         buflen = 8192;
  719.  
  720.         assert(css_select_ctx_create(myrealloc, NULL, &select) == CSS_OK);
  721.  
  722.         for (i = 0; i < ctx->n_sheets; i++) {
  723.                 assert(css_select_ctx_append_sheet(select,
  724.                                 ctx->sheets[i].sheet, ctx->sheets[i].origin,
  725.                                 ctx->sheets[i].media) == CSS_OK);
  726.         }
  727.  
  728.         testnum++;
  729.  
  730.         assert(css_select_style(select, ctx->target, ctx->media, NULL,
  731.                         &select_handler, ctx, &results) == CSS_OK);
  732.  
  733.         assert(results->styles[ctx->pseudo_element] != NULL);
  734.  
  735.         dump_computed_style(results->styles[ctx->pseudo_element], buf, &buflen);
  736.  
  737.         if (8192 - buflen != explen || memcmp(buf, exp, explen) != 0) {
  738.                 printf("Expected (%u):\n%.*s\n",
  739.                                 (int) explen, (int) explen, exp);
  740.                 printf("Result (%u):\n%.*s\n", (int) (8192 - buflen),
  741.                         (int) (8192 - buflen), buf);
  742.                 assert(0 && "Result doesn't match expected");
  743.         }
  744.  
  745.         /* Clean up */
  746.         css_select_results_destroy(results);
  747.         css_select_ctx_destroy(select);
  748.  
  749.         destroy_tree(ctx->tree);
  750.  
  751.         for (i = 0; i < ctx->n_sheets; i++) {
  752.                 css_stylesheet_destroy(ctx->sheets[i].sheet);
  753.         }
  754.  
  755.         ctx->tree = NULL;
  756.         ctx->current = NULL;
  757.         ctx->depth = 0;
  758.         ctx->n_sheets = 0;
  759.         free(ctx->sheets);
  760.         ctx->sheets = NULL;
  761.         ctx->target = NULL;
  762.  
  763.         free(buf);
  764.  
  765.         printf("Test %d: PASS\n", testnum);
  766. }
  767.  
  768. void destroy_tree(node *root)
  769. {
  770.         node *n, *p;
  771.         uint32_t i;
  772.  
  773.         for (n = root->children; n != NULL; n = p) {
  774.                 p = n->next;
  775.  
  776.                 destroy_tree(n);
  777.         }
  778.        
  779.         for (i = 0; i < root->n_attrs; ++i) {
  780.                 lwc_string_unref(root->attrs[i].name);
  781.                 lwc_string_unref(root->attrs[i].value);
  782.         }
  783.        
  784.         free(root->attrs);
  785.        
  786.         lwc_string_unref(root->name);
  787.         free(root);
  788. }
  789.  
  790.  
  791. css_error node_name(void *pw, void *n, css_qname *qname)
  792. {
  793.         node *node = n;
  794.  
  795.         UNUSED(pw);
  796.        
  797.         qname->name = lwc_string_ref(node->name);
  798.        
  799.         return CSS_OK;
  800. }
  801.  
  802. css_error node_classes(void *pw, void *n,
  803.                 lwc_string ***classes, uint32_t *n_classes)
  804. {
  805.         node *node = n;
  806.         uint32_t i;
  807.         line_ctx *lc = pw;
  808.  
  809.         for (i = 0; i < node->n_attrs; i++) {
  810.                 bool amatch = false;
  811.                 assert(lwc_string_caseless_isequal(
  812.                                 node->attrs[i].name, lc->attr_class, &amatch) ==
  813.                                 lwc_error_ok);
  814.                 if (amatch == true)
  815.                         break;
  816.         }
  817.  
  818.         if (i != node->n_attrs) {
  819.                 *classes = realloc(NULL, sizeof(lwc_string **));
  820.                 if (*classes == NULL)
  821.                         return CSS_NOMEM;
  822.  
  823.                 *(classes[0]) =
  824.                         lwc_string_ref(node->attrs[i].value);
  825.                 *n_classes = 1;
  826.         } else {
  827.                 *classes = NULL;
  828.                 *n_classes = 0;
  829.         }
  830.  
  831.         return CSS_OK;
  832.  
  833. }
  834.  
  835. css_error node_id(void *pw, void *n,
  836.                 lwc_string **id)
  837. {
  838.         node *node = n;
  839.         uint32_t i;
  840.         line_ctx *lc = pw;
  841.  
  842.         for (i = 0; i < node->n_attrs; i++) {
  843.                 bool amatch = false;
  844.                 assert(lwc_string_caseless_isequal(
  845.                                 node->attrs[i].name, lc->attr_id, &amatch) ==
  846.                                 lwc_error_ok);
  847.                 if (amatch == true)
  848.                         break;
  849.         }
  850.  
  851.         if (i != node->n_attrs)
  852.                 *id = lwc_string_ref(node->attrs[i].value);
  853.         else
  854.                 *id = NULL;
  855.  
  856.         return CSS_OK;
  857. }
  858.  
  859. css_error named_ancestor_node(void *pw, void *n,
  860.                 const css_qname *qname,
  861.                 void **ancestor)
  862. {
  863.         node *node = n;
  864.         UNUSED(pw);
  865.  
  866.         for (node = node->parent; node != NULL; node = node->parent) {
  867.                 bool match = false;
  868.                 assert(lwc_string_caseless_isequal(
  869.                                 qname->name, node->name,
  870.                                 &match) == lwc_error_ok);
  871.                 if (match == true)
  872.                         break;
  873.         }
  874.  
  875.         *ancestor = (void *) node;
  876.  
  877.         return CSS_OK;
  878. }
  879.  
  880. css_error named_parent_node(void *pw, void *n,
  881.                 const css_qname *qname,
  882.                 void **parent)
  883. {
  884.         node *node = n;
  885.         UNUSED(pw);
  886.  
  887.         *parent = NULL;
  888.         if (node->parent != NULL) {
  889.                 bool match = false;
  890.                 assert(lwc_string_caseless_isequal(
  891.                                 qname->name, node->parent->name, &match) ==
  892.                                 lwc_error_ok);
  893.                 if (match == true)
  894.                         *parent = (void *) node->parent;
  895.         }
  896.  
  897.         return CSS_OK;
  898. }
  899.  
  900. css_error named_sibling_node(void *pw, void *n,
  901.                 const css_qname *qname,
  902.                 void **sibling)
  903. {
  904.         node *node = n;
  905.         UNUSED(pw);
  906.  
  907.         *sibling = NULL;
  908.         if (node->prev != NULL) {
  909.                 bool match = false;
  910.                 assert(lwc_string_caseless_isequal(
  911.                                 qname->name, node->prev->name, &match) ==
  912.                                 lwc_error_ok);
  913.                 if (match == true)
  914.                         *sibling = (void *) node->prev;
  915.         }
  916.  
  917.         return CSS_OK;
  918. }
  919.  
  920. css_error named_generic_sibling_node(void *pw, void *n,
  921.                 const css_qname *qname,
  922.                 void **sibling)
  923. {
  924.         node *node = n;
  925.         UNUSED(pw);
  926.  
  927.         for (node = node->prev; node != NULL; node = node->prev) {
  928.                 bool match = false;
  929.                 assert(lwc_string_caseless_isequal(
  930.                                 qname->name, node->name,
  931.                                 &match) == lwc_error_ok);
  932.                 if (match == true)
  933.                         break;
  934.         }
  935.  
  936.         *sibling = (void *) node;
  937.  
  938.         return CSS_OK;
  939. }
  940.  
  941. css_error parent_node(void *pw, void *n, void **parent)
  942. {
  943.         node *node = n;
  944.  
  945.         UNUSED(pw);
  946.  
  947.         *parent = (void *) node->parent;
  948.  
  949.         return CSS_OK;
  950. }
  951.  
  952. css_error sibling_node(void *pw, void *n, void **sibling)
  953. {
  954.         node *node = n;
  955.  
  956.         UNUSED(pw);
  957.  
  958.         *sibling = (void *) node->prev;
  959.  
  960.         return CSS_OK;
  961. }
  962.  
  963. css_error node_has_name(void *pw, void *n,
  964.                 const css_qname *qname,
  965.                 bool *match)
  966. {
  967.         node *node = n;
  968.         UNUSED(pw);
  969.  
  970.         if (lwc_string_length(qname->name) == 1 &&
  971.                         lwc_string_data(qname->name)[0] == '*')
  972.                 *match = true;
  973.         else
  974.                 assert(lwc_string_caseless_isequal(node->name,
  975.                         qname->name, match) == lwc_error_ok);
  976.  
  977.         return CSS_OK;
  978. }
  979.  
  980. css_error node_has_class(void *pw, void *n,
  981.                 lwc_string *name,
  982.                 bool *match)
  983. {
  984.         node *node = n;
  985.         uint32_t i;
  986.         line_ctx *ctx = pw;
  987.  
  988.         for (i = 0; i < node->n_attrs; i++) {
  989.                 bool amatch = false;
  990.                 assert(lwc_string_caseless_isequal(
  991.                                 node->attrs[i].name, ctx->attr_class,
  992.                                 &amatch) == lwc_error_ok);
  993.                 if (amatch == true)
  994.                         break;
  995.         }
  996.  
  997.         /* Classes are case-sensitive in HTML */
  998.         if (i != node->n_attrs && name == node->attrs[i].value)
  999.                 *match = true;
  1000.         else
  1001.                 *match = false;
  1002.  
  1003.         return CSS_OK;
  1004. }
  1005.  
  1006. css_error node_has_id(void *pw, void *n,
  1007.                 lwc_string *name,
  1008.                 bool *match)
  1009. {
  1010.         node *node = n;
  1011.         uint32_t i;
  1012.         line_ctx *ctx = pw;
  1013.  
  1014.         for (i = 0; i < node->n_attrs; i++) {
  1015.                 bool amatch = false;
  1016.                 assert(lwc_string_caseless_isequal(
  1017.                                 node->attrs[i].name, ctx->attr_id, &amatch) ==
  1018.                                 lwc_error_ok);
  1019.                 if (amatch == true)
  1020.                         break;
  1021.         }
  1022.  
  1023.         /* IDs are case-sensitive in HTML */
  1024.         if (i != node->n_attrs && name == node->attrs[i].value)
  1025.                 *match = true;
  1026.         else
  1027.                 *match = false;
  1028.  
  1029.         return CSS_OK;
  1030. }
  1031.  
  1032. css_error node_has_attribute(void *pw, void *n,
  1033.                 const css_qname *qname,
  1034.                 bool *match)
  1035. {
  1036.         node *node = n;
  1037.         uint32_t i;
  1038.         UNUSED(pw);
  1039.        
  1040.         *match = false;
  1041.         for (i = 0; i < node->n_attrs; i++) {
  1042.                 assert(lwc_string_caseless_isequal(
  1043.                                 node->attrs[i].name, qname->name, match) ==
  1044.                                 lwc_error_ok);
  1045.                 if (*match == true)
  1046.                         break;
  1047.         }
  1048.  
  1049.         return CSS_OK;
  1050. }
  1051.  
  1052. css_error node_has_attribute_equal(void *pw, void *n,
  1053.                 const css_qname *qname,
  1054.                 lwc_string *value,
  1055.                 bool *match)
  1056. {
  1057.         node *node = n;
  1058.         uint32_t i;
  1059.         UNUSED(pw);
  1060.  
  1061.         *match = false;
  1062.        
  1063.         for (i = 0; i < node->n_attrs; i++) {
  1064.                 assert(lwc_string_caseless_isequal(
  1065.                                 node->attrs[i].name, qname->name, match) ==
  1066.                                 lwc_error_ok);
  1067.                 if (*match == true)
  1068.                         break;
  1069.         }
  1070.        
  1071.         if (*match == true) {
  1072.                 assert(lwc_string_caseless_isequal(
  1073.                                 node->attrs[i].name, value, match) ==
  1074.                                 lwc_error_ok);
  1075.         }
  1076.        
  1077.         return CSS_OK;
  1078. }
  1079.  
  1080. css_error node_has_attribute_includes(void *pw, void *n,
  1081.                 const css_qname *qname,
  1082.                 lwc_string *value,
  1083.                 bool *match)
  1084. {
  1085.         node *node = n;
  1086.         uint32_t i;
  1087.         size_t vlen = lwc_string_length(value);
  1088.         UNUSED(pw);
  1089.  
  1090.         *match = false;
  1091.        
  1092.         for (i = 0; i < node->n_attrs; i++) {
  1093.                 assert(lwc_string_caseless_isequal(
  1094.                                 node->attrs[i].name, qname->name, match) ==
  1095.                                 lwc_error_ok);
  1096.                 if (*match == true)
  1097.                         break;
  1098.         }
  1099.  
  1100.         if (*match == true) {
  1101.                 const char *p;
  1102.                 const char *start = lwc_string_data(node->attrs[i].value);
  1103.                 const char *end = start +
  1104.                                 lwc_string_length(node->attrs[i].value);
  1105.                
  1106.                 *match = false;
  1107.                
  1108.                 for (p = start; p < end; p++) {
  1109.                         if (*p == ' ') {
  1110.                                 if ((size_t) (p - start) == vlen &&
  1111.                                                 strncasecmp(start,
  1112.                                                         lwc_string_data(value),
  1113.                                                         vlen) == 0) {
  1114.                                         *match = true;
  1115.                                         break;
  1116.                                 }
  1117.  
  1118.                                 start = p + 1;
  1119.                         }
  1120.                 }
  1121.         }
  1122.  
  1123.         return CSS_OK;
  1124. }
  1125.  
  1126. css_error node_has_attribute_dashmatch(void *pw, void *n,
  1127.                 const css_qname *qname,
  1128.                 lwc_string *value,
  1129.                 bool *match)
  1130. {
  1131.         node *node = n;
  1132.         uint32_t i;
  1133.         size_t vlen = lwc_string_length(value);
  1134.         UNUSED(pw);
  1135.  
  1136.         *match = false;
  1137.        
  1138.         for (i = 0; i < node->n_attrs; i++) {
  1139.                 assert(lwc_string_caseless_isequal(
  1140.                                 node->attrs[i].name, qname->name, match) ==
  1141.                                 lwc_error_ok);
  1142.                 if (*match == true)
  1143.                         break;
  1144.         }
  1145.  
  1146.         if (*match == true) {
  1147.                 const char *p;
  1148.                 const char *start = lwc_string_data(node->attrs[i].value);
  1149.                 const char *end = start +
  1150.                                 lwc_string_length(node->attrs[i].value);
  1151.                
  1152.                 *match = false;
  1153.                
  1154.                 for (p = start; p < end; p++) {
  1155.                         if (*p == '-') {
  1156.                                 if ((size_t) (p - start) == vlen &&
  1157.                                                 strncasecmp(start,
  1158.                                                         lwc_string_data(value),
  1159.                                                         vlen) == 0) {
  1160.                                         *match = true;
  1161.                                         break;
  1162.                                 }
  1163.  
  1164.                                 start = p + 1;
  1165.                         }
  1166.                 }
  1167.         }
  1168.  
  1169.         return CSS_OK;
  1170. }
  1171.  
  1172. css_error node_has_attribute_prefix(void *pw, void *n,
  1173.                 const css_qname *qname,
  1174.                 lwc_string *value,
  1175.                 bool *match)
  1176. {
  1177.         node *node = n;
  1178.         uint32_t i;
  1179.         UNUSED(pw);
  1180.  
  1181.         *match = false;
  1182.        
  1183.         for (i = 0; i < node->n_attrs; i++) {
  1184.                 assert(lwc_string_caseless_isequal(
  1185.                                 node->attrs[i].name, qname->name, match) ==
  1186.                                 lwc_error_ok);
  1187.                 if (*match == true)
  1188.                         break;
  1189.         }
  1190.        
  1191.         if (*match == true) {
  1192.                 size_t len = lwc_string_length(node->attrs[i].value);
  1193.                 const char *data = lwc_string_data(node->attrs[i].value);
  1194.  
  1195.                 size_t vlen = lwc_string_length(value);
  1196.                 const char *vdata = lwc_string_data(value);
  1197.  
  1198.                 if (len < vlen)
  1199.                         *match = false;
  1200.                 else
  1201.                         *match = (strncasecmp(data, vdata, vlen) == 0);
  1202.         }
  1203.        
  1204.         return CSS_OK;
  1205. }
  1206.  
  1207. css_error node_has_attribute_suffix(void *pw, void *n,
  1208.                 const css_qname *qname,
  1209.                 lwc_string *value,
  1210.                 bool *match)
  1211. {
  1212.         node *node = n;
  1213.         uint32_t i;
  1214.         UNUSED(pw);
  1215.  
  1216.         *match = false;
  1217.        
  1218.         for (i = 0; i < node->n_attrs; i++) {
  1219.                 assert(lwc_string_caseless_isequal(
  1220.                                 node->attrs[i].name, qname->name, match) ==
  1221.                                 lwc_error_ok);
  1222.                 if (*match == true)
  1223.                         break;
  1224.         }
  1225.        
  1226.         if (*match == true) {
  1227.                 size_t len = lwc_string_length(node->attrs[i].value);
  1228.                 const char *data = lwc_string_data(node->attrs[i].value);
  1229.  
  1230.                 size_t vlen = lwc_string_length(value);
  1231.                 const char *vdata = lwc_string_data(value);
  1232.  
  1233.                 size_t suffix_start = len - vlen;
  1234.  
  1235.                 if (len < vlen)
  1236.                         *match = false;
  1237.                 else {
  1238.                         *match = (strncasecmp(data + suffix_start,
  1239.                                         vdata, vlen) == 0);
  1240.                 }
  1241.         }
  1242.  
  1243.         return CSS_OK;
  1244. }
  1245.  
  1246. css_error node_has_attribute_substring(void *pw, void *n,
  1247.                 const css_qname *qname,
  1248.                 lwc_string *value,
  1249.                 bool *match)
  1250. {
  1251.         node *node = n;
  1252.         uint32_t i;
  1253.         UNUSED(pw);
  1254.  
  1255.         *match = false;
  1256.        
  1257.         for (i = 0; i < node->n_attrs; i++) {
  1258.                 assert(lwc_string_caseless_isequal(
  1259.                                 node->attrs[i].name, qname->name, match) ==
  1260.                                 lwc_error_ok);
  1261.                 if (*match == true)
  1262.                         break;
  1263.         }
  1264.        
  1265.         if (*match == true) {
  1266.                 size_t len = lwc_string_length(node->attrs[i].value);
  1267.                 const char *data = lwc_string_data(node->attrs[i].value);
  1268.  
  1269.                 size_t vlen = lwc_string_length(value);
  1270.                 const char *vdata = lwc_string_data(value);
  1271.  
  1272.                 const char *last_start = data + len - vlen;
  1273.  
  1274.                 if (len < vlen)
  1275.                         *match = false;
  1276.                 else {
  1277.                         while (data <= last_start) {
  1278.                                 if (strncasecmp(data, vdata, vlen) == 0) {
  1279.                                         *match = true;
  1280.                                         break;
  1281.                                 }
  1282.  
  1283.                                 data++;
  1284.                         }
  1285.  
  1286.                         if (data > last_start)
  1287.                                 *match = false;
  1288.                 }
  1289.         }
  1290.  
  1291.         return CSS_OK;
  1292. }
  1293.  
  1294. css_error node_is_root(void *pw, void *n, bool *match)
  1295. {
  1296.         node *node = n;
  1297.         UNUSED(pw);
  1298.  
  1299.         *match = (node->parent == NULL);
  1300.  
  1301.         return CSS_OK;
  1302. }
  1303.  
  1304. css_error node_count_siblings(void *pw, void *n,
  1305.                 bool same_name, bool after, int32_t *count)
  1306. {
  1307.         int32_t cnt = 0;
  1308.         bool match = false;
  1309.         node *node = n;
  1310.         lwc_string *name = node->name;
  1311.         UNUSED(pw);
  1312.  
  1313.         if (after) {
  1314.                 while (node->next != NULL) {
  1315.                         if (same_name) {
  1316.                                 assert(lwc_string_caseless_isequal(
  1317.                                         name, node->next->name, &match) ==
  1318.                                         lwc_error_ok);
  1319.  
  1320.                                 if (match)
  1321.                                         cnt++;
  1322.                         } else {
  1323.                                 cnt++;
  1324.                         }
  1325.  
  1326.                         node = node->next;
  1327.                 }
  1328.         } else {
  1329.                 while (node->prev != NULL) {
  1330.                         if (same_name) {
  1331.                                 assert(lwc_string_caseless_isequal(
  1332.                                         name, node->prev->name, &match) ==
  1333.                                         lwc_error_ok);
  1334.  
  1335.                                 if (match)
  1336.                                         cnt++;
  1337.                         } else {
  1338.                                 cnt++;
  1339.                         }
  1340.  
  1341.                         node = node->prev;
  1342.                 }
  1343.         }
  1344.  
  1345.         *count = cnt;
  1346.  
  1347.         return CSS_OK;
  1348. }
  1349.  
  1350. css_error node_is_empty(void *pw, void *n, bool *match)
  1351. {
  1352.         node *node = n;
  1353.         UNUSED(pw);
  1354.  
  1355.         *match = (node->children == NULL);
  1356.  
  1357.         return CSS_OK;
  1358. }
  1359.  
  1360. css_error node_is_link(void *pw, void *n, bool *match)
  1361. {
  1362.         node *node = n;
  1363.  
  1364.         UNUSED(pw);
  1365.         UNUSED(node);
  1366.  
  1367.         *match = false;
  1368.  
  1369.         return CSS_OK;
  1370. }
  1371.  
  1372. css_error node_is_visited(void *pw, void *n, bool *match)
  1373. {
  1374.         node *node = n;
  1375.  
  1376.         UNUSED(pw);
  1377.         UNUSED(node);
  1378.  
  1379.         *match = false;
  1380.  
  1381.         return CSS_OK;
  1382. }
  1383.  
  1384. css_error node_is_hover(void *pw, void *n, bool *match)
  1385. {
  1386.         node *node = n;
  1387.  
  1388.         UNUSED(pw);
  1389.         UNUSED(node);
  1390.  
  1391.         *match = false;
  1392.  
  1393.         return CSS_OK;
  1394. }
  1395.  
  1396. css_error node_is_active(void *pw, void *n, bool *match)
  1397. {
  1398.         node *node = n;
  1399.  
  1400.         UNUSED(pw);
  1401.         UNUSED(node);
  1402.  
  1403.         *match = false;
  1404.  
  1405.         return CSS_OK;
  1406. }
  1407.  
  1408. css_error node_is_focus(void *pw, void *n, bool *match)
  1409. {
  1410.         node *node = n;
  1411.  
  1412.         UNUSED(pw);
  1413.         UNUSED(node);
  1414.  
  1415.         *match = false;
  1416.  
  1417.         return CSS_OK;
  1418. }
  1419.  
  1420. css_error node_is_enabled(void *pw, void *n, bool *match)
  1421. {
  1422.         node *node = n;
  1423.  
  1424.         UNUSED(pw);
  1425.         UNUSED(node);
  1426.  
  1427.         *match = false;
  1428.  
  1429.         return CSS_OK;
  1430. }
  1431.  
  1432. css_error node_is_disabled(void *pw, void *n, bool *match)
  1433. {
  1434.         node *node = n;
  1435.  
  1436.         UNUSED(pw);
  1437.         UNUSED(node);
  1438.  
  1439.         *match = false;
  1440.  
  1441.         return CSS_OK;
  1442. }
  1443.  
  1444. css_error node_is_checked(void *pw, void *n, bool *match)
  1445. {
  1446.         node *node = n;
  1447.  
  1448.         UNUSED(pw);
  1449.         UNUSED(node);
  1450.  
  1451.         *match = false;
  1452.  
  1453.         return CSS_OK;
  1454. }
  1455.  
  1456. css_error node_is_target(void *pw, void *n, bool *match)
  1457. {
  1458.         node *node = n;
  1459.  
  1460.         UNUSED(pw);
  1461.         UNUSED(node);
  1462.  
  1463.         *match = false;
  1464.  
  1465.         return CSS_OK;
  1466. }
  1467.  
  1468. css_error node_is_lang(void *pw, void *n,
  1469.                 lwc_string *lang,
  1470.                 bool *match)
  1471. {
  1472.         node *node = n;
  1473.  
  1474.         UNUSED(pw);
  1475.         UNUSED(node);
  1476.         UNUSED(lang);
  1477.  
  1478.         *match = false;
  1479.  
  1480.         return CSS_OK;
  1481. }
  1482.  
  1483. css_error node_presentational_hint(void *pw, void *node,
  1484.                 uint32_t property, css_hint *hint)
  1485. {
  1486.         UNUSED(pw);
  1487.         UNUSED(node);
  1488.         UNUSED(property);
  1489.         UNUSED(hint);
  1490.  
  1491.         return CSS_PROPERTY_NOT_SET;
  1492. }
  1493.  
  1494. css_error ua_default_for_property(void *pw, uint32_t property, css_hint *hint)
  1495. {
  1496.         UNUSED(pw);
  1497.  
  1498.         if (property == CSS_PROP_COLOR) {
  1499.                 hint->data.color = 0xff000000;
  1500.                 hint->status = CSS_COLOR_COLOR;
  1501.         } else if (property == CSS_PROP_FONT_FAMILY) {
  1502.                 hint->data.strings = NULL;
  1503.                 hint->status = CSS_FONT_FAMILY_SANS_SERIF;
  1504.         } else if (property == CSS_PROP_QUOTES) {
  1505.                 /* Not exactly useful :) */
  1506.                 hint->data.strings = NULL;
  1507.                 hint->status = CSS_QUOTES_NONE;
  1508.         } else if (property == CSS_PROP_VOICE_FAMILY) {
  1509.                 /** \todo Fix this when we have voice-family done */
  1510.                 hint->data.strings = NULL;
  1511.                 hint->status = 0;
  1512.         } else {
  1513.                 return CSS_INVALID;
  1514.         }
  1515.  
  1516.         return CSS_OK;
  1517. }
  1518.  
  1519. css_error compute_font_size(void *pw, const css_hint *parent, css_hint *size)
  1520. {
  1521.         static css_hint_length sizes[] = {
  1522.                 { FLTTOFIX(6.75), CSS_UNIT_PT },
  1523.                 { FLTTOFIX(7.50), CSS_UNIT_PT },
  1524.                 { FLTTOFIX(9.75), CSS_UNIT_PT },
  1525.                 { FLTTOFIX(12.0), CSS_UNIT_PT },
  1526.                 { FLTTOFIX(13.5), CSS_UNIT_PT },
  1527.                 { FLTTOFIX(18.0), CSS_UNIT_PT },
  1528.                 { FLTTOFIX(24.0), CSS_UNIT_PT }
  1529.         };
  1530.         const css_hint_length *parent_size;
  1531.  
  1532.         UNUSED(pw);
  1533.  
  1534.         /* Grab parent size, defaulting to medium if none */
  1535.         if (parent == NULL) {
  1536.                 parent_size = &sizes[CSS_FONT_SIZE_MEDIUM - 1];
  1537.         } else {
  1538.                 assert(parent->status == CSS_FONT_SIZE_DIMENSION);
  1539.                 assert(parent->data.length.unit != CSS_UNIT_EM);
  1540.                 assert(parent->data.length.unit != CSS_UNIT_EX);
  1541.                 parent_size = &parent->data.length;
  1542.         }
  1543.  
  1544.         assert(size->status != CSS_FONT_SIZE_INHERIT);
  1545.  
  1546.         if (size->status < CSS_FONT_SIZE_LARGER) {
  1547.                 /* Keyword -- simple */
  1548.                 size->data.length = sizes[size->status - 1];
  1549.         } else if (size->status == CSS_FONT_SIZE_LARGER) {
  1550.                 /** \todo Step within table, if appropriate */
  1551.                 size->data.length.value =
  1552.                                 FMUL(parent_size->value, FLTTOFIX(1.2));
  1553.                 size->data.length.unit = parent_size->unit;
  1554.         } else if (size->status == CSS_FONT_SIZE_SMALLER) {
  1555.                 /** \todo Step within table, if appropriate */
  1556.                 size->data.length.value =
  1557.                                 FMUL(parent_size->value, FLTTOFIX(1.2));
  1558.                 size->data.length.unit = parent_size->unit;
  1559.         } else if (size->data.length.unit == CSS_UNIT_EM ||
  1560.                         size->data.length.unit == CSS_UNIT_EX) {
  1561.                 size->data.length.value =
  1562.                         FMUL(size->data.length.value, parent_size->value);
  1563.  
  1564.                 if (size->data.length.unit == CSS_UNIT_EX) {
  1565.                         size->data.length.value = FMUL(size->data.length.value,
  1566.                                         FLTTOFIX(0.6));
  1567.                 }
  1568.  
  1569.                 size->data.length.unit = parent_size->unit;
  1570.         } else if (size->data.length.unit == CSS_UNIT_PCT) {
  1571.                 size->data.length.value = FDIV(FMUL(size->data.length.value,
  1572.                                 parent_size->value), FLTTOFIX(100));
  1573.                 size->data.length.unit = parent_size->unit;
  1574.         }
  1575.  
  1576.         size->status = CSS_FONT_SIZE_DIMENSION;
  1577.  
  1578.         return CSS_OK;
  1579. }
  1580.  
  1581.