summaryrefslogtreecommitdiff
path: root/css
diff options
context:
space:
mode:
authorJames Bursa <james@netsurf-browser.org>2004-05-01 17:48:38 +0000
committerJames Bursa <james@netsurf-browser.org>2004-05-01 17:48:38 +0000
commitd7a4adf48149e0d6d1ae0cc79c1a6d73aa02dd3f (patch)
tree817197439dc2fde67e6b6100eef7eae02ad2a89f /css
parent199eee4b0bc2d825d0b48fda3644e81712a2b892 (diff)
downloadnetsurf-d7a4adf48149e0d6d1ae0cc79c1a6d73aa02dd3f.tar.gz
netsurf-d7a4adf48149e0d6d1ae0cc79c1a6d73aa02dd3f.tar.bz2
[project @ 2004-05-01 17:48:38 by bursa]
CSS parsing improvements: new tokeniser using re2c, improve memory-exhaustion behaviour, plug leaks, reduce memory usage, clean up code, add source documention. svn path=/import/netsurf/; revision=806
Diffstat (limited to 'css')
-rw-r--r--css/css.c1093
-rw-r--r--css/css.h110
-rwxr-xr-xcss/makeenum7
-rw-r--r--css/parser.y260
-rw-r--r--css/ruleset.c352
-rw-r--r--css/scanner.l145
6 files changed, 1338 insertions, 629 deletions
diff --git a/css/css.c b/css/css.c
index 9745ed281..72bc32d40 100644
--- a/css/css.c
+++ b/css/css.c
@@ -5,6 +5,72 @@
* Copyright 2004 James Bursa <bursa@users.sourceforge.net>
*/
+/** \file
+ * CSS handling (implementation).
+ *
+ * See CSS 2.1 chapter 5 for the terms used here.
+ *
+ * CSS style sheets are stored as a hash table mapping selectors to styles.
+ * Selectors are hashed by the <em>type selector</em> of the last <em>simple
+ * selector</em> in the selector. The <em>universal selector</em> is hashed to
+ * chain 0.
+ *
+ * A <em>simple selector</em> is a struct css_selector with type
+ * CSS_SELECTOR_ELEMENT. The data field is the <em>type selector</em>, or 0 for
+ * the <em>universal selector</em>. Any <em>attribute selectors</em>, <em>ID
+ * selectors</em>, or <em>pseudo-classes</em> form a linked list of
+ * css_selector hanging from detail.
+ *
+ * A <em>selector</em> is a linked list by the combiner field of these simple
+ * selectors, in reverse order that they appear in the concrete syntax. The last
+ * simple selector is first, then the previous one is linked at combiner and has
+ * relationship comb to the last, etc.
+ *
+ * Selectors are then linked in each hash chain by next, in order of increasing
+ * specificity.
+ *
+ * As an example, the stylesheet
+ * \code
+ * th { [1] }
+ * div#id1 > h4.class1 { [2] }
+ * center * { [3] } \endcode
+ *
+ * would result in a struct css_stylesheet (content::data.css.css) like this
+ * \dot
+ * digraph example {
+ * node [shape=record, fontname=Helvetica, fontsize=9];
+ * edge [fontname=Helvetica, fontsize=9];
+ * css -> n0 [label="rule[0]"];
+ * css -> n2 [label="rule[29]"];
+ *
+ * n0 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata 0\lcomb CSS_COMB_ANCESTOR\lspecificity 2\l"];
+ * n0 -> n1 [label="combiner"];
+ * n0 -> n0style [label="style"]; n0style [label="[3]"];
+ *
+ * n1 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"center\"\lcomb CSS_COMB_NONE\lspecificity 1\l"];
+ *
+ * n2 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"th\"\lcomb CSS_COMB_NONE\lspecificity 1\l"];
+ * n2 -> n3 [label="next"];
+ * n2 -> n2style [label="style"]; n2style [label="[1]"];
+ *
+ * n3 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"h4\"\lcomb CSS_COMB_PARENT\lspecificity 0x10102\l"];
+ * n3 -> n4 [label="detail"];
+ * n3 -> n5 [label="combiner"];
+ * n3 -> n3style [label="style"]; n3style [label="[2]"];
+ *
+ * n4 [label="css_selector\ntype CSS_SELECTOR_CLASS\ldata \"class1\"\lcomb CSS_COMB_NONE\lspecificity 0x100\l"];
+ *
+ * n5 [label="css_selector\ntype CSS_SELECTOR_ELEMENT\ldata \"div\"\lcomb CSS_COMB_NONE\lspecificity 0x10001\l"];
+ * n5 -> n6 [label="detail"];
+ *
+ * n6 [label="css_selector\ntype CSS_SELECTOR_ID\ldata \"#id1\"\lcomb CSS_COMB_NONE\lspecificity 0x10000\l"];
+ * }
+ * \enddot
+ *
+ * (any fields not shown are 0). In this example the first two rules happen to
+ * have hashed to the same value.
+ */
+
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@@ -24,19 +90,17 @@
#include "netsurf/utils/url.h"
#include "netsurf/utils/utils.h"
-/**
- * internal structures
- */
-
-struct decl {
- unsigned long score;
- struct rule * rule;
-};
static void css_atimport_callback(content_msg msg, struct content *css,
void *p1, void *p2, const char *error);
-static bool css_match_rule(struct css_node *rule, xmlNode *element);
-
+static bool css_match_rule(struct css_selector *rule, xmlNode *element);
+static bool css_match_detail(const struct css_selector *detail,
+ xmlNode *element);
+static void css_dump_length(const struct css_length * const length);
+static void css_dump_selector(const struct css_selector *r);
+
+/** Default style for a document. These are the 'Initial values' from the
+ * spec. */
const struct css_style css_base_style = {
0xffffff,
{ { 0x000000, { CSS_BORDER_WIDTH_LENGTH,
@@ -76,6 +140,7 @@ const struct css_style css_base_style = {
CSS_WHITE_SPACE_NORMAL
};
+/** Style with no values set. */
const struct css_style css_empty_style = {
CSS_COLOR_INHERIT,
{ { CSS_COLOR_INHERIT, { CSS_BORDER_WIDTH_INHERIT,
@@ -115,6 +180,8 @@ const struct css_style css_empty_style = {
CSS_WHITE_SPACE_INHERIT
};
+/** Default style for an element. These should be INHERIT if 'Inherited' is yes,
+ * and the 'Initial value' otherwise. */
const struct css_style css_blank_style = {
TRANSPARENT,
{ { 0x000000, { CSS_BORDER_WIDTH_LENGTH,
@@ -155,27 +222,27 @@ const struct css_style css_blank_style = {
};
+/**
+ * Convert a CONTENT_CSS for use.
+ */
int css_convert(struct content *c, unsigned int width, unsigned int height)
{
- char *source_data;
+ unsigned char *source_data;
+ unsigned char *current, *end, *token_text;
unsigned int i;
int token;
- int error;
void *parser;
- struct parse_params param = {0, c, 0, false};
- yyscan_t lexer;
- YY_BUFFER_STATE buffer;
+ struct css_parser_params param = {false, c, 0, false, false};
+ struct css_parser_token token_data;
c->data.css.css = malloc(sizeof *c->data.css.css);
parser = css_parser_Alloc(malloc);
- error = css_lex_init(&lexer);
- source_data = realloc(c->source_data, c->source_size + 2);
+ source_data = realloc(c->source_data, c->source_size + 10);
- if (!c->data.css.css || !parser || error || !source_data) {
+ if (!c->data.css.css || !parser || !source_data) {
free(c->data.css.css);
- free(parser);
- css_lex_destroy(lexer);
+ css_parser_Free(parser, free);
return 1;
}
@@ -187,27 +254,33 @@ int css_convert(struct content *c, unsigned int width, unsigned int height)
c->active = 0;
c->source_data = source_data;
- source_data[c->source_size] = 0;
- source_data[c->source_size + 1] = 0;
- /** \todo handle errors from the lexer (YY_FATAL_ERROR etc.) */
- buffer = css__scan_buffer(source_data, c->source_size + 2, lexer);
- assert(buffer);
- while ((token = css_lex(lexer))) {
- css_parser_(parser, token,
- xstrdup(css_get_text(lexer)),
- &param);
+ for (i = 0; i != 10; i++)
+ source_data[c->source_size + i] = 0;
+
+ current = source_data;
+ end = source_data + c->source_size;
+ while (current < end && (token = css_tokenise(&current, end + 10,
+ &token_text))) {
+ token_data.text = token_text;
+ token_data.length = current - token_text;
+ printf("%i \"%.*s\"\n", token, token_data.length,
+ token_data.text);
+ css_parser_(parser, token, token_data, &param);
if (param.syntax_error) {
- int line = css_get_lineno(lexer);
- LOG(("syntax error near line %i", line));
+ LOG(("syntax error near offset %i",
+ token_text - source_data));
param.syntax_error = false;
+ } else if (param.memory_error) {
+ LOG(("out of memory"));
+ break;
}
}
- css__delete_buffer(buffer, lexer);
- css_parser_(parser, 0, 0, &param);
+ css_parser_(parser, 0, token_data, &param);
css_parser_Free(parser, free);
- css_lex_destroy(lexer);
+ if (param.memory_error)
+ return 1;
/*css_dump_stylesheet(c->data.css.css);*/
@@ -251,17 +324,21 @@ void css_revive(struct content *c, unsigned int width, unsigned int height)
}
+/**
+ * Destroy a CONTENT_CSS and free all resources it owns.
+ */
+
void css_destroy(struct content *c)
{
unsigned int i;
- struct css_node *r;
+ struct css_selector *r;
for (i = 0; i != HASH_SIZE; i++) {
for (r = c->data.css.css->rule[i]; r != 0; r = r->next)
- xfree(r->style);
- css_free_node(c->data.css.css->rule[i]);
+ free(r->style);
+ css_free_selector(c->data.css.css->rule[i]);
}
- xfree(c->data.css.css);
+ free(c->data.css.css);
/* imported stylesheets */
for (i = 0; i != c->data.css.import_count; i++)
@@ -270,24 +347,32 @@ void css_destroy(struct content *c)
content_remove_user(c->data.css.import_content[i],
css_atimport_callback, c, (void*)i);
}
- xfree(c->data.css.import_url);
- xfree(c->data.css.import_content);
+ free(c->data.css.import_url);
+ free(c->data.css.import_content);
}
/**
- * parser support functions
+ * Create a new struct css_node.
+ *
+ * \param type type of node
+ * \param data string for data, not copied
+ * \param data_length length of data
+ * \return allocated node, or 0 if memory exhausted
+ *
+ * Used by the parser.
*/
-struct css_node * css_new_node(css_node_type type, char *data,
- struct css_node *left, struct css_node *right)
+struct css_node * css_new_node(css_node_type type,
+ const char *data, unsigned int data_length)
{
- struct css_node *node = xcalloc(1, sizeof(*node));
+ struct css_node *node = malloc(sizeof *node);
+ if (!node)
+ return 0;
node->type = type;
node->data = data;
- node->data2 = 0;
- node->left = left;
- node->right = right;
+ node->data_length = data_length;
+ node->value = 0;
node->next = 0;
node->comb = CSS_COMB_NONE;
node->style = 0;
@@ -295,65 +380,144 @@ struct css_node * css_new_node(css_node_type type, char *data,
return node;
}
+
+/**
+ * Free a struct css_node recursively.
+ *
+ * \param node css_node to free
+ *
+ * Used by the parser.
+ */
+
void css_free_node(struct css_node *node)
{
- if (node == 0)
+ if (!node)
return;
- if (node->left != 0)
- css_free_node(node->left);
- if (node->right != 0)
- css_free_node(node->right);
- if (node->next != 0)
+ if (node->value)
+ css_free_node(node->value);
+ if (node->next)
css_free_node(node->next);
- if (node->data != 0)
- free(node->data);
free(node);
}
-char *css_unquote(char *s)
+
+/**
+ * Create a new struct css_selector.
+ *
+ * \param type type of selector
+ * \param data string for data, not copied
+ * \param data_length length of data
+ * \return allocated selector, or 0 if memory exhausted
+ */
+
+struct css_selector * css_new_selector(css_selector_type type,
+ const char *data, unsigned int data_length)
{
- unsigned int len = strlen(s);
- memmove(s, s + 1, len);
- s[len - 2] = 0;
- return s;
+ struct css_selector *node = malloc(sizeof *node);
+ if (!node)
+ return 0;
+ node->type = type;
+ node->data = data;
+ node->data_length = data_length;
+ node->data2 = 0;
+ node->detail = 0;
+ node->combiner = 0;
+ node->next = 0;
+ node->comb = CSS_COMB_NONE;
+ node->style = 0;
+ node->specificity = 0;
+ return node;
}
+/**
+ * Free a struct css_selector recursively.
+ *
+ * \param node css_selector to free
+ */
+
+void css_free_selector(struct css_selector *node)
+{
+ if (!node)
+ return;
+ if (node->detail)
+ css_free_selector(node->detail);
+ if (node->combiner)
+ css_free_selector(node->combiner);
+ if (node->next)
+ css_free_selector(node->next);
+ free(node);
+}
+
+
+/**
+ * Process an @import rule.
+ */
+
void css_atimport(struct content *c, struct css_node *node)
{
- char *s, *url, *url1;
- int string = 0, screen = 1;
+ const char *s;
+ char *t, *url, *url1;
+ bool string = false, screen = true;
unsigned int i;
+ char **import_url;
+ struct content **import_content;
LOG(("@import rule"));
+ import_url = realloc(c->data.css.import_url,
+ (c->data.css.import_count + 1) *
+ sizeof(*c->data.css.import_url));
+ if (!import_url) {
+ /** \todo report to user */
+ return;
+ }
+ c->data.css.import_url = import_url;
+
+ import_content = realloc(c->data.css.import_content,
+ (c->data.css.import_count + 1) *
+ sizeof(*c->data.css.import_content));
+ if (!import_content) {
+ /** \todo report to user */
+ return;
+ }
+ c->data.css.import_content = import_content;
+
/* uri(...) or "..." */
switch (node->type) {
case CSS_NODE_URI:
- LOG(("URI '%s'", node->data));
+ LOG(("URI '%.*s'", node->data_length, node->data));
for (s = node->data + 4;
*s == ' ' || *s == '\t' || *s == '\r' ||
*s == '\n' || *s == '\f';
s++)
;
if (*s == '\'' || *s == '"') {
- string = 1;
+ string = true;
s++;
}
- url = xstrdup(s);
- for (s = url + strlen(url) - 2;
- *s == ' ' || *s == '\t' || *s == '\r' ||
- *s == '\n' || *s == '\f';
- s--)
+ url = strndup(s, node->data_length - (s - node->data));
+ if (!url) {
+ /** \todo report to user */
+ return;
+ }
+ for (t = url + strlen(url) - 2;
+ *t == ' ' || *t == '\t' || *t == '\r' ||
+ *t == '\n' || *t == '\f';
+ t--)
;
if (string)
- *s = 0;
+ *t = 0;
else
- *(s + 1) = 0;
+ *(t + 1) = 0;
break;
case CSS_NODE_STRING:
- LOG(("STRING '%s'", node->data));
- url = xstrdup(node->data);
+ LOG(("STRING '%.*s'", node->data_length, node->data));
+ url = strndup(node->data, node->data_length);
+ if (!url) {
+ /** \todo report to user */
+ return;
+ }
break;
default:
return;
@@ -361,14 +525,17 @@ void css_atimport(struct content *c, struct css_node *node)
/* media not specified, 'screen', or 'all' */
for (node = node->next; node != 0; node = node->next) {
- screen = 0;
+ screen = false;
if (node->type != CSS_NODE_IDENT) {
free(url);
return;
}
LOG(("medium '%s'", node->data));
- if (strcmp(node->data, "screen") == 0 || strcmp(node->data, "all") == 0) {
- screen = 1;
+ if ((node->data_length == 6 &&
+ strncmp(node->data, "screen", 6) == 0) ||
+ (node->data_length == 3 &&
+ strncmp(node->data, "all", 3) == 0)) {
+ screen = true;
break;
}
node = node->next;
@@ -390,11 +557,6 @@ void css_atimport(struct content *c, struct css_node *node)
/* start the fetch */
c->data.css.import_count++;
- c->data.css.import_url = xrealloc(c->data.css.import_url,
- c->data.css.import_count * sizeof(*c->data.css.import_url));
- c->data.css.import_content = xrealloc(c->data.css.import_content,
- c->data.css.import_count * sizeof(*c->data.css.import_content));
-
i = c->data.css.import_count - 1;
c->data.css.import_url[i] = url1;
c->data.css.import_content[i] = fetchcache(
@@ -415,6 +577,10 @@ void css_atimport(struct content *c, struct css_node *node)
}
+/**
+ * Fetchcache callback for imported stylesheets.
+ */
+
void css_atimport_callback(content_msg msg, struct content *css,
void *p1, void *p2, const char *error)
{
@@ -451,7 +617,12 @@ void css_atimport_callback(content_msg msg, struct content *css,
case CONTENT_MSG_REDIRECT:
c->active--;
free(c->data.css.import_url[i]);
- c->data.css.import_url[i] = xstrdup(error);
+ c->data.css.import_url[i] = strdup(error);
+ if (!c->data.css.import_url[i]) {
+ /** \todo report to user */
+ c->error = 1;
+ return;
+ }
c->data.css.import_content[i] = fetchcache(
c->data.css.import_url[i], c->url, css_atimport_callback,
c, (void*)i, css->width, css->height, true
@@ -475,13 +646,19 @@ void css_atimport_callback(content_msg msg, struct content *css,
/**
* Find the style which applies to an element.
+ *
+ * \param css content of type CONTENT_CSS
+ * \param element element in xml tree to match
+ * \param style style to update
+ *
+ * The style is updated with any rules that match the element.
*/
void css_get_style(struct content *css, xmlNode *element,
struct css_style *style)
{
struct css_stylesheet *stylesheet = css->data.css.css;
- struct css_node *rule;
+ struct css_selector *rule;
unsigned int hash, i;
/* imported stylesheets */
@@ -491,7 +668,8 @@ void css_get_style(struct content *css, xmlNode *element,
element, style);
/* match rules which end with the same element */
- hash = css_hash((char *) element->name);
+ hash = css_hash((const char *) element->name,
+ strlen((const char *) element->name));
for (rule = stylesheet->rule[hash]; rule; rule = rule->next)
if (css_match_rule(rule, element))
css_merge(style, rule->style);
@@ -507,110 +685,32 @@ void css_get_style(struct content *css, xmlNode *element,
* Determine if a rule applies to an element.
*/
-bool css_match_rule(struct css_node *rule, xmlNode *element)
+bool css_match_rule(struct css_selector *rule, xmlNode *element)
{
- bool match;
- char *s, *word, *space;
- unsigned int i;
- struct css_node *detail;
+ struct css_selector *detail;
xmlNode *anc, *prev;
assert(element->type == XML_ELEMENT_NODE);
- if (rule->data && strcasecmp(rule->data, (char *) element->name) != 0)
+ if (rule->data && (rule->data_length !=
+ strlen((const char *) element->name) ||
+ strncasecmp(rule->data, (const char *) element->name,
+ rule->data_length) != 0))
return false;
- for (detail = rule->left; detail; detail = detail->next) {
- s = 0;
- match = false;
- switch (detail->type) {
- case CSS_NODE_ID:
- s = (char *) xmlGetProp(element, (const xmlChar *) "id");
- if (s && strcasecmp(detail->data + 1, s) == 0)
- match = true;
- break;
-
- case CSS_NODE_CLASS:
- s = (char *) xmlGetProp(element, (const xmlChar *) "class");
- 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 CSS_NODE_ATTRIB:
- /* matches if an attribute is present */
- s = (char *) xmlGetProp(element, (const xmlChar *) detail->data);
- if (s)
- match = true;
- break;
-
- case CSS_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 CSS_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 + 1;
- } while (space);
- }
- break;
-
- case CSS_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;
-
- case CSS_NODE_PSEUDO:
- break;
-
- default:
- assert(0);
- }
- if (s)
- xmlFree(s);
- if (!match)
+ for (detail = rule->detail; detail; detail = detail->next) {
+ if (!css_match_detail(detail, element))
return false;
}
- if (!rule->right)
+ if (!rule->combiner)
return true;
switch (rule->comb) {
case CSS_COMB_ANCESTOR:
for (anc = element->parent; anc; anc = anc->parent)
if (anc->type == XML_ELEMENT_NODE &&
- css_match_rule(rule->right, anc))
+ css_match_rule(rule->combiner, anc))
return true;
break;
@@ -621,7 +721,7 @@ bool css_match_rule(struct css_node *rule, xmlNode *element)
;
if (!prev)
return false;
- return css_match_rule(rule->right, prev);
+ return css_match_rule(rule->combiner, prev);
break;
case CSS_COMB_PARENT:
@@ -631,7 +731,7 @@ bool css_match_rule(struct css_node *rule, xmlNode *element)
;
if (!anc)
return false;
- return css_match_rule(rule->right, anc);
+ return css_match_rule(rule->combiner, anc);
break;
default:
@@ -642,173 +742,468 @@ bool css_match_rule(struct css_node *rule, xmlNode *element)
}
+/**
+ * Determine if a selector detail matches an element.
+ *
+ * \param detail a css_selector of type other than CSS_SELECTOR_ELEMENT
+ * \param element element in xml tree to match
+ * \return true if the selector matches the element
+ */
+
+bool css_match_detail(const struct css_selector *detail,
+ xmlNode *element)
+{
+ bool match = false;
+ char *s = 0;
+ char *space, *word;
+ unsigned int length;
+
+ switch (detail->type) {
+ case CSS_SELECTOR_ID:
+ s = (char *) xmlGetProp(element,
+ (const xmlChar *) "id");
+ if (s && strlen(s) == detail->data_length &&
+ strncasecmp(detail->data, s,
+ detail->data_length) == 0)
+ match = true;
+ break;
+
+ case CSS_SELECTOR_CLASS:
+ s = (char *) xmlGetProp(element,
+ (const xmlChar *) "class");
+ if (!s)
+ break;
+ word = s;
+ do {
+ space = strchr(word, ' ');
+ if (space)
+ length = space - word;
+ else
+ length = strlen(word);
+ if (length == detail->data_length &&
+ strncasecmp(word, detail->data,
+ length) == 0) {
+ match = true;
+ break;
+ }
+ word = space + 1;
+ } while (space);
+ break;
+
+ case CSS_SELECTOR_ATTRIB:
+ /* matches if an attribute is present */
+ word = strndup(detail->data, detail->data_length);
+ if (!word) {
+ /** \todo report to user */
+ return false;
+ }
+ s = (char *) xmlGetProp(element,
+ (const xmlChar *) word);
+ free(word);
+ if (s)
+ match = true;
+ break;
+
+ case CSS_SELECTOR_ATTRIB_EQ:
+ /* matches if an attribute has a certain value*/
+ word = strndup(detail->data, detail->data_length);
+ if (!word) {
+ /** \todo report to user */
+ return false;
+ }
+ s = (char *) xmlGetProp(element,
+ (const xmlChar *) word);
+ free(word);
+ if (s && strlen(s) == detail->data2_length &&
+ strncasecmp(detail->data2, s,
+ detail->data2_length) == 0)
+ match = true;
+ break;
+
+ case CSS_SELECTOR_ATTRIB_INC:
+ /* matches if one of the space separated words
+ * in the attribute is equal */
+ word = strndup(detail->data,
+ detail->data_length);
+ if (!word) {
+ /** \todo report to user */
+ return false;
+ }
+ s = (char *) xmlGetProp(element,
+ (const xmlChar *) word);
+ free(word);
+ if (!s)
+ break;
+ word = s;
+ do {
+ space = strchr(word, ' ');
+ if (space)
+ length = space - word;
+ else
+ length = strlen(word);
+ if (length == detail->data2_length &&
+ strncasecmp(word, detail->data2,
+ length) == 0) {
+ match = true;
+ break;
+ }
+ word = space + 1;
+ } while (space);
+ break;
+
+ case CSS_SELECTOR_ATTRIB_DM:
+ /* matches if a prefix up to a hyphen matches */
+ word = strndup(detail->data,
+ detail->data_length);
+ if (!word) {
+ /** \todo report to user */
+ return false;
+ }
+ s = (char *) xmlGetProp(element,
+ (const xmlChar *) word);
+ free(word);
+ if (!s)
+ break;
+ length = detail->data2_length;
+ if (strncasecmp(detail->data2, s, length) == 0 &&
+ (s[length] == '-' || s[length] == 0))
+ match = true;
+ break;
+
+ case CSS_SELECTOR_PSEUDO:
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (s)
+ xmlFree(s);
+
+ return match;
+}
+
+
+/**
+ * Parse a stand-alone CSS property list.
+ *
+ * \param style css_style to update
+ * \param str property list, as found in HTML style attributes
+ */
+
void css_parse_property_list(struct css_style * style, char * str)
{
- yyscan_t lexer;
- void *parser;
- YY_BUFFER_STATE buffer;
+ unsigned char *source_data;
+ unsigned char *current, *end, *token_text;
+ size_t length;
+ unsigned int i;
int token;
- struct parse_params param = {1, 0, 0, false};
+ void *parser;
+ struct css_parser_params param = {true, 0, 0, false, false};
+ struct css_parser_token token_data;
+ const struct css_parser_token token_start = { "{", 1 };
+ const struct css_parser_token token_end = { "}", 1 };
- css_lex_init(&lexer);
- parser = css_parser_Alloc((void*)malloc);
- css_parser_(parser, LBRACE, xstrdup("{"), &param);
+ length = strlen(str);
- buffer = css__scan_string(str, lexer);
- while ((token = css_lex(lexer))) {
- css_parser_(parser, token,
- xstrdup(css_get_text(lexer)),
- &param);
+ parser = css_parser_Alloc(malloc);
+ source_data = malloc(length + 10);
+
+ if (!parser || !source_data) {
+ free(parser);
+ css_parser_Free(parser, free);
+ return;
}
- css__delete_buffer(buffer, lexer);
- css_parser_(parser, RBRACE, xstrdup("}"), &param);
- css_parser_(parser, 0, 0, &param);
+
+ strcpy(source_data, str);
+ for (i = 0; i != 10; i++)
+ source_data[length + i] = 0;
+
+ css_parser_(parser, LBRACE, token_start, &param);
+
+ current = source_data;
+ end = source_data + strlen(str);
+ while (current < end && (token = css_tokenise(&current, end + 10,
+ &token_text))) {
+ token_data.text = token_text;
+ token_data.length = current - token_text;
+ css_parser_(parser, token, token_data, &param);
+ if (param.syntax_error) {
+ LOG(("syntax error near offset %i",
+ token_text - source_data));
+ param.syntax_error = false;
+ } else if (param.memory_error) {
+ LOG(("out of memory"));
+ break;
+ }
+ }
+ css_parser_(parser, RBRACE, token_end, &param);
+ css_parser_(parser, 0, token_data, &param);
css_parser_Free(parser, free);
- css_lex_destroy(lexer);
+
+ if (param.memory_error) {
+ css_free_node(param.declaration);
+ return;
+ }
css_add_declarations(style, param.declaration);
css_free_node(param.declaration);
-}
+ free(source_data);
+}
/**
- * dump a style
+ * Dump a css_style to stderr in CSS-like syntax.
*/
-static void dump_length(const struct css_length * const length)
-{
- fprintf(stderr, "%g%s", length->value,
- css_unit_name[length->unit]);
-}
-
void css_dump_style(const struct css_style * const style)
{
unsigned int i;
fprintf(stderr, "{ ");
- fprintf(stderr, "background-color: #%lx; ", style->background_color);
- fprintf(stderr, "clear: %s; ", css_clear_name[style->clear]);
- fprintf(stderr, "color: #%lx; ", style->color);
- fprintf(stderr, "cursor: %s", css_cursor_name[style->cursor]);
- fprintf(stderr, "display: %s; ", css_display_name[style->display]);
- fprintf(stderr, "float: %s; ", css_float_name[style->float_]);
- fprintf(stderr, "font: %s %s ", css_font_style_name[style->font_style],
- css_font_weight_name[style->font_weight]);
- switch (style->font_size.size) {
- case CSS_FONT_SIZE_ABSOLUTE: fprintf(stderr, "[%g]", style->font_size.value.absolute); break;
- case CSS_FONT_SIZE_LENGTH: dump_length(&style->font_size.value.length); break;
- case CSS_FONT_SIZE_PERCENT: fprintf(stderr, "%g%%", style->font_size.value.percent); break;
- case CSS_FONT_SIZE_INHERIT: fprintf(stderr, "inherit"); break;
- default: fprintf(stderr, "UNKNOWN"); break;
+
+#define DUMP_COLOR(z, s) \
+ if (style->z != css_empty_style.z) { \
+ if (style->z == TRANSPARENT) \
+ fprintf(stderr, s ": transparent; "); \
+ else if (style->z == CSS_COLOR_NONE) \
+ fprintf(stderr, s ": none; "); \
+ else \
+ fprintf(stderr, s ": #%.6lx; ", style->z); \
}
- fprintf(stderr, "/");
- switch (style->line_height.size) {
- case CSS_LINE_HEIGHT_ABSOLUTE: fprintf(stderr, "[%g]", style->line_height.value.absolute); break;
- case CSS_LINE_HEIGHT_LENGTH: dump_length(&style->line_height.value.length); break;
- case CSS_LINE_HEIGHT_PERCENT: fprintf(stderr, "%g%%", style->line_height.value.percent); break;
- case CSS_LINE_HEIGHT_INHERIT: fprintf(stderr, "inherit"); break;
- default: fprintf(stderr, "UNKNOWN"); break;
+
+#define DUMP_KEYWORD(z, s, n) \
+ if (style->z != css_empty_style.z) \
+ fprintf(stderr, s ": %s; ", n[style->z]);
+
+ DUMP_COLOR(background_color, "background-color");
+ DUMP_KEYWORD(clear, "clear", css_clear_name);
+ DUMP_COLOR(color, "color");
+ DUMP_KEYWORD(cursor, "cursor", css_cursor_name);
+ DUMP_KEYWORD(display, "display", css_display_name);
+ DUMP_KEYWORD(float_, "float", css_float_name);
+
+ if (style->font_style != css_empty_style.font_style ||
+ style->font_weight != css_empty_style.font_weight ||
+ style->font_size.size !=
+ css_empty_style.font_size.size ||
+ style->line_height.size !=
+ css_empty_style.line_height.size ||
+ style->font_family != css_empty_style.font_family ||
+ style->font_variant != css_empty_style.font_variant) {
+ fprintf(stderr, "font: %s %s ",
+ css_font_style_name[style->font_style],
+ css_font_weight_name[style->font_weight]);
+ switch (style->font_size.size) {
+ case CSS_FONT_SIZE_ABSOLUTE:
+ fprintf(stderr, "[%g]",
+ style->font_size.value.absolute);
+ break;
+ case CSS_FONT_SIZE_LENGTH:
+ css_dump_length(&style->font_size.value.length);
+ break;
+ case CSS_FONT_SIZE_PERCENT:
+ fprintf(stderr, "%g%%",
+ style->font_size.value.percent);
+ break;
+ case CSS_FONT_SIZE_INHERIT:
+ fprintf(stderr, "inherit");
+ break;
+ default:
+ fprintf(stderr, "UNKNOWN");
+ break;
+ }
+ fprintf(stderr, "/");
+ switch (style->line_height.size) {
+ case CSS_LINE_HEIGHT_ABSOLUTE:
+ fprintf(stderr, "[%g]",
+ style->line_height.value.absolute);
+ break;
+ case CSS_LINE_HEIGHT_LENGTH:
+ css_dump_length(&style->line_height.value.length);
+ break;
+ case CSS_LINE_HEIGHT_PERCENT:
+ fprintf(stderr, "%g%%",
+ style->line_height.value.percent);
+ break;
+ case CSS_LINE_HEIGHT_INHERIT:
+ fprintf(stderr, "inherit");
+ break;
+ default:
+ fprintf(stderr, "UNKNOWN");
+ break;
+ }
+ fprintf(stderr, " %s",
+ css_font_family_name[style->font_family]);
+ fprintf(stderr, " %s",
+ css_font_variant_name[style->font_variant]);
+ fprintf(stderr, "; ");
}
- fprintf(stderr, " %s", css_font_family_name[style->font_family]);
- fprintf(stderr, " %s", css_font_variant_name[style->font_variant]);
- fprintf(stderr, "; ");
- fprintf(stderr, "height: ");
- switch (style->height.height) {
- case CSS_HEIGHT_AUTO: fprintf(stderr, "auto"); break;
- case CSS_HEIGHT_LENGTH: dump_length(&style->height.length); break;
- default: fprintf(stderr, "UNKNOWN"); break;
+
+ if (style->height.height != css_empty_style.height.height) {
+ fprintf(stderr, "height: ");
+ switch (style->height.height) {
+ case CSS_HEIGHT_AUTO:
+ fprintf(stderr, "auto"); break;
+ case CSS_HEIGHT_LENGTH:
+ css_dump_length(&style->height.length); break;
+ default:
+ fprintf(stderr, "UNKNOWN"); break;
+ }
+ fprintf(stderr, "; ");
}
- fprintf(stderr, "; ");
- fprintf(stderr, "margin:");
- for (i = 0; i != 4; i++) {
- switch (style->margin[i].margin) {
- case CSS_MARGIN_INHERIT: fprintf(stderr, " inherit"); break;
- case CSS_MARGIN_LENGTH: fprintf(stderr, " ");
- dump_length(&style->margin[i].value.length); break;
- case CSS_MARGIN_PERCENT: fprintf(stderr, " %g%%", style->margin[i].value.percent);
- case CSS_MARGIN_AUTO: fprintf(stderr, " auto"); break;
- default: fprintf(stderr, "UNKNOWN"); break;
+
+ if (style->margin[0].margin != css_empty_style.margin[0].margin ||
+ style->margin[1].margin != css_empty_style.margin[1].margin ||
+ style->margin[2].margin != css_empty_style.margin[2].margin ||
+ style->margin[3].margin != css_empty_style.margin[3].margin) {
+ fprintf(stderr, "margin:");
+ for (i = 0; i != 4; i++) {
+ switch (style->margin[i].margin) {
+ case CSS_MARGIN_INHERIT:
+ fprintf(stderr, " inherit");
+ break;
+ case CSS_MARGIN_LENGTH:
+ fprintf(stderr, " ");
+ css_dump_length(&style->margin[i].value.length);
+ break;
+ case CSS_MARGIN_PERCENT:
+ fprintf(stderr, " %g%%",
+ style->margin[i].value.percent);
+ break;
+ case CSS_MARGIN_AUTO:
+ fprintf(stderr, " auto");
+ break;
+ default:
+ fprintf(stderr, "UNKNOWN");
+ break;
+ }
}
+ fprintf(stderr, "; ");
}
- fprintf(stderr, "; ");
- fprintf(stderr, "padding:");
- for (i = 0; i != 4; i++) {
- switch (style->padding[i].padding) {
- case CSS_PADDING_INHERIT: fprintf(stderr, " inherit"); break;
- case CSS_PADDING_LENGTH: fprintf(stderr, " ");
- dump_length(&style->padding[i].value.length); break;
- case CSS_PADDING_PERCENT: fprintf(stderr, " %g%%", style->padding[i].value.percent);
- default: fprintf(stderr, "UNKNOWN"); break;
+
+ if (style->padding[0].padding != css_empty_style.padding[0].padding ||
+ style->padding[1].padding != css_empty_style.padding[1].padding ||
+ style->padding[2].padding != css_empty_style.padding[2].padding ||
+ style->padding[3].padding != css_empty_style.padding[3].padding) {
+ fprintf(stderr, "padding:");
+ for (i = 0; i != 4; i++) {
+ switch (style->padding[i].padding) {
+ case CSS_PADDING_INHERIT:
+ fprintf(stderr, " inherit");
+ break;
+ case CSS_PADDING_LENGTH:
+ fprintf(stderr, " ");
+ css_dump_length(&style->padding[i].value.length);
+ break;
+ case CSS_PADDING_PERCENT:
+ fprintf(stderr, " %g%%",
+ style->padding[i].value.percent);
+ break;
+ default:
+ fprintf(stderr, "UNKNOWN");
+ break;
+ }
}
+ fprintf(stderr, "; ");
}
- fprintf(stderr, "; ");
- fprintf(stderr, "text-align: %s; ", css_text_align_name[style->text_align]);
- fprintf(stderr, "text-decoration:");
- switch (style->text_decoration) {
- case CSS_TEXT_DECORATION_NONE: fprintf(stderr, " none"); break;
- case CSS_TEXT_DECORATION_INHERIT: fprintf(stderr, " inherit"); break;
- default:
- if (style->text_decoration & CSS_TEXT_DECORATION_UNDERLINE)
- fprintf(stderr, " underline");
- if (style->text_decoration & CSS_TEXT_DECORATION_OVERLINE)
- fprintf(stderr, " overline");
- if (style->text_decoration & CSS_TEXT_DECORATION_LINE_THROUGH)
- fprintf(stderr, " line-through");
- if (style->text_decoration & CSS_TEXT_DECORATION_BLINK)
- fprintf(stderr, " blink");
+
+ DUMP_KEYWORD(text_align, "text-align", css_text_align_name);
+
+ if (style->text_decoration != css_empty_style.text_decoration) {
+ fprintf(stderr, "text-decoration:");
+ if (style->text_decoration == CSS_TEXT_DECORATION_NONE)
+ fprintf(stderr, " none");
+ if (style->text_decoration == CSS_TEXT_DECORATION_INHERIT)
+ fprintf(stderr, " inherit");
+ if (style->text_decoration & CSS_TEXT_DECORATION_UNDERLINE)
+ fprintf(stderr, " underline");
+ if (style->text_decoration & CSS_TEXT_DECORATION_OVERLINE)
+ fprintf(stderr, " overline");
+ if (style->text_decoration & CSS_TEXT_DECORATION_LINE_THROUGH)
+ fprintf(stderr, " line-through");
+ if (style->text_decoration & CSS_TEXT_DECORATION_BLINK)
+ fprintf(stderr, " blink");
+ fprintf(stderr, "; ");
}
- fprintf(stderr, "; ");
- fprintf(stderr, "text-indent: ");
- switch (style->text_indent.size) {
- case CSS_TEXT_INDENT_LENGTH: dump_length(&style->text_indent.value.length); break;
- case CSS_TEXT_INDENT_PERCENT: fprintf(stderr, "%g%%", style->text_indent.value.percent); break;
- case CSS_TEXT_INDENT_INHERIT: fprintf(stderr, "inherit"); break;
- default: fprintf(stderr, "UNKNOWN"); break;
+
+ if (style->text_indent.size != css_empty_style.text_indent.size) {
+ fprintf(stderr, "text-indent: ");
+ switch (style->text_indent.size) {
+ case CSS_TEXT_INDENT_LENGTH:
+ css_dump_length(&style->text_indent.value.length);
+ break;
+ case CSS_TEXT_INDENT_PERCENT:
+ fprintf(stderr, "%g%%",
+ style->text_indent.value.percent);
+ break;
+ case CSS_TEXT_INDENT_INHERIT:
+ fprintf(stderr, "inherit");
+ break;
+ default:
+ fprintf(stderr, "UNKNOWN");
+ break;
+ }
+ fprintf(stderr, "; ");
}
- fprintf(stderr, "; ");
- fprintf(stderr, "text-transform: %s; ", css_text_transform_name[style->text_transform]);
- fprintf(stderr, "visibility: %s; ", css_visibility_name[style->visibility]);
- fprintf(stderr, "width: ");
- switch (style->width.width) {
- case CSS_WIDTH_INHERIT: fprintf(stderr, "inherit"); break;
- case CSS_WIDTH_AUTO: fprintf(stderr, "auto"); break;
- case CSS_WIDTH_LENGTH: dump_length(&style->width.value.length); break;
- case CSS_WIDTH_PERCENT: fprintf(stderr, "%g%%", style->width.value.percent); break;
- default: fprintf(stderr, "UNKNOWN"); break;
+
+ DUMP_KEYWORD(text_transform, "text-transform", css_text_transform_name);
+ DUMP_KEYWORD(visibility, "visibility", css_visibility_name);
+
+ if (style->width.width != css_empty_style.width.width) {
+ fprintf(stderr, "width: ");
+ switch (style->width.width) {
+ case CSS_WIDTH_INHERIT:
+ fprintf(stderr, "inherit");
+ break;
+ case CSS_WIDTH_AUTO:
+ fprintf(stderr, "auto");
+ break;
+ case CSS_WIDTH_LENGTH:
+ css_dump_length(&style->width.value.length);
+ break;
+ case CSS_WIDTH_PERCENT:
+ fprintf(stderr, "%g%%",
+ style->width.value.percent);
+ break;
+ default:
+ fprintf(stderr, "UNKNOWN");
+ break;
+ }
+ fprintf(stderr, "; ");
}
- fprintf(stderr, "; ");
- fprintf(stderr, "white-space: %s; ", css_white_space_name[style->white_space]);
+
+ DUMP_KEYWORD(white_space, "white-space", css_white_space_name);
+
fprintf(stderr, "}");
}
+/**
+ * Dump a css_length to stderr.
+ */
+
+void css_dump_length(const struct css_length * const length)
+{
+ fprintf(stderr, "%g%s", length->value, css_unit_name[length->unit]);
+}
+
+
+/**
+ * Dump a complete css_stylesheet to stderr in CSS syntax.
+ */
+
void css_dump_stylesheet(const struct css_stylesheet * stylesheet)
{
unsigned int i;
- struct css_node *r, *n, *m;
+ struct css_selector *r;
for (i = 0; i != HASH_SIZE; i++) {
/*fprintf(stderr, "hash %i:\n", i);*/
for (r = stylesheet->rule[i]; r != 0; r = r->next) {
- for (n = r; n != 0; n = n->right) {
- if (n->data != 0)
- fprintf(stderr, "%s", n->data);
- for (m = n->left; m != 0; m = m->next) {
- switch (m->type) {
- case CSS_NODE_ID: fprintf(stderr, "%s", m->data); break;
- case CSS_NODE_CLASS: fprintf(stderr, ".%s", m->data); break;
- case CSS_NODE_ATTRIB: fprintf(stderr, "[%s]", m->data); break;
- case CSS_NODE_ATTRIB_EQ: fprintf(stderr, "[%s=%s]", m->data, m->data2); break;
- case CSS_NODE_ATTRIB_INC: fprintf(stderr, "[%s~=%s]", m->data, m->data2); break;
- case CSS_NODE_ATTRIB_DM: fprintf(stderr, "[%s|=%s]", m->data, m->data2); break;
- case CSS_NODE_PSEUDO: fprintf(stderr, ":%s", m->data); break;
- default: fprintf(stderr, "unexpected node");
- }
- }
- fprintf(stderr, " ");
- }
- fprintf(stderr, "%lx ", r->specificity);
+ css_dump_selector(r);
+ fprintf(stderr, " <%lx> ", r->specificity);
css_dump_style(r->style);
fprintf(stderr, "\n");
}
@@ -817,10 +1212,81 @@ void css_dump_stylesheet(const struct css_stylesheet * stylesheet)
/**
- * cascade styles
+ * Dump a css_selector to stderr in CSS syntax.
+ */
+
+void css_dump_selector(const struct css_selector *r)
+{
+ struct css_selector *m;
+
+ if (r->combiner)
+ css_dump_selector(r->combiner);
+
+ switch (r->comb) {
+ case CSS_COMB_NONE: break;
+ case CSS_COMB_ANCESTOR: fprintf(stderr, " "); break;
+ case CSS_COMB_PARENT: fprintf(stderr, " > "); break;
+ case CSS_COMB_PRECEDED: fprintf(stderr, " + "); break;
+ }
+
+ if (r->data)
+ fprintf(stderr, "%.*s", r->data_length, r->data);
+ else
+ fprintf(stderr, "*");
+
+ for (m = r->detail; m; m = m->next) {
+ switch (m->type) {
+ case CSS_SELECTOR_ID:
+ fprintf(stderr, "#%.*s",
+ m->data_length, m->data);
+ break;
+ case CSS_SELECTOR_CLASS:
+ fprintf(stderr, ".%.*s",
+ m->data_length, m->data);
+ break;
+ case CSS_SELECTOR_ATTRIB:
+ fprintf(stderr, "[%.*s]",
+ m->data_length, m->data);
+ break;
+ case CSS_SELECTOR_ATTRIB_EQ:
+ fprintf(stderr, "[%.*s=%.*s]",
+ m->data_length, m->data,
+ m->data2_length, m->data2);
+ break;
+ case CSS_SELECTOR_ATTRIB_INC:
+ fprintf(stderr, "[%.*s~=%.*s]",
+ m->data_length, m->data,
+ m->data2_length, m->data2);
+ break;
+ case CSS_SELECTOR_ATTRIB_DM:
+ fprintf(stderr, "[%.*s|=%.*s]",
+ m->data_length, m->data,
+ m->data2_length, m->data2);
+ break;
+ case CSS_SELECTOR_PSEUDO:
+ fprintf(stderr, ":%.*s",
+ m->data_length, m->data);
+ break;
+ default:
+ fprintf(stderr, "(unexpected detail)");
+ }
+ }
+}
+
+
+/**
+ * Cascade styles.
+ *
+ * \param style css_style to modify
+ * \param apply css_style to cascade onto style
+ *
+ * Attributes which have the value 'inherit' in apply are unchanged in style.
+ * Other attributes are copied to style, calculating percentages relative to
+ * style where applicable.
*/
-void css_cascade(struct css_style * const style, const struct css_style * const apply)
+void css_cascade(struct css_style * const style,
+ const struct css_style * const apply)
{
unsigned int i;
float f;
@@ -923,7 +1389,18 @@ void css_cascade(struct css_style * const style, const struct css_style * const
}
-void css_merge(struct css_style * const style, const struct css_style * const apply)
+/**
+ * Merge styles.
+ *
+ * \param style css_style to modify
+ * \param apply css_style to merge onto style
+ *
+ * Attributes which have the value 'inherit' in apply are unchanged in style.
+ * Other attributes are copied to style, overwriting it.
+ */
+
+void css_merge(struct css_style * const style,
+ const struct css_style * const apply)
{
unsigned int i;
@@ -985,14 +1462,20 @@ void css_merge(struct css_style * const style, const struct css_style * const ap
}
+/**
+ * Calculate a hash for an element name.
+ *
+ * The hash is case-insensitive.
+ */
-unsigned int css_hash(const char *s)
+unsigned int css_hash(const char *s, int length)
{
+ int i;
unsigned int z = 0;
if (s == 0)
return 0;
- for (; *s != 0; s++)
- z += *s & 0x1f; /* lower 5 bits, case insensitive */
+ for (i = 0; i != length; i++)
+ z += s[i] & 0x1f; /* lower 5 bits, case insensitive */
return (z % (HASH_SIZE - 1)) + 1;
}
diff --git a/css/css.h b/css/css.h
index 344fa4b83..f00e7121e 100644
--- a/css/css.h
+++ b/css/css.h
@@ -27,9 +27,6 @@
#include "libxml/HTMLparser.h"
#include "css_enum.h"
-/*
- * structures and typedefs
- */
typedef unsigned long colour; /* 0xbbggrr */
#define TRANSPARENT 0x1000000
@@ -180,8 +177,43 @@ extern const struct css_style css_blank_style;
#ifdef CSS_INTERNALS
+/** Type of a css_selector. */
+typedef enum {
+ CSS_SELECTOR_ELEMENT,
+ CSS_SELECTOR_ID,
+ CSS_SELECTOR_CLASS,
+ CSS_SELECTOR_ATTRIB,
+ CSS_SELECTOR_ATTRIB_EQ,
+ CSS_SELECTOR_ATTRIB_INC,
+ CSS_SELECTOR_ATTRIB_DM,
+ CSS_SELECTOR_PSEUDO,
+} css_selector_type;
+
+/** Relationship to combiner in a css_selector. */
+typedef enum {
+ CSS_COMB_NONE,
+ CSS_COMB_ANCESTOR,
+ CSS_COMB_PARENT,
+ CSS_COMB_PRECEDED,
+} css_combinator;
+
+/** Representation of a CSS selector. */
+struct css_selector {
+ css_selector_type type;
+ const char *data;
+ unsigned int data_length;
+ const char *data2;
+ unsigned int data2_length;
+ struct css_selector *detail;
+ struct css_selector *combiner;
+ struct css_selector *next;
+ css_combinator comb;
+ struct css_style *style;
+ unsigned long specificity;
+};
+
+/** Type of a css_node. */
typedef enum {
- CSS_NODE_BLOCK,
CSS_NODE_DECLARATION,
CSS_NODE_IDENT,
CSS_NODE_NUMBER,
@@ -202,55 +234,45 @@ typedef enum {
CSS_NODE_GT,
CSS_NODE_PAREN,
CSS_NODE_BRAC,
- CSS_NODE_SELECTOR,
- CSS_NODE_ID,
- CSS_NODE_CLASS,
- CSS_NODE_ATTRIB,
- CSS_NODE_ATTRIB_EQ,
- CSS_NODE_ATTRIB_INC,
- CSS_NODE_ATTRIB_DM,
- CSS_NODE_PSEUDO,
} css_node_type;
-typedef enum {
- CSS_COMB_NONE,
- CSS_COMB_ANCESTOR,
- CSS_COMB_PARENT,
- CSS_COMB_PRECEDED,
-} css_combinator;
-
+/** A node in a CSS parse tree. */
struct css_node {
css_node_type type;
- char *data;
- char *data2;
- struct css_node *left;
- struct css_node *right;
+ const char *data;
+ unsigned int data_length;
+ struct css_node *value;
struct css_node *next;
css_combinator comb;
struct css_style *style;
unsigned long specificity;
};
-#include "netsurf/css/scanner.h"
#define HASH_SIZE (47 + 1)
+/** Representation of a CSS 2 style sheet. */
struct css_stylesheet {
- struct css_node *rule[HASH_SIZE];
+ struct css_selector *rule[HASH_SIZE];
};
-struct parse_params {
- int ruleset_only;
+/** Parameters to and results from the CSS parser. */
+struct css_parser_params {
+ bool ruleset_only;
struct content *stylesheet;
struct css_node *declaration;
bool syntax_error;
+ bool memory_error;
+};
+
+/** Token type for the CSS parser. */
+struct css_parser_token {
+ const char *text;
+ unsigned int length;
};
#endif
-/*
- * interface
- */
struct content;
@@ -260,28 +282,36 @@ void css_destroy(struct content *c);
#ifdef CSS_INTERNALS
-struct css_node * css_new_node(css_node_type type, char *data,
- struct css_node *left, struct css_node *right);
+struct css_node * css_new_node(css_node_type type,
+ const char *data, unsigned int data_length);
void css_free_node(struct css_node *node);
-char *css_unquote(char *s);
+struct css_selector * css_new_selector(css_selector_type type,
+ const char *data, unsigned int data_length);
+void css_free_selector(struct css_selector *node);
void css_atimport(struct content *c, struct css_node *node);
void css_add_ruleset(struct content *c,
- struct css_node *selector,
+ struct css_selector *selector,
+ struct css_node *declaration);
+void css_add_declarations(struct css_style *style,
struct css_node *declaration);
-void css_add_declarations(struct css_style *style, struct css_node *declaration);
-unsigned int css_hash(const char *s);
+unsigned int css_hash(const char *s, int length);
+
+int css_tokenise(unsigned char **buffer, unsigned char *end,
+ unsigned char **token_text);
void css_parser_Trace(FILE *TraceFILE, char *zTracePrompt);
void *css_parser_Alloc(void *(*mallocProc)(/*size_t*/int));
void css_parser_Free(void *p, void (*freeProc)(void*));
-void css_parser_(void *yyp, int yymajor, char* yyminor,
- struct parse_params *param);
+void css_parser_(void *yyp, int yymajor, struct css_parser_token yyminor,
+ struct css_parser_params *param);
#endif
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_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);
colour named_colour(const char *name);
void css_dump_style(const struct css_style * const style);
diff --git a/css/makeenum b/css/makeenum
index 4950cc14d..004313d93 100755
--- a/css/makeenum
+++ b/css/makeenum
@@ -28,17 +28,18 @@ while (<>) {
print H join ",\n ${uc_name}_", @uc_enum;
print H ",\n ${uc_name}_UNKNOWN\n";
print H "} $name;\n";
- print H "$name ${name}_parse(const char * const s);\n\n";
+ print H "$name ${name}_parse(const char * const s, int length);\n\n";
print C "/**\n * $name\n */\n\n";
print C "const char * const ${name}_name[] = {\n \"";
print C join "\",\n \"", @enum;
print C "\"\n};\n\n";
- print C "$name ${name}_parse(const char * const s)\n{\n";
+ print C "$name ${name}_parse(const char * const s, int length)\n{\n";
foreach $x (@enum) {
$ux = uc $x;
$ux =~ s/-/_/g;
- print C " if (strcasecmp(s, \"$x\") == 0) return ${uc_name}_$ux;\n";
+ $len = length $x;
+ print C " if (length == $len && strncasecmp(s, \"$x\", $len) == 0) return ${uc_name}_$ux;\n";
}
print C " return ${uc_name}_UNKNOWN;\n}\n\n";
}
diff --git a/css/parser.y b/css/parser.y
index b373ad585..81a2772c1 100644
--- a/css/parser.y
+++ b/css/parser.y
@@ -2,12 +2,12 @@
* 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 2003 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
*/
-/*
+/** \file
-CSS parser using the lemon parser generator
+CSS parser using the lemon parser generator.
see CSS2 Specification, chapter 4
http://www.w3.org/TR/REC-CSS2/syndata.html,
@@ -40,9 +40,10 @@ statement ::= at_rule.
at_rule ::= ATKEYWORD any_list block.
at_rule ::= ATKEYWORD(A) any_list(B) SEMI.
- { if (strcasecmp(A, "@import") == 0)
+ { if ((A.length == 7) && (strncasecmp(A.text, "@import", 7) == 0)
+ && B)
css_atimport(param->stylesheet, B);
- free(A); css_free_node(B); }
+ css_free_node(B); }
block ::= LBRACE block_body RBRACE.
block_body ::= .
@@ -52,7 +53,10 @@ block_body ::= block_body ATKEYWORD.
block_body ::= block_body SEMI.
ruleset ::= selector_list(A) LBRACE declaration_list(B) RBRACE.
- { css_add_ruleset(param->stylesheet, A, B);
+ { if (A && B)
+ css_add_ruleset(param->stylesheet, A, B);
+ else
+ css_free_selector(A);
css_free_node(B); }
ruleset ::= LBRACE declaration_list(A) RBRACE.
/* this form of ruleset not used in CSS2
@@ -65,13 +69,28 @@ ruleset ::= any_list_1(A) LBRACE declaration_list(B) RBRACE.
selector_list(A) ::= selector(B).
{ A = B; }
selector_list(A) ::= selector_list(B) COMMA selector(C).
- { C->next = B; A = C; }
+ { if (B && C) {
+ C->next = B;
+ A = C;
+ } else {
+ css_free_selector(B);
+ css_free_selector(C);
+ A = 0;
+ } }
selector(A) ::= simple_selector(B).
{ A = B; }
selector(A) ::= selector(B) css_combinator(C) simple_selector(D).
- { D->right = B; D->comb = C; A = D;
- A->specificity += B->specificity; }
+ { if (B && D) {
+ D->combiner = B;
+ D->comb = C;
+ D->specificity += B->specificity;
+ A = D;
+ } else {
+ css_free_selector(B);
+ css_free_selector(D);
+ A = 0;
+ } }
css_combinator(A) ::= .
{ A = CSS_COMB_ANCESTOR; }
@@ -81,64 +100,106 @@ css_combinator(A) ::= GT.
{ A = CSS_COMB_PARENT; }
simple_selector(A) ::= element_name(B) detail_list(C).
- { A = css_new_node(CSS_NODE_SELECTOR, B, C, 0);
- A->specificity = 1 + C->specificity; }
+ { if (C && (A = css_new_selector(CSS_SELECTOR_ELEMENT,
+ B.text, B.length))) {
+ A->detail = C;
+ A->specificity = 1 + C->specificity;
+ } else {
+ param->memory_error = true;
+ css_free_selector(C);
+ A = 0;
+ } }
simple_selector(A) ::= element_name(B).
- { A = css_new_node(CSS_NODE_SELECTOR, B, 0, 0);
- A->specificity = 1; }
+ { if ((A = css_new_selector(CSS_SELECTOR_ELEMENT,
+ B.text, B.length)))
+ A->specificity = 1;
+ else
+ param->memory_error = true;
+ }
simple_selector(A) ::= detail_list(C).
- { A = css_new_node(CSS_NODE_SELECTOR, 0, C, 0);
- A->specificity = C->specificity; }
+ { if (C && (A = css_new_selector(CSS_SELECTOR_ELEMENT, 0, 0))) {
+ A->detail = C;
+ A->specificity = C->specificity;
+ } else {
+ param->memory_error = true;
+ css_free_selector(C);
+ A = 0;
+ } }
element_name(A) ::= IDENT(B).
{ A = B; }
element_name(A) ::= ASTERISK.
- { A = 0; }
+ { A.text = 0; }
detail_list(A) ::= detail(B).
{ A = B; }
detail_list(A) ::= detail(B) detail_list(C).
- { A = B; A->specificity += C->specificity; A->next = C; }
+ { if (B && C) {
+ B->specificity += C->specificity;
+ B->next = C;
+ A = B;
+ } else {
+ css_free_selector(B);
+ css_free_selector(C);
+ A = 0;
+ } }
detail(A) ::= HASH(B).
- { A = css_new_node(CSS_NODE_ID, B, 0, 0);
- A->specificity = 0x10000; }
+ { A = css_new_selector(CSS_SELECTOR_ID, B.text+1, B.length-1);
+ if (A) A->specificity = 0x10000;
+ else param->memory_error = true; }
detail(A) ::= DOT IDENT(B).
- { A = css_new_node(CSS_NODE_CLASS, B, 0, 0);
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_CLASS, B.text, B.length);
+ if (A) A->specificity = 0x100;
+ else param->memory_error = true; }
detail(A) ::= LBRAC IDENT(B) RBRAC.
- { A = css_new_node(CSS_NODE_ATTRIB, B, 0, 0);
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_ATTRIB, B.text, B.length);
+ if (A) A->specificity = 0x100;
+ else param->memory_error = true; }
detail(A) ::= LBRAC IDENT(B) EQUALS IDENT(C) RBRAC.
- { A = css_new_node(CSS_NODE_ATTRIB_EQ, B, 0, 0); A->data2 = C;
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_ATTRIB_EQ, B.text, B.length);
+ if (A) { A->data2 = C.text; A->data2_length = C.length;
+ A->specificity = 0x100; }
+ else param->memory_error = true; }
detail(A) ::= LBRAC IDENT(B) EQUALS STRING(C) RBRAC.
- { A = css_new_node(CSS_NODE_ATTRIB_EQ, B, 0, 0); A->data2 = css_unquote(C);
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_ATTRIB_EQ, B.text, B.length);
+ if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2;
+ A->specificity = 0x100; }
+ else param->memory_error = true; }
detail(A) ::= LBRAC IDENT(B) INCLUDES IDENT(C) RBRAC.
- { A = css_new_node(CSS_NODE_ATTRIB_INC, B, 0, 0); A->data2 = C;
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_ATTRIB_INC, B.text, B.length);
+ if (A) { A->data2 = C.text; A->data2_length = C.length;
+ A->specificity = 0x100; }
+ else param->memory_error = true; }
detail(A) ::= LBRAC IDENT(B) INCLUDES STRING(C) RBRAC.
- { A = css_new_node(CSS_NODE_ATTRIB_INC, B, 0, 0); A->data2 = css_unquote(C);
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_ATTRIB_INC, B.text, B.length);
+ if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2;
+ A->specificity = 0x100; }
+ else param->memory_error = true; }
detail(A) ::= LBRAC IDENT(B) DASHMATCH IDENT(C) RBRAC.
- { A = css_new_node(CSS_NODE_ATTRIB_DM, B, 0, 0); A->data2 = C;
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_ATTRIB_DM, B.text, B.length);
+ if (A) { A->data2 = C.text; A->data2_length = C.length;
+ A->specificity = 0x100; }
+ else param->memory_error = true; }
detail(A) ::= LBRAC IDENT(B) DASHMATCH STRING(C) RBRAC.
- { A = css_new_node(CSS_NODE_ATTRIB_DM, B, 0, 0); A->data2 = css_unquote(C);
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_ATTRIB_DM, B.text, B.length);
+ if (A) { A->data2 = C.text + 1; A->data2_length = C.length - 2;
+ A->specificity = 0x100; }
+ else param->memory_error = true; }
detail(A) ::= COLON IDENT(B).
- { if (strcasecmp(B, "link") == 0) {
- A = css_new_node(CSS_NODE_ATTRIB, xstrdup("href"), 0, 0);
- A->specificity = 0x100;
- free(B);
+ { if (B.length == 4 && strncasecmp(B.text, "link", 4) == 0) {
+ A = css_new_selector(CSS_SELECTOR_ATTRIB, "href", 4);
+ if (A) A->specificity = 0x100;
+ else param->memory_error = true;
} else {
- A = css_new_node(CSS_NODE_PSEUDO, B, 0, 0);
- A->specificity = 0x100;
+ A = css_new_selector(CSS_SELECTOR_PSEUDO, B.text, B.length);
+ if (A) A->specificity = 0x100;
+ else param->memory_error = true;
} }
detail(A) ::= COLON FUNCTION(B) IDENT RPAREN.
- { A = css_new_node(CSS_NODE_PSEUDO, B, 0, 0);
- A->specificity = 0x100; }
+ { A = css_new_selector(CSS_SELECTOR_PSEUDO, B.text, B.length);
+ if (A) A->specificity = 0x100;
+ else param->memory_error = true; }
declaration_list(A) ::= .
{ A = 0; }
@@ -150,7 +211,14 @@ declaration_list(A) ::= declaration(B) SEMI declaration_list(C).
{ if (B) { B->next = C; A = B; } else { A = C; } }
declaration(A) ::= property(B) COLON value(C).
- { A = css_new_node(CSS_NODE_DECLARATION, B, C, 0); }
+ { if (C && (A = css_new_node(CSS_NODE_DECLARATION,
+ B.text, B.length)))
+ A->value = C;
+ else {
+ param->memory_error = true;
+ css_free_node(C);
+ A = 0;
+ } }
declaration(A) ::= any_list_1(B). /* malformed declaration: ignore */
{ A = 0; css_free_node(B); }
@@ -160,7 +228,8 @@ property(A) ::= IDENT(B).
value(A) ::= any(B).
{ A = B; }
value(A) ::= any(B) value(C).
- { B->next = C; A = B; }
+ { if (B && C) { B->next = C; A = B; }
+ else { css_free_node(B); css_free_node(C); A = 0; } }
value(A) ::= value(B) block.
{ A = B; }
value(A) ::= value(B) ATKEYWORD.
@@ -170,70 +239,100 @@ value(A) ::= value(B) ATKEYWORD.
any_list(A) ::= .
{ A = 0; }
any_list(A) ::= any(B) any_list(C).
- { B->next = C; A = B; }
+ { if (B) { B->next = C; A = B; }
+ else { css_free_node(B); css_free_node(C); A = 0; } }
any_list_1(A) ::= any(B) any_list(C).
- { B->next = C; A = B; }
+ { if (B) { B->next = C; A = B; }
+ else { css_free_node(B); css_free_node(C); A = 0; } }
any(A) ::= IDENT(B).
- { A = css_new_node(CSS_NODE_IDENT, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_IDENT, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= NUMBER(B).
- { A = css_new_node(CSS_NODE_NUMBER, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_NUMBER, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= PERCENTAGE(B).
- { A = css_new_node(CSS_NODE_PERCENTAGE, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_PERCENTAGE, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= DIMENSION(B).
- { A = css_new_node(CSS_NODE_DIMENSION, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_DIMENSION, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= STRING(B).
- { A = css_new_node(CSS_NODE_STRING, css_unquote(B), 0, 0); }
+ { A = css_new_node(CSS_NODE_STRING, B.text + 1, B.length - 2);
+ if (!A) param->memory_error = true; }
any(A) ::= DELIM(B).
- { A = css_new_node(CSS_NODE_DELIM, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_DELIM, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= URI(B).
- { A = css_new_node(CSS_NODE_URI, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_URI, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= HASH(B).
- { A = css_new_node(CSS_NODE_HASH, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_HASH, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= UNICODE_RANGE(B).
- { A = css_new_node(CSS_NODE_UNICODE_RANGE, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_UNICODE_RANGE, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= INCLUDES.
- { A = css_new_node(CSS_NODE_INCLUDES, 0, 0, 0); }
+ { A = css_new_node(CSS_NODE_INCLUDES, 0, 0);
+ if (!A) param->memory_error = true; }
any(A) ::= FUNCTION(B).
- { A = css_new_node(CSS_NODE_FUNCTION, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_FUNCTION, B.text, B.length);
+ if (!A) param->memory_error = true; }
any(A) ::= DASHMATCH.
- { A = css_new_node(CSS_NODE_DASHMATCH, 0, 0, 0); }
+ { A = css_new_node(CSS_NODE_DASHMATCH, 0, 0);
+ if (!A) param->memory_error = true; }
any(A) ::= COLON.
- { A = css_new_node(CSS_NODE_COLON, 0, 0, 0); }
+ { A = css_new_node(CSS_NODE_COLON, 0, 0);
+ if (!A) param->memory_error = true; }
any(A) ::= COMMA.
- { A = css_new_node(CSS_NODE_COMMA, 0, 0, 0); }
+ { A = css_new_node(CSS_NODE_COMMA, 0, 0);
+ if (!A) param->memory_error = true; }
any(A) ::= DOT.
- { A = css_new_node(CSS_NODE_DOT, 0, 0, 0); }
+ { A = css_new_node(CSS_NODE_DOT, 0, 0);
+ if (!A) param->memory_error = true; }
any(A) ::= PLUS.
- { A = css_new_node(CSS_NODE_PLUS, 0, 0, 0); }
+ { A = css_new_node(CSS_NODE_PLUS, 0, 0);
+ if (!A) param->memory_error = true; }
any(A) ::= GT.
- { A = css_new_node(CSS_NODE_GT, 0, 0, 0); }
+ { A = css_new_node(CSS_NODE_GT, 0, 0);
+ if (!A) param->memory_error = true; }
any(A) ::= LPAREN any_list(B) RPAREN.
- { A = css_new_node(CSS_NODE_PAREN, 0, B, 0); }
+ { if ((A = css_new_node(CSS_NODE_PAREN, 0, 0)))
+ A->value = B;
+ else {
+ param->memory_error = true;
+ css_free_node(B);
+ A = 0;
+ } }
any(A) ::= LBRAC any_list(B) RBRAC.
- { A = css_new_node(CSS_NODE_BRAC, 0, B, 0); }
+ { if ((A = css_new_node(CSS_NODE_BRAC, 0, 0)))
+ A->value = B;
+ else {
+ param->memory_error = true;
+ css_free_node(B);
+ A = 0;
+ } }
any(A) ::= ASTERISK(B).
- { A = css_new_node(CSS_NODE_DELIM, B, 0, 0); }
+ { A = css_new_node(CSS_NODE_DELIM, B.text, B.length);
+ if (!A) param->memory_error = true; }
/* lemon directives */
-%extra_argument { struct parse_params *param }
+%extra_argument { struct css_parser_params *param }
%include {
#define CSS_INTERNALS
-#include "netsurf/css/scanner.h"
#include "netsurf/css/css.h"
#include "netsurf/utils/utils.h" }
%name css_parser_
-%token_type { char* }
-%token_destructor { xfree($$); }
+%token_type { struct css_parser_token }
-%type selector_list { struct css_node * }
-%type selector { struct css_node * }
+%type selector_list { struct css_selector * }
+%type selector { struct css_selector * }
%type css_combinator { css_combinator }
-%type simple_selector { struct css_node * }
-%type detail_list { struct css_node * }
-%type detail { struct css_node * }
+%type simple_selector { struct css_selector * }
+%type detail_list { struct css_selector * }
+%type detail { struct css_selector * }
%type declaration_list { struct css_node * }
%type declaration { struct css_node * }
%type value { struct css_node * }
@@ -241,10 +340,11 @@ any(A) ::= ASTERISK(B).
%type any_list_1 { struct css_node * }
%type any { struct css_node * }
-%destructor selector_list { css_free_node($$); }
-%destructor selector { css_free_node($$); }
-%destructor simple_selector { css_free_node($$); }
-%destructor detail_list { css_free_node($$); }
+%destructor selector_list { css_free_selector($$); }
+%destructor selector { css_free_selector($$); }
+%destructor simple_selector { css_free_selector($$); }
+%destructor detail_list { css_free_selector($$); }
+%destructor detail { css_free_selector($$); }
%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 d898dbcab..e2e0d03bd 100644
--- a/css/ruleset.c
+++ b/css/ruleset.c
@@ -5,6 +5,14 @@
* Copyright 2004 James Bursa <bursa@users.sourceforge.net>
*/
+/** \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.
+ */
+
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
@@ -18,23 +26,9 @@
#include "netsurf/utils/utils.h"
-struct property_entry {
- const char name[20];
- void (*parse) (struct css_style * const s, const struct css_node * const v);
-};
-
-struct colour_entry {
- const char name[12];
- colour col;
-};
-
-struct font_size_entry {
- const char name[10];
- float size;
-};
-
-static int compare_selectors(const struct css_node *n0, const struct css_node *n1);
+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);
@@ -102,11 +96,19 @@ static void parse_text_transform(struct css_style * const s, const struct css_no
static void parse_visibility(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 css_text_decoration css_text_decoration_parse(const char * const s);
+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[20];
+ 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 property_entry property_table[] = {
+/** Table of property parsers. MUST be sorted by property name. */
+static const struct css_property_entry css_property_table[] = {
{ "background", parse_background },
{ "background-color", parse_background_color },
{ "border", parse_border },
@@ -161,9 +163,16 @@ static const struct property_entry property_table[] = {
{ "width", parse_width },
};
-/* table of standard colour names: MUST be sorted by colour name
- * note: colour is 0xbbggrr */
-static const struct colour_entry colour_table[] = {
+
+/** 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 },
@@ -184,9 +193,16 @@ static const struct colour_entry colour_table[] = {
{ "yellow", 0x00ffff },
};
-/* table of font sizes: MUST be sorted by name */
+
+/** 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 font_size_entry font_size_table[] = {
+static const struct css_font_size_entry css_font_size_table[] = {
{ "large", 1.0 * SIZE_FACTOR },
{ "medium", 1.0 },
{ "small", 1.0 / SIZE_FACTOR },
@@ -198,42 +214,26 @@ static const struct font_size_entry font_size_table[] = {
/**
- * css_add_ruleset -- add a ruleset to a stylesheet
+ * Add a ruleset to a stylesheet.
*/
void css_add_ruleset(struct content *c,
- struct css_node *selector,
+ struct css_selector *selector,
struct css_node *declaration)
{
bool found;
struct css_stylesheet *stylesheet = c->data.css.css;
- struct css_node *n, *sel, *next_sel, *prev;
+ 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;
- /*LOG(("+++"));
- for (n = sel; n != 0; n = n->right) {
- struct css_node *m;
- if (n->data != 0)
- fprintf(stderr, "%s", n->data);
- for (m = n->left; m != 0; m = m->next) {
- switch (m->type) {
- case CSS_NODE_ID: fprintf(stderr, "%s", m->data); break;
- case CSS_NODE_CLASS: fprintf(stderr, ".%s", m->data); break;
- default: fprintf(stderr, "unexpected node");
- }
- }
- fprintf(stderr, " ");
- }
- fprintf(stderr, "\n");*/
-
/* check if this selector is already present */
found = false;
prev = 0;
- hash = css_hash(sel->data);
+ 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;
@@ -242,7 +242,7 @@ void css_add_ruleset(struct content *c,
for ( ; n && n->specificity == sel->specificity;
n = n->next) {
prev = n;
- if (compare_selectors(sel, n)) {
+ if (css_compare_selectors(sel, n)) {
found = true;
break;
}
@@ -250,7 +250,12 @@ void css_add_ruleset(struct content *c,
if (!found) {
/* not present: construct a new struct css_style */
LOG(("constructing new style"));
- style = xcalloc(1, sizeof(*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;
@@ -264,7 +269,7 @@ void css_add_ruleset(struct content *c,
LOG(("augumenting existing style"));
style = n->style;
sel->next = 0;
- css_free_node(sel);
+ css_free_selector(sel);
}
/* fill in the declarations */
@@ -273,69 +278,91 @@ void css_add_ruleset(struct content *c,
}
+/**
+ * Add declarations to a style.
+ */
+
void css_add_declarations(struct css_style *style, struct css_node *declaration)
{
+ char name[20];
struct css_node *n;
for (n = declaration; n != 0; n = n->next) {
- struct property_entry *p;
- assert(n->type == CSS_NODE_DECLARATION && n->data != 0 && n->left != 0);
- p = bsearch(n->data, property_table,
- sizeof(property_table) / sizeof(property_table[0]),
- sizeof(property_table[0]), (void*)strcasecmp);
+ struct css_property_entry *p;
+ assert(n->type == CSS_NODE_DECLARATION && n->data && n->value);
+ if (19 < 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->left);
+ p->parse(style, n->value);
}
}
-int compare_selectors(const struct css_node *n0, const struct css_node *n1)
+/**
+ * Compare two css_selectors.
+ */
+
+bool css_compare_selectors(const struct css_selector *n0,
+ const struct css_selector *n1)
{
- struct css_node *m0, *m1;
+ 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 && strcmp(n0->data, n1->data) == 0)))
- return 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 0;
+ return false;
/* compare classes and ids */
- for (m0 = n0->left; m0 != 0; m0 = m0->next)
+ for (m0 = n0->detail; m0 != 0; m0 = m0->next)
count0++;
- for (m1 = n1->left; m1 != 0; m1 = m1->next)
+ for (m1 = n1->detail; m1 != 0; m1 = m1->next)
count1++;
if (count0 != count1)
- return 0;
- for (m0 = n0->left; m0 != 0; m0 = m0->next) {
- int found = 0;
- for (m1 = n1->left; m1 != 0; m1 = m1->next) {
+ 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 &&
- strcasecmp(m0->data, m1->data) == 0 &&
+ m0->data_length == m1->data_length &&
+ strncasecmp(m0->data, m1->data,
+ m0->data_length) == 0 &&
((m0->data2 == 0 && m1->data2 == 0) ||
- strcasecmp(m0->data2, m1->data2) == 0)) {
- found = 1;
+ (m0->data2_length == m1->data2_length &&
+ strncasecmp(m0->data2, m1->data2,
+ m0->data2_length) == 0))) {
+ found = true;
break;
}
}
if (!found)
- return 0;
+ return false;
}
/* compare ancestors */
if (n0->comb == CSS_COMB_NONE)
- return 1;
+ return true;
- return compare_selectors(n0->right, n1->right);
+ return css_compare_selectors(n0->combiner, n1->combiner);
}
-
-/**
- * property parsers
+/*
+ * Property parsers.
*/
/* TODO: consider CSS_NODE_NUMBER whenever a value may be '0' */
@@ -345,6 +372,7 @@ int parse_length(struct css_length * const length,
{
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;
@@ -352,7 +380,8 @@ int parse_length(struct css_length * const length,
}
if (v->type != CSS_NODE_DIMENSION)
return 1;
- u = css_unit_parse(v->data + strspn(v->data, "0123456789+-."));
+ 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);
@@ -366,12 +395,13 @@ int parse_length(struct css_length * const length,
colour named_colour(const char *name)
{
- struct colour_entry *col;
+ struct css_colour_entry *col;
unsigned int r, g, b;
- col = bsearch(name, colour_table,
- sizeof(colour_table) / sizeof(colour_table[0]),
- sizeof(colour_table[0]), (void*)strcasecmp);
+ 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.
@@ -391,17 +421,16 @@ colour named_colour(const char *name)
colour parse_colour(const struct css_node * const v)
{
colour c = CSS_COLOR_NONE;
- int len;
unsigned int r, g, b;
- struct colour_entry *col;
+ struct css_colour_entry *col;
+ char colour_name[12];
switch (v->type) {
case CSS_NODE_HASH:
- len = strlen(v->data);
- if (len == 4) {
+ 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 (len == 7) {
+ } else if (v->data_length == 7) {
if (sscanf(v->data + 1, "%2x%2x%2x", &r, &g, &b) == 3)
c = (b << 16) | (g << 8) | r;
}
@@ -412,9 +441,16 @@ colour parse_colour(const struct css_node * const v)
break;
case CSS_NODE_IDENT:
- col = bsearch(v->data, colour_table,
- sizeof(colour_table) / sizeof(colour_table[0]),
- sizeof(colour_table[0]), (void*)strcasecmp);
+ 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;
@@ -460,10 +496,14 @@ void parse_border_width(struct css_style * const s,
for (w = v; w; w = w->next, count++)
if (!((w->type == CSS_NODE_IDENT && (
- (strcasecmp(w->data, "inherit") == 0) ||
- (strcasecmp(w->data, "thin") == 0) ||
- (strcasecmp(w->data, "medium") == 0) ||
- (strcasecmp(w->data, "thick") == 0))) ||
+ (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;
@@ -521,17 +561,21 @@ 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 (strcasecmp(v->data, "inherit") == 0)
+ if (v->data_length == 7 &&
+ strncasecmp(v->data, "inherit", 7) == 0)
s->border[i].width.width = CSS_BORDER_WIDTH_INHERIT;
- else if (strcasecmp(v->data, "thin") == 0) {
+ 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 (strcasecmp(v->data, "medium") == 0) {
+ } 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 (strcasecmp(v->data, "thick") == 0) {
+ } 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;
@@ -673,7 +717,7 @@ 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);
+ css_border_style z = css_border_style_parse(v->data, v->data_length);
if (z != CSS_BORDER_STYLE_UNKNOWN)
s->border[i].style = z;
}
@@ -706,7 +750,8 @@ void parse_border_side(struct css_style * const s,
css_border_style z;
if (!v->next && v->type == CSS_NODE_IDENT &&
- strcasecmp(v->data, "inherit") == 0) {
+ 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;
@@ -721,7 +766,7 @@ void parse_border_side(struct css_style * const s,
}
if (v->type == CSS_NODE_IDENT) {
- z = css_border_style_parse(v->data);
+ z = css_border_style_parse(v->data, v->data_length);
if (z != CSS_BORDER_STYLE_UNKNOWN) {
s->border[i].style = z;
continue;
@@ -737,7 +782,7 @@ 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);
+ z = css_clear_parse(v->data, v->data_length);
if (z != CSS_CLEAR_UNKNOWN)
s->clear = z;
}
@@ -755,7 +800,7 @@ void parse_cursor(struct css_style * const s, const struct css_node * v)
for (; v; v = v->next) {
switch (v->type) {
case CSS_NODE_IDENT:
- z = css_cursor_parse(v->data);
+ z = css_cursor_parse(v->data, v->data_length);
if (z != CSS_CURSOR_UNKNOWN) {
s->cursor = z;
return;
@@ -772,7 +817,7 @@ 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);
+ z = css_display_parse(v->data, v->data_length);
if (z != CSS_DISPLAY_UNKNOWN)
s->display = z;
}
@@ -782,7 +827,7 @@ 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);
+ z = css_float_parse(v->data, v->data_length);
if (z != CSS_FLOAT_UNKNOWN)
s->float_ = z;
}
@@ -802,23 +847,27 @@ void parse_font(struct css_style * const s, const struct css_node * v)
switch (v->type) {
case CSS_NODE_IDENT:
/* font-family */
- ff = css_font_family_parse(v->data);
+ 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);
+ 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);
+ 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);
+ fw = css_font_weight_parse(v->data,
+ v->data_length);
if (fw != CSS_FONT_WEIGHT_UNKNOWN) {
s->font_weight = fw;
break;
@@ -828,7 +877,7 @@ void parse_font(struct css_style * const s, const struct css_node * v)
parse_font_size(s, v);
break;
case CSS_NODE_DELIM:
- if (v->data[0] == '/' && v->data[1] == 0 &&
+ if (v->data[0] == '/' && v->data_length == 1 &&
v->next) {
v = v->next;
parse_line_height(s, v);
@@ -846,7 +895,8 @@ void parse_font_family(struct css_style * const s, const struct css_node * v)
for (; v; v = v->next) {
switch (v->type) {
case CSS_NODE_IDENT:
- z = css_font_family_parse(v->data);
+ z = css_font_family_parse(v->data,
+ v->data_length);
if (z != CSS_FONT_FAMILY_UNKNOWN) {
s->font_family = z;
return;
@@ -860,21 +910,31 @@ void parse_font_family(struct css_style * const s, const struct css_node * v)
void parse_font_size(struct css_style * const s, const struct css_node * const v)
{
- struct font_size_entry *fs;
+ char font_size_name[10];
+ struct css_font_size_entry *fs;
switch (v->type) {
case CSS_NODE_IDENT:
- fs = bsearch(v->data, font_size_table,
- sizeof(font_size_table) / sizeof(font_size_table[0]),
- sizeof(font_size_table[0]), (void*)strcasecmp);
+ 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 (strcasecmp(v->data, "larger") == 0) {
+ } 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 (strcmp(v->data, "smaller") == 0) {
+ } 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;
}
@@ -900,7 +960,7 @@ void parse_font_style(struct css_style * const s, const struct css_node * const
css_font_style z;
if (v->type != CSS_NODE_IDENT || v->next != 0)
return;
- z = css_font_style_parse(v->data);
+ z = css_font_style_parse(v->data, v->data_length);
if (z != CSS_FONT_STYLE_UNKNOWN)
s->font_style = z;
}
@@ -910,7 +970,7 @@ void parse_font_variant(struct css_style * const s, const struct css_node * cons
css_font_variant z;
if (v->type != CSS_NODE_IDENT || v->next != 0)
return;
- z = css_font_variant_parse(v->data);
+ z = css_font_variant_parse(v->data, v->data_length);
if (z != CSS_FONT_VARIANT_UNKNOWN)
s->font_variant = z;
}
@@ -920,14 +980,15 @@ void parse_font_weight(struct css_style * const s, const struct css_node * const
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);
+ 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 && strcasecmp(v->data, "auto") == 0)
+ 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)
@@ -936,7 +997,8 @@ void parse_height(struct css_style * const s, const struct css_node * const v)
void parse_line_height(struct css_style * const s, const struct css_node * const v)
{
- if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "normal") == 0) {
+ 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) {
@@ -958,8 +1020,10 @@ void parse_margin(struct css_style * const s, const struct css_node * const v)
for (w = v; w; w = w->next, count++)
if (!((w->type == CSS_NODE_IDENT && (
- (strcasecmp(w->data, "inherit") == 0) ||
- (strcasecmp(w->data, "auto") == 0))) ||
+ (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)))
@@ -1017,9 +1081,11 @@ 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 && strcasecmp(v->data, "inherit") == 0)
+ 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 && strcasecmp(v->data, "auto") == 0)
+ 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;
@@ -1036,7 +1102,8 @@ void parse_padding(struct css_style * const s, const struct css_node * const v)
const struct css_node *w;
for (w = v; w; w = w->next, count++)
- if (!((w->type == CSS_NODE_IDENT && strcasecmp(w->data, "inherit") == 0) ||
+ 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)))
@@ -1094,7 +1161,8 @@ 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 && strcasecmp(v->data, "inherit") == 0)
+ 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;
@@ -1110,7 +1178,7 @@ void parse_text_align(struct css_style * const s, const struct css_node * const
css_text_align z;
if (v->type != CSS_NODE_IDENT || v->next != 0)
return;
- z = css_text_align_parse(v->data);
+ z = css_text_align_parse(v->data, v->data_length);
if (z != CSS_TEXT_ALIGN_UNKNOWN)
s->text_align = z;
}
@@ -1134,7 +1202,7 @@ void parse_text_decoration(struct css_style * const s, const struct css_node * c
css_text_decoration z;
if (v->type != CSS_NODE_IDENT)
return;
- z = css_text_decoration_parse(v->data);
+ 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;
@@ -1143,7 +1211,7 @@ void parse_text_decoration(struct css_style * const s, const struct css_node * c
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);
+ z = css_text_decoration_parse(temp->data, temp->data_length);
if (z != CSS_TEXT_DECORATION_UNKNOWN)
s->text_decoration |= z;
}
@@ -1154,7 +1222,7 @@ void parse_text_transform(struct css_style * const s, const struct css_node * co
css_text_transform z;
if (v->type != CSS_NODE_IDENT || v->next != 0)
return;
- z = css_text_transform_parse(v->data);
+ z = css_text_transform_parse(v->data, v->data_length);
if (z != CSS_TEXT_TRANSFORM_UNKNOWN)
s->text_transform = z;
}
@@ -1164,14 +1232,15 @@ void parse_visibility(struct css_style * const s, const struct css_node * const
css_visibility z;
if (v->type != CSS_NODE_IDENT || v->next != 0)
return;
- z = css_visibility_parse(v->data);
+ z = css_visibility_parse(v->data, v->data_length);
if (z != CSS_VISIBILITY_UNKNOWN)
s->visibility = z;
}
void parse_width(struct css_style * const s, const struct css_node * const v)
{
- if (v->type == CSS_NODE_IDENT && strcasecmp(v->data, "auto") == 0)
+ 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;
@@ -1186,19 +1255,26 @@ void parse_white_space(struct css_style * const s, const struct css_node * const
css_white_space z;
if (v->type != CSS_NODE_IDENT || v->next != 0)
return;
- z = css_white_space_parse(v->data);
+ z = css_white_space_parse(v->data, v->data_length);
if (z != CSS_WHITE_SPACE_UNKNOWN)
s->white_space = z;
}
-css_text_decoration css_text_decoration_parse(const char * const s)
+css_text_decoration css_text_decoration_parse(const char * const s,
+ int length)
{
- if (strcasecmp(s, "inherit") == 0) return CSS_TEXT_DECORATION_INHERIT;
- if (strcasecmp(s, "none") == 0) return CSS_TEXT_DECORATION_NONE;
- if (strcasecmp(s, "blink") == 0) return CSS_TEXT_DECORATION_BLINK;
- if (strcasecmp(s, "line-through") == 0) return CSS_TEXT_DECORATION_LINE_THROUGH;
- if (strcasecmp(s, "overline") == 0) return CSS_TEXT_DECORATION_OVERLINE;
- if (strcasecmp(s, "underline") == 0) return CSS_TEXT_DECORATION_UNDERLINE;
+ 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;
}
diff --git a/css/scanner.l b/css/scanner.l
index 1f8cc1d92..36347b3a8 100644
--- a/css/scanner.l
+++ b/css/scanner.l
@@ -2,78 +2,97 @@
* 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 2003 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
*/
-%{
-#include "parser.h"
-%}
+/** \file
+ * CSS tokeniser using re2c.
+ *
+ * see CSS2 Specification, chapter 4
+ * http://www.w3.org/TR/REC-CSS2/syndata.html,
+ * and errata
+ * http://www.w3.org/Style/css2-updates/REC-CSS2-19980512-errata
+ */
+
+#include <stdbool.h>
+#define CSS_INTERNALS
+#include "netsurf/css/css.h"
+#include "netsurf/css/parser.h"
+
+#define YYCTYPE unsigned char
+#define YYCURSOR (*buffer)
+#define YYLIMIT end
+#define YYMARKER marker
+#define YYFILL(n) { return 0; }
-%option 8bit
-%option batch
-%option case-insensitive
-%option header-file="scanner.h"
-%option outfile="scanner.c"
-%option prefix="css_"
-%option reentrant
-%option never-interactive
-%option noyywrap
-%option yylineno
-/* see CSS2 Specification, chapter 4
- http://www.w3.org/TR/REC-CSS2/syndata.html,
- and errata
- http://www.w3.org/Style/css2-updates/REC-CSS2-19980512-errata */
+/**
+ * Identify a CSS source token.
+ *
+ * \param buffer source to tokenise, updated to new position
+ * \param end end of source
+ * \param token_text updated to start of recognized token
+ * \return token number
+ */
+
+int css_tokenise(unsigned char **buffer, unsigned char *end,
+ unsigned char **token_text)
+{
+ unsigned char *marker;
-ident {nmstart}{nmchar}*
-name {nmchar}+
-nmstart [a-zA-Z_]|{nonascii}|{escape}
-nonascii [\200-\377]
-unicode \\[0-9a-f]{1,6}[ \n\r\t\f]?
-escape {unicode}|\\[ -~\200-\377]
-nmchar [-a-zA-Z0-9_]|{nonascii}|{escape}
-num [+-]?[0-9]+|[0-9]*\.[0-9]+
-string {string1}|{string2}
-string1 \"([\t !#$%&(-~]|\\{nl}|\'|{nonascii}|{escape})*\"
-string2 \'([\t !#$%&(-~]|\\{nl}|\"|{nonascii}|{escape})*\'
-nl \n|\r\n|\r|\f
-w [ \t\r\n\f]*
+start:
+ *token_text = YYCURSOR;
-%%
+/*!re2c
+nonascii = [\200-\377];
+unicode = "\\" [0-9a-f]+ [ \n\r\t\f]?;
+escape = unicode | "\\" [ -~\200-\377];
+nmchar = [-a-zA-Z0-9_] | nonascii | escape;
+nmstart = [a-zA-Z_] | nonascii | escape;
+ident = nmstart nmchar*;
+name = nmchar+;
+num = [+-]? [0-9]+ | [0-9]* "." [0-9]+;
+nl = "\n" | "\r\n" | "\r" | "\f";
+string1 = "\"" ([\t !#$%&(-~] | "\\" nl | "'" | nonascii | escape)* "\"";
+string2 = "'" ([\t !#$%&(-~] | "\\" nl | "\""| nonascii | escape)* "'";
+string = string1 | string2;
+w = [ \t\r\n\f]*;
+any = [\000-\377];
-{ident} { return IDENT; }
-@{ident} { return ATKEYWORD; }
-{string} { return STRING; }
-#{name} { return HASH; }
-{num} { return NUMBER; }
-{num}% { return PERCENTAGE; }
-{num}{ident} { return DIMENSION; }
-url\({w}{string}{w}\)|url\({w}([!#$%&*-~]|{nonascii}|{escape})*{w}\) {
- return URI; }
-U\+[0-9A-F?]{1,6}(-[0-9A-F]{1,6})? {
- return UNICODE_RANGE; }
-"<!--" /* ignore CDO */
-"-->" /* ignore CDC */
-; { return SEMI; }
-\{ { return LBRACE; }
-\} { return RBRACE; }
-\( { return LPAREN; }
-\) { return RPAREN; }
-\[ { return LBRAC; }
-\] { return RBRAC; }
-[ \t\r\n\f]+ /* ignore whitespace */
-\/\*[^*]*\*+([^/][^*]*\*+)*\/ /* ignore comments */
-{ident}\( { return FUNCTION; }
-= { return EQUALS; }
-~= { return INCLUDES; }
+ident { return IDENT; }
+"@" ident { return ATKEYWORD; }
+string { return STRING; }
+"#" name { return HASH; }
+num { return NUMBER; }
+num "%" { return PERCENTAGE; }
+num ident { return DIMENSION; }
+"url(" w string w ")" | "url(" w ([!#$%&*-~]|nonascii|escape)* w ")"
+ { return URI; }
+"U+" [0-9A-F?]+ ("-" [0-9A-F]+ )?
+ { return UNICODE_RANGE; }
+"<!--" { goto start; /* ignore CDO */ }
+"-->" { goto start; /* ignore CDC */ }
+";" { return SEMI; }
+"{" { return LBRACE; }
+"}" { return RBRACE; }
+"(" { return LPAREN; }
+")" { return RPAREN; }
+"[" { return LBRAC; }
+"]" { return RBRAC; }
+[ \t\r\n\f]+ { goto start; /* ignore whitespace */ }
+"/*" (any\[*])* "*"+ ((any\[/]) (any\[*])* "*"+)* "/"
+ { goto start; /* ignore comments */ }
+ident "(" { return FUNCTION; }
+"=" { return EQUALS; }
+"~=" { return INCLUDES; }
"|=" { return DASHMATCH; }
-: { return COLON; }
-, { return COMMA; }
+":" { return COLON; }
+"," { return COMMA; }
"+" { return PLUS; }
-> { return GT; }
+">" { return GT; }
"." { return DOT; }
"*" { return ASTERISK; }
-. { return DELIM; }
-
-%%
+any { return DELIM; }
+*/
+}