From fd7078b1ad470c3de96d32c3699eb862259df990 Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sun, 28 Sep 2003 17:37:43 +0000 Subject: [project @ 2003-09-28 17:37:43 by bursa] Implement CSS specificity and fix bugs. svn path=/import/netsurf/; revision=329 --- !NetSurf/Resources/CSS,f79 | 20 +++++++++++++++++--- css/css.c | 46 +++++++++++++++++++++++++++++++++------------- css/css.h | 2 ++ css/parser.y | 35 +++++++++++++++++++++++------------ css/ruleset.c | 32 ++++++++++++++++++++++++-------- render/box.c | 20 -------------------- 6 files changed, 99 insertions(+), 56 deletions(-) diff --git a/!NetSurf/Resources/CSS,f79 b/!NetSurf/Resources/CSS,f79 index f3bdf7464..d87681dd8 100644 --- a/!NetSurf/Resources/CSS,f79 +++ b/!NetSurf/Resources/CSS,f79 @@ -38,7 +38,21 @@ hr { background-color: #000; height: 1px; } center { text-align: center; } small { font-size: smaller; } big { font-size: larger; } -input, select { width: 10em; height: 2em; background-color: #eeeebb; } -textarea { display: inline-block; width: 20em; height: 4em; background-color: #eeeebb; } -button { background-color: #ddd; } +select, input { background-color: #eeb; color: #000; width: 10em; height: 2em; } +textarea { background-color: #eeb; color: #000; } +input[type=button], input[type=image], input[type=reset], input[type=submit], + button { background-color: #ddd; color: #000; width: auto; + height: auto; } +input[type=checkbox], input[type=radio] { background-color: transparent; + width: 22px; height: 22px; } + +[align=left] { text-align: left; } +[align=center] { text-align: center; } +[align=right] { text-align: right; } +img[align=left], table[align=left] { float: left; } +img[align=right], table[align=right] { float: right; text-align: left; } + +[clear=all] { clear: both; } +[clear=left] { clear: left; } +[clear=right] { clear: right; } diff --git a/css/css.c b/css/css.c index 3330acf13..735412e0d 100644 --- a/css/css.c +++ b/css/css.c @@ -207,6 +207,7 @@ struct node * css_new_node(node_type type, char *data, node->next = 0; node->comb = COMB_NONE; node->style = 0; + node->specificity = 0; return node; } @@ -225,6 +226,14 @@ void css_free_node(struct node *node) free(node); } +char *css_unquote(char *s) +{ + unsigned int len = strlen(s); + memmove(s, s + 1, len); + s[len - 2] = 0; + return s; +} + void css_atimport(struct content *c, struct node *node) { @@ -260,8 +269,7 @@ void css_atimport(struct content *c, struct node *node) break; case NODE_STRING: LOG(("STRING '%s'", node->data)); - url = xstrdup(node->data + 1); - *(url + strlen(url) - 1) = 0; + url = xstrdup(node->data); break; default: return; @@ -372,6 +380,12 @@ void css_get_style(struct content *css, xmlNode *element, struct node *rule; unsigned int hash, i; + /* imported stylesheets */ + for (i = 0; i != css->data.css.import_count; i++) + if (css->data.css.import_content[i] != 0) + css_get_style(css->data.css.import_content[i], + element, style); + /* match rules which end with the same element */ hash = css_hash((char *) element->name); for (rule = stylesheet->rule[hash]; rule; rule = rule->next) @@ -382,12 +396,6 @@ void css_get_style(struct content *css, xmlNode *element, for (rule = stylesheet->rule[0]; rule; rule = rule->next) if (css_match_rule(rule, element)) css_merge(style, rule->style); - - /* imported stylesheets */ - for (i = 0; i != css->data.css.import_count; i++) - if (css->data.css.import_content[i] != 0) - css_get_style(css->data.css.import_content[i], - element, style); } @@ -411,14 +419,25 @@ bool css_match_rule(struct node *rule, xmlNode *element) switch (detail->type) { case NODE_ID: s = (char *) xmlGetProp(element, (const xmlChar *) "id"); - if (s && strcasecmp(detail->data, s) == 0) + if (s && strcasecmp(detail->data + 1, s) == 0) match = true; break; case NODE_CLASS: s = (char *) xmlGetProp(element, (const xmlChar *) "class"); - if (s && strcasecmp(detail->data, s) == 0) - match = true; + if (s) { + word = s; + do { + space = strchr(word, ' '); + if (space) + *space = 0; + if (strcasecmp(word, detail->data) == 0) { + match = true; + break; + } + word = space + 1; + } while (space); + } break; case NODE_ATTRIB: @@ -449,8 +468,8 @@ bool css_match_rule(struct node *rule, xmlNode *element) match = true; break; } - word = space; - } while (word); + word = space + 1; + } while (space); } break; @@ -619,6 +638,7 @@ void css_dump_stylesheet(const struct css_stylesheet * stylesheet) } fprintf(stderr, " "); } + fprintf(stderr, "%lx ", r->specificity); css_dump_style(r->style); fprintf(stderr, "\n"); } diff --git a/css/css.h b/css/css.h index f09cf55a0..378dc5304 100644 --- a/css/css.h +++ b/css/css.h @@ -148,6 +148,7 @@ struct node { struct node *next; combinator comb; struct css_style *style; + unsigned long specificity; }; #include "netsurf/css/scanner.h" @@ -186,6 +187,7 @@ void css_destroy(struct content *c); struct node * css_new_node(node_type type, char *data, struct node *left, struct node *right); void css_free_node(struct node *node); +char *css_unquote(char *s); void css_atimport(struct content *c, struct node *node); void css_add_ruleset(struct content *c, struct node *selector, diff --git a/css/parser.y b/css/parser.y index 0e2ff02ab..75706eb16 100644 --- a/css/parser.y +++ b/css/parser.y @@ -70,7 +70,8 @@ selector_list(A) ::= selector_list(B) COMMA selector(C). selector(A) ::= simple_selector(B). { A = B; } selector(A) ::= selector(B) combinator(C) simple_selector(D). - { D->right = B; D->comb = C; A = D; } + { D->right = B; D->comb = C; A = D; + A->specificity += B->specificity; } combinator(A) ::= . { A = COMB_ANCESTOR; } @@ -80,7 +81,8 @@ combinator(A) ::= GT. { A = COMB_PARENT; } simple_selector(A) ::= element_name(B) detail_list(C). - { A = css_new_node(NODE_SELECTOR, B, C, 0); } + { A = css_new_node(NODE_SELECTOR, B, C, 0); + A->specificity = (B ? 1 : 0) + (C ? C->specificity : 0); } element_name(A) ::= . { A = 0; } @@ -90,23 +92,32 @@ element_name(A) ::= IDENT(B). detail_list(A) ::= . { A = 0; } detail_list(A) ::= HASH(B) detail_list(C). - { A = css_new_node(NODE_ID, B, 0, 0); A->next = C; } + { A = css_new_node(NODE_ID, B, 0, 0); + A->specificity = 0x10000 + (C ? C->specificity : 0); A->next = C; } detail_list(A) ::= DOT IDENT(B) detail_list(C). - { A = css_new_node(NODE_CLASS, B, 0, 0); A->next = C; } + { A = css_new_node(NODE_CLASS, B, 0, 0); + A->specificity = 0x100 + (C ? C->specificity : 0); A->next = C; } detail_list(A) ::= LBRAC IDENT(B) RBRAC detail_list(C). - { A = css_new_node(NODE_ATTRIB, B, 0, 0); A->next = C; } + { A = css_new_node(NODE_ATTRIB, B, 0, 0); + A->specificity = 0x100 + (C ? C->specificity : 0); A->next = C; } detail_list(A) ::= LBRAC IDENT(B) EQUALS IDENT(C) RBRAC detail_list(D). - { A = css_new_node(NODE_ATTRIB_EQ, B, 0, 0); A->data2 = C; A->next = D; } + { A = css_new_node(NODE_ATTRIB_EQ, B, 0, 0); A->data2 = C; + A->specificity = 0x100 + (D ? D->specificity : 0); A->next = D; } detail_list(A) ::= LBRAC IDENT(B) EQUALS STRING(C) RBRAC detail_list(D). - { A = css_new_node(NODE_ATTRIB_EQ, B, 0, 0); A->data2 = C; A->next = D; } + { A = css_new_node(NODE_ATTRIB_EQ, B, 0, 0); A->data2 = css_unquote(C); + A->specificity = 0x100 + (D ? D->specificity : 0); A->next = D; } detail_list(A) ::= LBRAC IDENT(B) INCLUDES IDENT(C) RBRAC detail_list(D). - { A = css_new_node(NODE_ATTRIB_INC, B, 0, 0); A->data2 = C; A->next = D; } + { A = css_new_node(NODE_ATTRIB_INC, B, 0, 0); A->data2 = C; + A->specificity = 0x100 + (D ? D->specificity : 0); A->next = D; } detail_list(A) ::= LBRAC IDENT(B) INCLUDES STRING(C) RBRAC detail_list(D). - { A = css_new_node(NODE_ATTRIB_INC, B, 0, 0); A->data2 = C; A->next = D; } + { A = css_new_node(NODE_ATTRIB_INC, B, 0, 0); A->data2 = css_unquote(C); + A->specificity = 0x100 + (D ? D->specificity : 0); A->next = D; } detail_list(A) ::= LBRAC IDENT(B) DASHMATCH IDENT(C) RBRAC detail_list(D). - { A = css_new_node(NODE_ATTRIB_DM, B, 0, 0); A->data2 = C; A->next = D; } + { A = css_new_node(NODE_ATTRIB_DM, B, 0, 0); A->data2 = C; + A->specificity = 0x100 + (D ? D->specificity : 0); A->next = D; } detail_list(A) ::= LBRAC IDENT(B) DASHMATCH STRING(C) RBRAC detail_list(D). - { A = css_new_node(NODE_ATTRIB_DM, B, 0, 0); A->data2 = C; A->next = D; } + { A = css_new_node(NODE_ATTRIB_DM, B, 0, 0); A->data2 = css_unquote(C); + A->specificity = 0x100 + (D ? D->specificity : 0); A->next = D; } /* TODO: pseudo */ declaration_list(A) ::= . @@ -148,7 +159,7 @@ any(A) ::= PERCENTAGE(B). any(A) ::= DIMENSION(B). { A = css_new_node(NODE_DIMENSION, B, 0, 0); } any(A) ::= STRING(B). - { A = css_new_node(NODE_STRING, B, 0, 0); } + { A = css_new_node(NODE_STRING, css_unquote(B), 0, 0); } any(A) ::= DELIM(B). { A = css_new_node(NODE_DELIM, B, 0, 0); } any(A) ::= URI(B). diff --git a/css/ruleset.c b/css/ruleset.c index a211bfb10..52403c241 100644 --- a/css/ruleset.c +++ b/css/ruleset.c @@ -33,7 +33,7 @@ struct font_size_entry { }; -static int compare_selectors(struct node *n0, struct node *n1); +static int compare_selectors(const struct node *n0, const struct node *n1); static int parse_length(struct css_length * const length, const struct node * const v, bool non_negative); static colour parse_colour(const struct node * const v); @@ -110,8 +110,9 @@ void css_add_ruleset(struct content *c, struct node *selector, struct node *declaration) { + bool found; struct css_stylesheet *stylesheet = c->data.css.css; - struct node *n, *sel, *next_sel; + struct node *n, *sel, *next_sel, *prev; struct css_style *style; unsigned int hash; @@ -139,18 +140,33 @@ void css_add_ruleset(struct content *c, continue; /* check if this selector is already present */ + found = false; + prev = 0; hash = css_hash(sel->data); - for (n = stylesheet->rule[hash]; n != 0; n = n->next) - if (compare_selectors(sel, n)) + /* 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 (compare_selectors(sel, n)) { + found = true; break; - if (n == 0) { + } + } + if (!found) { /* not present: construct a new struct css_style */ LOG(("constructing new style")); style = xcalloc(1, sizeof(*style)); memcpy(style, &css_empty_style, sizeof(*style)); sel->style = style; - sel->next = stylesheet->rule[hash]; - stylesheet->rule[hash] = sel; + sel->next = n; + if (prev) + prev->next = sel; + else + stylesheet->rule[hash] = sel; c->size += sizeof(*style); } else { /* already exists: augument existing style */ @@ -182,7 +198,7 @@ void css_add_declarations(struct css_style *style, struct node *declaration) } -int compare_selectors(struct node *n0, struct node *n1) +int compare_selectors(const struct node *n0, const struct node *n1) { struct node *m0, *m1; unsigned int count0 = 0, count1 = 0; diff --git a/render/box.c b/render/box.c index 8d87eca23..b681b9637 100644 --- a/render/box.c +++ b/render/box.c @@ -485,19 +485,6 @@ struct css_style * box_get_style(struct content ** stylesheet, css_cascade(style, style_new); free(style_new); - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "align"))) { - if (strcmp((const char *) n->name, "table") == 0 || - strcmp((const char *) n->name, "img") == 0) { - if (stricmp(s, "left") == 0) style->float_ = CSS_FLOAT_LEFT; - else if (stricmp(s, "right") == 0) style->float_ = CSS_FLOAT_RIGHT; - } else { - if (stricmp(s, "left") == 0) style->text_align = CSS_TEXT_ALIGN_LEFT; - else if (stricmp(s, "center") == 0) style->text_align = CSS_TEXT_ALIGN_CENTER; - else if (stricmp(s, "right") == 0) style->text_align = CSS_TEXT_ALIGN_RIGHT; - } - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "bgcolor"))) { unsigned int r, g, b; if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) @@ -507,13 +494,6 @@ struct css_style * box_get_style(struct content ** stylesheet, xmlFree(s); } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "clear"))) { - if (stricmp(s, "all") == 0) style->clear = CSS_CLEAR_BOTH; - else if (stricmp(s, "left") == 0) style->clear = CSS_CLEAR_LEFT; - else if (stricmp(s, "right") == 0) style->clear = CSS_CLEAR_RIGHT; - xmlFree(s); - } - if ((s = (char *) xmlGetProp(n, (const xmlChar *) "color"))) { unsigned int r, g, b; if (s[0] == '#' && sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3) -- cgit v1.2.3