From 737d6ad557a7ee6300cb6c02cf0d32ed55b13531 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sun, 11 Apr 2010 10:52:18 +0000 Subject: Merge jmb/kill-reentrancy. r=vince svn path=/trunk/netsurf/; revision=10346 --- content/content.c | 4 +- css/css.c | 301 ++++++++++++++++++++++++++++++++++-------------------- css/css.h | 11 +- render/html.c | 194 ++++++++++++++++++++--------------- 4 files changed, 316 insertions(+), 194 deletions(-) diff --git a/content/content.c b/content/content.c index a5890e970..397c645e4 100644 --- a/content/content.c +++ b/content/content.c @@ -667,8 +667,10 @@ void content_convert(struct content *c) if (c->status == CONTENT_STATUS_READY) content_set_ready(c); - if (c->status == CONTENT_STATUS_DONE) + if (c->status == CONTENT_STATUS_DONE) { + content_set_ready(c); content_set_done(c); + } } /** diff --git a/css/css.c b/css/css.c index 0c0a4f560..85374decd 100644 --- a/css/css.c +++ b/css/css.c @@ -30,6 +30,23 @@ #include "utils/http.h" #include "utils/messages.h" +/** + * Context for import fetches + */ +typedef struct { + struct content_css_data *css; /**< Object containing import */ + + const char *referer; /**< URL of containing object */ + + nscss_done_callback cb; /**< Completion callback */ + void *pw; /**< Client data */ +} nscss_import_ctx; + +static void nscss_content_done(struct content_css_data *css, void *pw); +static css_error nscss_request_import(struct content_css_data *c, + nscss_import_ctx *ctx); +static css_error nscss_import_complete(struct content_css_data *c, + const hlcache_handle *import); static nserror nscss_import(hlcache_handle *handle, const hlcache_event *event, void *pw); @@ -159,11 +176,9 @@ css_error nscss_process_css_data(struct content_css_data *c, const char *data, bool nscss_convert(struct content *c) { union content_msg_data msg_data; - uint32_t i; - size_t size; css_error error; - error = nscss_convert_css_data(&c->data.css); + error = nscss_convert_css_data(&c->data.css, nscss_content_done, c); if (error != CSS_OK) { msg_data.error = "?"; content_broadcast(c, CONTENT_MSG_ERROR, msg_data); @@ -171,21 +186,38 @@ bool nscss_convert(struct content *c) return false; } + return true; +} + +/** + * Handle notification that a CSS object is done + * + * \param css CSS object + * \param pw Private data + */ +void nscss_content_done(struct content_css_data *css, void *pw) +{ + union content_msg_data msg_data; + struct content *c = pw; + uint32_t i; + size_t size; + css_error error; + /* Retrieve the size of this sheet */ - error = css_stylesheet_size(c->data.css.sheet, &size); + error = css_stylesheet_size(css->sheet, &size); if (error != CSS_OK) { msg_data.error = "?"; content_broadcast(c, CONTENT_MSG_ERROR, msg_data); c->status = CONTENT_STATUS_ERROR; - return false; + return; } c->size += size; /* Add on the size of the imported sheets */ - for (i = 0; i < c->data.css.import_count; i++) { - if (c->data.css.imports[i].c != NULL) { + for (i = 0; i < css->import_count; i++) { + if (css->imports[i].c != NULL) { struct content *import = hlcache_handle_get_content( - c->data.css.imports[i].c); + css->imports[i].c); if (import != NULL) { c->size += import->size; @@ -193,108 +225,57 @@ bool nscss_convert(struct content *c) } } - c->status = CONTENT_STATUS_DONE; - - return error == CSS_OK; + /* Finally, catch the content's users up with reality */ + if (css->import_count == 0) { + /* No imports? Ok, so we've not returned from nscss_convert yet. + * Just set the status, as content_convert will notify users */ + c->status = CONTENT_STATUS_DONE; + } else { + content_set_ready(c); + content_set_done(c); + } } /** * Convert CSS data ready for use * - * \param c CSS data to convert + * \param c CSS data to convert + * \param callback Callback to call when imports are fetched + * \param pw Client data for callback * \return CSS error */ -css_error nscss_convert_css_data(struct content_css_data *c) +css_error nscss_convert_css_data(struct content_css_data *c, + nscss_done_callback callback, void *pw) { - static const content_type accept[] = { CONTENT_CSS, CONTENT_UNKNOWN }; - const char *referer; - uint32_t i = 0; css_error error; - nserror nerror; - - error = css_stylesheet_get_url(c->sheet, &referer); - if (error != CSS_OK) { - return error; - } error = css_stylesheet_data_done(c->sheet); /* Process pending imports */ - while (error == CSS_IMPORTS_PENDING) { - hlcache_child_context child; - struct nscss_import *imports; - lwc_string *uri; - uint64_t media; - css_stylesheet *sheet; - - error = css_stylesheet_next_pending_import(c->sheet, - &uri, &media); - if (error != CSS_OK && error != CSS_INVALID) { - return error; - } + if (error == CSS_IMPORTS_PENDING) { + const char *referer; + nscss_import_ctx *ctx; - /* Give up if there are no more imports */ - if (error == CSS_INVALID) { - error = CSS_OK; - break; - } - - /* Increase space in table */ - imports = realloc(c->imports, (c->import_count + 1) * - sizeof(struct nscss_import)); - if (imports == NULL) { - return CSS_NOMEM; - } - c->imports = imports; - - /** \todo fallback charset */ - child.charset = NULL; - error = css_stylesheet_quirks_allowed(c->sheet, &child.quirks); + error = css_stylesheet_get_url(c->sheet, &referer); if (error != CSS_OK) { return error; } - /* Create content */ - i = c->import_count; - c->imports[c->import_count].media = media; - nerror = hlcache_handle_retrieve(lwc_string_data(uri), - 0, referer, NULL, nscss_import, c, - &child, accept, - &c->imports[c->import_count++].c); - if (error != NSERROR_OK) { + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) return CSS_NOMEM; - } - - /* Wait for import to fetch + convert */ - /** \todo This blocking approach needs to die */ - while (c->imports[i].c != NULL && - content_get_status(c->imports[i].c) != - CONTENT_STATUS_DONE) { - llcache_poll(); - gui_multitask(); - } - - if (c->imports[i].c != NULL) { - struct content *s = hlcache_handle_get_content( - c->imports[i].c); - sheet = s->data.css.sheet; - } else { - error = css_stylesheet_create(CSS_LEVEL_DEFAULT, - NULL, "", NULL, false, false, - myrealloc, NULL, - nscss_resolve_url, NULL, - &sheet); - if (error != CSS_OK) { - return error; - } - } - error = css_stylesheet_register_import(c->sheet, sheet); - if (error != CSS_OK) { - return error; - } - - error = CSS_IMPORTS_PENDING; + ctx->css = c; + ctx->referer = referer; + ctx->cb = callback; + ctx->pw = pw; + + error = nscss_request_import(c, ctx); + if (error != CSS_OK) + free(ctx); + } else { + /* No imports, so complete conversion */ + callback(c, pw); } return error; @@ -382,45 +363,136 @@ struct nscss_import *nscss_get_imports(hlcache_handle *h, uint32_t *n) return c->data.css.imports; } +/** + * Request that the next import fetch is triggered + * + * \param c CSS object requesting the import + * \param ctx Import context + * \return CSS_OK on success, + * CSS_NOMEM on memory exhaustion + * CSS_INVALID if no imports remain + */ +css_error nscss_request_import(struct content_css_data *c, + nscss_import_ctx *ctx) +{ + static const content_type accept[] = { CONTENT_CSS, CONTENT_UNKNOWN }; + hlcache_child_context child; + struct nscss_import *imports; + lwc_string *uri; + uint64_t media; + css_error error; + nserror nerror; + + error = css_stylesheet_next_pending_import(c->sheet, &uri, &media); + if (error != CSS_OK) { + return error; + } + + /* Increase space in table */ + imports = realloc(c->imports, (c->import_count + 1) * + sizeof(struct nscss_import)); + if (imports == NULL) { + return CSS_NOMEM; + } + c->imports = imports; + + /** \todo fallback charset */ + child.charset = NULL; + error = css_stylesheet_quirks_allowed(c->sheet, &child.quirks); + if (error != CSS_OK) { + return error; + } + + /* Create content */ + c->imports[c->import_count].media = media; + nerror = hlcache_handle_retrieve(lwc_string_data(uri), + 0, ctx->referer, NULL, nscss_import, ctx, + &child, accept, + &c->imports[c->import_count++].c); + if (error != NSERROR_OK) { + return CSS_NOMEM; + } + + return CSS_OK; +} + +/** + * Handle the completion of an import fetch + * + * \param c CSS object that requested the import + * \param import Cache handle of import, or NULL on failure + * \return CSS_OK on success, appropriate error otherwise + */ +css_error nscss_import_complete(struct content_css_data *c, + const hlcache_handle *import) +{ + css_stylesheet *sheet; + css_error error; + + if (import != NULL) { + struct content *s = hlcache_handle_get_content(import); + sheet = s->data.css.sheet; + } else { + error = css_stylesheet_create(CSS_LEVEL_DEFAULT, + NULL, "", NULL, false, false, + myrealloc, NULL, + nscss_resolve_url, NULL, + &sheet); + if (error != CSS_OK) { + return error; + } + } + + error = css_stylesheet_register_import(c->sheet, sheet); + if (error != CSS_OK) { + return error; + } + + return error; +} + /** * Handler for imported stylesheet events * * \param handle Handle for stylesheet * \param event Event object - * \param pw Callback context + * \param pw Callback context * \return NSERROR_OK on success, appropriate error otherwise */ nserror nscss_import(hlcache_handle *handle, const hlcache_event *event, void *pw) { - struct content_css_data *parent = pw; - uint32_t i = 0; + nscss_import_ctx *ctx = pw; + css_error error = CSS_OK; + bool next = false; switch (event->type) { case CONTENT_MSG_LOADING: if (content_get_type(handle) != CONTENT_CSS) { - hlcache_handle_release(handle); - - for (i = 0; i < parent->import_count; i++) { - if (parent->imports[i].c == handle) { - parent->imports[i].c = NULL; - break; - } - } + assert(0 && "Non-CSS type unexpected"); } break; case CONTENT_MSG_READY: break; case CONTENT_MSG_DONE: + error = nscss_import_complete(ctx->css, handle); + if (error != CSS_OK) { + hlcache_handle_release(handle); + ctx->css->imports[ctx->css->import_count - 1].c = NULL; + } + next = true; break; case CONTENT_MSG_ERROR: + assert(ctx->css->imports[ + ctx->css->import_count - 1].c == handle); + hlcache_handle_release(handle); - for (i = 0; i < parent->import_count; i++) { - if (parent->imports[i].c == handle) { - parent->imports[i].c = NULL; - break; - } - } + ctx->css->imports[ctx->css->import_count - 1].c = NULL; + + error = nscss_import_complete(ctx->css, NULL); + /* Already released handle */ + + next = true; break; case CONTENT_MSG_STATUS: break; @@ -428,6 +500,19 @@ nserror nscss_import(hlcache_handle *handle, assert(0); } - return NSERROR_OK; + /* Request next import, if we're in a position to do so */ + if (error == CSS_OK && next) + error = nscss_request_import(ctx->css, ctx); + + if (error != CSS_OK) { + /* No more imports, or error: notify parent that we're DONE */ + ctx->cb(ctx->css, ctx->pw); + + /* No longer need import context */ + free(ctx); + } + + /* Preserve out-of-memory. Invalid is OK */ + return error == CSS_NOMEM ? NSERROR_NOMEM : NSERROR_OK; } diff --git a/css/css.h b/css/css.h index e192b969c..25b91a3e2 100644 --- a/css/css.h +++ b/css/css.h @@ -49,6 +49,14 @@ struct nscss_import { uint64_t media; /**< Media types that sheet applies to */ }; +/** + * Type of callback called when a CSS object has finished importing sheets + * + * \param css CSS object that has completed + * \param pw Client-specific data + */ +typedef void (*nscss_done_callback)(struct content_css_data *css, void *pw); + bool nscss_create(struct content *c, const struct http_parameter *params); bool nscss_process_data(struct content *c, const char *data, unsigned int size); @@ -63,7 +71,8 @@ nserror nscss_create_css_data(struct content_css_data *c, const char *url, const char *charset, bool quirks); css_error nscss_process_css_data(struct content_css_data *c, const char *data, unsigned int size); -css_error nscss_convert_css_data(struct content_css_data *c); +css_error nscss_convert_css_data(struct content_css_data *c, + nscss_done_callback callback, void *pw); void nscss_destroy_css_data(struct content_css_data *c); struct nscss_import *nscss_get_imports(struct hlcache_handle *h, uint32_t *n); diff --git a/render/html.c b/render/html.c index a71689793..12cce974f 100644 --- a/render/html.c +++ b/render/html.c @@ -58,6 +58,7 @@ #define ALWAYS_DUMP_FRAMESET 0 #define ALWAYS_DUMP_BOX 0 +static void html_finish_conversion(struct content *c); static nserror html_convert_css_callback(hlcache_handle *css, const hlcache_event *event, void *pw); static bool html_meta_refresh(struct content *c, xmlNode *head); @@ -65,6 +66,7 @@ static bool html_head(struct content *c, xmlNode *head); static bool html_find_stylesheets(struct content *c, xmlNode *html); static bool html_process_style_element(struct content *c, unsigned int *index, xmlNode *style); +static void html_inline_style_done(struct content_css_data *css, void *pw); static bool html_replace_object(struct content *c, unsigned int i, const char *url); static nserror html_object_callback(hlcache_handle *object, @@ -419,13 +421,6 @@ bool html_convert(struct content *c) return false; } - /* get stylesheets */ - if (!html_find_stylesheets(c, html)) - return false; - - /* get icon */ - favicon_get_icon(c, html); - /* Retrieve forms from parser */ c->data.html.forms = binding_get_forms(c->data.html.parser_binding); for (f = c->data.html.forms; f != NULL; f = f->prev) { @@ -455,6 +450,84 @@ bool html_convert(struct content *c) } } + /* get stylesheets */ + if (!html_find_stylesheets(c, html)) + return false; + + return true; +} + +/** + * Complete conversion of an HTML document + * + * \param c Content to convert + */ +void html_finish_conversion(struct content *c) +{ + union content_msg_data msg_data; + xmlNode *html; + uint32_t i; + css_error error; + + html = xmlDocGetRootElement(c->data.html.document); + assert(html != NULL); + + /* check that the base stylesheet loaded; layout fails without it */ + if (c->data.html.stylesheets[STYLESHEET_BASE].data.external == NULL) { + msg_data.error = "Base stylesheet failed to load"; + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + c->status = CONTENT_STATUS_ERROR; + return; + } + + /* Create selection context */ + error = css_select_ctx_create(myrealloc, c, &c->data.html.select_ctx); + if (error != CSS_OK) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + c->status = CONTENT_MSG_ERROR; + return; + } + + /* Add sheets to it */ + for (i = STYLESHEET_BASE; i != c->data.html.stylesheet_count; i++) { + const struct html_stylesheet *hsheet = + &c->data.html.stylesheets[i]; + css_stylesheet *sheet; + css_origin origin = CSS_ORIGIN_AUTHOR; + + if (i < STYLESHEET_START) + origin = CSS_ORIGIN_UA; + + if (hsheet->type == HTML_STYLESHEET_EXTERNAL && + hsheet->data.external != NULL) { + struct content *s = hlcache_handle_get_content( + hsheet->data.external); + + sheet = s-> data.css.sheet; + } else if (hsheet->type == HTML_STYLESHEET_INTERNAL) { + sheet = hsheet->data.internal->sheet; + } else { + sheet = NULL; + } + + if (sheet != NULL) { + error = css_select_ctx_append_sheet( + c->data.html.select_ctx, sheet, + origin, CSS_MEDIA_SCREEN); + if (error != CSS_OK) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, + msg_data); + c->status = CONTENT_STATUS_ERROR; + return; + } + } + } + + /* get icon */ + favicon_get_icon(c, html); + /* convert xml tree to box tree */ LOG(("XML to box")); content_set_status(c, messages_get("Processing")); @@ -462,7 +535,8 @@ bool html_convert(struct content *c) if (!xml_to_box(html, c)) { msg_data.error = messages_get("NoMemory"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; + c->status = CONTENT_STATUS_ERROR; + return; } #if ALWAYS_DUMP_BOX box_dump(stderr, c->data.html.layout->children, 0); @@ -477,7 +551,8 @@ bool html_convert(struct content *c) LOG(("imagemap extraction failed")); msg_data.error = messages_get("NoMemory"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; + c->status = CONTENT_STATUS_ERROR; + return; } /*imagemap_dump(c);*/ @@ -485,13 +560,12 @@ bool html_convert(struct content *c) binding_destroy_tree(c->data.html.parser_binding); c->data.html.parser_binding = NULL; + content_set_ready(c); + if (c->active == 0) - c->status = CONTENT_STATUS_DONE; - else - c->status = CONTENT_STATUS_READY; - html_set_status(c, ""); + content_set_done(c); - return true; + html_set_status(c, ""); } @@ -787,12 +861,10 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) xmlNode *node; char *rel, *type, *media, *href, *url, *url2; unsigned int i = STYLESHEET_START; - unsigned int last_active = 0; union content_msg_data msg_data; url_func_result res; struct html_stylesheet *stylesheets; hlcache_child_context child; - css_error error; nserror ns_error; child.charset = c->data.html.encoding; @@ -972,60 +1044,6 @@ bool html_find_stylesheets(struct content *c, xmlNode *html) assert(c->data.html.stylesheet_count == i); - /* complete the fetches */ - while (c->active != 0) { - if (c->active != last_active) { - html_set_status(c, ""); - content_broadcast(c, CONTENT_MSG_STATUS, msg_data); - last_active = c->active; - } - llcache_poll(); - gui_multitask(); - } - - /* check that the base stylesheet loaded; layout fails without it */ - if (c->data.html.stylesheets[STYLESHEET_BASE].data.external == NULL) { - msg_data.error = "Base stylesheet failed to load"; - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } - - /* Create selection context */ - error = css_select_ctx_create(myrealloc, c, &c->data.html.select_ctx); - if (error != CSS_OK) - goto no_memory; - - /* Add sheets to it */ - for (i = STYLESHEET_BASE; i != c->data.html.stylesheet_count; i++) { - const struct html_stylesheet *hsheet = - &c->data.html.stylesheets[i]; - css_stylesheet *sheet; - css_origin origin = CSS_ORIGIN_AUTHOR; - - if (i < STYLESHEET_START) - origin = CSS_ORIGIN_UA; - - if (hsheet->type == HTML_STYLESHEET_EXTERNAL && - hsheet->data.external != NULL) { - struct content *s = hlcache_handle_get_content( - hsheet->data.external); - - sheet = s-> data.css.sheet; - } else if (hsheet->type == HTML_STYLESHEET_INTERNAL) { - sheet = hsheet->data.internal->sheet; - } else { - sheet = NULL; - } - - if (sheet != NULL) { - error = css_select_ctx_append_sheet( - c->data.html.select_ctx, sheet, - origin, CSS_MEDIA_SCREEN); - if (error != CSS_OK) - goto no_memory; - } - } - return true; no_memory: @@ -1118,9 +1136,13 @@ bool html_process_style_element(struct content *c, unsigned int *index, xmlFree(data); } + c->active++; + /* Convert the content -- manually, as we want the result */ - if (nscss_convert_css_data(sheet) != CSS_OK) { + if (nscss_convert_css_data(sheet, + html_inline_style_done, c) != CSS_OK) { /* conversion failed */ + c->active--; nscss_destroy_css_data(sheet); talloc_free(sheet); sheet = NULL; @@ -1138,6 +1160,19 @@ no_memory: return false; } +/** + * Handle notification of inline style completion + * + * \param css Inline style object + * \param pw Private data + */ +void html_inline_style_done(struct content_css_data *css, void *pw) +{ + struct content *html = pw; + + if (--html->active == 0) + html_finish_conversion(html); +} /** * Callback for fetchcache() for linked stylesheets. @@ -1164,19 +1199,7 @@ nserror html_convert_css_callback(hlcache_handle *css, case CONTENT_MSG_LOADING: /* check that the stylesheet is really CSS */ if (content_get_type(css) != CONTENT_CSS) { - hlcache_handle_release(css); - s->data.external = NULL; - - parent->active--; - - LOG(("%s is not CSS", content_get_url(css))); - - content_add_error(parent, "NotCSS", 0); - - html_set_status(parent, messages_get("NotCSS")); - - content_broadcast(parent, CONTENT_MSG_STATUS, - event->data); + assert(0 && "Non-CSS type unexpected"); } break; @@ -1206,6 +1229,9 @@ nserror html_convert_css_callback(hlcache_handle *css, assert(0); } + if (parent->active == 0) + html_finish_conversion(parent); + return NSERROR_OK; } -- cgit v1.2.3