summaryrefslogtreecommitdiff
path: root/src/stylesheet.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stylesheet.c')
-rw-r--r--src/stylesheet.c253
1 files changed, 198 insertions, 55 deletions
diff --git a/src/stylesheet.c b/src/stylesheet.c
index 0b1e2cd..7e634a9 100644
--- a/src/stylesheet.c
+++ b/src/stylesheet.c
@@ -10,6 +10,7 @@
#include "stylesheet.h"
#include "bytecode/bytecode.h"
#include "parse/css21.h"
+#include "utils/parserutilserror.h"
#include "utils/utils.h"
/**
@@ -36,6 +37,7 @@ css_error css_stylesheet_create(css_language_level level,
css_import_handler import_callback, void *import_pw,
css_alloc alloc, void *alloc_pw, css_stylesheet **stylesheet)
{
+ parserutils_error perror;
css_error error;
css_stylesheet *sheet;
size_t len;
@@ -49,10 +51,18 @@ css_error css_stylesheet_create(css_language_level level,
memset(sheet, 0, sizeof(css_stylesheet));
+ perror = parserutils_dict_create((parserutils_alloc) alloc, alloc_pw,
+ &sheet->dictionary);
+ if (perror != PARSERUTILS_OK) {
+ alloc(sheet, 0, alloc_pw);
+ return css_error_from_parserutils_error(perror);
+ }
+
error = css_parser_create(charset,
charset ? CSS_CHARSET_DICTATED : CSS_CHARSET_DEFAULT,
- alloc, alloc_pw, &sheet->parser);
+ sheet->dictionary, alloc, alloc_pw, &sheet->parser);
if (error != CSS_OK) {
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return error;
}
@@ -60,6 +70,7 @@ css_error css_stylesheet_create(css_language_level level,
/* We only support CSS 2.1 */
if (level != CSS_LEVEL_21) {
css_parser_destroy(sheet->parser);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return CSS_INVALID; /** \todo better error */
}
@@ -69,6 +80,7 @@ css_error css_stylesheet_create(css_language_level level,
&sheet->parser_frontend);
if (error != CSS_OK) {
css_parser_destroy(sheet->parser);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return error;
}
@@ -80,6 +92,7 @@ css_error css_stylesheet_create(css_language_level level,
if (sheet->url == NULL) {
css_css21_destroy(sheet->parser_frontend);
css_parser_destroy(sheet->parser);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return CSS_NOMEM;
}
@@ -89,9 +102,10 @@ css_error css_stylesheet_create(css_language_level level,
len = strlen(title) + 1;
sheet->title = alloc(NULL, len, alloc_pw);
if (sheet->title == NULL) {
+ alloc(sheet->url, 0, alloc_pw);
css_css21_destroy(sheet->parser_frontend);
css_parser_destroy(sheet->parser);
- alloc(sheet->url, 0, alloc_pw);
+ parserutils_dict_destroy(sheet->dictionary);
alloc(sheet, 0, alloc_pw);
return CSS_NOMEM;
}
@@ -123,6 +137,8 @@ css_error css_stylesheet_destroy(css_stylesheet *sheet)
if (sheet == NULL)
return CSS_BADPARM;
+ parserutils_dict_destroy(sheet->dictionary);
+
if (sheet->title != NULL)
sheet->alloc(sheet->title, 0, sheet->pw);
@@ -349,7 +365,9 @@ css_error css_stylesheet_selector_create(css_stylesheet *sheet,
css_selector_type type, const css_string *name,
const css_string *value, css_selector **selector)
{
+ const css_string *iname, *ivalue;
css_selector *sel;
+ parserutils_error perror;
if (sheet == NULL || name == NULL || selector == NULL)
return CSS_BADPARM;
@@ -360,14 +378,33 @@ css_error css_stylesheet_selector_create(css_stylesheet *sheet,
memset(sel, 0, sizeof(css_selector));
- sel->type = type;
- sel->data.name = *name;
+ sel->data.type = type;
+
+ /** \todo Given that this information is already guaranteed to be in
+ * the dictionary, it would be more efficient to pass a pointer to the
+ * dictionary entry around rather than re-discovering it here. The same
+ * applies for value, and in css_stylesheet_selector_detail_create() */
+ perror = parserutils_dict_insert(sheet->dictionary, name->data,
+ name->len, &iname);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(sel, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ sel->data.name = iname;
+
if (value != NULL) {
- sel->data.value = *value;
+ perror = parserutils_dict_insert(sheet->dictionary,
+ value->data, value->len, &ivalue);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(sel, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ sel->data.value = ivalue;
}
+
/** \todo specificity */
sel->specificity = 0;
- sel->combinator_type = CSS_COMBINATOR_NONE;
+ sel->data.comb = CSS_COMBINATOR_NONE;
*selector = sel;
@@ -395,32 +432,120 @@ css_error css_stylesheet_selector_destroy(css_stylesheet *sheet,
}
/**
+ * Create a selector detail
+ *
+ * \param sheet The stylesheet context
+ * \param type The type of selector to create
+ * \param name Name of selector
+ * \param value Value of selector, or NULL
+ * \param detail Pointer to location to receive detail object
+ * \return CSS_OK on success,
+ * CSS_BADPARM on bad parameters,
+ * CSS_NOMEM on memory exhaustion
+ */
+css_error css_stylesheet_selector_detail_create(css_stylesheet *sheet,
+ css_selector_type type, const css_string *name,
+ const css_string *value, css_selector_detail **detail)
+{
+ parserutils_error perror;
+ const css_string *iname, *ivalue;
+ css_selector_detail *det;
+
+ if (sheet == NULL || name == NULL || detail == NULL)
+ return CSS_BADPARM;
+
+ det = sheet->alloc(NULL, sizeof(css_selector_detail), sheet->pw);
+ if (det == NULL)
+ return CSS_NOMEM;
+
+ memset(det, 0, sizeof(css_selector_detail));
+
+ det->type = type;
+
+ perror = parserutils_dict_insert(sheet->dictionary, name->data,
+ name->len, &iname);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(det, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ det->name = iname;
+
+ if (value != NULL) {
+ perror = parserutils_dict_insert(sheet->dictionary,
+ value->data, value->len, &ivalue);
+ if (perror != PARSERUTILS_OK) {
+ sheet->alloc(det, 0, sheet->pw);
+ return css_error_from_parserutils_error(perror);
+ }
+ det->value = ivalue;
+ }
+
+ *detail = det;
+
+ return CSS_OK;
+}
+
+/**
+ * Destroy a selector detail
+ *
+ * \param sheet The stylesheet context
+ * \param detail The detail to destroy
+ * \return CSS_OK on success, appropriate error otherwise
+ */
+css_error css_stylesheet_selector_detail_destroy(css_stylesheet *sheet,
+ css_selector_detail *detail)
+{
+ if (sheet == NULL || detail == NULL)
+ return CSS_BADPARM;
+
+ sheet->alloc(detail, 0, sheet->pw);
+
+ return CSS_OK;
+}
+
+
+/**
* Append a selector to the specifics chain of another selector
*
* \param sheet The stylesheet context
- * \param parent The parent selector
- * \param specific The selector to append
+ * \param parent Pointer to pointer to the parent selector (updated on exit)
+ * \param specific The selector to append (destroyed)
* \return CSS_OK on success, appropriate error otherwise.
*/
css_error css_stylesheet_selector_append_specific(css_stylesheet *sheet,
- css_selector *parent, css_selector *specific)
+ css_selector **parent, css_selector_detail *detail)
{
- css_selector *s;
+ css_selector *temp;
+ css_selector_detail *d;
+ size_t num_details = 0;
- if (sheet == NULL || parent == NULL || specific == NULL)
+ if (sheet == NULL || parent == NULL ||
+ *parent == NULL || detail == NULL)
return CSS_BADPARM;
- /** \todo this may want optimising */
- for (s = parent->specifics; s != NULL && s->next != NULL; s = s->next)
- /* do nothing */;
- if (s == NULL) {
- specific->prev = specific->next = NULL;
- parent->specifics = specific;
- } else {
- s->next = specific;
- specific->prev = s;
- specific->next = NULL;
- }
+ /** \todo this may want optimising -- counting blocks is O(n)
+ * In practice, however, n isn't likely to be large, so may be fine
+ */
+
+ /* Count current number of detail blocks */
+ for (d = &(*parent)->data; d->next != 0; d++)
+ num_details++;
+
+ /* Grow selector by one detail block */
+ temp = sheet->alloc((*parent), sizeof(css_selector) +
+ (num_details + 1) * sizeof(css_selector_detail),
+ sheet->pw);
+ if (temp == NULL)
+ return CSS_NOMEM;
+
+ /* Copy detail into empty block */
+ (&temp->data)[num_details + 1] = *detail;
+ /* Flag that there's another block */
+ (&temp->data)[num_details].next = 1;
+
+ css_stylesheet_selector_detail_destroy(sheet, detail);
+
+ (*parent) = temp;
return CSS_OK;
}
@@ -451,7 +576,7 @@ css_error css_stylesheet_selector_combine(css_stylesheet *sheet,
return CSS_INVALID;
b->combinator = a;
- b->combinator_type = type;
+ b->data.comb = type;
return CSS_OK;
}
@@ -666,7 +791,9 @@ static void css_stylesheet_dump_selector_list(css_selector *list, FILE *target,
size_t *size);
static void css_stylesheet_dump_selector(css_selector *selector, FILE *target,
size_t *size);
-static void css_stylesheet_dump_string(css_string *string, FILE *target);
+static void css_stylesheet_dump_selector_detail(css_selector_detail *detail,
+ FILE *target, size_t *size);
+static void css_stylesheet_dump_string(const css_string *string, FILE *target);
/**
* Dump a stylesheet
@@ -757,7 +884,7 @@ void css_stylesheet_dump_selector_list(css_selector *list, FILE *target,
size);
}
- switch (list->combinator_type) {
+ switch (list->data.comb) {
case CSS_COMBINATOR_NONE:
break;
case CSS_COMBINATOR_ANCESTOR:
@@ -784,71 +911,87 @@ void css_stylesheet_dump_selector_list(css_selector *list, FILE *target,
void css_stylesheet_dump_selector(css_selector *selector, FILE *target,
size_t *size)
{
- css_selector *s;
+ css_selector_detail *d = &selector->data;
+
+ *size += sizeof(css_selector) - sizeof(css_selector_detail);
+
+ while (true) {
+ css_stylesheet_dump_selector_detail(d, target, size);
- *size += sizeof(css_selector);
+ if (d->next == 0)
+ break;
- switch (selector->type) {
+ d++;
+ }
+}
+
+/**
+ * Dump a CSS selector detail
+ *
+ * \param detail The detail to dump
+ * \param target The file handle to output to
+ * \param size Pointer to current size of sheet, updated on exit
+ */
+void css_stylesheet_dump_selector_detail(css_selector_detail *detail,
+ FILE *target, size_t *size)
+{
+
+ *size += sizeof(css_selector_detail);
+
+ switch (detail->type) {
case CSS_SELECTOR_ELEMENT:
- if (selector->data.name.len == 1 &&
- selector->data.name.ptr[0] == '*' &&
- selector->specifics == NULL) {
- css_stylesheet_dump_string(&selector->data.name,
- target);
- } else if (selector->data.name.len != 1 ||
- selector->data.name.ptr[0] != '*') {
- css_stylesheet_dump_string(&selector->data.name,
- target);
+ if (detail->name->len == 1 && detail->name->data[0] == '*' &&
+ detail->next == 0) {
+ css_stylesheet_dump_string(detail->name, target);
+ } else if (detail->name->len != 1 ||
+ detail->name->data[0] != '*') {
+ css_stylesheet_dump_string(detail->name, target);
}
break;
case CSS_SELECTOR_CLASS:
fprintf(target, ".");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
break;
case CSS_SELECTOR_ID:
fprintf(target, "#");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
break;
case CSS_SELECTOR_PSEUDO:
fprintf(target, ":");
- css_stylesheet_dump_string(&selector->data.name, target);
- if (selector->data.value.len > 0) {
+ css_stylesheet_dump_string(detail->name, target);
+ if (detail->value != NULL) {
fprintf(target, "(");
- css_stylesheet_dump_string(&selector->data.value,
- target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, ")");
}
break;
case CSS_SELECTOR_ATTRIBUTE:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "]");
break;
case CSS_SELECTOR_ATTRIBUTE_EQUAL:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "=\"");
- css_stylesheet_dump_string(&selector->data.value, target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, "\"]");
break;
case CSS_SELECTOR_ATTRIBUTE_DASHMATCH:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "|=\"");
- css_stylesheet_dump_string(&selector->data.value, target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, "\"]");
break;
case CSS_SELECTOR_ATTRIBUTE_INCLUDES:
fprintf(target, "[");
- css_stylesheet_dump_string(&selector->data.name, target);
+ css_stylesheet_dump_string(detail->name, target);
fprintf(target, "~=\"");
- css_stylesheet_dump_string(&selector->data.value, target);
+ css_stylesheet_dump_string(detail->value, target);
fprintf(target, "\"]");
break;
}
-
- for (s = selector->specifics; s != NULL; s = s->next)
- css_stylesheet_dump_selector(s, target, size);
}
/**
@@ -857,8 +1000,8 @@ void css_stylesheet_dump_selector(css_selector *selector, FILE *target,
* \param string The string to dump
* \param target The file handle to output to
*/
-void css_stylesheet_dump_string(css_string *string, FILE *target)
+void css_stylesheet_dump_string(const css_string *string, FILE *target)
{
- fprintf(target, "%.*s", (int) string->len, string->ptr);
+ fprintf(target, "%.*s", (int) string->len, string->data);
}