mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-11-26 08:19:55 +03:00
Improve performance of style selection
svn path=/trunk/netsurf/; revision=13000
This commit is contained in:
parent
1e828006aa
commit
4e574f1252
369
css/select.c
369
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);
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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 &&
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user