/* * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2004 James Bursa * Copyright 2004 John M Bell */ /** \file * CSS ruleset parsing. * * This file implements the last stage of CSS parsing. It converts trees of * struct css_node produced by the parser into struct style, and adds them to a * stylesheet. * * This code is complicated by the CSS error handling rules. According to * CSS 2.1 4.2 "Illegal values", the whole of a declaration must be legal for * any of it to be used. */ #include #include #include #include #define CSS_INTERNALS #define NDEBUG #include "netsurf/css/css.h" #include "netsurf/content/content.h" #include "netsurf/desktop/options.h" #include "netsurf/utils/log.h" #include "netsurf/utils/url.h" #include "netsurf/utils/utils.h" static bool css_compare_selectors(const struct css_selector *n0, const struct css_selector *n1); static int parse_length(struct css_length * const length, const struct css_node * const v, bool non_negative); static colour parse_colour(const struct css_node * const v); static colour css_parse_rgb(struct css_node *v); static void parse_background(struct css_style * const s, const struct css_node * v); static void parse_background_attachment(struct css_style * const s, const struct css_node * const v); static void parse_background_color(struct css_style * const s, const struct css_node * const v); static void parse_background_image(struct css_style * const s, const struct css_node * const v); static bool css_background_image_parse(const struct css_node *v, css_background_image_type *type, char **uri); static struct css_background_entry *css_background_lookup( const struct css_node *v); static void parse_background_position(struct css_style * const s, const struct css_node * const v); static bool css_background_position_parse(const struct css_node **node, struct css_background_position *horz, struct css_background_position *vert); static void parse_background_repeat(struct css_style * const s, const struct css_node * const v); static void parse_border(struct css_style * const s, const struct css_node * v); static void parse_border_bottom(struct css_style * const s, const struct css_node * v); static void parse_border_bottom_color(struct css_style * const s, const struct css_node * v); static void parse_border_bottom_style(struct css_style * const s, const struct css_node * v); static void parse_border_bottom_width(struct css_style * const s, const struct css_node * v); static void parse_border_collapse(struct css_style * const s, const struct css_node * v); static void parse_border_color(struct css_style * const s, const struct css_node * v); static void parse_border_color_side(struct css_style * const s, const struct css_node * const v, unsigned int i); static void parse_border_left(struct css_style * const s, const struct css_node * v); static void parse_border_left_color(struct css_style * const s, const struct css_node * v); static void parse_border_left_style(struct css_style * const s, const struct css_node * v); static void parse_border_left_width(struct css_style * const s, const struct css_node * v); static void parse_border_right(struct css_style * const s, const struct css_node * v); static void parse_border_right_color(struct css_style * const s, const struct css_node * v); static void parse_border_right_style(struct css_style * const s, const struct css_node * v); static void parse_border_right_width(struct css_style * const s, const struct css_node * v); static void parse_border_side(struct css_style * const s, const struct css_node *v, unsigned int i); static void parse_border_spacing(struct css_style * const s, const struct css_node * v); static void parse_border_style(struct css_style * const s, const struct css_node * v); static void parse_border_style_side(struct css_style * const s, const struct css_node * const v, unsigned int i); static void parse_border_top(struct css_style * const s, const struct css_node * v); static void parse_border_top_color(struct css_style * const s, const struct css_node * v); static void parse_border_top_style(struct css_style * const s, const struct css_node * v); static void parse_border_top_width(struct css_style * const s, const struct css_node * v); static void parse_border_width(struct css_style * const s, const struct css_node * v); static void parse_border_width_side(struct css_style * const s, const struct css_node * const v, unsigned int i); static void parse_bottom(struct css_style * const s, const struct css_node * v); static void parse_caption_side(struct css_style * const s, const struct css_node * v); static void parse_clear(struct css_style * const s, const struct css_node * const v); static void parse_clip(struct css_style * const s, const struct css_node * v); static void parse_color(struct css_style * const s, const struct css_node * const v); static void parse_cursor(struct css_style * const s, const struct css_node * v); static void parse_direction(struct css_style * const s, const struct css_node * v); static void parse_display(struct css_style * const s, const struct css_node * const v); static void parse_empty_cells(struct css_style * const s, const struct css_node * v); static void parse_float(struct css_style * const s, const struct css_node * const v); static void parse_font(struct css_style * const s, const struct css_node * v); static void parse_font_family(struct css_style * const s, const struct css_node * v); static void parse_font_size(struct css_style * const s, const struct css_node * const v); static void parse_font_style(struct css_style * const s, const struct css_node * const v); static void parse_font_variant(struct css_style * const s, const struct css_node * const v); static void parse_font_weight(struct css_style * const s, const struct css_node * const v); static void parse_height(struct css_style * const s, const struct css_node * const v); static void parse_left(struct css_style * const s, const struct css_node * v); static void parse_letter_spacing(struct css_style * const s, const struct css_node * v); static void parse_line_height(struct css_style * const s, const struct css_node * const v); static void parse_list_style(struct css_style * const s, const struct css_node * v); static void parse_list_style_image(struct css_style * const s, const struct css_node * v); static bool css_list_style_image_parse(const struct css_node *v, css_list_style_image_type *type, char **uri); static void parse_list_style_position(struct css_style * const s, const struct css_node * v); static void parse_list_style_type(struct css_style * const s, const struct css_node * v); static void parse_margin(struct css_style * const s, const struct css_node * const v); static void parse_margin_bottom(struct css_style * const s, const struct css_node * const v); static void parse_margin_left(struct css_style * const s, const struct css_node * const v); static void parse_margin_right(struct css_style * const s, const struct css_node * const v); static void parse_margin_top(struct css_style * const s, const struct css_node * const v); static void parse_margin_side(struct css_style * const s, const struct css_node * const v, unsigned int i); static void parse_max_height(struct css_style *const s, const struct css_node * v); static void parse_max_width(struct css_style *const s, const struct css_node * v); static void parse_min_height(struct css_style *const s, const struct css_node * v); static void parse_min_width(struct css_style *const s, const struct css_node * v); static void parse_orphans(struct css_style * const s, const struct css_node * const v); static void parse_outline(struct css_style * const s, const struct css_node * v); static void parse_outline_color(struct css_style * const s, const struct css_node * const v); static void parse_outline_style(struct css_style * const s, const struct css_node * const v); static void parse_outline_width(struct css_style * const s, const struct css_node * const v); static bool css_outline_width_parse(const struct css_node * v, struct css_border_width * w); static void parse_overflow(struct css_style * const s, const struct css_node * const v); static void parse_padding(struct css_style * const s, const struct css_node * const v); static void parse_padding_bottom(struct css_style * const s, const struct css_node * const v); static void parse_padding_left(struct css_style * const s, const struct css_node * const v); static void parse_padding_right(struct css_style * const s, const struct css_node * const v); static void parse_padding_top(struct css_style * const s, const struct css_node * const v); static void parse_padding_side(struct css_style * const s, const struct css_node * const v, unsigned int i); static void parse_page_break_after(struct css_style * const s, const struct css_node * v); static void parse_page_break_before(struct css_style * const s, const struct css_node * v); static void parse_page_break_inside(struct css_style * const s, const struct css_node * v); static void parse_pos(struct css_style * const s, const struct css_node * v, unsigned int i); static void parse_position(struct css_style * const s, const struct css_node * v); static void parse_right(struct css_style * const s, const struct css_node * v); static void parse_table_layout(struct css_style * const s, const struct css_node * v); static void parse_text_align(struct css_style * const s, const struct css_node * const v); static void parse_text_decoration(struct css_style * const s, const struct css_node * const v); static void parse_text_indent(struct css_style * const s, const struct css_node * const v); static void parse_text_transform(struct css_style * const s, const struct css_node * const v); static void parse_top(struct css_style * const s, const struct css_node * v); static void parse_unicode_bidi(struct css_style * const s, const struct css_node * const v); static void parse_vertical_align(struct css_style * const s, const struct css_node * v); static void parse_visibility(struct css_style * const s, const struct css_node * const v); static void parse_widows(struct css_style * const s, const struct css_node * const v); static void parse_width(struct css_style * const s, const struct css_node * const v); static void parse_white_space(struct css_style * const s, const struct css_node * const v); static void parse_word_spacing(struct css_style * const s, const struct css_node * v); static void parse_z_index(struct css_style * const s, const struct css_node * const v); static css_text_decoration css_text_decoration_parse(const char * const s, int length); /** An entry in css_property_table. */ struct css_property_entry { const char name[25]; void (*parse) (struct css_style * const s, const struct css_node * const v); }; /** Table of property parsers. MUST be sorted by property name. */ static const struct css_property_entry css_property_table[] = { { "background", parse_background }, { "background-attachment", parse_background_attachment }, { "background-color", parse_background_color }, { "background-image", parse_background_image }, { "background-position", parse_background_position }, { "background-repeat", parse_background_repeat }, { "border", parse_border }, { "border-bottom", parse_border_bottom }, { "border-bottom-color", parse_border_bottom_color }, { "border-bottom-style", parse_border_bottom_style }, { "border-bottom-width", parse_border_bottom_width }, { "border-collapse", parse_border_collapse }, { "border-color", parse_border_color }, { "border-left", parse_border_left }, { "border-left-color", parse_border_left_color }, { "border-left-style", parse_border_left_style }, { "border-left-width", parse_border_left_width }, { "border-right", parse_border_right }, { "border-right-color", parse_border_right_color }, { "border-right-style", parse_border_right_style }, { "border-right-width", parse_border_right_width }, { "border-spacing", parse_border_spacing }, { "border-style", parse_border_style }, { "border-top", parse_border_top }, { "border-top-color", parse_border_top_color }, { "border-top-style", parse_border_top_style }, { "border-top-width", parse_border_top_width }, { "border-width", parse_border_width }, { "bottom", parse_bottom }, { "caption-side", parse_caption_side }, { "clear", parse_clear }, { "clip", parse_clip }, { "color", parse_color }, { "cursor", parse_cursor }, { "direction", parse_direction }, { "display", parse_display }, { "empty-cells", parse_empty_cells }, { "float", parse_float }, { "font", parse_font }, { "font-family", parse_font_family }, { "font-size", parse_font_size }, { "font-style", parse_font_style }, { "font-variant", parse_font_variant }, { "font-weight", parse_font_weight }, { "height", parse_height }, { "left", parse_left }, { "letter-spacing", parse_letter_spacing }, { "line-height", parse_line_height }, { "list-style", parse_list_style }, { "list-style-image", parse_list_style_image }, { "list-style-position", parse_list_style_position }, { "list-style-type", parse_list_style_type }, { "margin", parse_margin }, { "margin-bottom", parse_margin_bottom }, { "margin-left", parse_margin_left }, { "margin-right", parse_margin_right }, { "margin-top", parse_margin_top }, { "max-height", parse_max_height }, { "max-width", parse_max_width }, { "min-height", parse_min_height }, { "min-width", parse_min_width }, { "orphans", parse_orphans }, { "outline", parse_outline }, { "outline-color", parse_outline_color }, { "outline-style", parse_outline_style }, { "outline-width", parse_outline_width }, { "overflow", parse_overflow }, { "padding", parse_padding }, { "padding-bottom", parse_padding_bottom }, { "padding-left", parse_padding_left }, { "padding-right", parse_padding_right }, { "padding-top", parse_padding_top }, { "page-break-after", parse_page_break_after }, { "page-break-before", parse_page_break_before }, { "page-break-inside", parse_page_break_inside }, { "position", parse_position }, { "right", parse_right }, { "table-layout", parse_table_layout }, { "text-align", parse_text_align }, { "text-decoration", parse_text_decoration }, { "text-indent", parse_text_indent }, { "text-transform", parse_text_transform }, { "top", parse_top }, { "unicode-bidi", parse_unicode_bidi }, { "vertical-align", parse_vertical_align }, { "visibility", parse_visibility }, { "white-space", parse_white_space }, { "widows", parse_widows }, { "width", parse_width }, { "word-spacing", parse_word_spacing }, { "z-index", parse_z_index } }; /** An entry in css_colour_table. */ struct css_colour_entry { const char name[12]; colour col; }; /* Table of standard colour names. MUST be sorted by colour name. * Note: colour is 0xbbggrr. */ static const struct css_colour_entry css_colour_table[] = { { "aqua", 0xffff00 }, { "black", 0x000000 }, { "blue", 0xff0000 }, { "fuchsia", 0xff00ff }, { "gray", 0x808080 }, { "green", 0x008000 }, { "lime", 0x00ff00 }, { "maroon", 0x000080 }, { "navy", 0x800000 }, { "olive", 0x008080 }, { "orange", 0x00a5ff }, { "purple", 0x800080 }, { "red", 0x0000ff }, { "silver", 0xc0c0c0 }, { "teal", 0x808000 }, { "transparent", TRANSPARENT }, { "white", 0xffffff }, { "yellow", 0x00ffff }, }; /** An entry in css_font_size_table. */ struct css_font_size_entry { const char name[10]; float size; }; /** Table of font sizes. MUST be sorted by name. */ #define SIZE_FACTOR 1.2 static const struct css_font_size_entry css_font_size_table[] = { { "large", 1.0 * SIZE_FACTOR }, { "medium", 1.0 }, { "small", 1.0 / SIZE_FACTOR }, { "x-large", 1.0 * SIZE_FACTOR * SIZE_FACTOR }, { "x-small", 1.0 / (SIZE_FACTOR * SIZE_FACTOR) }, { "xx-large", 1.0 * SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR }, { "xx-small", 1.0 / (SIZE_FACTOR * SIZE_FACTOR * SIZE_FACTOR) }, }; /** * Add a ruleset to a stylesheet. */ void css_add_ruleset(struct content *c, struct css_selector *selector, struct css_node *declaration) { bool found; struct css_stylesheet *stylesheet = c->data.css.css; struct css_selector *n, *sel, *next_sel, *prev; struct css_style *style; unsigned int hash; for (sel = selector; sel != 0; sel = next_sel) { next_sel = sel->next; /* check if this selector is already present */ found = false; prev = 0; hash = css_hash(sel->data, sel->data_length); /* selectors are ordered by specificity in the hash chain */ for (n = stylesheet->rule[hash]; n && n->specificity < sel->specificity; n = n->next) prev = n; for ( ; n && n->specificity == sel->specificity; n = n->next) { prev = n; if (css_compare_selectors(sel, n)) { found = true; break; } } if (!found) { /* not present: construct a new struct css_style */ LOG(("constructing new style")); style = malloc(sizeof *style); if (!style) { /** \todo report to user */ css_free_selector(sel); return; } memcpy(style, &css_empty_style, sizeof(*style)); sel->style = style; sel->next = n; if (prev) prev->next = sel; else stylesheet->rule[hash] = sel; c->size += sizeof(*style); } else { /* already exists: augument existing style */ LOG(("augumenting existing style")); style = n->style; sel->next = 0; css_free_selector(sel); } /* fill in the declarations */ css_add_declarations(style, declaration); } } /** * Add declarations to a style. */ void css_add_declarations(struct css_style *style, struct css_node *declaration) { char name[25]; /* this must be the same length as p->name */ struct css_node *n; for (n = declaration; n != 0; n = n->next) { struct css_property_entry *p; assert(n->type == CSS_NODE_DECLARATION && n->data && n->value); if (24 < n->data_length) continue; strncpy(name, n->data, n->data_length); name[n->data_length] = 0; p = bsearch(name, css_property_table, sizeof css_property_table / sizeof css_property_table[0], sizeof css_property_table[0], (int (*)(const void *, const void *)) strcasecmp); if (p == 0) continue; p->parse(style, n->value); } } /** * Compare two css_selectors. */ bool css_compare_selectors(const struct css_selector *n0, const struct css_selector *n1) { struct css_selector *m0, *m1; unsigned int count0 = 0, count1 = 0; /* compare element name */ if (!((n0->data == 0 && n1->data == 0) || (n0->data != 0 && n1->data != 0 && n0->data_length == n1->data_length && strncmp(n0->data, n1->data, n0->data_length) == 0))) return false; if (n0->comb != n1->comb) return false; /* compare classes and ids */ for (m0 = n0->detail; m0 != 0; m0 = m0->next) count0++; for (m1 = n1->detail; m1 != 0; m1 = m1->next) count1++; if (count0 != count1) return false; for (m0 = n0->detail; m0 != 0; m0 = m0->next) { bool found = false; for (m1 = n1->detail; m1 != 0; m1 = m1->next) { /* TODO: should this be case sensitive for IDs? */ if (m0->type == m1->type && m0->data_length == m1->data_length && strncasecmp(m0->data, m1->data, m0->data_length) == 0 && ((m0->data2 == 0 && m1->data2 == 0) || (m0->data2_length == m1->data2_length && strncasecmp(m0->data2, m1->data2, m0->data2_length) == 0))) { found = true; break; } } if (!found) return false; } /* compare ancestors */ if (n0->comb == CSS_COMB_NONE) return true; return css_compare_selectors(n0->combiner, n1->combiner); } /* * Property parsers. */ /* TODO: consider CSS_NODE_NUMBER whenever a value may be '0' */ int parse_length(struct css_length * const length, const struct css_node * const v, bool non_negative) { css_unit u; float value; int num_length; if (v->type == CSS_NODE_NUMBER && atof(v->data) == 0) { length->unit = CSS_UNIT_EM; length->value = 0; return 0; } if (v->type != CSS_NODE_DIMENSION) return 1; num_length = strspn(v->data, "0123456789+-."); u = css_unit_parse(v->data + num_length, v->data_length - num_length); if (u == CSS_UNIT_UNKNOWN) return 1; value = atof(v->data); if (non_negative && value < 0) return 1; length->unit = u; length->value = value; return 0; } colour named_colour(const char *name) { struct css_colour_entry *col; unsigned int r, g, b; col = bsearch(name, css_colour_table, sizeof css_colour_table / sizeof css_colour_table[0], sizeof css_colour_table[0], (int (*)(const void *, const void *)) strcasecmp); if (col == 0) { /* A common error is the omission of the '#' from the * start of a colour specified in #rrggbb format. * This attempts to detect and recover from this. */ if (strlen(name) == 6 && sscanf(name, "%2x%2x%2x", &r, &g, &b) == 3) { return (b << 16) | (g << 8) | r; } else return TRANSPARENT; } return col->col; } colour parse_colour(const struct css_node * const v) { colour c = CSS_COLOR_NONE; unsigned int r, g, b; struct css_colour_entry *col; char colour_name[12]; switch (v->type) { case CSS_NODE_HASH: if (v->data_length == 4) { if (sscanf(v->data + 1, "%1x%1x%1x", &r, &g, &b) == 3) c = (b << 20) | (b << 16) | (g << 12) | (g << 8) | (r << 4) | r; } else if (v->data_length == 7) { if (sscanf(v->data + 1, "%2x%2x%2x", &r, &g, &b) == 3) c = (b << 16) | (g << 8) | r; } break; case CSS_NODE_FUNCTION: if (v->data_length == 4 && strncasecmp(v->data, "rgb", 3) == 0) c = css_parse_rgb(v->value); break; case CSS_NODE_IDENT: if (11 < v->data_length) break; strncpy(colour_name, v->data, v->data_length); colour_name[v->data_length] = 0; col = bsearch(colour_name, css_colour_table, sizeof css_colour_table / sizeof css_colour_table[0], sizeof css_colour_table[0], (int (*)(const void *, const void *)) strcasecmp); if (col != 0) c = col->col; break; default: break; } return c; } /** * Parse an RGB value in functional notation. */ colour css_parse_rgb(struct css_node *v) { unsigned int i; int c[3]; /* we expect exactly the nodes * X COMMA X COMMA X * where X is NUMBER or PERCENTAGE */ for (i = 0; i != 3; i++) { if (!v) return CSS_COLOR_NONE; if (v->type == CSS_NODE_NUMBER) c[i] = atoi(v->data); else if (v->type == CSS_NODE_PERCENTAGE) c[i] = atoi(v->data) * 255 / 100; else return CSS_COLOR_NONE; if (c[i] < 0) c[i] = 0; if (255 < c[i]) c[i] = 255; v = v->next; if (i == 2) { if (v) return CSS_COLOR_NONE; } else { if (!v || v->type != CSS_NODE_COMMA) return CSS_COLOR_NONE; v = v->next; } } return (c[2] << 16) | (c[1] << 8) | c[0]; } /** * \name Individual property parsers. * \{ */ void parse_background(struct css_style * const s, const struct css_node * v) { colour c = TRANSPARENT, c2; css_background_image_type bi = CSS_BACKGROUND_IMAGE_NONE, bi2; char *bi_uri = 0; css_background_repeat br = CSS_BACKGROUND_REPEAT_REPEAT, br2; css_background_attachment ba = CSS_BACKGROUND_ATTACHMENT_SCROLL, ba2; struct css_background_position horz = { CSS_BACKGROUND_POSITION_PERCENT, { 0 } }; struct css_background_position vert = { CSS_BACKGROUND_POSITION_PERCENT, { 0 } }; struct css_background_position horz2, vert2; while (v) { switch (v->type) { case CSS_NODE_URI: case CSS_NODE_STRING: /* background-image */ if (!css_background_image_parse(v, &bi2, &bi_uri)) return; bi = bi2; v = v->next; break; case CSS_NODE_DIMENSION: case CSS_NODE_PERCENTAGE: /* background-position */ if (!css_background_position_parse(&v, &horz2, &vert2)) return; horz = horz2; vert = vert2; break; case CSS_NODE_IDENT: /* could be background-image: none */ if (v->data_length == 4 && strncasecmp(v->data, "none", 4) == 0) { bi = CSS_BACKGROUND_IMAGE_NONE; v = v->next; break; } /* background-repeat */ br2 = css_background_repeat_parse(v->data, v->data_length); if (br2 != CSS_BACKGROUND_REPEAT_UNKNOWN) { br = br2; v = v->next; break; } /* background-attachment */ ba2 = css_background_attachment_parse(v->data, v->data_length); if (ba2 != CSS_BACKGROUND_ATTACHMENT_UNKNOWN) { ba = ba2; v = v->next; break; } /* background-position */ if (css_background_position_parse(&v, &horz2, &vert2)) { horz = horz2; vert = vert2; break; } /* fall through */ case CSS_NODE_HASH: case CSS_NODE_FUNCTION: /* background-color */ c2 = parse_colour(v); if (c2 != CSS_COLOR_NONE) { c = c2; v = v->next; break; } /* fall through */ default: /* parsing failed */ return; } } s->background_color = c; s->background_image.type = bi; s->background_image.uri = bi_uri; s->background_repeat = br; s->background_attachment = ba; s->background_position.horz = horz; s->background_position.vert = vert; } void parse_background_attachment(struct css_style * const s, const struct css_node * const v) { css_background_attachment z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_background_attachment_parse(v->data, v->data_length); if (z != CSS_BACKGROUND_ATTACHMENT_UNKNOWN) s->background_attachment = z; } void parse_background_color(struct css_style * const s, const struct css_node * const v) { colour c; if (v->next) return; c = parse_colour(v); if (c != CSS_COLOR_NONE) s->background_color = c; } void parse_background_image(struct css_style * const s, const struct css_node * const v) { css_background_image_type type; char *uri = 0; if (v->next) return; if (!css_background_image_parse(v, &type, &uri)) return; s->background_image.type = type; s->background_image.uri = uri; } /** * Parse a background-image property. * * \param node node to parse * \param type updated to background image type * \param uri updated to background image uri, if type is * CSS_BACKGROUND_IMAGE_URI * \return true on success, false on parse failure */ bool css_background_image_parse(const struct css_node *v, css_background_image_type *type, char **uri) { bool string = false; const char *u; char *t, *url; switch (v->type) { case CSS_NODE_URI: for (u = v->data + 4; *u == ' ' || *u == '\t' || *u == '\r' || *u == '\n' || *u == '\f'; u++) ; if (*u == '\'' || *u == '"') { string = true; u++; } url = strndup(u, v->data_length - (u - v->data)); if (!url) return false; for (t = url + strlen(url) - 2; *t == ' ' || *t == '\t' || *t == '\r' || *t == '\n' || *t == '\f'; t--) ; if (string) *t = 0; else *(t + 1) = 0; /* for inline style attributes, the stylesheet * content is the parent HTML content */ if (v->stylesheet->type == CONTENT_HTML) *uri = url_join(url, v->stylesheet->data.html.base_url); else *uri = url_join(url, v->stylesheet->url); free(url); if (!*uri) return false; *type = CSS_BACKGROUND_IMAGE_URI; break; case CSS_NODE_STRING: url = strndup(v->data, v->data_length); if (!url) return false; *uri = url_join(url, v->stylesheet->url); free(url); if (!*uri) return false; *type = CSS_BACKGROUND_IMAGE_URI; break; case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) *type = CSS_BACKGROUND_IMAGE_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "none", 4) == 0) *type = CSS_BACKGROUND_IMAGE_NONE; break; default: return false; } return true; } /** An entry in css_background_table. */ struct css_background_entry { const char *keyword; unsigned int length; float value; bool horizontal; bool vertical; }; /** Lookup table for parsing background-postion. */ struct css_background_entry css_background_table[] = { { "left", 4, 0, true, false }, { "right", 5, 100, true, false }, { "top", 3, 0, false, true }, { "bottom", 6, 100, false, true }, { "center", 6, 50, false, false } /* true, true would be more logical, but this actually simplifies the code */ }; #define CSS_BACKGROUND_TABLE_ENTRIES (sizeof css_background_table / \ sizeof css_background_table[0]) /** * Lookup a background-position keyword in css_background_table. */ struct css_background_entry *css_background_lookup( const struct css_node *v) { unsigned int i; for (i = 0; i != CSS_BACKGROUND_TABLE_ENTRIES; i++) if (css_background_table[i].length == v->data_length && strncasecmp(v->data, css_background_table[i].keyword, css_background_table[i].length) == 0) break; if (i == CSS_BACKGROUND_TABLE_ENTRIES) return 0; return &css_background_table[i]; } void parse_background_position(struct css_style * const s, const struct css_node * v) { const struct css_node *node = v; struct css_background_position horz, vert; if (v->next && v->next->next) /* more than two nodes */ return; if (!css_background_position_parse(&node, &horz, &vert)) return; if (node) /* didn't parse all the nodes */ return; s->background_position.horz = horz; s->background_position.vert = vert; } /** * Parse a background-position property. * * \param node list of nodes, updated to first unused node * \param horz updated to horizontal background position * \param vert updated to vertical background position * \return true on success, false on parse failure */ bool css_background_position_parse(const struct css_node **node, struct css_background_position *horz, struct css_background_position *vert) { const struct css_node *v = *node; const struct css_node *w = v->next; struct css_background_entry *bg = 0, *bg2 = 0; if (v->type == CSS_NODE_IDENT) bg = css_background_lookup(v); if (w && w->type == CSS_NODE_IDENT) bg2 = css_background_lookup(w); if (!(w && ((w->type == CSS_NODE_IDENT && bg2) || w->type == CSS_NODE_PERCENTAGE || w->type == CSS_NODE_DIMENSION))) { /* only one value specified */ if (v->type == CSS_NODE_IDENT) { if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) { horz->pos = vert->pos = CSS_BACKGROUND_POSITION_INHERIT; return false; } if (!bg) return false; horz->pos = vert->pos = CSS_BACKGROUND_POSITION_PERCENT; horz->value.percent = bg->horizontal ? bg->value : 50; vert->value.percent = bg->vertical ? bg->value : 50; } else if (v->type == CSS_NODE_PERCENTAGE) { horz->pos = vert->pos = CSS_BACKGROUND_POSITION_PERCENT; horz->value.percent = atof(v->data); vert->value.percent = 50.0; } else if (v->type == CSS_NODE_DIMENSION) { if (parse_length(&horz->value. length, v, false) == 0) { horz->pos = CSS_BACKGROUND_POSITION_LENGTH; vert->pos = CSS_BACKGROUND_POSITION_PERCENT; vert->value.percent = 50.0; } } *node = w; return true; } /* two values specified */ if (v->type == CSS_NODE_IDENT && w->type == CSS_NODE_IDENT) { /* both keywords */ if (!bg || !bg2) return false; if ((bg->horizontal && bg2->horizontal) || (bg->vertical && bg2->vertical)) return false; horz->pos = vert->pos = CSS_BACKGROUND_POSITION_PERCENT; horz->value.percent = vert->value.percent = 50; if (bg->horizontal) horz->value.percent = bg->value; else if (bg2->horizontal) horz->value.percent = bg2->value; if (bg->vertical) vert->value.percent = bg->value; else if (bg2->vertical) vert->value.percent = bg2->value; *node = w->next; return true; } if (v->type == CSS_NODE_IDENT) { /* horizontal value */ if (!bg || bg->vertical) return false; } if (w->type == CSS_NODE_IDENT) { /* vertical value */ if (!bg2 || bg2->horizontal) return false; } if (v->type == CSS_NODE_IDENT) { /* horizontal value */ horz->pos = CSS_BACKGROUND_POSITION_PERCENT; horz->value.percent = bg->value; } else if (v->type == CSS_NODE_PERCENTAGE) { horz->pos = CSS_BACKGROUND_POSITION_PERCENT; horz->value.percent = atof(v->data); } else if (v->type == CSS_NODE_DIMENSION) { if (parse_length(&horz->value.length, v, false) == 0) horz->pos = CSS_BACKGROUND_POSITION_LENGTH; } if (w->type == CSS_NODE_IDENT) { /* vertical value */ vert->pos = CSS_BACKGROUND_POSITION_PERCENT; vert->value.percent = bg2->value; } else if (w->type == CSS_NODE_PERCENTAGE) { vert->pos = CSS_BACKGROUND_POSITION_PERCENT; vert->value.percent = atof(w->data); } else if (w->type == CSS_NODE_DIMENSION) { if (parse_length(&vert->value.length, w, false) == 0) vert->pos = CSS_BACKGROUND_POSITION_LENGTH; } *node = w->next; return true; } void parse_background_repeat(struct css_style * const s, const struct css_node * const v) { css_background_repeat z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_background_repeat_parse(v->data, v->data_length); if (z != CSS_BACKGROUND_REPEAT_UNKNOWN) s->background_repeat = z; } void parse_border_width(struct css_style * const s, const struct css_node * const v) { unsigned int count = 0; const struct css_node *w; for (w = v; w; w = w->next, count++) if (!((w->type == CSS_NODE_IDENT && ( (w->data_length == 7 && strncasecmp(w->data, "inherit", 7) == 0) || (w->data_length == 4 && strncasecmp(w->data, "thin", 4) == 0) || (w->data_length == 6 && strncasecmp(w->data, "medium", 6) == 0) || (w->data_length == 5 && strncasecmp(w->data, "thick", 5) == 0))) || (w->type == CSS_NODE_DIMENSION) || (w->type == CSS_NODE_NUMBER))) return; w = v; switch (count) { case 1: /* one value: applies to all sides */ parse_border_width_side(s, w, TOP); parse_border_width_side(s, w, RIGHT); parse_border_width_side(s, w, BOTTOM); parse_border_width_side(s, w, LEFT); break; case 2: /* (top and bottom), (left and right) */ parse_border_width_side(s, w, TOP); parse_border_width_side(s, w, BOTTOM); w = w->next; parse_border_width_side(s, w, RIGHT); parse_border_width_side(s, w, LEFT); break; case 3: /* top, (left and right), bottom */ parse_border_width_side(s, w, TOP); w = w->next; parse_border_width_side(s, w, RIGHT); parse_border_width_side(s, w, LEFT); w = w->next; parse_border_width_side(s, w, BOTTOM); break; case 4: /* top, right, bottom, left */ parse_border_width_side(s, w, TOP); w = w->next; parse_border_width_side(s, w, RIGHT); w = w->next; parse_border_width_side(s, w, BOTTOM); w = w->next; parse_border_width_side(s, w, LEFT); break; } } #define PARSE_BORDER_WIDTH(side, z) \ void parse_border_ ## side ## _width(struct css_style * const s, \ const struct css_node * const v) \ { \ if (v->next != 0) \ return; \ parse_border_width_side(s, v, z); \ } PARSE_BORDER_WIDTH(top, TOP) PARSE_BORDER_WIDTH(right, RIGHT) PARSE_BORDER_WIDTH(bottom, BOTTOM) PARSE_BORDER_WIDTH(left, LEFT) void parse_border_width_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { if (v->type == CSS_NODE_IDENT) { if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->border[i].width.width = CSS_BORDER_WIDTH_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "thin", 4) == 0) { s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; s->border[i].width.value.value = 1; s->border[i].width.value.unit = CSS_UNIT_PX; } else if (v->data_length == 6 && strncasecmp(v->data, "medium", 6) == 0) { s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; s->border[i].width.value.value = 2; s->border[i].width.value.unit = CSS_UNIT_PX; } else if (v->data_length == 5 && strncasecmp(v->data, "thick", 5) == 0) { s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; s->border[i].width.value.value = 4; s->border[i].width.value.unit = CSS_UNIT_PX; } } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&s->border[i].width.value, v, true) == 0) s->border[i].width.width = CSS_BORDER_WIDTH_LENGTH; } void parse_border_color(struct css_style * const s, const struct css_node * const v) { unsigned int count = 0; const struct css_node *w; for (w = v; w; w = w->next, count++) if (!(w->type == CSS_NODE_HASH || w->type == CSS_NODE_FUNCTION || w->type == CSS_NODE_IDENT)) return; w = v; switch (count) { case 1: /* one value: applies to all sides */ parse_border_color_side(s, w, TOP); parse_border_color_side(s, w, RIGHT); parse_border_color_side(s, w, BOTTOM); parse_border_color_side(s, w, LEFT); break; case 2: /* (top and bottom), (left and right) */ parse_border_color_side(s, w, TOP); parse_border_color_side(s, w, BOTTOM); w = w->next; parse_border_color_side(s, w, RIGHT); parse_border_color_side(s, w, LEFT); break; case 3: /* top, (left and right), bottom */ parse_border_color_side(s, w, TOP); w = w->next; parse_border_color_side(s, w, RIGHT); parse_border_color_side(s, w, LEFT); w = w->next; parse_border_color_side(s, w, BOTTOM); break; case 4: /* top, right, bottom, left */ parse_border_color_side(s, w, TOP); w = w->next; parse_border_color_side(s, w, RIGHT); w = w->next; parse_border_color_side(s, w, BOTTOM); w = w->next; parse_border_color_side(s, w, LEFT); break; } } #define PARSE_BORDER_COLOR(side, z) \ void parse_border_ ## side ## _color(struct css_style * const s, \ const struct css_node * const v) \ { \ if (v->next != 0) \ return; \ parse_border_color_side(s, v, z); \ } PARSE_BORDER_COLOR(top, TOP) PARSE_BORDER_COLOR(right, RIGHT) PARSE_BORDER_COLOR(bottom, BOTTOM) PARSE_BORDER_COLOR(left, LEFT) void parse_border_color_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { colour c = parse_colour(v); if (c != CSS_COLOR_NONE) s->border[i].color = c; } void parse_border_style(struct css_style * const s, const struct css_node * const v) { unsigned int count = 0; const struct css_node *w; for (w = v; w; w = w->next, count++) if (w->type != CSS_NODE_IDENT) return; w = v; switch (count) { case 1: /* one value: applies to all sides */ parse_border_style_side(s, w, TOP); parse_border_style_side(s, w, RIGHT); parse_border_style_side(s, w, BOTTOM); parse_border_style_side(s, w, LEFT); break; case 2: /* (top and bottom), (left and right) */ parse_border_style_side(s, w, TOP); parse_border_style_side(s, w, BOTTOM); w = w->next; parse_border_style_side(s, w, RIGHT); parse_border_style_side(s, w, LEFT); break; case 3: /* top, (left and right), bottom */ parse_border_style_side(s, w, TOP); w = w->next; parse_border_style_side(s, w, RIGHT); parse_border_style_side(s, w, LEFT); w = w->next; parse_border_style_side(s, w, BOTTOM); break; case 4: /* top, right, bottom, left */ parse_border_style_side(s, w, TOP); w = w->next; parse_border_style_side(s, w, RIGHT); w = w->next; parse_border_style_side(s, w, BOTTOM); w = w->next; parse_border_style_side(s, w, LEFT); break; } } #define PARSE_BORDER_STYLE(side, z) \ void parse_border_ ## side ## _style(struct css_style * const s, \ const struct css_node * const v) \ { \ if (v->next != 0 || v->type != CSS_NODE_IDENT) \ return; \ parse_border_style_side(s, v, z); \ } PARSE_BORDER_STYLE(top, TOP) PARSE_BORDER_STYLE(right, RIGHT) PARSE_BORDER_STYLE(bottom, BOTTOM) PARSE_BORDER_STYLE(left, LEFT) void parse_border_style_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { css_border_style z = css_border_style_parse(v->data, v->data_length); if (z != CSS_BORDER_STYLE_UNKNOWN) s->border[i].style = z; } void parse_border(struct css_style * const s, const struct css_node * const v) { parse_border_side(s, v, TOP); parse_border_side(s, v, RIGHT); parse_border_side(s, v, BOTTOM); parse_border_side(s, v, LEFT); } #define PARSE_BORDER(side, z) \ void parse_border_ ## side(struct css_style * const s, \ const struct css_node * const v) \ { \ parse_border_side(s, v, z); \ } PARSE_BORDER(top, TOP) PARSE_BORDER(right, RIGHT) PARSE_BORDER(bottom, BOTTOM) PARSE_BORDER(left, LEFT) void parse_border_side(struct css_style * const s, const struct css_node *v, unsigned int i) { colour c; css_border_style z; if (!v->next && v->type == CSS_NODE_IDENT && v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) { s->border[i].color = CSS_COLOR_INHERIT; s->border[i].width.width = CSS_BORDER_WIDTH_INHERIT; s->border[i].style = CSS_BORDER_STYLE_INHERIT; return; } for (; v; v = v->next) { c = parse_colour(v); if (c != CSS_COLOR_NONE) { s->border[i].color = c; continue; } if (v->type == CSS_NODE_IDENT) { z = css_border_style_parse(v->data, v->data_length); if (z != CSS_BORDER_STYLE_UNKNOWN) { s->border[i].style = z; continue; } } parse_border_width_side(s, v, i); } } void parse_border_collapse(struct css_style * const s, const struct css_node * v) { css_border_collapse z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_border_collapse_parse(v->data, v->data_length); if (z != CSS_BORDER_COLLAPSE_UNKNOWN) s->border_collapse = z; } void parse_border_spacing(struct css_style * const s, const struct css_node * v) { if (v->next && v->next->next) /* more than two nodes */ return; if (!v->next) { /* one node */ if (v->type == CSS_NODE_IDENT && v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->border_spacing.border_spacing = CSS_BORDER_SPACING_INHERIT; else if (v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) { if (parse_length(&s->border_spacing.horz, v, true) == 0 && parse_length(&s->border_spacing.vert, v, true == 0)) s->border_spacing.border_spacing = CSS_BORDER_SPACING_LENGTH; } } else { /* two nodes */ if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && (v->next->type == CSS_NODE_DIMENSION || v->next->type == CSS_NODE_NUMBER)) { if (parse_length(&s->border_spacing.horz, v, true) == 0 && parse_length(&s->border_spacing.vert, v->next, true == 0)) s->border_spacing.border_spacing = CSS_BORDER_SPACING_LENGTH; } } } void parse_caption_side(struct css_style * const s, const struct css_node * v) { css_caption_side z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_caption_side_parse(v->data, v->data_length); if (z != CSS_CAPTION_SIDE_UNKNOWN) s->caption_side = z; } void parse_clear(struct css_style * const s, const struct css_node * const v) { css_clear z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_clear_parse(v->data, v->data_length); if (z != CSS_CLEAR_UNKNOWN) s->clear = z; } void parse_clip(struct css_style * const s, const struct css_node * v) { int i; struct css_node *t; if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->clip.clip = CSS_CLIP_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "auto", 4) == 0) s->clip.clip = CSS_CLIP_AUTO; break; case CSS_NODE_FUNCTION: /* must be rect(X,X,X,X) */ if (v->data_length == 5 && strncasecmp(v->data, "rect", 4) == 0) { t = v->value; for (i = 0; i != 4; i++) { switch (t->type) { case CSS_NODE_IDENT: if (t->data_length == 4 && strncasecmp(t->data, "auto", 4) == 0) { s->clip.rect[i].rect = CSS_CLIP_AUTO; } else return; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (parse_length(&s->clip.rect[i].value, t, false) != 0) return; s->clip.rect[i].rect = CSS_CLIP_RECT_LENGTH; break; default: return; } t = t->next; if (i == 3) { if (t) return; } else { if (!t || t->type != CSS_NODE_COMMA) return; } t = t->next; } s->clip.clip = CSS_CLIP_RECT; } break; default: break; } } void parse_color(struct css_style * const s, const struct css_node * const v) { colour c; if (v->next) return; c = parse_colour(v); if (c != CSS_COLOR_NONE) s->color = c; } void parse_cursor(struct css_style * const s, const struct css_node * v) { css_cursor z; for (; v; v = v->next) { switch (v->type) { case CSS_NODE_IDENT: z = css_cursor_parse(v->data, v->data_length); if (z != CSS_CURSOR_UNKNOWN) { s->cursor = z; return; } break; default: break; } } } void parse_direction(struct css_style * const s, const struct css_node * v) { css_direction z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_direction_parse(v->data, v->data_length); if (z != CSS_DIRECTION_UNKNOWN) s->direction = z; } void parse_display(struct css_style * const s, const struct css_node * const v) { css_display z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_display_parse(v->data, v->data_length); if (z != CSS_DISPLAY_UNKNOWN) s->display = z; } void parse_empty_cells(struct css_style * const s, const struct css_node * v) { css_empty_cells z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_empty_cells_parse(v->data, v->data_length); if (z != CSS_EMPTY_CELLS_UNKNOWN) s->empty_cells = z; } void parse_float(struct css_style * const s, const struct css_node * const v) { css_float z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_float_parse(v->data, v->data_length); if (z != CSS_FLOAT_UNKNOWN) s->float_ = z; } void parse_font(struct css_style * const s, const struct css_node * v) { css_font_family ff; css_font_style fs; css_font_variant fv; css_font_weight fw; s->font_family = CSS_FONT_FAMILY_SANS_SERIF; s->font_style = CSS_FONT_STYLE_NORMAL; s->font_weight = CSS_FONT_WEIGHT_NORMAL; s->line_height.size = CSS_LINE_HEIGHT_ABSOLUTE; s->line_height.value.absolute = 1.3; for (; v; v = v->next) { switch (v->type) { case CSS_NODE_IDENT: /* font-family */ ff = css_font_family_parse(v->data, v->data_length); if (ff != CSS_FONT_FAMILY_UNKNOWN) { s->font_family = ff; break; } /* font-style, font-variant, or font-weight */ fs = css_font_style_parse(v->data, v->data_length); if (fs != CSS_FONT_STYLE_UNKNOWN) { s->font_style = fs; break; } fv = css_font_variant_parse(v->data, v->data_length); if (fv != CSS_FONT_VARIANT_UNKNOWN) { s->font_variant = fv; break; } fw = css_font_weight_parse(v->data, v->data_length); if (fw != CSS_FONT_WEIGHT_UNKNOWN) { s->font_weight = fw; break; } case CSS_NODE_PERCENTAGE: case CSS_NODE_DIMENSION: parse_font_size(s, v); break; case CSS_NODE_DELIM: if (v->data[0] == '/' && v->data_length == 1 && v->next) { v = v->next; parse_line_height(s, v); } break; default: break; } } } void parse_font_family(struct css_style * const s, const struct css_node * v) { css_font_family z; for (; v; v = v->next) { switch (v->type) { case CSS_NODE_IDENT: z = css_font_family_parse(v->data, v->data_length); if (z != CSS_FONT_FAMILY_UNKNOWN) { s->font_family = z; return; } break; default: break; } } } void parse_font_size(struct css_style * const s, const struct css_node * const v) { char font_size_name[10]; struct css_font_size_entry *fs; switch (v->type) { case CSS_NODE_IDENT: if (9 < v->data_length) break; strncpy(font_size_name, v->data, v->data_length); font_size_name[v->data_length] = 0; fs = bsearch(font_size_name, css_font_size_table, sizeof css_font_size_table / sizeof css_font_size_table[0], sizeof css_font_size_table[0], (int (*)(const void *, const void *)) strcasecmp); if (fs != 0) { s->font_size.size = CSS_FONT_SIZE_LENGTH; s->font_size.value.length.unit = CSS_UNIT_PT; s->font_size.value.length.value = fs->size * option_font_size / 10; } else if (v->data_length == 6 && strncasecmp(v->data, "larger", 6) == 0) { s->font_size.size = CSS_FONT_SIZE_PERCENT; s->font_size.value.percent = SIZE_FACTOR * 100; } else if (v->data_length == 7 && strncasecmp(v->data, "smaller", 7) == 0) { s->font_size.size = CSS_FONT_SIZE_PERCENT; s->font_size.value.percent = 1 / SIZE_FACTOR * 100; } break; case CSS_NODE_PERCENTAGE: s->font_size.size = CSS_FONT_SIZE_PERCENT; s->font_size.value.percent = atof(v->data); break; case CSS_NODE_DIMENSION: if (parse_length(&s->font_size.value.length, v, true) == 0) s->font_size.size = CSS_FONT_SIZE_LENGTH; break; default: break; } } void parse_font_style(struct css_style * const s, const struct css_node * const v) { css_font_style z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_font_style_parse(v->data, v->data_length); if (z != CSS_FONT_STYLE_UNKNOWN) s->font_style = z; } void parse_font_variant(struct css_style * const s, const struct css_node * const v) { css_font_variant z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_font_variant_parse(v->data, v->data_length); if (z != CSS_FONT_VARIANT_UNKNOWN) s->font_variant = z; } void parse_font_weight(struct css_style * const s, const struct css_node * const v) { css_font_weight z; if ((v->type != CSS_NODE_IDENT && v->type != CSS_NODE_NUMBER) || v->next != 0) return; z = css_font_weight_parse(v->data, v->data_length); if (z != CSS_FONT_WEIGHT_UNKNOWN) s->font_weight = z; } void parse_height(struct css_style * const s, const struct css_node * const v) { if (v->type == CSS_NODE_IDENT && v->data_length == 4 && strncasecmp(v->data, "auto", 4) == 0) s->height.height = CSS_HEIGHT_AUTO; else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&s->height.length, v, true) == 0) s->height.height = CSS_HEIGHT_LENGTH; } void parse_letter_spacing(struct css_style * const s, const struct css_node * v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->letter_spacing.letter_spacing = CSS_LETTER_SPACING_INHERIT; else if (v->data_length == 6 && strncasecmp(v->data, "normal", 6) == 0) s->letter_spacing.letter_spacing = CSS_LETTER_SPACING_NORMAL; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (parse_length(&s->letter_spacing.length, v, false) == 0) s->letter_spacing.letter_spacing = CSS_LETTER_SPACING_LENGTH; break; default: break; } } void parse_line_height(struct css_style * const s, const struct css_node * const v) { if (v->type == CSS_NODE_IDENT && v->data_length == 6 && strncasecmp(v->data, "normal", 6) == 0) { s->line_height.size = CSS_LINE_HEIGHT_ABSOLUTE; s->line_height.value.absolute = 1.3; } else if (v->type == CSS_NODE_PERCENTAGE) { s->line_height.size = CSS_LINE_HEIGHT_PERCENT; s->line_height.value.percent = atof(v->data); } else if (v->type == CSS_NODE_DIMENSION && parse_length(&s->line_height.value.length, v, true) == 0) { s->line_height.size = CSS_LINE_HEIGHT_LENGTH; } else if (v->type == CSS_NODE_NUMBER) { s->line_height.size = CSS_LINE_HEIGHT_ABSOLUTE; s->line_height.value.absolute = atof(v->data); } } void parse_list_style(struct css_style * const s, const struct css_node * v) { css_list_style_type t = CSS_LIST_STYLE_TYPE_DISC, t2; css_list_style_position p = CSS_LIST_STYLE_POSITION_OUTSIDE, p2; css_list_style_image_type i = CSS_LIST_STYLE_IMAGE_NONE, i2; char *lsi_uri = 0; while (v) { switch (v->type) { case CSS_NODE_IDENT: t2 = css_list_style_type_parse(v->data, v->data_length); if (t2 != CSS_LIST_STYLE_TYPE_UNKNOWN) { t = t2; v = v->next; break; } p2 = css_list_style_position_parse(v->data, v->data_length); if (p2 != CSS_LIST_STYLE_POSITION_UNKNOWN) { p = p2; v = v->next; break; } /* drop through */ case CSS_NODE_STRING: case CSS_NODE_URI: if (!css_list_style_image_parse(v, &i2, &lsi_uri)) return; i = i2; v = v->next; break; default: return; } } s->list_style_type = t; s->list_style_position = p; s->list_style_image.type = i; s->list_style_image.uri = lsi_uri; } void parse_list_style_image(struct css_style * const s, const struct css_node * v) { css_list_style_image_type type; char *uri; if (v->next != 0) return; if (!css_list_style_image_parse(v, &type, &uri)) return; s->list_style_image.type = type; s->list_style_image.uri = uri; } /** * Parse a list-style-image property. * * \param node node to parse * \param type updated to list-style-image type * \param uri updated to image uri, if type is * CSS_LIST_STYLE_IMAGE_URI * \return true on success, false on parse failure */ bool css_list_style_image_parse(const struct css_node *v, css_list_style_image_type *type, char **uri) { bool string = false; const char *u; char *t, *url; switch (v->type) { case CSS_NODE_URI: for (u = v->data + 4; *u == ' ' || *u == '\t' || *u == '\r' || *u == '\n' || *u == '\f'; u++) ; if (*u == '\'' || *u == '"') { string = true; u++; } url = strndup(u, v->data_length - (u - v->data)); if (!url) return false; for (t = url + strlen(url) - 2; *t == ' ' || *t == '\t' || *t == '\r' || *t == '\n' || *t == '\f'; t--) ; if (string) *t = 0; else *(t + 1) = 0; /* for inline style attributes, the stylesheet * content is the parent HTML content */ if (v->stylesheet->type == CONTENT_HTML) *uri = url_join(url, v->stylesheet->data.html.base_url); else *uri = url_join(url, v->stylesheet->url); free(url); if (!*uri) return false; *type = CSS_LIST_STYLE_IMAGE_URI; break; case CSS_NODE_STRING: url = strndup(v->data, v->data_length); if (!url) return false; *uri = url_join(url, v->stylesheet->url); free(url); if (!*uri) return false; *type = CSS_LIST_STYLE_IMAGE_URI; break; case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) *type = CSS_LIST_STYLE_IMAGE_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "none", 4) == 0) *type = CSS_LIST_STYLE_IMAGE_NONE; break; default: return false; } return true; } void parse_list_style_position(struct css_style * const s, const struct css_node * v) { css_list_style_position z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_list_style_position_parse(v->data, v->data_length); if (z != CSS_LIST_STYLE_POSITION_UNKNOWN) s->list_style_position = z; } void parse_list_style_type(struct css_style * const s, const struct css_node * v) { css_list_style_type z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_list_style_type_parse(v->data, v->data_length); if (z != CSS_LIST_STYLE_TYPE_UNKNOWN) s->list_style_type = z; } void parse_margin(struct css_style * const s, const struct css_node * const v) { unsigned int count = 0; const struct css_node *w; for (w = v; w; w = w->next, count++) if (!((w->type == CSS_NODE_IDENT && ( (w->data_length == 7 && strncasecmp(w->data, "inherit", 7) == 0) || (w->data_length == 4 && strncasecmp(w->data, "auto", 4) == 0))) || (w->type == CSS_NODE_PERCENTAGE) || (w->type == CSS_NODE_DIMENSION) || (w->type == CSS_NODE_NUMBER))) return; w = v; switch (count) { case 1: /* one value: applies to all sides */ parse_margin_side(s, w, TOP); parse_margin_side(s, w, RIGHT); parse_margin_side(s, w, BOTTOM); parse_margin_side(s, w, LEFT); break; case 2: /* (top and bottom), (left and right) */ parse_margin_side(s, w, TOP); parse_margin_side(s, w, BOTTOM); w = w->next; parse_margin_side(s, w, RIGHT); parse_margin_side(s, w, LEFT); break; case 3: /* top, (left and right), bottom */ parse_margin_side(s, w, TOP); w = w->next; parse_margin_side(s, w, RIGHT); parse_margin_side(s, w, LEFT); w = w->next; parse_margin_side(s, w, BOTTOM); break; case 4: /* top, right, bottom, left */ parse_margin_side(s, w, TOP); w = w->next; parse_margin_side(s, w, RIGHT); w = w->next; parse_margin_side(s, w, BOTTOM); w = w->next; parse_margin_side(s, w, LEFT); break; } } #define PARSE_MARGIN_(side, z) \ void parse_margin_ ## side(struct css_style * const s, \ const struct css_node * const v) \ { \ if (v->next != 0) \ return; \ parse_margin_side(s, v, z); \ } PARSE_MARGIN_(top, TOP) PARSE_MARGIN_(right, RIGHT) PARSE_MARGIN_(bottom, BOTTOM) PARSE_MARGIN_(left, LEFT) void parse_margin_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { if (v->type == CSS_NODE_IDENT && v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->margin[i].margin = CSS_MARGIN_INHERIT; else if (v->type == CSS_NODE_IDENT && v->data_length == 4 && strncasecmp(v->data, "auto", 4) == 0) s->margin[i].margin = CSS_MARGIN_AUTO; else if (v->type == CSS_NODE_PERCENTAGE) { s->margin[i].margin = CSS_MARGIN_PERCENT; s->margin[i].value.percent = atof(v->data); } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&s->margin[i].value.length, v, false) == 0) { s->margin[i].margin = CSS_MARGIN_LENGTH; } } void parse_max_height(struct css_style *const s, const struct css_node * v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->max_height.max_height = CSS_MAX_HEIGHT_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "none", 4) == 0) s->max_height.max_height = CSS_MAX_HEIGHT_NONE; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (!parse_length(&s->max_height.value.length, v, true)) s->max_height.max_height = CSS_MAX_HEIGHT_LENGTH; break; case CSS_NODE_PERCENTAGE: s->max_height.value.percent = atof(v->data); s->max_height.max_height = CSS_MAX_HEIGHT_PERCENT; break; default: break; } } void parse_max_width(struct css_style *const s, const struct css_node * v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->max_width.max_width = CSS_MAX_WIDTH_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "none", 4) == 0) s->max_width.max_width = CSS_MAX_WIDTH_NONE; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (!parse_length(&s->max_width.value.length, v, true)) s->max_width.max_width = CSS_MAX_WIDTH_LENGTH; break; case CSS_NODE_PERCENTAGE: s->max_width.value.percent = atof(v->data); s->max_width.max_width = CSS_MAX_WIDTH_PERCENT; break; default: break; } } void parse_min_height(struct css_style *const s, const struct css_node * v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->min_height.min_height = CSS_MIN_HEIGHT_INHERIT; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (!parse_length(&s->min_height.value.length, v, true)) s->min_height.min_height = CSS_MIN_HEIGHT_LENGTH; break; case CSS_NODE_PERCENTAGE: s->min_height.value.percent = atof(v->data); s->min_height.min_height = CSS_MIN_HEIGHT_PERCENT; break; default: break; } } void parse_min_width(struct css_style *const s, const struct css_node * v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->min_width.min_width = CSS_MIN_WIDTH_INHERIT; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (!parse_length(&s->min_width.value.length, v, true)) s->min_width.min_width = CSS_MIN_WIDTH_LENGTH; break; case CSS_NODE_PERCENTAGE: s->min_width.value.percent = atof(v->data); s->min_width.min_width = CSS_MIN_WIDTH_PERCENT; break; default: break; } } void parse_orphans(struct css_style * const s, const struct css_node * const v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->orphans.orphans = CSS_ORPHANS_INHERIT; break; case CSS_NODE_NUMBER: s->orphans.value = atoi(v->data); s->orphans.orphans = CSS_ORPHANS_INTEGER; break; default: break; } } void parse_outline(struct css_style * const s, const struct css_node * v) { css_outline_color_type c = CSS_OUTLINE_COLOR_INVERT; colour col = 0, col2; css_border_style b = CSS_BORDER_STYLE_NONE, b2; struct css_border_width w = { CSS_BORDER_WIDTH_LENGTH, { 2, CSS_UNIT_PX } }; struct css_border_width w2; while (v) { switch (v->type) { case CSS_NODE_HASH: case CSS_NODE_FUNCTION: case CSS_NODE_IDENT: col2 = parse_colour(v); if (col2 != CSS_COLOR_NONE) { col = col2; c = CSS_OUTLINE_COLOR_COLOR; v = v->next; break; } if (v->type == CSS_NODE_HASH || v->type == CSS_NODE_FUNCTION) return; /* could be inherit */ if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) { c = CSS_OUTLINE_COLOR_INHERIT; v = v->next; break; } b2 = css_border_style_parse(v->data, v->data_length); if (b2 != CSS_BORDER_STYLE_UNKNOWN) { b = b2; v = v->next; break; } /* fall through */ case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (css_outline_width_parse(v, &w2)) { w = w2; v = v->next; break; } /* fall through */ default: return; } } s->outline.color.color = c; s->outline.color.value = col; s->outline.width = w; s->outline.style = b; } void parse_outline_color(struct css_style * const s, const struct css_node * const v) { colour c; if (v->next != 0) return; c = parse_colour(v); if (c == CSS_COLOR_NONE && v->type == CSS_NODE_IDENT) { if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->outline.color.color = CSS_OUTLINE_COLOR_INHERIT; else if (v->data_length == 6 && strncasecmp(v->data, "invert", 6) == 0) s->outline.color.color = CSS_OUTLINE_COLOR_INVERT; } else { s->outline.color.value = c; s->outline.color.color = CSS_OUTLINE_COLOR_COLOR; } } void parse_outline_style(struct css_style * const s, const struct css_node * const v) { css_border_style z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_border_style_parse(v->data, v->data_length); if (z != CSS_BORDER_STYLE_UNKNOWN) s->outline.style = z; } void parse_outline_width(struct css_style * const s, const struct css_node * const v) { struct css_border_width w; if (v->next != 0) return; if (!css_outline_width_parse(v, &w)) return; s->outline.width = w; } bool css_outline_width_parse(const struct css_node * v, struct css_border_width * w) { if (v->type == CSS_NODE_IDENT) { if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) { w->width = CSS_BORDER_WIDTH_INHERIT; return true; } else if (v->data_length == 4 && strncasecmp(v->data, "thin", 4) == 0) { w->width = CSS_BORDER_WIDTH_LENGTH; w->value.value = 1; w->value.unit = CSS_UNIT_PX; return true; } else if (v->data_length == 6 && strncasecmp(v->data, "medium", 6) == 0) { w->width = CSS_BORDER_WIDTH_LENGTH; w->value.value = 2; w->value.unit = CSS_UNIT_PX; return true; } else if (v->data_length == 5 && strncasecmp(v->data, "thick", 5) == 0) { w->width = CSS_BORDER_WIDTH_LENGTH; w->value.value = 4; w->value.unit = CSS_UNIT_PX; return true; } } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&w->value, v, true) == 0) { w->width = CSS_BORDER_WIDTH_LENGTH; return true; } return false; } void parse_overflow(struct css_style * const s, const struct css_node * const v) { css_overflow z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_overflow_parse(v->data, v->data_length); if (z != CSS_OVERFLOW_UNKNOWN) s->overflow = z; } void parse_padding(struct css_style * const s, const struct css_node * const v) { unsigned int count = 0; const struct css_node *w; for (w = v; w; w = w->next, count++) if (!((w->type == CSS_NODE_IDENT && w->data_length == 7 && strncasecmp(w->data, "inherit", 7) == 0) || (w->type == CSS_NODE_PERCENTAGE) || (w->type == CSS_NODE_DIMENSION) || (w->type == CSS_NODE_NUMBER))) return; w = v; switch (count) { case 1: /* one value: applies to all sides */ parse_padding_side(s, w, TOP); parse_padding_side(s, w, RIGHT); parse_padding_side(s, w, BOTTOM); parse_padding_side(s, w, LEFT); break; case 2: /* (top and bottom), (left and right) */ parse_padding_side(s, w, TOP); parse_padding_side(s, w, BOTTOM); w = w->next; parse_padding_side(s, w, RIGHT); parse_padding_side(s, w, LEFT); break; case 3: /* top, (left and right), bottom */ parse_padding_side(s, w, TOP); w = w->next; parse_padding_side(s, w, RIGHT); parse_padding_side(s, w, LEFT); w = w->next; parse_padding_side(s, w, BOTTOM); break; case 4: /* top, right, bottom, left */ parse_padding_side(s, w, TOP); w = w->next; parse_padding_side(s, w, RIGHT); w = w->next; parse_padding_side(s, w, BOTTOM); w = w->next; parse_padding_side(s, w, LEFT); break; } } #define PARSE_PADDING_(side, z) \ void parse_padding_ ## side(struct css_style * const s, \ const struct css_node * const v) \ { \ if (v->next != 0) \ return; \ parse_padding_side(s, v, z); \ } PARSE_PADDING_(top, TOP) PARSE_PADDING_(right, RIGHT) PARSE_PADDING_(bottom, BOTTOM) PARSE_PADDING_(left, LEFT) void parse_padding_side(struct css_style * const s, const struct css_node * const v, unsigned int i) { if (v->type == CSS_NODE_IDENT && v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->padding[i].padding = CSS_PADDING_INHERIT; else if (v->type == CSS_NODE_PERCENTAGE) { s->padding[i].padding = CSS_PADDING_PERCENT; s->padding[i].value.percent = atof(v->data); } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&s->padding[i].value.length, v, true) == 0) { s->padding[i].padding = CSS_PADDING_LENGTH; } } void parse_page_break_after(struct css_style * const s, const struct css_node * v) { css_page_break_after z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_page_break_after_parse(v->data, v->data_length); if (z != CSS_PAGE_BREAK_AFTER_UNKNOWN) s->page_break_after = z; } void parse_page_break_before(struct css_style * const s, const struct css_node * v) { css_page_break_before z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_page_break_before_parse(v->data, v->data_length); if (z != CSS_PAGE_BREAK_BEFORE_UNKNOWN) s->page_break_before = z; } void parse_page_break_inside(struct css_style * const s, const struct css_node * v) { css_page_break_inside z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_page_break_inside_parse(v->data, v->data_length); if (z != CSS_PAGE_BREAK_INSIDE_UNKNOWN) s->page_break_inside = z; } #define PARSE_POS(side, z) \ void parse_ ## side(struct css_style * const s, \ const struct css_node * const v) \ { \ parse_pos(s, v, z); \ } PARSE_POS(top, TOP) PARSE_POS(right, RIGHT) PARSE_POS(bottom, BOTTOM) PARSE_POS(left, LEFT) void parse_pos(struct css_style * const s, const struct css_node * v, unsigned int i) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->pos[i].pos = CSS_POS_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "auto", 4) == 0) s->pos[i].pos = CSS_POS_AUTO; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (parse_length(&s->pos[i].value.length, v, false) == 0) s->pos[i].pos = CSS_POS_LENGTH; break; case CSS_NODE_PERCENTAGE: s->pos[i].pos = CSS_POS_PERCENT; s->pos[i].value.percent = atof(v->data); break; default: break; } } void parse_position(struct css_style * const s, const struct css_node * v) { css_position z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_position_parse(v->data, v->data_length); if (z != CSS_POSITION_UNKNOWN) s->position = z; } void parse_table_layout(struct css_style * const s, const struct css_node * v) { css_table_layout z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_table_layout_parse(v->data, v->data_length); if (z != CSS_TABLE_LAYOUT_UNKNOWN) s->table_layout = z; } void parse_text_align(struct css_style * const s, const struct css_node * const v) { css_text_align z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_text_align_parse(v->data, v->data_length); if (z != CSS_TEXT_ALIGN_UNKNOWN) s->text_align = z; } void parse_text_indent(struct css_style * const s, const struct css_node * const v) { if (v->type == CSS_NODE_IDENT) { return; } else if (v->type == CSS_NODE_PERCENTAGE) { s->text_indent.size = CSS_TEXT_INDENT_PERCENT; s->text_indent.value.percent = atof(v->data); } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&s->text_indent.value.length, v, true) == 0) { s->text_indent.size = CSS_TEXT_INDENT_LENGTH; } } void parse_text_decoration(struct css_style * const s, const struct css_node * const v) { struct css_node *temp; css_text_decoration z; if (v->type != CSS_NODE_IDENT) return; z = css_text_decoration_parse(v->data, v->data_length); if (z == CSS_TEXT_DECORATION_INHERIT || z == CSS_TEXT_DECORATION_NONE) { if (v->next != 0) return; s->text_decoration = z; } if (z != CSS_TEXT_DECORATION_UNKNOWN) s->text_decoration |= z; for (temp = v->next; temp; temp = temp->next) { z = css_text_decoration_parse(temp->data, temp->data_length); if (z != CSS_TEXT_DECORATION_UNKNOWN) s->text_decoration |= z; } } void parse_text_transform(struct css_style * const s, const struct css_node * const v) { css_text_transform z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_text_transform_parse(v->data, v->data_length); if (z != CSS_TEXT_TRANSFORM_UNKNOWN) s->text_transform = z; } void parse_unicode_bidi(struct css_style * const s, const struct css_node * const v) { css_unicode_bidi z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_unicode_bidi_parse(v->data, v->data_length); if (z != CSS_UNICODE_BIDI_UNKNOWN) s->unicode_bidi = z; } void parse_vertical_align(struct css_style * const s, const struct css_node * v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_INHERIT; else if (v->data_length == 8 && strncasecmp(v->data, "baseline", 8) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_BASELINE; else if (v->data_length == 3 && strncasecmp(v->data, "sub", 3) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_SUB; else if (v->data_length == 5 && strncasecmp(v->data, "super", 5) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_SUPER; else if (v->data_length == 3 && strncasecmp(v->data, "top", 3) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_TOP; else if (v->data_length == 8 && strncasecmp(v->data, "text-top", 8) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_TEXT_TOP; else if (v->data_length == 6 && strncasecmp(v->data, "middle", 6) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_MIDDLE; else if (v->data_length == 6 && strncasecmp(v->data, "bottom", 6) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_BOTTOM; else if (v->data_length == 11 && strncasecmp(v->data, "text-bottom", 11) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_TEXT_BOTTOM; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (parse_length(&s->vertical_align.value.length, v, false) == 0) s->vertical_align.type = CSS_VERTICAL_ALIGN_LENGTH; break; case CSS_NODE_PERCENTAGE: s->vertical_align.value.percent = atof(v->data); s->vertical_align.type = CSS_VERTICAL_ALIGN_PERCENT; break; default: break; } } void parse_visibility(struct css_style * const s, const struct css_node * const v) { css_visibility z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_visibility_parse(v->data, v->data_length); if (z != CSS_VISIBILITY_UNKNOWN) s->visibility = z; } void parse_widows(struct css_style * const s, const struct css_node * const v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->widows.widows = CSS_WIDOWS_INHERIT; break; case CSS_NODE_NUMBER: s->widows.value = atoi(v->data); s->widows.widows = CSS_WIDOWS_INTEGER; break; default: break; } } void parse_width(struct css_style * const s, const struct css_node * const v) { if (v->type == CSS_NODE_IDENT && v->data_length == 4 && strncasecmp(v->data, "auto", 4) == 0) s->width.width = CSS_WIDTH_AUTO; else if (v->type == CSS_NODE_PERCENTAGE) { s->width.width = CSS_WIDTH_PERCENT; s->width.value.percent = atof(v->data); } else if ((v->type == CSS_NODE_DIMENSION || v->type == CSS_NODE_NUMBER) && parse_length(&s->width.value.length, v, true) == 0) s->width.width = CSS_WIDTH_LENGTH; } void parse_white_space(struct css_style * const s, const struct css_node * const v) { css_white_space z; if (v->type != CSS_NODE_IDENT || v->next != 0) return; z = css_white_space_parse(v->data, v->data_length); if (z != CSS_WHITE_SPACE_UNKNOWN) s->white_space = z; } void parse_word_spacing(struct css_style * const s, const struct css_node * v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->word_spacing.word_spacing = CSS_WORD_SPACING_INHERIT; else if (v->data_length == 6 && strncasecmp(v->data, "normal", 6) == 0) s->word_spacing.word_spacing = CSS_WORD_SPACING_NORMAL; break; case CSS_NODE_DIMENSION: case CSS_NODE_NUMBER: if (parse_length(&s->word_spacing.length, v, false) == 0) s->word_spacing.word_spacing = CSS_WORD_SPACING_LENGTH; break; default: break; } } void parse_z_index(struct css_style * const s, const struct css_node * const v) { if (v->next != 0) return; switch (v->type) { case CSS_NODE_IDENT: if (v->data_length == 7 && strncasecmp(v->data, "inherit", 7) == 0) s->z_index.z_index = CSS_Z_INDEX_INHERIT; else if (v->data_length == 4 && strncasecmp(v->data, "auto", 4) == 0) s->z_index.z_index = CSS_Z_INDEX_AUTO; break; case CSS_NODE_NUMBER: s->z_index.value = atoi(v->data); s->z_index.z_index = CSS_Z_INDEX_INTEGER; break; default: break; } } css_text_decoration css_text_decoration_parse(const char * const s, int length) { if (length == 7 && strncasecmp(s, "inherit", 7) == 0) return CSS_TEXT_DECORATION_INHERIT; if (length == 4 && strncasecmp(s, "none", 4) == 0) return CSS_TEXT_DECORATION_NONE; if (length == 5 && strncasecmp(s, "blink", 5) == 0) return CSS_TEXT_DECORATION_BLINK; if (length == 12 && strncasecmp(s, "line-through", 12) == 0) return CSS_TEXT_DECORATION_LINE_THROUGH; if (length == 8 && strncasecmp(s, "overline", 8) == 0) return CSS_TEXT_DECORATION_OVERLINE; if (length == 9 && strncasecmp(s, "underline", 9) == 0) return CSS_TEXT_DECORATION_UNDERLINE; return CSS_TEXT_DECORATION_UNKNOWN; } /** \} */