summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2011-10-08 00:21:59 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2011-10-08 00:21:59 +0000
commit4e574f12527b241ea266a9fad21f6424a9af3a44 (patch)
tree939fef2635fedee440bd6fcf08cadc95f4471930
parent1e828006aa6f15151b0a0786db79de90de54046a (diff)
downloadnetsurf-4e574f12527b241ea266a9fad21f6424a9af3a44.tar.gz
netsurf-4e574f12527b241ea266a9fad21f6424a9af3a44.tar.bz2
Improve performance of style selection
svn path=/trunk/netsurf/; revision=13000
-rw-r--r--css/select.c365
-rw-r--r--css/select.h1
-rw-r--r--render/box_construct.c2
-rw-r--r--render/box_normalise.c7
-rw-r--r--render/html.c21
-rw-r--r--render/html_internal.h2
-rw-r--r--render/hubbub_binding.c270
-rw-r--r--render/parser_binding.h6
8 files changed, 379 insertions, 295 deletions
diff --git a/css/select.c b/css/select.c
index af49b0d84..57b7251b5 100644
--- a/css/select.c
+++ b/css/select.c
@@ -28,6 +28,7 @@
#include "css/utils.h"
#include "desktop/gui.h"
#include "desktop/options.h"
+#include "render/parser_binding.h"
#include "utils/log.h"
#include "utils/url.h"
#include "utils/utils.h"
@@ -455,24 +456,15 @@ bool nscss_parse_colour(const char *data, css_color *result)
css_error node_name(void *pw, void *node, css_qname *qname)
{
xmlNode *n = node;
- lwc_error lerror;
+ binding_private *p = n->_private;
qname->ns = NULL;
- lerror = lwc_intern_string((const char *) n->name,
- strlen((const char *) n->name),
- &qname->name);
- switch (lerror) {
- case lwc_error_oom:
- return CSS_NOMEM;
- case lwc_error_range:
- assert(0);
- default:
- break;
- }
+ assert(p != NULL && p->localname != NULL);
- return CSS_OK;
+ qname->name = lwc_string_ref(p->localname);
+ return CSS_OK;
}
/**
@@ -493,93 +485,27 @@ css_error node_classes(void *pw, void *node,
lwc_string ***classes, uint32_t *n_classes)
{
xmlNode *n = node;
- xmlAttr *class;
- xmlChar *value = NULL;
- const char *p;
- const char *start;
- lwc_string **result = NULL;
- uint32_t items = 0;
- lwc_error lerror;
- css_error error = CSS_OK;
+ binding_private *p = n->_private;
*classes = NULL;
*n_classes = 0;
- /* See if there is a class attribute on this node */
- class = xmlHasProp(n, (const xmlChar *) "class");
- if (class == NULL)
- return CSS_OK;
-
- /* We have a class attribute -- extract its value */
- if (class->children != NULL && class->children->next == NULL &&
- class->children->children == NULL) {
- /* Simple case -- no XML entities */
- start = (const char *) class->children->content;
- } else {
- /* Awkward case -- fall back to string copying */
- value = xmlGetProp(n, (const xmlChar *) "class");
- if (value == NULL)
- return CSS_OK;
-
- start = (const char *) value;
- }
-
- /* The class attribute is a space separated list of tokens. */
- do {
- lwc_string **temp;
-
- /* Find next space or end of string */
- p = strchrnul(start, ' ');
+ if (p->nclasses > 0) {
+ lwc_string **result;
+ uint32_t items;
- temp = realloc(result, (items + 1) * sizeof(lwc_string *));
- if (temp == NULL) {
- error = CSS_NOMEM;
- goto cleanup;
- }
- result = temp;
-
- lerror = lwc_intern_string(start, p - start, &result[items]);
- switch (lerror) {
- case lwc_error_oom:
- error = CSS_NOMEM;
- goto cleanup;
- case lwc_error_range:
- assert(0);
- default:
- break;
- }
-
- items++;
+ result = malloc(p->nclasses * sizeof(lwc_string *));
+ if (result == NULL)
+ return CSS_NOMEM;
- /* Move to start of next token in string */
- start = p + 1;
- } while (*p != '\0');
+ for (items = 0; items < p->nclasses; items++)
+ result[items] = lwc_string_ref(p->classes[items]);
- /* Clean up, if necessary */
- if (value != NULL) {
- xmlFree(value);
+ *classes = result;
+ *n_classes = p->nclasses;
}
- *classes = result;
- *n_classes = items;
-
return CSS_OK;
-
-cleanup:
- if (result != NULL) {
- uint32_t i;
-
- for (i = 0; i < items; i++)
- lwc_string_unref(result[i]);
-
- free(result);
- }
-
- if (value != NULL) {
- xmlFree(value);
- }
-
- return error;
}
/**
@@ -594,52 +520,14 @@ cleanup:
css_error node_id(void *pw, void *node, lwc_string **id)
{
xmlNode *n = node;
- xmlAttr *attr;
- xmlChar *value = NULL;
- const char *start;
- lwc_error lerror;
- css_error error = CSS_OK;
+ binding_private *p = n->_private;
*id = NULL;
- /* See if there's an id attribute on this node */
- attr = xmlHasProp(n, (const xmlChar *) "id");
- if (attr == NULL)
- return CSS_OK;
-
- /* We have an id attribute -- extract its value */
- if (attr->children != NULL && attr->children->next == NULL &&
- attr->children->children == NULL) {
- /* Simple case -- no XML entities */
- start = (const char *) attr->children->content;
- } else {
- /* Awkward case -- fall back to string copying */
- value = xmlGetProp(n, (const xmlChar *) "id");
- if (value == NULL)
- return CSS_OK;
-
- start = (const char *) value;
- }
-
- /* Intern value */
- lerror = lwc_intern_string(start, strlen(start), id);
- switch (lerror) {
- case lwc_error_oom:
- error = CSS_NOMEM;
- break;
- case lwc_error_range:
- assert(0);
- break;
- default:
- break;
- }
-
- /* Clean up if necessary */
- if (value != NULL) {
- xmlFree(value);
- }
+ if (p->id != NULL)
+ *id = lwc_string_ref(p->id);
- return error;
+ return CSS_OK;
}
/**
@@ -657,8 +545,8 @@ css_error named_ancestor_node(void *pw, void *node,
const css_qname *qname, void **ancestor)
{
xmlNode *n = node;
- size_t len = lwc_string_length(qname->name);
- const char *data = lwc_string_data(qname->name);
+ binding_private *p;
+ bool match;
*ancestor = NULL;
@@ -666,9 +554,11 @@ css_error named_ancestor_node(void *pw, void *node,
if (n->type != XML_ELEMENT_NODE)
continue;
- if (strlen((const char *) n->name) == len &&
- strncasecmp((const char *) n->name,
- data, len) == 0) {
+ p = n->_private;
+
+ if (lwc_string_caseless_isequal(qname->name,
+ p->localname, &match) == lwc_error_ok &&
+ match) {
*ancestor = (void *) n;
break;
}
@@ -692,8 +582,8 @@ css_error named_parent_node(void *pw, void *node,
const css_qname *qname, void **parent)
{
xmlNode *n = node;
- size_t len = lwc_string_length(qname->name);
- const char *data = lwc_string_data(qname->name);
+ binding_private *p;
+ bool match;
*parent = NULL;
@@ -703,9 +593,13 @@ css_error named_parent_node(void *pw, void *node,
break;
}
- if (n != NULL && strlen((const char *) n->name) == len &&
- strncasecmp((const char *) n->name,
- data, len) == 0)
+ if (n == NULL)
+ return CSS_OK;
+
+ p = n->_private;
+
+ if (lwc_string_caseless_isequal(qname->name, p->localname,
+ &match) == lwc_error_ok && match)
*parent = (void *) n;
return CSS_OK;
@@ -726,8 +620,8 @@ css_error named_sibling_node(void *pw, void *node,
const css_qname *qname, void **sibling)
{
xmlNode *n = node;
- size_t len = lwc_string_length(qname->name);
- const char *data = lwc_string_data(qname->name);
+ binding_private *p;
+ bool match;
*sibling = NULL;
@@ -737,9 +631,13 @@ css_error named_sibling_node(void *pw, void *node,
break;
}
- if (n != NULL && strlen((const char *) n->name) == len &&
- strncasecmp((const char *) n->name,
- data, len) == 0)
+ if (n == NULL)
+ return CSS_OK;
+
+ p = n->_private;
+
+ if (lwc_string_caseless_isequal(qname->name, p->localname,
+ &match) == lwc_error_ok && match)
*sibling = (void *) n;
return CSS_OK;
@@ -760,8 +658,8 @@ css_error named_generic_sibling_node(void *pw, void *node,
const css_qname *qname, void **sibling)
{
xmlNode *n = node;
- size_t len = lwc_string_length(qname->name);
- const char *data = lwc_string_data(qname->name);
+ binding_private *p;
+ bool match;
*sibling = NULL;
@@ -769,9 +667,11 @@ css_error named_generic_sibling_node(void *pw, void *node,
if (n->type != XML_ELEMENT_NODE)
continue;
- if (strlen((const char *) n->name) == len &&
- strncasecmp((const char *) n->name,
- data, len) == 0) {
+ p = n->_private;
+
+ if (lwc_string_caseless_isequal(qname->name,
+ p->localname, &match) == lwc_error_ok &&
+ match) {
*sibling = (void *) n;
break;
}
@@ -844,17 +744,16 @@ css_error sibling_node(void *pw, void *node, void **sibling)
css_error node_has_name(void *pw, void *node,
const css_qname *qname, bool *match)
{
+ nscss_select_ctx *ctx = pw;
xmlNode *n = node;
- size_t len = lwc_string_length(qname->name);
- const char *data = lwc_string_data(qname->name);
+ binding_private *p = n->_private;
+ lwc_string *name = qname->name;
- if (len == 1 && data[0] == '*') {
- *match = true;
- } else {
+ lwc_string_isequal(name, ctx->universal, match);
+ if (*match == false) {
/* Element names are case insensitive in HTML */
- *match = strlen((const char *) n->name) == len &&
- strncasecmp((const char *) n->name, data, len) == 0;
- }
+ lwc_string_caseless_isequal(p->localname, name, match);
+ }
return CSS_OK;
}
@@ -875,65 +774,24 @@ css_error node_has_class(void *pw, void *node,
{
nscss_select_ctx *ctx = pw;
xmlNode *n = node;
- xmlAttr *class;
- xmlChar *value = NULL;
- const char *p;
- const char *start;
- const char *data;
- size_t len;
- int (*cmp)(const char *, const char *, size_t);
-
- /* Class names are case insensitive in quirks mode */
- if (ctx->quirks)
- cmp = strncasecmp;
- else
- cmp = strncmp;
+ binding_private *p = n->_private;
+ uint32_t count;
*match = false;
- /* See if there is a class attribute on this node */
- class = xmlHasProp(n, (const xmlChar *) "class");
- if (class == NULL)
- return CSS_OK;
-
- /* We have a class attribute -- extract its value */
- if (class->children != NULL && class->children->next == NULL &&
- class->children->children == NULL) {
- /* Simple case -- no XML entities */
- start = (const char *) class->children->content;
+ /* Class names are case insensitive in quirks mode */
+ if (ctx->quirks) {
+ for (count = 0; count < p->nclasses; count++) {
+ if (lwc_string_caseless_isequal(name, p->classes[count],
+ match) == lwc_error_ok && *match)
+ break;
+ }
} else {
- /* Awkward case -- fall back to string copying */
- value = xmlGetProp(n, (const xmlChar *) "class");
- if (value == NULL)
- return CSS_OK;
-
- start = (const char *) value;
- }
-
- /* Extract expected class name data */
- data = lwc_string_data(name);
- len = lwc_string_length(name);
-
- /* The class attribute is a space separated list of tokens.
- * Search it for the one we're looking for.
- */
- do {
- /* Find next space or end of string */
- p = strchrnul(start, ' ');
-
- /* Does it match? */
- if ((size_t) (p - start) == len && cmp(start, data, len) == 0) {
- *match = true;
- break;
+ for (count = 0; count < p->nclasses; count++) {
+ if (lwc_string_isequal(name, p->classes[count],
+ match) == lwc_error_ok && *match)
+ break;
}
-
- /* Move to start of next token in string */
- start = p + 1;
- } while (*p != '\0');
-
- /* Clean up, if necessary */
- if (value != NULL) {
- xmlFree(value);
}
return CSS_OK;
@@ -954,44 +812,12 @@ css_error node_has_id(void *pw, void *node,
lwc_string *name, bool *match)
{
xmlNode *n = node;
- xmlAttr *id;
- xmlChar *value = NULL;
- const char *start;
- const char *data;
- size_t len;
+ binding_private *p = n->_private;
*match = false;
- /* See if there's an id attribute on this node */
- id = xmlHasProp(n, (const xmlChar *) "id");
- if (id == NULL)
- return CSS_OK;
-
- /* We have an id attribute -- extract its value */
- if (id->children != NULL && id->children->next == NULL &&
- id->children->children == NULL) {
- /* Simple case -- no XML entities */
- start = (const char *) id->children->content;
- } else {
- /* Awkward case -- fall back to string copying */
- value = xmlGetProp(n, (const xmlChar *) "id");
- if (value == NULL)
- return CSS_OK;
-
- start = (const char *) value;
- }
-
- /* Extract expected id data */
- len = lwc_string_length(name);
- data = lwc_string_data(name);
-
- /* Compare */
- *match = strlen(start) == len && strncmp(start, data, len) == 0;
-
- /* Clean up if necessary */
- if (value != NULL) {
- xmlFree(value);
- }
+ if (p->id != NULL)
+ lwc_string_isequal(name, p->id, match);
return CSS_OK;
}
@@ -1018,7 +844,6 @@ css_error node_has_attribute(void *pw, void *node,
*match = attr != NULL;
return CSS_OK;
-
}
/**
@@ -1328,21 +1153,43 @@ css_error node_count_siblings(void *pw, void *node, bool same_name,
bool after, int32_t *count)
{
xmlNode *n = node;
- const char *name = (char *) n->name;
int32_t cnt = 0;
- do {
- n = after ? n->next : n->prev;
+ if (same_name) {
+ binding_private *p = n->_private;
+ lwc_string *name = p->localname;
+ bool match;
+
+ do {
+ n = after ? n->next : n->prev;
- if (n != NULL && n->type == XML_ELEMENT_NODE) {
- if (same_name) {
- if (strcasecmp(name, (char *) n->name) == 0)
+ if (n != NULL && n->type == XML_ELEMENT_NODE) {
+ p = n->_private;
+
+ if (lwc_string_caseless_isequal(p->localname,
+ name, &match) == lwc_error_ok &&
+ match) {
cnt++;
- } else {
+ }
+ }
+ } while (n != NULL);
+ } else if (after) {
+ do {
+ n = n->next;
+
+ if (n != NULL && n->type == XML_ELEMENT_NODE) {
cnt++;
}
- }
- } while (n != NULL);
+ } while (n != NULL);
+ } else {
+ do {
+ n = n->prev;
+
+ if (n != NULL && n->type == XML_ELEMENT_NODE) {
+ cnt++;
+ }
+ } while (n != NULL);
+ }
*count = cnt;
diff --git a/css/select.h b/css/select.h
index 237af5ffb..2fcaa9186 100644
--- a/css/select.h
+++ b/css/select.h
@@ -36,6 +36,7 @@ typedef struct nscss_select_ctx
css_select_ctx *ctx;
bool quirks;
nsurl *base_url;
+ lwc_string *universal;
} nscss_select_ctx;
css_stylesheet *nscss_create_inline_style(const uint8_t *data, size_t len,
diff --git a/render/box_construct.c b/render/box_construct.c
index 2e6d52567..3d5e91ec3 100644
--- a/render/box_construct.c
+++ b/render/box_construct.c
@@ -1102,6 +1102,7 @@ css_select_results *box_get_style(html_content *c,
ctx.ctx = c->select_ctx;
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
/* Select partial style for element */
styles = nscss_get_style(&ctx, n, CSS_MEDIA_SCREEN, inline_style,
@@ -1118,7 +1119,6 @@ css_select_results *box_get_style(html_content *c,
/* If there's a parent style, compose with partial to obtain
* complete computed style for element */
if (parent_style != NULL) {
-
/* Complete the computed style, by composing with the parent
* element's style */
error = css_computed_style_compose(parent_style,
diff --git a/render/box_normalise.c b/render/box_normalise.c
index 8d88a2ef2..fb378d3e7 100644
--- a/render/box_normalise.c
+++ b/render/box_normalise.c
@@ -154,6 +154,7 @@ bool box_normalise_block(struct box *block, html_content *c)
ctx.ctx = c->select_ctx;
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
style = nscss_get_blank_style(&ctx, block->style,
box_style_alloc, NULL);
@@ -255,6 +256,7 @@ bool box_normalise_table(struct box *table, html_content * c)
ctx.ctx = c->select_ctx;
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
style = nscss_get_blank_style(&ctx, table->style,
box_style_alloc, NULL);
@@ -341,6 +343,7 @@ bool box_normalise_table(struct box *table, html_content * c)
ctx.ctx = c->select_ctx;
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
style = nscss_get_blank_style(&ctx, table->style,
box_style_alloc, NULL);
@@ -476,6 +479,7 @@ bool box_normalise_table_spans(struct box *table, struct span_info *spans,
ctx.quirks = (c->quirks ==
BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
style = nscss_get_blank_style(&ctx,
table_row->style,
@@ -585,6 +589,7 @@ bool box_normalise_table_row_group(struct box *row_group,
ctx.ctx = c->select_ctx;
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
style = nscss_get_blank_style(&ctx, row_group->style,
box_style_alloc, NULL);
@@ -659,6 +664,7 @@ bool box_normalise_table_row_group(struct box *row_group,
ctx.ctx = c->select_ctx;
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
style = nscss_get_blank_style(&ctx, row_group->style,
box_style_alloc, NULL);
@@ -728,6 +734,7 @@ bool box_normalise_table_row(struct box *row,
ctx.ctx = c->select_ctx;
ctx.quirks = (c->quirks == BINDING_QUIRKS_MODE_FULL);
ctx.base_url = c->base_url;
+ ctx.universal = c->universal;
style = nscss_get_blank_style(&ctx, row->style,
box_style_alloc, NULL);
diff --git a/render/html.c b/render/html.c
index d9e6c4834..205469934 100644
--- a/render/html.c
+++ b/render/html.c
@@ -274,6 +274,7 @@ nserror html_create_html_data(html_content *c, const http_parameter *params)
c->stylesheet_count = 0;
c->stylesheets = NULL;
c->select_ctx = NULL;
+ c->universal = NULL;
c->num_objects = 0;
c->object_list = NULL;
c->forms = NULL;
@@ -286,6 +287,11 @@ nserror html_create_html_data(html_content *c, const http_parameter *params)
c->font_func = &nsfont;
c->scrollbar = NULL;
+ if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) {
+ error = BINDING_NOMEM;
+ goto error;
+ }
+
selection_prepare(&c->sel, (struct content *)c, true);
nerror = http_parameter_list_find_item(params, html_charset, &charset);
@@ -329,6 +335,16 @@ error:
content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data);
+ if (c->universal != NULL) {
+ lwc_string_unref(c->universal);
+ c->universal = NULL;
+ }
+
+ if (c->base_url != NULL) {
+ nsurl_unref(c->base_url);
+ c->base_url = NULL;
+ }
+
return nerror;
}
@@ -1979,6 +1995,11 @@ void html_destroy(struct content *c)
html->select_ctx = NULL;
}
+ if (html->universal != NULL) {
+ lwc_string_unref(html->universal);
+ html->universal = NULL;
+ }
+
/* Free stylesheets */
for (i = 0; i != html->stylesheet_count; i++) {
if (html->stylesheets[i].type == HTML_STYLESHEET_EXTERNAL &&
diff --git a/render/html_internal.h b/render/html_internal.h
index 83547401c..7c1acd4be 100644
--- a/render/html_internal.h
+++ b/render/html_internal.h
@@ -64,6 +64,8 @@ typedef struct html_content {
struct html_stylesheet *stylesheets;
/**< Style selection context */
css_select_ctx *select_ctx;
+ /**< Universal selector */
+ lwc_string *universal;
/** Number of entries in object_list. */
unsigned int num_objects;
diff --git a/render/hubbub_binding.c b/render/hubbub_binding.c
index 5012b4b41..2b7836346 100644
--- a/render/hubbub_binding.c
+++ b/render/hubbub_binding.c
@@ -33,6 +33,7 @@
#include "utils/config.h"
#include "utils/log.h"
#include "utils/talloc.h"
+#include "utils/utils.h"
/**
* Private data attached to each DOM node
@@ -79,6 +80,8 @@ static struct {
};
static hubbub_private *create_private(uint32_t refcnt);
+static hubbub_private *copy_private(const hubbub_private *p, uint32_t refcnt);
+static void destroy_private(hubbub_private *p);
static inline char *c_string_from_hubbub_string(hubbub_ctx *ctx,
const hubbub_string *str);
static void create_namespaces(hubbub_ctx *ctx, xmlNode *root);
@@ -311,7 +314,7 @@ void binding_destroy_document(xmlDocPtr doc)
xmlNode *n = (xmlNode *) doc;
while (n != NULL) {
- free(n->_private);
+ destroy_private(n->_private);
if (n->children != NULL) {
n = n->children;
@@ -343,6 +346,55 @@ hubbub_private *create_private(uint32_t refcnt)
return pvt;
}
+hubbub_private *copy_private(const hubbub_private *p, uint32_t refcnt)
+{
+ hubbub_private *pvt = calloc(1, sizeof(*pvt));
+
+ if (pvt != NULL) {
+ pvt->refcnt = refcnt;
+
+ if (p->base.nclasses > 0) {
+ pvt->base.classes =
+ malloc(p->base.nclasses * sizeof(lwc_string *));
+ if (pvt->base.classes == NULL) {
+ free(pvt);
+ return NULL;
+ }
+
+ while (pvt->base.nclasses < p->base.nclasses) {
+ pvt->base.classes[pvt->base.nclasses] =
+ lwc_string_ref(p->base.classes[
+ pvt->base.nclasses]);
+ }
+ }
+
+ if (p->base.localname != NULL)
+ pvt->base.localname = lwc_string_ref(p->base.localname);
+
+ if (p->base.id != NULL)
+ pvt->base.id = lwc_string_ref(p->base.id);
+ }
+
+ return pvt;
+}
+
+void destroy_private(hubbub_private *p)
+{
+ if (p->base.localname != NULL)
+ lwc_string_unref(p->base.localname);
+
+ if (p->base.id != NULL)
+ lwc_string_unref(p->base.id);
+
+ while (p->base.nclasses > 0)
+ lwc_string_unref(p->base.classes[--p->base.nclasses]);
+
+ if (p->base.classes != NULL)
+ free(p->base.classes);
+
+ free(p);
+}
+
char *c_string_from_hubbub_string(hubbub_ctx *ctx, const hubbub_string *str)
{
return strndup((const char *) str->ptr, (int) str->len);
@@ -451,18 +503,20 @@ hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype,
hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result)
{
hubbub_ctx *c = (hubbub_ctx *) ctx;
- char *name;
+ lwc_string *iname;
xmlNodePtr n;
- name = c_string_from_hubbub_string(c, &tag->name);
- if (name == NULL)
+ if (lwc_intern_string((const char *) tag->name.ptr, tag->name.len,
+ &iname) != lwc_error_ok) {
return HUBBUB_NOMEM;
+ }
if (c->namespaces[0] != NULL) {
n = xmlNewDocNode(c->document, c->namespaces[tag->ns - 1],
- BAD_CAST name, NULL);
+ BAD_CAST lwc_string_data(iname), NULL);
} else {
- n = xmlNewDocNode(c->document, NULL, BAD_CAST name, NULL);
+ n = xmlNewDocNode(c->document, NULL,
+ BAD_CAST lwc_string_data(iname), NULL);
/* We're creating the root node of the document. Therefore,
* create the namespaces and set this node's namespace */
@@ -473,32 +527,33 @@ hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result)
}
}
if (n == NULL) {
- free(name);
+ lwc_string_unref(iname);
return HUBBUB_NOMEM;
}
n->_private = create_private(1);
if (n->_private == NULL) {
xmlFreeNode(n);
- free(name);
+ lwc_string_unref(iname);
return HUBBUB_NOMEM;
}
if (tag->n_attributes > 0 && add_attributes(ctx, (void *) n,
tag->attributes, tag->n_attributes) != HUBBUB_OK) {
- free(n->_private);
+ destroy_private(n->_private);
xmlFreeNode(n);
- free(name);
+ lwc_string_unref(iname);
return HUBBUB_NOMEM;
}
- if (strcasecmp(name, "form") == 0) {
+ if (lwc_string_length(iname) == SLEN("form") &&
+ strcasecmp(lwc_string_data(iname), "form") == 0) {
struct form *form = parse_form_element(n, c->encoding);
/* Memory exhaustion */
if (form == NULL) {
- free(n->_private);
+ destroy_private(n->_private);
xmlFreeNode(n);
- free(name);
+ lwc_string_unref(iname);
return HUBBUB_NOMEM;
}
@@ -507,9 +562,9 @@ hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result)
c->forms = form;
}
- *result = (void *) n;
+ ((binding_private *) n->_private)->localname = iname;
- free(name);
+ *result = (void *) n;
return HUBBUB_OK;
}
@@ -575,7 +630,7 @@ hubbub_error unref_node(void *ctx, void *node)
pvt->refcnt--;
if (pvt->refcnt == 0 && n->parent == NULL) {
- free(pvt);
+ destroy_private(pvt);
xmlFreeNode(n);
}
}
@@ -662,20 +717,88 @@ hubbub_error remove_child(void *ctx, void *parent, void *child, void **result)
hubbub_error clone_node(void *ctx, void *node, bool deep, void **result)
{
xmlNode *n = (xmlNode *) node;
- xmlNode *copy = xmlCopyNode(n, deep ? 1 : 2);
+ xmlNode *clonedtree;
- if (copy == NULL)
+ /* Shallow clone node */
+ clonedtree = xmlCopyNode(n, 2);
+ if (clonedtree == NULL)
return HUBBUB_NOMEM;
- copy->_private = create_private(1);
- if (copy->_private == NULL) {
- xmlFreeNode(copy);
+ clonedtree->_private = copy_private(n->_private, 1);
+ if (clonedtree->_private == NULL) {
+ xmlFreeNode(clonedtree);
return HUBBUB_NOMEM;
}
- *result = copy;
+ /* Iteratively clone children too, if required */
+ if (deep && n->children != NULL) {
+ xmlNode *parent = clonedtree, *copy;
+
+ n = n->children;
+
+ while (n != node) {
+ copy = xmlCopyNode(n, 2);
+ if (copy == NULL)
+ goto error;
+
+ copy->_private = copy_private(n->_private, 0);
+ if (copy->_private == NULL) {
+ xmlFreeNode(copy);
+ goto error;
+ }
+
+ xmlAddChild(parent, copy);
+
+ if (n->children != NULL) {
+ parent = copy;
+ n = n->children;
+ } else if (n->next != NULL) {
+ n = n->next;
+ } else {
+ while (n->parent != node &&
+ n->parent->next == NULL) {
+ parent = parent->parent;
+ n = n->parent;
+ }
+
+ if (n->parent != node) {
+ parent = parent->parent;
+ n = n->parent->next;
+ } else
+ n = node;
+ }
+ }
+ }
+
+ *result = clonedtree;
return HUBBUB_OK;
+
+error:
+ n = clonedtree;
+
+ while (n != NULL) {
+ destroy_private(n->_private);
+
+ if (n->children != NULL) {
+ n = n->children;
+ } else if (n->next != NULL) {
+ n = n->next;
+ } else {
+ while (n->parent != NULL && n->parent->next == NULL) {
+ n = n->parent;
+ }
+
+ if (n->parent != NULL)
+ n = n->parent->next;
+ else
+ n = NULL;
+ }
+ }
+
+ xmlFreeNode(clonedtree);
+
+ return HUBBUB_NOMEM;
}
hubbub_error reparent_children(void *ctx, void *node, void *new_parent)
@@ -787,24 +910,81 @@ hubbub_error form_associate(void *ctx, void *form, void *node)
return HUBBUB_OK;
}
+static hubbub_error parse_class_attr(lwc_string *value,
+ lwc_string ***classes, uint32_t *nclasses)
+{
+ const char *pv;
+ lwc_string **cls = NULL;
+ uint32_t count = 0;
+
+ /* Count number of classes */
+ for (pv = lwc_string_data(value); *pv != '\0'; ) {
+ if (*pv != ' ') {
+ while (*pv != ' ' && *pv != '\0')
+ pv++;
+ count++;
+ } else {
+ while (*pv == ' ')
+ pv++;
+ }
+ }
+
+ /* If there are some, unpack them */
+ if (count > 0) {
+ cls = malloc(count * sizeof(lwc_string *));
+ if (cls == NULL)
+ return HUBBUB_NOMEM;
+
+ for (pv = lwc_string_data(value), count = 0; *pv != '\0'; ) {
+ if (*pv != ' ') {
+ const char *s = pv;
+ while (*pv != ' ' && *pv != '\0')
+ pv++;
+ if (lwc_intern_string(s, pv - s,
+ &cls[count]) != lwc_error_ok)
+ goto error;
+ count++;
+ } else {
+ while (*pv == ' ')
+ pv++;
+ }
+ }
+ }
+
+ *classes = cls;
+ *nclasses = count;
+
+ return HUBBUB_OK;
+error:
+ while (count > 0)
+ lwc_string_unref(cls[--count]);
+
+ free(cls);
+
+ return HUBBUB_NOMEM;
+}
+
hubbub_error add_attributes(void *ctx, void *node,
const hubbub_attribute *attributes, uint32_t n_attributes)
{
hubbub_ctx *c = (hubbub_ctx *) ctx;
xmlNode *n = (xmlNode *) node;
+ binding_private *p = n->_private;
uint32_t attr;
for (attr = 0; attr < n_attributes; attr++) {
xmlAttr *prop;
- char *name, *value;
+ lwc_string *name, *value;
- name = c_string_from_hubbub_string(c, &attributes[attr].name);
- if (name == NULL)
+ if (lwc_intern_string((const char *) attributes[attr].name.ptr,
+ attributes[attr].name.len,
+ &name) != lwc_error_ok)
return HUBBUB_NOMEM;
- value = c_string_from_hubbub_string(c, &attributes[attr].value);
- if (value == NULL) {
- free(name);
+ if (lwc_intern_string((const char *) attributes[attr].value.ptr,
+ attributes[attr].value.len,
+ &value) != lwc_error_ok) {
+ lwc_string_unref(name);
return HUBBUB_NOMEM;
}
@@ -812,18 +992,38 @@ hubbub_error add_attributes(void *ctx, void *node,
c->namespaces[0] != NULL) {
prop = xmlNewNsProp(n,
c->namespaces[attributes[attr].ns - 1],
- BAD_CAST name, BAD_CAST value);
+ BAD_CAST lwc_string_data(name),
+ BAD_CAST lwc_string_data(value));
} else {
- prop = xmlNewProp(n, BAD_CAST name, BAD_CAST value);
+ prop = xmlNewProp(n, BAD_CAST lwc_string_data(name),
+ BAD_CAST lwc_string_data(value));
+ }
+
+ /* Handle @id / @class */
+ if (p->id == NULL && lwc_string_length(name) == SLEN("id") &&
+ strcasecmp(lwc_string_data(name), "id") == 0) {
+ p->id = lwc_string_ref(value);
+ } else if (p->nclasses == 0 &&
+ lwc_string_length(name) == SLEN("class") &&
+ strcasecmp(lwc_string_data(name),
+ "class") == 0) {
+ hubbub_error error;
+
+ error = parse_class_attr(value, &p->classes,
+ &p->nclasses);
+ if (error != HUBBUB_OK) {
+ lwc_string_unref(value);
+ lwc_string_unref(name);
+ return error;
+ }
}
+
+ lwc_string_unref(value);
+ lwc_string_unref(name);
+
if (prop == NULL) {
- free(value);
- free(name);
return HUBBUB_NOMEM;
}
-
- free(value);
- free(name);
}
return HUBBUB_OK;
diff --git a/render/parser_binding.h b/render/parser_binding.h
index afab250eb..b60f5d048 100644
--- a/render/parser_binding.h
+++ b/render/parser_binding.h
@@ -31,7 +31,13 @@ struct form_control;
* Private data attached to each DOM node
*/
typedef struct binding_private {
+ /* All the following only apply to ELEMENT nodes */
+
struct box *box; /**< Root box if ELEMENT node, or NULL */
+ lwc_string *localname; /**< Local name of node */
+ lwc_string *id; /**< Value of id attribute, or NULL */
+ lwc_string **classes; /**< Pre-parsed class names, or NULL */
+ uint32_t nclasses; /**< Number of class names */
} binding_private;
typedef enum binding_error {