From 1d8b0aad719898be0f1b8a862be41c2b51075c80 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sat, 14 Feb 2009 17:20:19 +0000 Subject: Rework handling of imported stylesheets. No longer is the client called back mid-parse. Instead, they must acquire details of and process imported stylesheets after css_stylesheet_data_done() has been called on the parent sheet. The return code of css_stylesheet_data_done() informs the client of the need to process imported sheets. svn path=/trunk/libcss/; revision=6504 --- include/libcss/errors.h | 1 + include/libcss/stylesheet.h | 17 ++--- src/parse/language.c | 39 +---------- src/stylesheet.c | 154 +++++++++++++++++++++++++++++++++++++++----- src/stylesheet.h | 11 ++-- src/utils/errors.c | 3 + test/css21.c | 37 ++++++++++- test/dump.h | 7 +- test/parse-auto.c | 44 ++++++++++--- test/parse2-auto.c | 2 +- test/select-auto.c | 3 +- 11 files changed, 233 insertions(+), 85 deletions(-) diff --git a/include/libcss/errors.h b/include/libcss/errors.h index 8e321ce..2a36492 100644 --- a/include/libcss/errors.h +++ b/include/libcss/errors.h @@ -20,6 +20,7 @@ typedef enum css_error { CSS_NEEDDATA = 5, CSS_BADCHARSET = 6, CSS_EOF = 7, + CSS_IMPORTS_PENDING = 8 } css_error; /* Convert a libcss error value to a string */ diff --git a/include/libcss/stylesheet.h b/include/libcss/stylesheet.h index 6a89372..eb14ad3 100644 --- a/include/libcss/stylesheet.h +++ b/include/libcss/stylesheet.h @@ -11,23 +11,24 @@ #include #include -/** - * Type of stylesheet import handler - */ -typedef css_error (*css_import_handler)(void *pw, const char *url, - css_stylesheet *sheet); - css_error css_stylesheet_create(css_language_level level, const char *charset, const char *url, const char *title, css_origin origin, uint64_t media, - css_import_handler import_callback, void *import_pw, - css_allocator_fn alloc, void *alloc_pw, css_stylesheet **stylesheet); + css_allocator_fn alloc, void *alloc_pw, + css_stylesheet **stylesheet); css_error css_stylesheet_destroy(css_stylesheet *sheet); css_error css_stylesheet_append_data(css_stylesheet *sheet, const uint8_t *data, size_t len); css_error css_stylesheet_data_done(css_stylesheet *sheet); +css_error css_stylesheet_next_pending_import(css_stylesheet *parent, + css_string *url, uint64_t *media); +css_error css_stylesheet_register_import(css_stylesheet *parent, + css_stylesheet *child); + +css_error css_stylesheet_get_language_level(css_stylesheet *sheet, + css_language_level *level); css_error css_stylesheet_get_url(css_stylesheet *sheet, const char **url); css_error css_stylesheet_get_title(css_stylesheet *sheet, const char **title); css_error css_stylesheet_get_origin(css_stylesheet *sheet, css_origin *origin); diff --git a/src/parse/language.c b/src/parse/language.c index a187379..11fe972 100644 --- a/src/parse/language.c +++ b/src/parse/language.c @@ -408,7 +408,6 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector) } else if (atkeyword->ilower == c->strings[IMPORT]) { if (c->state != HAD_RULE) { css_rule *rule; - css_stylesheet *import; css_media_type media = 0; css_error error; @@ -477,49 +476,15 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector) if (error != CSS_OK) return error; - /** \todo resolve URI */ - char url[uri->idata->len + 1]; - memcpy(url, uri->idata->data, uri->idata->len); - url[uri->idata->len] = '\0'; - - /* Create imported sheet */ - error = css_stylesheet_create(c->sheet->level, NULL, - url, NULL, c->sheet->origin, media, - c->sheet->import, c->sheet->import_pw, - c->alloc, c->pw, &import); + error = css_stylesheet_rule_set_nascent_import(c->sheet, + rule, uri->idata, media); if (error != CSS_OK) { css_stylesheet_rule_destroy(c->sheet, rule); return error; } - /* Trigger fetch of imported sheet */ - if (c->sheet->import != NULL) { - error = c->sheet->import(c->sheet->import_pw, - url, import); - if (error != CSS_OK) { - css_stylesheet_destroy(import); - css_stylesheet_rule_destroy(c->sheet, - rule); - return error; - } - } - - error = css_stylesheet_rule_set_import(c->sheet, rule, - import); - if (error != CSS_OK) { - /** \todo we need to tell the client to stop - * doing stuff with the imported sheet */ - css_stylesheet_destroy(import); - css_stylesheet_rule_destroy(c->sheet, rule); - return error; - } - - /* Imported sheet is now owned by the rule */ - error = css_stylesheet_add_rule(c->sheet, rule); if (error != CSS_OK) { - /** \todo we need to tell the client to stop - * doing stuff with the imported sheet */ css_stylesheet_rule_destroy(c->sheet, rule); return error; } diff --git a/src/stylesheet.c b/src/stylesheet.c index 4174859..7072bfc 100644 --- a/src/stylesheet.c +++ b/src/stylesheet.c @@ -26,8 +26,6 @@ static css_error _remove_selectors(css_stylesheet *sheet, css_rule *rule); * \param title Title of stylesheet * \param origin Origin of stylesheet * \param media Media stylesheet applies to - * \param import_callback Handler for imported stylesheets, or NULL - * \param import_pw Client private data for import_callback * \param alloc Memory (de)allocation function * \param alloc_pw Client private data for alloc * \param stylesheet Pointer to location to receive stylesheet @@ -38,8 +36,8 @@ static css_error _remove_selectors(css_stylesheet *sheet, css_rule *rule); css_error css_stylesheet_create(css_language_level level, const char *charset, const char *url, const char *title, css_origin origin, uint64_t media, - css_import_handler import_callback, void *import_pw, - css_allocator_fn alloc, void *alloc_pw, css_stylesheet **stylesheet) + css_allocator_fn alloc, void *alloc_pw, + css_stylesheet **stylesheet) { parserutils_error perror; css_error error; @@ -120,9 +118,6 @@ css_error css_stylesheet_create(css_language_level level, sheet->origin = origin; sheet->media = media; - sheet->import = import_callback; - sheet->import_pw = import_pw; - sheet->alloc = alloc; sheet->pw = alloc_pw; @@ -200,10 +195,13 @@ css_error css_stylesheet_append_data(css_stylesheet *sheet, * Flag that the last of a stylesheet's data has been seen * * \param sheet The stylesheet in question - * \return CSS_OK on success, appropriate error otherwise + * \return CSS_OK on success, + * CSS_IMPORTS_PENDING if there are imports pending, + * appropriate error otherwise */ css_error css_stylesheet_data_done(css_stylesheet *sheet) { + const css_rule *r; css_error error; if (sheet == NULL) @@ -223,6 +221,127 @@ css_error css_stylesheet_data_done(css_stylesheet *sheet) sheet->parser_frontend = NULL; sheet->parser = NULL; + /* Determine if there are any pending imports */ + for (r = sheet->rule_list; r != NULL; r = r->next) { + const css_rule_import *i = (const css_rule_import *) r; + + if (r->type != CSS_RULE_UNKNOWN && + r->type != CSS_RULE_CHARSET && + r->type != CSS_RULE_IMPORT) + break; + + if (r->type == CSS_RULE_IMPORT && i->sheet == NULL) + return CSS_IMPORTS_PENDING; + } + + return CSS_OK; +} + +/** + * Retrieve the next pending import for the parent stylesheet + * + * \param parent Parent stylesheet + * \param url Pointer to object to be populated with details of URL of + * imported stylesheet (potentially relative) + * \param media Pointer to location to receive applicable media types for + * imported sheet, + * \return CSS_OK on success, + * CSS_INVALID if there are no pending imports remaining + * + * The client must resolve the absolute URL of the imported stylesheet, + * using the parent's URL as the base. It must then fetch the imported + * stylesheet, and parse it to completion, including fetching any stylesheets + * it may import. The resultant sheet must then be registered with the + * parent using css_stylesheet_register_import(). + * + * The client must then call this function again, to determine if there + * are any further imports for the parent stylesheet, and, if so, + * process them as described above. + * + * If the client is unable to fetch an imported stylesheet, it must + * register an empty stylesheet with the parent in its place. + */ +css_error css_stylesheet_next_pending_import(css_stylesheet *parent, + css_string *url, uint64_t *media) +{ + const css_rule *r; + + if (parent == NULL || url == NULL || media == NULL) + return CSS_BADPARM; + + for (r = parent->rule_list; r != NULL; r = r->next) { + const css_rule_import *i = (const css_rule_import *) r; + + if (r->type != CSS_RULE_UNKNOWN && + r->type != CSS_RULE_CHARSET && + r->type != CSS_RULE_IMPORT) + break; + + if (r->type == CSS_RULE_IMPORT && i->sheet == NULL) { + url->len = i->url->len; + url->data = (uint8_t *) i->url->data; + + *media = i->media; + + return CSS_OK; + } + } + + return CSS_INVALID; +} + +/** + * Register an imported stylesheet with its parent + * + * \param parent Parent stylesheet + * \param import Imported sheet + * \return CSS_OK on success, + * CSS_INVALID if there are no outstanding imports, + * appropriate error otherwise. + * + * Ownership of the imported stylesheet is transferred to the parent. + */ +css_error css_stylesheet_register_import(css_stylesheet *parent, + css_stylesheet *import) +{ + css_rule *r; + + if (parent == NULL || import == NULL) + return CSS_BADPARM; + + for (r = parent->rule_list; r != NULL; r = r->next) { + css_rule_import *i = (css_rule_import *) r; + + if (r->type != CSS_RULE_UNKNOWN && + r->type != CSS_RULE_CHARSET && + r->type != CSS_RULE_IMPORT) + break; + + if (r->type == CSS_RULE_IMPORT && i->sheet == NULL) { + i->sheet = import; + + return CSS_OK; + } + } + + return CSS_INVALID; +} + +/** + * Retrieve the language level of a stylesheet + * + * \param sheet The stylesheet to retrieve the language level of + * \param level Pointer to location to receive language level + * \return CSS_OK on success, appropriate error otherwise + */ +css_error css_stylesheet_get_language_level(css_stylesheet *sheet, + css_language_level *level) +{ + if (sheet == NULL || level == NULL) + return CSS_BADPARM; + + *level = sheet->level; + return CSS_OK; } @@ -689,7 +808,8 @@ css_error css_stylesheet_rule_destroy(css_stylesheet *sheet, css_rule *rule) { css_rule_import *import = (css_rule_import *) rule; - css_stylesheet_destroy(import->sheet); + if (import->sheet != NULL) + css_stylesheet_destroy(import->sheet); } break; case CSS_RULE_MEDIA: @@ -864,27 +984,31 @@ css_error css_stylesheet_rule_set_charset(css_stylesheet *sheet, return CSS_OK; } + /** - * Set the imported stylesheet associated with a rule + * Set the necessary data to import a stylesheet associated with a rule * * \param sheet The stylesheet context * \param rule The rule to add to (must be of type CSS_RULE_IMPORT) - * \param import The imported sheet + * \param url The URL of the imported stylesheet + * \param media The applicable media types for the imported stylesheet * \return CSS_OK on success, appropriate error otherwise */ -css_error css_stylesheet_rule_set_import(css_stylesheet *sheet, - css_rule *rule, css_stylesheet *import) +css_error css_stylesheet_rule_set_nascent_import(css_stylesheet *sheet, + css_rule *rule, const parserutils_hash_entry *url, + uint64_t media) { css_rule_import *r = (css_rule_import *) rule; - if (sheet == NULL || rule == NULL || import == NULL) + if (sheet == NULL || rule == NULL || url == NULL) return CSS_BADPARM; /* Ensure rule is a CSS_RULE_IMPORT */ assert(rule->type == CSS_RULE_IMPORT); /* Set the rule's sheet field */ - r->sheet = import; + r->url = url; + r->media = media; return CSS_OK; } diff --git a/src/stylesheet.h b/src/stylesheet.h index 2b5860f..d2316d1 100644 --- a/src/stylesheet.h +++ b/src/stylesheet.h @@ -133,6 +133,9 @@ typedef struct css_rule_page { typedef struct css_rule_import { css_rule base; + const parserutils_hash_entry *url; + uint64_t media; + css_stylesheet *sheet; } css_rule_import; @@ -161,9 +164,6 @@ struct css_stylesheet { void *ownerNode; /**< Owning node in document */ css_rule *ownerRule; /**< Owning rule in parent */ - css_import_handler import; /**< Import callback */ - void *import_pw; /**< Import handler data */ - css_language_level level; /**< Language level of sheet */ css_parser *parser; /**< Core parser for sheet */ void *parser_frontend; /**< Frontend parser */ @@ -207,8 +207,9 @@ css_error css_stylesheet_rule_append_style(css_stylesheet *sheet, css_error css_stylesheet_rule_set_charset(css_stylesheet *sheet, css_rule *rule, const parserutils_hash_entry *charset); -css_error css_stylesheet_rule_set_import(css_stylesheet *sheet, - css_rule *rule, css_stylesheet *import); +css_error css_stylesheet_rule_set_nascent_import(css_stylesheet *sheet, + css_rule *rule, const parserutils_hash_entry *url, + uint64_t media); /** \todo registering other rule-type data with css_rules */ diff --git a/src/utils/errors.c b/src/utils/errors.c index b5b4792..c53a442 100644 --- a/src/utils/errors.c +++ b/src/utils/errors.c @@ -44,6 +44,9 @@ const char *css_error_to_string(css_error error) case CSS_EOF: result = "EOF encountered"; break; + case CSS_IMPORTS_PENDING: + result = "Imports pending"; + break; } return result; diff --git a/test/css21.c b/test/css21.c index 3da2bfb..47558bc 100644 --- a/test/css21.c +++ b/test/css21.c @@ -40,8 +40,8 @@ int main(int argc, char **argv) for (int count = 0; count < ITERATIONS; count++) { assert(css_stylesheet_create(CSS_LEVEL_21, "UTF-8", argv[2], - NULL, CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, NULL, - NULL, myrealloc, NULL, &sheet) == CSS_OK); + NULL, CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, + myrealloc, NULL, &sheet) == CSS_OK); fp = fopen(argv[2], "rb"); if (fp == NULL) { @@ -74,7 +74,38 @@ int main(int argc, char **argv) fclose(fp); - assert(css_stylesheet_data_done(sheet) == CSS_OK); + error = css_stylesheet_data_done(sheet); + assert(error == CSS_OK || error == CSS_IMPORTS_PENDING); + + while (error == CSS_IMPORTS_PENDING) { + css_string url; + uint64_t media; + + error = css_stylesheet_next_pending_import(sheet, + &url, &media); + assert(error == CSS_OK || error == CSS_INVALID); + + if (error == CSS_OK) { + css_stylesheet *import; + char buf[url.len + 1]; + + memcpy(buf, url.data, url.len); + buf[url.len] = '\0'; + + assert(css_stylesheet_create(CSS_LEVEL_21, + "UTF-8", buf, NULL, CSS_ORIGIN_AUTHOR, + media, myrealloc, NULL, &import) == + CSS_OK); + + assert(css_stylesheet_data_done(import) == + CSS_OK); + + assert(css_stylesheet_register_import(sheet, + import) == CSS_OK); + + error = CSS_IMPORTS_PENDING; + } + } #if DUMP_HASH parserutils_hash_dump(sheet->dictionary); diff --git a/test/dump.h b/test/dump.h index 98ec1bb..4626894 100644 --- a/test/dump.h +++ b/test/dump.h @@ -87,11 +87,8 @@ void dump_rule_import(css_rule_import *s, char **buf, size_t *buflen) { char *ptr = *buf; - if (s->sheet == NULL) { - assert(0 && "No imported sheet"); - } - - ptr += sprintf(ptr, "| @import url(\"%s\")", s->sheet->url); + ptr += sprintf(ptr, "| @import url(\"%.*s\")", + (int) s->url->len, (const char *) s->url->data); /** \todo media list */ diff --git a/test/parse-auto.c b/test/parse-auto.c index bef54cd..4c42359 100644 --- a/test/parse-auto.c +++ b/test/parse-auto.c @@ -308,7 +308,7 @@ void run_test(const uint8_t *data, size_t len, exp_entry *exp, size_t explen) static int testnum; assert(css_stylesheet_create(CSS_LEVEL_21, "UTF-8", "foo", NULL, - CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, NULL, NULL, + CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, myrealloc, NULL, &sheet) == CSS_OK); error = css_stylesheet_append_data(sheet, data, len); @@ -317,7 +317,35 @@ void run_test(const uint8_t *data, size_t len, exp_entry *exp, size_t explen) assert(0); } - assert(css_stylesheet_data_done(sheet) == CSS_OK); + error = css_stylesheet_data_done(sheet); + assert(error == CSS_OK || error == CSS_IMPORTS_PENDING); + + while (error == CSS_IMPORTS_PENDING) { + css_string url; + uint64_t media; + + error = css_stylesheet_next_pending_import(sheet, + &url, &media); + assert(error == CSS_OK || error == CSS_INVALID); + + if (error == CSS_OK) { + css_stylesheet *import; + char buf[url.len + 1]; + + memcpy(buf, url.data, url.len); + buf[url.len] = '\0'; + + assert(css_stylesheet_create(CSS_LEVEL_21, + "UTF-8", buf, NULL, CSS_ORIGIN_AUTHOR, + media, myrealloc, NULL, &import) == + CSS_OK); + + assert(css_stylesheet_register_import(sheet, + import) == CSS_OK); + + error = CSS_IMPORTS_PENDING; + } + } e = 0; testnum++; @@ -460,13 +488,11 @@ void validate_rule_charset(css_rule_charset *s, exp_entry *e, int testnum) void validate_rule_import(css_rule_import *s, exp_entry *e, int testnum) { - if (s->sheet == NULL) { - assert(0 && "No imported sheet"); - } - - if (strcmp(s->sheet->url, e->name) != 0) { - printf("%d: Got URL '%s'. Expected '%s'\n", - testnum, s->sheet->url, e->name); + if (strncmp((const char *) s->url->data, e->name, + (int) s->url->len) != 0) { + printf("%d: Got URL '%.*s'. Expected '%s'\n", + testnum, (int) s->url->len, (const char *) s->url->data, + e->name); assert(0 && "Mismatched URLs"); } } diff --git a/test/parse2-auto.c b/test/parse2-auto.c index 59ae220..80fcd24 100644 --- a/test/parse2-auto.c +++ b/test/parse2-auto.c @@ -172,7 +172,7 @@ void run_test(const uint8_t *data, size_t len, const char *exp, size_t explen) buflen = 2 * explen; assert(css_stylesheet_create(CSS_LEVEL_21, "UTF-8", "foo", NULL, - CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, NULL, NULL, + CSS_ORIGIN_AUTHOR, CSS_MEDIA_ALL, myrealloc, NULL, &sheet) == CSS_OK); error = css_stylesheet_append_data(sheet, data, len); diff --git a/test/select-auto.c b/test/select-auto.c index 3f859a0..bd34a99 100644 --- a/test/select-auto.c +++ b/test/select-auto.c @@ -439,8 +439,7 @@ void parse_sheet(line_ctx *ctx, const char *data, size_t len) /** \todo How are we going to handle @import? */ assert(css_stylesheet_create(CSS_LEVEL_21, "UTF-8", "foo", "foo", - origin, media, NULL, NULL, myrealloc, NULL, &sheet) == - CSS_OK); + origin, media, myrealloc, NULL, &sheet) == CSS_OK); /* Extend array of sheets and append new sheet to it */ temp = realloc(ctx->sheets, -- cgit v1.2.3