Improve performance of style selection

svn path=/trunk/netsurf/; revision=13000
This commit is contained in:
John Mark Bell 2011-10-08 00:21:59 +00:00
parent 1e828006aa
commit 4e574f1252
8 changed files with 383 additions and 299 deletions

View File

@ -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);
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;
if (p->nclasses > 0) {
lwc_string **result;
uint32_t items;
/* 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;
result = malloc(p->nclasses * sizeof(lwc_string *));
if (result == NULL)
return CSS_NOMEM;
start = (const char *) value;
for (items = 0; items < p->nclasses; items++)
result[items] = lwc_string_ref(p->classes[items]);
*classes = result;
*n_classes = p->nclasses;
}
/* The class attribute is a space separated list of tokens. */
do {
lwc_string **temp;
/* Find next space or end of string */
p = strchrnul(start, ' ');
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++;
/* Move to start of next token in string */
start = p + 1;
} while (*p != '\0');
/* Clean up, if necessary */
if (value != NULL) {
xmlFree(value);
}
*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;
if (p->id != NULL)
*id = lwc_string_ref(p->id);
/* 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);
}
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;
} 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;
/* 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 {
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;
if (n != NULL && n->type == XML_ELEMENT_NODE) {
if (same_name) {
if (strcasecmp(name, (char *) n->name) == 0)
do {
n = after ? n->next : n->prev;
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;

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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 &&

View File

@ -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;

View File

@ -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);
}
if (prop == NULL) {
free(value);
free(name);
return HUBBUB_NOMEM;
prop = xmlNewProp(n, BAD_CAST lwc_string_data(name),
BAD_CAST lwc_string_data(value));
}
free(value);
free(name);
/* 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) {
return HUBBUB_NOMEM;
}
}
return HUBBUB_OK;

View File

@ -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 {