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 --- render/html_css_fetcher.c | 298 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 render/html_css_fetcher.c (limited to 'render/html_css_fetcher.c') 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; +} + -- cgit v1.2.3