summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Bursa <james@netsurf-browser.org>2003-09-27 23:36:34 +0000
committerJames Bursa <james@netsurf-browser.org>2003-09-27 23:36:34 +0000
commit1975b5cd1aa6290f1e42d5ea3bdf141566080769 (patch)
tree752e2c4c193c9895a65724ef5ff9cf8cfbb14871
parent3e023a74a7389fdba9620958f4771b183f2ebc7f (diff)
downloadnetsurf-1975b5cd1aa6290f1e42d5ea3bdf141566080769.tar.gz
netsurf-1975b5cd1aa6290f1e42d5ea3bdf141566080769.tar.bz2
[project @ 2003-09-27 23:36:34 by bursa]
Implement CSS attribute selectors and parent / preceding combinators. svn path=/import/netsurf/; revision=324
-rw-r--r--css/css.c204
-rw-r--r--css/css.h9
-rw-r--r--css/parser.y17
-rw-r--r--css/ruleset.c5
-rw-r--r--css/scanner.l1
-rw-r--r--debug/filetyped.c4
-rw-r--r--render/box.c2
7 files changed, 168 insertions, 74 deletions
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; }
diff --git a/debug/filetyped.c b/debug/filetyped.c
index 1695a1c18..283c4d142 100644
--- a/debug/filetyped.c
+++ b/debug/filetyped.c
@@ -16,8 +16,10 @@
const char *fetch_filetype(const char *unix_path)
{
+ int l;
LOG(("unix path %s", unix_path));
- if (strcasecmp(unix_path, "home/james/Projects/netsurf/CSS") == 0)
+ l = strlen(unix_path);
+ if (2 < l && strcasecmp(unix_path + l - 3, "css") == 0)
return "text/css";
return "text/html";
}
diff --git a/render/box.c b/render/box.c
index 0c67fa8df..8d87eca23 100644
--- a/render/box.c
+++ b/render/box.c
@@ -479,7 +479,7 @@ struct css_style * box_get_style(struct content ** stylesheet,
for (i = 0; i != stylesheet_count; i++) {
if (stylesheet[i] != 0) {
assert(stylesheet[i]->type == CONTENT_CSS);
- css_get_style(stylesheet[i], selector, depth, style_new);
+ css_get_style(stylesheet[i], n, style_new);
}
}
css_cascade(style, style_new);