From 1975b5cd1aa6290f1e42d5ea3bdf141566080769 Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sat, 27 Sep 2003 23:36:34 +0000 Subject: [project @ 2003-09-27 23:36:34 by bursa] Implement CSS attribute selectors and parent / preceding combinators. svn path=/import/netsurf/; revision=324 --- css/css.c | 204 ++++++++++++++++++++++++++++++++++++++-------------------- css/css.h | 9 ++- css/parser.y | 17 ++++- css/ruleset.c | 5 +- css/scanner.l | 1 + 5 files changed, 164 insertions(+), 72 deletions(-) (limited to 'css') diff --git a/css/css.c b/css/css.c index 58eaa662c..3330acf13 100644 --- a/css/css.c +++ b/css/css.c @@ -33,6 +33,7 @@ struct decl { static void css_atimport_callback(content_msg msg, struct content *css, void *p1, void *p2, const char *error); +static bool css_match_rule(struct node *rule, xmlNode *element); const struct css_style css_base_style = { 0xffffff, @@ -126,6 +127,8 @@ int css_convert(struct content *c, unsigned int width, unsigned int height) css_parser_Free(c->data.css.css->parser, free); css_lex_destroy(c->data.css.css->lexer); + /*css_dump_stylesheet(c->data.css.css);*/ + /* complete fetch of any imported stylesheets */ while (c->active != 0) { LOG(("importing %i from '%s'", c->active, c->url)); @@ -198,6 +201,7 @@ struct node * css_new_node(node_type type, char *data, struct node *node = xcalloc(1, sizeof(*node)); node->type = type; node->data = data; + node->data2 = 0; node->left = left; node->right = right; node->next = 0; @@ -357,90 +361,150 @@ void css_atimport_callback(content_msg msg, struct content *css, } +/** + * Find the style which applies to an element. + */ + +void css_get_style(struct content *css, xmlNode *element, + struct css_style *style) +{ + struct css_stylesheet *stylesheet = css->data.css.css; + struct node *rule; + unsigned int hash, i; + /* match rules which end with the same element */ + hash = css_hash((char *) element->name); + for (rule = stylesheet->rule[hash]; rule; rule = rule->next) + if (css_match_rule(rule, element)) + css_merge(style, rule->style); + /* match rules which apply to all elements */ + 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); +} +/** + * Determine if a rule applies to an element. + */ -void css_get_style(struct content *c, struct css_selector * selector, - unsigned int selectors, struct css_style * style) +bool css_match_rule(struct node *rule, xmlNode *element) { - struct css_stylesheet *stylesheet = c->data.css.css; - struct node *r, *n, *m; - unsigned int hash, i, done_empty = 0; + bool match; + char *s, *word, *space; + unsigned int i; + struct node *detail; + xmlNode *anc, *prev; + + if (rule->data && strcasecmp(rule->data, (char *) element->name) != 0) + return false; + + for (detail = rule->left; detail; detail = detail->next) { + match = false; + switch (detail->type) { + case NODE_ID: + s = (char *) xmlGetProp(element, (const xmlChar *) "id"); + if (s && strcasecmp(detail->data, s) == 0) + match = true; + break; - /*LOG(("stylesheet '%s'", c->url));*/ + case NODE_CLASS: + s = (char *) xmlGetProp(element, (const xmlChar *) "class"); + if (s && strcasecmp(detail->data, s) == 0) + match = true; + break; - hash = css_hash(selector[selectors - 1].element); - for (r = stylesheet->rule[hash]; ; r = r->next) { - if (r == 0 && !done_empty) { - r = stylesheet->rule[0]; - done_empty = 1; - } - if (r == 0) - break; - i = selectors - 1; - n = r; - /* compare element */ - if (n->data != 0) - if (strcasecmp(selector[i].element, n->data) != 0) - goto not_matched; - /*LOG(("top element '%s' matched", selector[i].element));*/ - while (1) { - /* class and id */ - for (m = n->left; m != 0; m = m->next) { - if (m->type == NODE_ID) { - /* TODO: check if case sensitive */ - if (selector[i].id == 0 || - strcmp(selector[i].id, m->data + 1) != 0) - goto not_matched; - } else if (m->type == NODE_CLASS) { - /* TODO: check if case sensitive */ - if (selector[i].class == 0 || - strcmp(selector[i].class, m->data) != 0) - goto not_matched; - } else { - goto not_matched; + case NODE_ATTRIB: + /* matches if an attribute is present */ + s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); + if (s) + match = true; + break; + + case NODE_ATTRIB_EQ: + /* matches if an attribute has a certain value */ + s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); + if (s && strcasecmp(detail->data2, s) == 0) + match = true; + break; + + case NODE_ATTRIB_INC: + /* matches if one of the space separated words + * in the attribute is equal */ + s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); + if (s) { + word = s; + do { + space = strchr(word, ' '); + if (space) + *space = 0; + if (strcasecmp(word, detail->data2) == 0) { + match = true; + break; + } + word = space; + } while (word); } - } - /*LOG(("class and id matched"));*/ - /* ancestors etc. */ - if (n->comb == COMB_NONE) - goto matched; /* match successful */ - else if (n->comb == COMB_ANCESTOR) { - /* search for ancestor */ - assert(n->right != 0); - n = n->right; - if (n->data == 0) - goto not_matched; /* TODO: handle this case */ - /*LOG(("searching for ancestor '%s'", n->data));*/ - while (i != 0 && strcasecmp(selector[i - 1].element, n->data) != 0) - i--; - if (i == 0) - goto not_matched; - i--; - /*LOG(("found"));*/ - } else { - /* TODO: COMB_PRECEDED, COMB_PARENT */ - goto not_matched; - } + break; + + case NODE_ATTRIB_DM: + /* matches if a prefix up to a hyphen matches */ + s = (char *) xmlGetProp(element, (const xmlChar *) detail->data); + if (s) { + i = strlen(detail->data2); + if (strncasecmp(detail->data2, s, i) == 0 && + (s[i] == '-' || s[i] == 0)) + match = true; + } + break; + + default: + assert(0); } + if (s) + xmlFree(s); + if (!match) + return false; + } + + if (!rule->right) + return true; + + switch (rule->comb) { + case COMB_ANCESTOR: + for (anc = element->parent; anc; anc = anc->parent) + if (css_match_rule(rule->right, anc)) + return true; + break; -matched: - /* TODO: sort by specificity */ - /*LOG(("matched rule %p", r));*/ - css_merge(style, r->style); + case COMB_PRECEDED: + for (prev = element->prev; + prev && prev->type != XML_ELEMENT_NODE; + prev = prev->prev) + ; + if (!prev) + return false; + return css_match_rule(rule->right, prev); + break; -not_matched: + case COMB_PARENT: + if (!element->parent) + return false; + return css_match_rule(rule->right, element->parent); + break; + default: + assert(0); } - /* imported stylesheets */ - for (i = 0; i != c->data.css.import_count; i++) - if (c->data.css.import_content[i] != 0) - css_get_style(c->data.css.import_content[i], selector, - selectors, style); + return false; } @@ -546,6 +610,10 @@ void css_dump_stylesheet(const struct css_stylesheet * stylesheet) switch (m->type) { case NODE_ID: fprintf(stderr, "%s", m->data); break; case NODE_CLASS: fprintf(stderr, ".%s", m->data); break; + case NODE_ATTRIB: fprintf(stderr, "[%s]", m->data); break; + case NODE_ATTRIB_EQ: fprintf(stderr, "[%s=%s]", m->data, m->data2); break; + case NODE_ATTRIB_INC: fprintf(stderr, "[%s~=%s]", m->data, m->data2); break; + case NODE_ATTRIB_DM: fprintf(stderr, "[%s|=%s]", m->data, m->data2); break; default: fprintf(stderr, "unexpected node"); } } diff --git a/css/css.h b/css/css.h index f3405a407..f09cf55a0 100644 --- a/css/css.h +++ b/css/css.h @@ -8,6 +8,7 @@ #ifndef _NETSURF_CSS_CSS_H_ #define _NETSURF_CSS_CSS_H_ +#include "libxml/HTMLparser.h" #include "css_enum.h" /** @@ -125,6 +126,10 @@ typedef enum { NODE_SELECTOR, NODE_ID, NODE_CLASS, + NODE_ATTRIB, + NODE_ATTRIB_EQ, + NODE_ATTRIB_INC, + NODE_ATTRIB_DM, } node_type; typedef enum { @@ -137,6 +142,7 @@ typedef enum { struct node { node_type type; char *data; + char *data2; struct node *left; struct node *right; struct node *next; @@ -195,8 +201,7 @@ void css_parser_(void *yyp, int yymajor, char* yyminor, #endif -void css_get_style(struct content *c, struct css_selector * selector, - unsigned int selectors, struct css_style * style); +void css_get_style(struct content *c, xmlNode *n, struct css_style * style); void css_cascade(struct css_style * const style, const struct css_style * const apply); void css_merge(struct css_style * const style, const struct css_style * const apply); void css_parse_property_list(struct css_style * style, char * str); diff --git a/css/parser.y b/css/parser.y index 63b01e264..0e2ff02ab 100644 --- a/css/parser.y +++ b/css/parser.y @@ -93,7 +93,21 @@ detail_list(A) ::= HASH(B) detail_list(C). { A = css_new_node(NODE_ID, B, 0, 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; } -/* TODO: attrib, pseudo */ +detail_list(A) ::= LBRAC IDENT(B) RBRAC detail_list(C). + { A = css_new_node(NODE_ATTRIB, B, 0, 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; } +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; } +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; } +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; } +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; } +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; } +/* TODO: pseudo */ declaration_list(A) ::= . { A = 0; } @@ -191,6 +205,7 @@ any(A) ::= LBRAC any_list(B) RBRAC. %destructor selector_list { css_free_node($$); } %destructor selector { css_free_node($$); } %destructor simple_selector { css_free_node($$); } +%destructor detail_list { css_free_node($$); } %destructor declaration_list { css_free_node($$); } %destructor declaration { css_free_node($$); } %destructor value { css_free_node($$); } diff --git a/css/ruleset.c b/css/ruleset.c index 62533fcc6..a211bfb10 100644 --- a/css/ruleset.c +++ b/css/ruleset.c @@ -206,7 +206,10 @@ int compare_selectors(struct node *n0, struct node *n1) int found = 0; for (m1 = n1->left; m1 != 0; m1 = m1->next) { /* TODO: should this be case sensitive for IDs? */ - if (m0->type == m1->type && strcasecmp(m0->data, m1->data) == 0) { + if (m0->type == m1->type && + strcasecmp(m0->data, m1->data) == 0 && + ((m0->data2 == 0 && m1->data2 == 0) || + strcasecmp(m0->data2, m1->data2) == 0)) { found = 1; break; } diff --git a/css/scanner.l b/css/scanner.l index 820f7cb5d..ccac33c1d 100644 --- a/css/scanner.l +++ b/css/scanner.l @@ -63,6 +63,7 @@ U\+[0-9A-F?]{1,6}(-[0-9A-F]{1,6})? { [ \t\r\n\f]+ /* ignore whitespace */ \/\*[^*]*\*+([^/][^*]*\*+)*\/ /* ignore comments */ {ident}\( { return FUNCTION; } += { return EQUALS; } ~= { return INCLUDES; } "|=" { return DASHMATCH; } : { return COLON; } -- cgit v1.2.3