From 4e574f12527b241ea266a9fad21f6424a9af3a44 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sat, 8 Oct 2011 00:21:59 +0000 Subject: Improve performance of style selection svn path=/trunk/netsurf/; revision=13000 --- css/select.c | 365 ++++++++++++++---------------------------------- css/select.h | 1 + render/box_construct.c | 2 +- render/box_normalise.c | 7 + render/html.c | 21 +++ render/html_internal.h | 2 + render/hubbub_binding.c | 270 ++++++++++++++++++++++++++++++----- render/parser_binding.h | 6 + 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 { -- cgit v1.2.3