From 97978e858b396157540d9e0bff91676bb8dcd500 Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Wed, 27 Feb 2013 03:11:10 +0000 Subject: Use custom fetcher for inline CSS --- Makefile.sources | 4 +- css/css.c | 42 ++++++- css/css.h | 34 ------ desktop/browser.c | 6 +- desktop/save_complete.c | 13 +- render/box.h | 1 + render/html.h | 9 +- render/html_css.c | 224 +++++++++++----------------------- render/html_css_fetcher.c | 298 ++++++++++++++++++++++++++++++++++++++++++++++ render/html_internal.h | 4 + 10 files changed, 418 insertions(+), 217 deletions(-) create mode 100644 render/html_css_fetcher.c diff --git a/Makefile.sources b/Makefile.sources index b53d7c50a..ab71ff36a 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -14,8 +14,8 @@ S_CSS := css.c dump.c internal.c select.c utils.c S_RENDER := box.c box_construct.c box_normalise.c box_textarea.c \ font.c form.c imagemap.c layout.c list.c search.c table.c \ textplain.c \ - html.c html_css.c html_script.c html_interaction.c \ - html_redraw.c html_forms.c html_object.c + html.c html_css.c html_css_fetcher.c html_script.c \ + html_interaction.c html_redraw.c html_forms.c html_object.c S_UTILS := base64.c corestrings.c filename.c filepath.c hashtable.c \ libdom.c locale.c log.c messages.c nsurl.c talloc.c url.c \ diff --git a/css/css.c b/css/css.c index 2cd111bab..8923bdc07 100644 --- a/css/css.c +++ b/css/css.c @@ -36,6 +36,30 @@ /* Define to trace import fetches */ #undef NSCSS_IMPORT_TRACE +struct content_css_data; + +/** + * Type of callback called when a CSS object has finished + * + * \param css CSS object that has completed + * \param pw Client-specific data + */ +typedef void (*nscss_done_callback)(struct content_css_data *css, void *pw); + +/** + * CSS content data + */ +struct content_css_data +{ + css_stylesheet *sheet; /**< Stylesheet object */ + char *charset; /**< Character set of stylesheet */ + struct nscss_import *imports; /**< Array of imported sheets */ + uint32_t import_count; /**< Number of sheets imported */ + uint32_t next_to_register; /**< Index of next import to register */ + nscss_done_callback done; /**< Completion callback */ + void *pw; /**< Client data */ +}; + /** * CSS content data */ @@ -67,6 +91,14 @@ static nserror nscss_clone(const struct content *old, struct content **newc); static bool nscss_matches_quirks(const struct content *c, bool quirks); static content_type nscss_content_type(void); +static nserror nscss_create_css_data(struct content_css_data *c, + const char *url, const char *charset, bool quirks, + nscss_done_callback done, void *pw); +static css_error nscss_process_css_data(struct content_css_data *c, const char *data, + unsigned int size); +static css_error nscss_convert_css_data(struct content_css_data *c); +static void nscss_destroy_css_data(struct content_css_data *c); + static void nscss_content_done(struct content_css_data *css, void *pw); static css_error nscss_handle_import(void *pw, css_stylesheet *parent, lwc_string *url, uint64_t media); @@ -155,7 +187,7 @@ nserror nscss_create(const content_handler *handler, * \param pw Client data for \a done * \return NSERROR_OK on success, NSERROR_NOMEM on memory exhaustion */ -nserror nscss_create_css_data(struct content_css_data *c, +static nserror nscss_create_css_data(struct content_css_data *c, const char *url, const char *charset, bool quirks, nscss_done_callback done, void *pw) { @@ -227,8 +259,8 @@ bool nscss_process_data(struct content *c, const char *data, unsigned int size) * \param size Number of bytes to process * \return CSS_OK on success, appropriate error otherwise */ -css_error nscss_process_css_data(struct content_css_data *c, const char *data, - unsigned int size) +static css_error nscss_process_css_data(struct content_css_data *c, + const char *data, unsigned int size) { return css_stylesheet_append_data(c->sheet, (const uint8_t *) data, size); @@ -262,7 +294,7 @@ bool nscss_convert(struct content *c) * \param c CSS data to convert * \return CSS error */ -css_error nscss_convert_css_data(struct content_css_data *c) +static css_error nscss_convert_css_data(struct content_css_data *c) { css_error error; @@ -310,7 +342,7 @@ void nscss_destroy(struct content *c) * * \param c CSS data to clean up */ -void nscss_destroy_css_data(struct content_css_data *c) +static void nscss_destroy_css_data(struct content_css_data *c) { uint32_t i; diff --git a/css/css.h b/css/css.h index dc6053f48..be8d4bcd8 100644 --- a/css/css.h +++ b/css/css.h @@ -25,33 +25,7 @@ #include "utils/errors.h" -struct content; -struct content_css_data; struct hlcache_handle; -struct http_parameter; -struct nscss_import; - -/** - * Type of callback called when a CSS object has finished - * - * \param css CSS object that has completed - * \param pw Client-specific data - */ -typedef void (*nscss_done_callback)(struct content_css_data *css, void *pw); - -/** - * CSS content data - */ -struct content_css_data -{ - css_stylesheet *sheet; /**< Stylesheet object */ - char *charset; /**< Character set of stylesheet */ - struct nscss_import *imports; /**< Array of imported sheets */ - uint32_t import_count; /**< Number of sheets imported */ - uint32_t next_to_register; /**< Index of next import to register */ - nscss_done_callback done; /**< Completion callback */ - void *pw; /**< Client data */ -}; /** * Imported stylesheet record @@ -63,14 +37,6 @@ struct nscss_import { nserror nscss_init(void); -nserror nscss_create_css_data(struct content_css_data *c, - const char *url, const char *charset, bool quirks, - nscss_done_callback done, void *pw); -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); -void nscss_destroy_css_data(struct content_css_data *c); - css_stylesheet *nscss_get_stylesheet(struct hlcache_handle *h); struct nscss_import *nscss_get_imports(struct hlcache_handle *h, uint32_t *n); diff --git a/desktop/browser.c b/desktop/browser.c index fa0e6d121..1eed1e072 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -1954,10 +1954,8 @@ void browser_window_reload(struct browser_window *bw, bool all) sheets = html_get_stylesheets(c, &count); for (i = STYLESHEET_START; i != count; i++) { - if (sheets[i].type == HTML_STYLESHEET_EXTERNAL && - sheets[i].data.external != NULL) { - content_invalidate_reuse_data( - sheets[i].data.external); + if (sheets[i].sheet != NULL) { + content_invalidate_reuse_data(sheets[i].sheet); } } } diff --git a/desktop/save_complete.c b/desktop/save_complete.c index 621544ded..0c67654a6 100644 --- a/desktop/save_complete.c +++ b/desktop/save_complete.c @@ -361,19 +361,10 @@ static bool save_complete_save_imported_sheets(save_complete_ctx *ctx, static bool save_complete_save_html_stylesheet(save_complete_ctx *ctx, struct html_stylesheet *sheet) { - if (sheet->type == HTML_STYLESHEET_INTERNAL) { - if (save_complete_save_imported_sheets(ctx, - sheet->data.internal.data->imports, - sheet->data.internal.data->import_count) == false) - return false; - - return true; - } - - if (sheet->data.external == NULL) + if (sheet->sheet == NULL) return true; - return save_complete_save_stylesheet(ctx, sheet->data.external); + return save_complete_save_stylesheet(ctx, sheet->sheet); } static bool save_complete_save_html_stylesheets(save_complete_ctx *ctx, diff --git a/render/box.h b/render/box.h index 0953d9f88..1ce26c28b 100644 --- a/render/box.h +++ b/render/box.h @@ -94,6 +94,7 @@ #include "utils/nsurl.h" #include "utils/types.h" +struct content; struct box; struct browser_window; struct column; diff --git a/render/html.h b/render/html.h index de9e29430..a17e4a50b 100644 --- a/render/html.h +++ b/render/html.h @@ -57,15 +57,8 @@ struct selection; */ struct html_stylesheet { /** Type of sheet */ - enum { HTML_STYLESHEET_EXTERNAL, HTML_STYLESHEET_INTERNAL } type; struct dom_node *node; /**< dom node associated with sheet */ - union { - struct hlcache_handle *external; - struct { - struct content_css_data *data; - bool done; - } internal; - } data; /**< Sheet data */ + struct hlcache_handle *sheet; }; /** diff --git a/render/html_css.c b/render/html_css.c index a1e9cd00d..b98632e61 100644 --- a/render/html_css.c +++ b/render/html_css.c @@ -73,7 +73,7 @@ static nserror css_error_to_nserror(css_error error) } /** - * Callback for fetchcache() for linked stylesheets. + * Callback for fetchcache() for stylesheets. */ static nserror @@ -89,8 +89,7 @@ html_convert_css_callback(hlcache_handle *css, for (i = 0, s = parent->stylesheets; i != parent->stylesheet_count; i++, s++) { - if (s->type == HTML_STYLESHEET_EXTERNAL && - s->data.external == css) + if (s->sheet == css) break; } @@ -115,7 +114,7 @@ html_convert_css_callback(hlcache_handle *css, nsurl_access(hlcache_handle_get_url(css)), event->data.error)); hlcache_handle_release(css); - s->data.external = NULL; + s->sheet = NULL; parent->base.active--; LOG(("%d fetches active", parent->base.active)); content_add_error(&parent->base, "?", 0); @@ -170,13 +169,8 @@ nserror html_css_free_stylesheets(html_content *html) unsigned int i; for (i = 0; i != html->stylesheet_count; i++) { - if ((html->stylesheets[i].type == HTML_STYLESHEET_EXTERNAL) && - (html->stylesheets[i].data.external != NULL)) { - hlcache_handle_release(html->stylesheets[i].data.external); - } else if ((html->stylesheets[i].type == HTML_STYLESHEET_INTERNAL) && - (html->stylesheets[i].data.internal.data != NULL)) { - nscss_destroy_css_data(html->stylesheets[i].data.internal.data); - free(html->stylesheets[i].data.internal.data); + if (html->stylesheets[i].sheet != NULL) { + hlcache_handle_release(html->stylesheets[i].sheet); } } free(html->stylesheets); @@ -200,7 +194,7 @@ nserror html_css_quirks_stylesheets(html_content *c) 0, content_get_url(&c->base), NULL, html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_QUIRKS].data.external); + &c->stylesheets[STYLESHEET_QUIRKS].sheet); if (ns_error != NSERROR_OK) { return ns_error; } @@ -231,14 +225,10 @@ nserror html_css_new_stylesheets(html_content *c) return NSERROR_NOMEM; } - c->stylesheets[STYLESHEET_BASE].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_BASE].data.external = NULL; - c->stylesheets[STYLESHEET_QUIRKS].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_QUIRKS].data.external = NULL; - c->stylesheets[STYLESHEET_ADBLOCK].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_ADBLOCK].data.external = NULL; - c->stylesheets[STYLESHEET_USER].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_USER].data.external = NULL; + c->stylesheets[STYLESHEET_BASE].sheet = NULL; + c->stylesheets[STYLESHEET_QUIRKS].sheet = NULL; + c->stylesheets[STYLESHEET_ADBLOCK].sheet = NULL; + c->stylesheets[STYLESHEET_USER].sheet = NULL; c->stylesheet_count = STYLESHEET_START; child.charset = c->encoding; @@ -247,7 +237,7 @@ nserror html_css_new_stylesheets(html_content *c) ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0, content_get_url(&c->base), NULL, html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_BASE].data.external); + &c->stylesheets[STYLESHEET_BASE].sheet); if (ns_error != NSERROR_OK) { return ns_error; } @@ -260,8 +250,7 @@ nserror html_css_new_stylesheets(html_content *c) ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url, 0, content_get_url(&c->base), NULL, html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_ADBLOCK]. - data.external); + &c->stylesheets[STYLESHEET_ADBLOCK].sheet); if (ns_error != NSERROR_OK) { return ns_error; } @@ -274,7 +263,7 @@ nserror html_css_new_stylesheets(html_content *c) ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0, content_get_url(&c->base), NULL, html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_USER].data.external); + &c->stylesheets[STYLESHEET_USER].sheet); if (ns_error != NSERROR_OK) { return ns_error; } @@ -285,141 +274,57 @@ nserror html_css_new_stylesheets(html_content *c) return ns_error; } -/** - * Handle notification of inline style completion - * - * \param css Inline style object - * \param pw Private data - */ -static void html_inline_style_done(struct content_css_data *css, void *pw) -{ - html_content *html = pw; - size_t i; - - LOG(("Inline style %p done", css)); - - /* Search HTML content for sheet */ - for (i = 0; i < html->stylesheet_count; i++) { - if (html->stylesheets[i].type == HTML_STYLESHEET_INTERNAL && - html->stylesheets[i].data.internal.data == css) - break; - } - - /* Not found: must have been replaced, so destroy it */ - if (i == html->stylesheet_count) { - LOG(("Not found: destroying")); - nscss_destroy_css_data(css); - free(css); - } else { - html->stylesheets[i].data.internal.done = true; - } - - html->base.active--; - LOG(("%d fetches active", html->base.active)); - if (html->base.active == 0) { - html_begin_conversion(html); - } -} - static nserror html_stylesheet_from_domnode(html_content *c, - struct html_stylesheet *s, - dom_node *node) + dom_node *node, + hlcache_handle **sheet) { - dom_node *child, *next; + hlcache_child_context child; + dom_string *style; + nsurl *url; dom_exception exc; - struct content_css_data *sheet, *old_sheet; - bool old_sheet_done; nserror error; - css_error csserror; + uint32_t key; + char urlbuf[64]; - /* create stylesheet */ - sheet = calloc(1, sizeof(struct content_css_data)); - if (sheet == NULL) { - return NSERROR_NOMEM; + child.charset = c->encoding; + child.quirks = c->base.quirks; + + exc = dom_node_get_text_content(node, &style); + if ((exc != DOM_NO_ERR) || (style == NULL)) { + LOG(("No text content")); + return NSERROR_OK; } - error = nscss_create_css_data(sheet, - nsurl_access(c->base_url), NULL, c->quirks, - html_inline_style_done, c); + error = html_css_fetcher_add_item(style, &key); if (error != NSERROR_OK) { - free(sheet); + dom_string_unref(style); return error; } - exc = dom_node_get_first_child(node, &child); - if (exc != DOM_NO_ERR) { - nscss_destroy_css_data(sheet); - free(sheet); - return NSERROR_DOM; - } - - while (child != NULL) { - dom_string *data; - - exc = dom_node_get_text_content(child, &data); - if (exc != DOM_NO_ERR) { - dom_node_unref(child); - nscss_destroy_css_data(sheet); - free(sheet); - return NSERROR_DOM; - } - - if (nscss_process_css_data(sheet, - dom_string_data(data), - dom_string_byte_length(data)) == false) { - dom_string_unref(data); - dom_node_unref(child); - nscss_destroy_css_data(sheet); - free(sheet); - return NSERROR_CSS; - } + dom_string_unref(style); - dom_string_unref(data); + snprintf(urlbuf, sizeof(urlbuf), "x-ns-css:%u", key); - exc = dom_node_get_next_sibling(child, &next); - if (exc != DOM_NO_ERR) { - dom_node_unref(child); - nscss_destroy_css_data(sheet); - free(sheet); - return NSERROR_DOM; - } + error = nsurl_create(urlbuf, &url); + if (error != NSERROR_OK) { + return error; + } - dom_node_unref(child); - child = next; + error = hlcache_handle_retrieve(url, 0, + content_get_url(&c->base), NULL, + html_convert_css_callback, c, &child, CONTENT_CSS, + sheet); + if (error != NSERROR_OK) { + nsurl_unref(url); + return error; } + nsurl_unref(url); + c->base.active++; LOG(("%d fetches active", c->base.active)); - LOG(("Updating sheet %p with %p", s->data.internal, sheet)); - - /* Update index */ - old_sheet = s->data.internal.data; - old_sheet_done = s->data.internal.done; - s->data.internal.data = sheet; - s->data.internal.done = false; - - /* Convert the content -- manually, as we want the result */ - csserror = nscss_convert_css_data(sheet); - if (csserror != CSS_OK) { - /* conversion failed */ - c->base.active--; - LOG(("%d fetches active", c->base.active)); - nscss_destroy_css_data(sheet); - free(sheet); - s->data.internal.data = old_sheet; - s->data.internal.done = old_sheet_done; - return css_error_to_nserror(csserror); - } - - /* Clean up old sheet if it was already complete */ - if (old_sheet != NULL && old_sheet_done) { - LOG(("Destroying old sheet %p", old_sheet)); - nscss_destroy_css_data(old_sheet); - free(old_sheet); - } - return NSERROR_OK; } @@ -472,10 +377,8 @@ html_create_style_element(html_content *c, dom_node *style) } c->stylesheets = stylesheets; - c->stylesheets[c->stylesheet_count].type = HTML_STYLESHEET_INTERNAL; c->stylesheets[c->stylesheet_count].node = style; - c->stylesheets[c->stylesheet_count].data.internal.data = NULL; - c->stylesheets[c->stylesheet_count].data.internal.done = false; + c->stylesheets[c->stylesheet_count].sheet = NULL; c->stylesheet_count++; return c->stylesheets + (c->stylesheet_count - 1); @@ -486,11 +389,11 @@ bool html_css_update_style(html_content *c, dom_node *style) nserror error; unsigned int i; struct html_stylesheet *s; + hlcache_handle *sheet = NULL; /* Find sheet */ for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) { - if ((s->type == HTML_STYLESHEET_INTERNAL) && - (s->node == style)) + if (s->node == style) break; } if (i == c->stylesheet_count) { @@ -504,13 +407,30 @@ bool html_css_update_style(html_content *c, dom_node *style) LOG(("Found sheet %p slot %d for node %p", s, i, style)); - error = html_stylesheet_from_domnode(c, s, style); + error = html_stylesheet_from_domnode(c, style, &sheet); if (error != NSERROR_OK) { LOG(("Failed to update sheet")); content_broadcast_errorcode(&c->base, error); return false; } + if (sheet != NULL) { + LOG(("Updating sheet %p with %p", s->sheet, sheet)); + + if (s->sheet != NULL) { + switch (content_get_status(s->sheet)) { + case CONTENT_STATUS_DONE: + break; + default: + hlcache_handle_abort(s->sheet); + c->base.active--; + LOG(("%d fetches active", c->base.active)); + } + hlcache_handle_release(s->sheet); + } + s->sheet = sheet; + } + return true; } @@ -588,7 +508,6 @@ bool html_css_process_link(html_content *htmlc, dom_node *node) } htmlc->stylesheets = stylesheets; - htmlc->stylesheets[htmlc->stylesheet_count].type = HTML_STYLESHEET_EXTERNAL; /* start fetch */ child.charset = htmlc->encoding; @@ -602,7 +521,7 @@ bool html_css_process_link(html_content *htmlc, dom_node *node) htmlc, &child, CONTENT_CSS, - &htmlc->stylesheets[htmlc->stylesheet_count].data.external); + &htmlc->stylesheets[htmlc->stylesheet_count].sheet); nsurl_unref(joined); @@ -629,7 +548,7 @@ html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx) css_select_ctx *select_ctx; /* check that the base stylesheet loaded; layout fails without it */ - if (c->stylesheets[STYLESHEET_BASE].data.external == NULL) { + if (c->stylesheets[STYLESHEET_BASE].sheet == NULL) { return NSERROR_CSS_BASE; } @@ -651,11 +570,8 @@ html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx) origin = CSS_ORIGIN_USER; } - if ((hsheet->type == HTML_STYLESHEET_EXTERNAL) && - (hsheet->data.external != NULL)) { - sheet = nscss_get_stylesheet(hsheet->data.external); - } else if (hsheet->type == HTML_STYLESHEET_INTERNAL) { - sheet = hsheet->data.internal.data->sheet; + if (hsheet->sheet != NULL) { + sheet = nscss_get_stylesheet(hsheet->sheet); } if (sheet != NULL) { @@ -679,6 +595,8 @@ nserror html_css_init(void) { nserror error; + html_css_fetcher_register(); + error = nsurl_create("resource:default.css", &html_default_stylesheet_url); if (error != NSERROR_OK) diff --git a/render/html_css_fetcher.c b/render/html_css_fetcher.c new file mode 100644 index 000000000..31e2cba13 --- /dev/null +++ b/render/html_css_fetcher.c @@ -0,0 +1,298 @@ +/* + * Copyright 2008 Rob Kendrick + * Copyright 2013 John-Mark Bell + * + * This file is part of NetSurf. + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include + +#include + +#include "utils/config.h" +#include "content/fetch.h" +#include "render/html_internal.h" +#include "utils/log.h" +#include "utils/ring.h" +#include "utils/nsurl.h" +#include "utils/utils.h" + +typedef struct html_css_fetcher_item { + uint32_t key; + dom_string *data; + + struct html_css_fetcher_item *r_next, *r_prev; +} html_css_fetcher_item; + +typedef struct html_css_fetcher_context { + struct fetch *parent_fetch; + + nsurl *url; + html_css_fetcher_item *item; + + bool aborted; + bool locked; + + struct html_css_fetcher_context *r_next, *r_prev; +} html_css_fetcher_context; + +static uint32_t current_key = 0; +static html_css_fetcher_item *items = NULL; +static html_css_fetcher_context *ring = NULL; + +static bool html_css_fetcher_initialise(lwc_string *scheme) +{ + LOG(("html_css_fetcher_initialise called for %s", lwc_string_data(scheme))); + return true; +} + +static void html_css_fetcher_finalise(lwc_string *scheme) +{ + LOG(("html_css_fetcher_finalise called for %s", lwc_string_data(scheme))); +} + +static bool html_css_fetcher_can_fetch(const nsurl *url) +{ + return true; +} + +static void *html_css_fetcher_setup(struct fetch *parent_fetch, nsurl *url, + bool only_2xx, bool downgrade_tls, const char *post_urlenc, + const struct fetch_multipart_data *post_multipart, + const char **headers) +{ + html_css_fetcher_context *ctx; + lwc_string *path; + uint32_t key; + html_css_fetcher_item *item, *found = NULL; + + /* format of a x-ns-css URL is: + * x-ns-url: + * Where key is an unsigned 32bit integer + */ + + path = nsurl_get_component(url, NSURL_PATH); + /* The path must exist */ + if (path == NULL) { + return NULL; + } + + key = strtoul(lwc_string_data(path), NULL, 10); + + lwc_string_unref(path); + + /* There must be at least one item */ + if (items == NULL) { + return NULL; + } + + item = items; + do { + if (item->key == key) { + found = item; + break; + } + + item = item->r_next; + } while (item != items); + + /* We must have found the item */ + if (found == NULL) { + return NULL; + } + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->parent_fetch = parent_fetch; + ctx->url = nsurl_ref(url); + ctx->item = found; + + RING_INSERT(ring, ctx); + + return ctx; +} + +static bool html_css_fetcher_start(void *ctx) +{ + return true; +} + +static void html_css_fetcher_free(void *ctx) +{ + html_css_fetcher_context *c = ctx; + + nsurl_unref(c->url); + if (c->item != NULL) { + dom_string_unref(c->item->data); + RING_REMOVE(items, c->item); + free(c->item); + } + RING_REMOVE(ring, c); + free(ctx); +} + +static void html_css_fetcher_abort(void *ctx) +{ + html_css_fetcher_context *c = ctx; + + /* To avoid the poll loop having to deal with the fetch context + * disappearing from under it, we simply flag the abort here. + * The poll loop itself will perform the appropriate cleanup. + */ + c->aborted = true; +} + +static void html_css_fetcher_send_callback(const fetch_msg *msg, + html_css_fetcher_context *c) +{ + c->locked = true; + fetch_send_callback(msg, c->parent_fetch); + c->locked = false; +} + +static void html_css_fetcher_poll(lwc_string *scheme) +{ + fetch_msg msg; + html_css_fetcher_context *c, *next; + + if (ring == NULL) return; + + /* Iterate over ring, processing each pending fetch */ + c = ring; + do { + /* Ignore fetches that have been flagged as locked. + * This allows safe re-entrant calls to this function. + * Re-entrancy can occur if, as a result of a callback, + * the interested party causes fetch_poll() to be called + * again. + */ + if (c->locked == true) { + next = c->r_next; + continue; + } + + /* Only process non-aborted fetches */ + if (c->aborted) { + /* Nothing to do */ + assert(c->locked == false); + } else if (c->item != NULL) { + char header[64]; + + fetch_set_http_code(c->parent_fetch, 200); + + /* Any callback can result in the fetch being aborted. + * Therefore, we _must_ check for this after _every_ + * call to html_css_fetcher_send_callback(). + */ + snprintf(header, sizeof header, + "Content-Type: text/css; charset=utf-8"); + msg.type = FETCH_HEADER; + msg.data.header_or_data.buf = (const uint8_t *) header; + msg.data.header_or_data.len = strlen(header); + html_css_fetcher_send_callback(&msg, c); + + if (c->aborted == false) { + snprintf(header, sizeof header, + "Content-Length: %"SSIZET_FMT, + dom_string_byte_length(c->item->data)); + msg.type = FETCH_HEADER; + msg.data.header_or_data.buf = + (const uint8_t *) header; + msg.data.header_or_data.len = strlen(header); + html_css_fetcher_send_callback(&msg, c); + } + + if (c->aborted == false) { + msg.type = FETCH_DATA; + msg.data.header_or_data.buf = + (const uint8_t *) + dom_string_data(c->item->data); + msg.data.header_or_data.len = + dom_string_byte_length(c->item->data); + html_css_fetcher_send_callback(&msg, c); + } + + if (c->aborted == false) { + msg.type = FETCH_FINISHED; + html_css_fetcher_send_callback(&msg, c); + } + } else { + LOG(("Processing of %s failed!", + nsurl_access(c->url))); + + /* Ensure that we're unlocked here. If we aren't, + * then html_css_fetcher_process() is broken. + */ + assert(c->locked == false); + } + + /* Compute next fetch item at the last possible moment as + * processing this item may have added to the ring. + */ + next = c->r_next; + + fetch_remove_from_queues(c->parent_fetch); + fetch_free(c->parent_fetch); + + /* Advance to next ring entry, exiting if we've reached + * the start of the ring or the ring has become empty + */ + } while ( (c = next) != ring && ring != NULL); +} + +void html_css_fetcher_register(void) +{ + lwc_string *scheme; + + if (lwc_intern_string("x-ns-css", SLEN("x-ns-css"), + &scheme) != lwc_error_ok) { + die("Failed to initialise the fetch module " + "(couldn't intern \"x-ns-css\")."); + } + + fetch_add_fetcher(scheme, + html_css_fetcher_initialise, + html_css_fetcher_can_fetch, + html_css_fetcher_setup, + html_css_fetcher_start, + html_css_fetcher_abort, + html_css_fetcher_free, + html_css_fetcher_poll, + html_css_fetcher_finalise); +} + +nserror html_css_fetcher_add_item(dom_string *data, uint32_t *key) +{ + html_css_fetcher_item *item = malloc(sizeof(*item)); + + if (item == NULL) { + return NSERROR_NOMEM; + } + + *key = item->key = current_key++; + item->data = dom_string_ref(data); + + RING_INSERT(items, item); + + return NSERROR_OK; +} + diff --git a/render/html_internal.h b/render/html_internal.h index 428cd3995..f92948b0d 100644 --- a/render/html_internal.h +++ b/render/html_internal.h @@ -270,6 +270,10 @@ bool html_css_update_style(html_content *c, dom_node *style); nserror html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx); +/* in render/html_css_fetcher.c */ +void html_css_fetcher_register(void); +nserror html_css_fetcher_add_item(dom_string *data, uint32_t *key); + /* in render/html_object.c */ /** -- cgit v1.2.3