summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--content/content.c4
-rw-r--r--css/css.c301
-rw-r--r--css/css.h11
-rw-r--r--render/html.c194
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;
@@ -383,44 +364,135 @@ struct nscss_import *nscss_get_imports(hlcache_handle *h, uint32_t *n)
}
/**
+ * 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;
}