mirror of
https://github.com/netsurf-browser/netsurf
synced 2025-01-11 21:39:56 +03:00
[project @ 2003-09-27 23:36:34 by bursa]
Implement CSS attribute selectors and parent / preceding combinators. svn path=/import/netsurf/; revision=324
This commit is contained in:
parent
3e023a74a7
commit
1975b5cd1a
224
css/css.c
224
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 *c, struct css_selector * selector,
|
||||
unsigned int selectors, struct css_style * style)
|
||||
void css_get_style(struct content *css, xmlNode *element,
|
||||
struct css_style *style)
|
||||
{
|
||||
struct css_stylesheet *stylesheet = c->data.css.css;
|
||||
struct node *r, *n, *m;
|
||||
unsigned int hash, i, done_empty = 0;
|
||||
struct css_stylesheet *stylesheet = css->data.css.css;
|
||||
struct node *rule;
|
||||
unsigned int hash, i;
|
||||
|
||||
/*LOG(("stylesheet '%s'", c->url));*/
|
||||
/* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
/*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;
|
||||
}
|
||||
}
|
||||
|
||||
matched:
|
||||
/* TODO: sort by specificity */
|
||||
/*LOG(("matched rule %p", r));*/
|
||||
css_merge(style, r->style);
|
||||
|
||||
not_matched:
|
||||
|
||||
}
|
||||
/* 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 != 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);
|
||||
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.
|
||||
*/
|
||||
|
||||
bool css_match_rule(struct node *rule, xmlNode *element)
|
||||
{
|
||||
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;
|
||||
|
||||
case NODE_CLASS:
|
||||
s = (char *) xmlGetProp(element, (const xmlChar *) "class");
|
||||
if (s && strcasecmp(detail->data, s) == 0)
|
||||
match = true;
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
case COMB_PARENT:
|
||||
if (!element->parent)
|
||||
return false;
|
||||
return css_match_rule(rule->right, element->parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
17
css/parser.y
17
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($$); }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user