Precis:

This patch removes all reentrant calls to llcache_poll().
llcache_poll() is now only ever called from netsurf_poll().


Added files




Changed files


 content/content.c |    4 
 css/css.c         |  299 ++++++++++++++++++++++++++++++++++--------------------
 css/css.h         |   11 +
 render/html.c     |  194 +++++++++++++++++++----------------
 4 files changed, 315 insertions(+), 193 deletions(-)


Index: render/html.c
===================================================================
--- render/html.c       (revision 10338)
+++ render/html.c       (working copy)
@@ -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_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 @@
                        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 @@
                }
        }
 
+       /* 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 @@
        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 @@
                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 @@
        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;
+               content_set_done(c);
+
        html_set_status(c, "");
-
-       return true;
 }
 
 
@@ -787,12 +861,10 @@
        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 @@
 
        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 @@
                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,7 +1160,20 @@
        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 @@
        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 @@
                assert(0);
        }
 
+       if (parent->active == 0)
+               html_finish_conversion(parent);
+
        return NSERROR_OK;
 }
 
Index: css/css.c
===================================================================
--- css/css.c   (revision 10338)
+++ css/css.c   (working copy)
@@ -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 @@
 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 @@
                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 @@
                }
        }
 
-       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();
-               }
+               ctx->css = c;
+               ctx->referer = referer;
+               ctx->cb = callback;
+               ctx->pw = pw;
 
-               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;
+               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 @@
 }
 
 /**
+ * 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 @@
                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;
 }
 
Index: css/css.h
===================================================================
--- css/css.h   (revision 10338)
+++ css/css.h   (working copy)
@@ -49,6 +49,14 @@
        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 @@
                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);
Index: content/content.c
===================================================================
--- content/content.c   (revision 10338)
+++ content/content.c   (working copy)
@@ -667,8 +667,10 @@
 
        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);
+       }
 }
 
 /**


Conflicted files




Removed files



Reply via email to