diff --git a/content/cache.c b/content/cache.c index 5e95f4c6e..760f54679 100644 --- a/content/cache.c +++ b/content/cache.c @@ -213,6 +213,7 @@ void cache_shrink(void) LOG(("size %lu, removing %p '%s'", size, e->content, e->content->url)); /* TODO: move to disc cache */ size -= e->content->size; + e->content->cache = 0; content_destroy(e->content); unused_list->prev = e->prev; e->prev->next = unused_list; diff --git a/content/content.c b/content/content.c index 596672b9d..4b4e19bff 100644 --- a/content/content.c +++ b/content/content.c @@ -195,6 +195,8 @@ struct content * content_create(char *url) user_sentinel->p1 = user_sentinel->p2 = 0; user_sentinel->next = 0; c->user_list = user_sentinel; + c->lock = 0; + c->destroy_pending = false; return c; } @@ -326,8 +328,16 @@ void content_reformat(struct content *c, unsigned long width, unsigned long heig void content_destroy(struct content *c) { struct content_user *user, *next; - assert(c != 0); + assert(c); LOG(("content %p %s", c, c->url)); + assert(!c->fetch); + assert(!c->cache); + + if (c->lock) { + c->destroy_pending = true; + return; + } + if (c->type < HANDLER_MAP_COUNT) handler_map[c->type].destroy(c); for (user = c->user_list; user != 0; user = next) { @@ -435,8 +445,10 @@ void content_remove_user(struct content *c, * and destroy content structure if not in state READY or DONE */ if (c->user_list->next == 0) { LOG(("no users for %p %s", c, c->url)); - if (c->fetch != 0) + if (c->fetch != 0) { fetch_abort(c->fetch); + c->fetch = 0; + } if (c->status < CONTENT_STATUS_READY) { if (c->cache) cache_destroy(c); @@ -444,6 +456,8 @@ void content_remove_user(struct content *c, } else { if (c->cache) cache_freeable(c); + else + content_destroy(c); } } } @@ -457,11 +471,14 @@ void content_broadcast(struct content *c, content_msg msg, char *error) { struct content_user *user, *next; LOG(("content %s, message %i", c->url, msg)); + c->lock++; for (user = c->user_list->next; user != 0; user = next) { next = user->next; /* user may be destroyed during callback */ if (user->callback != 0) user->callback(msg, c, user->p1, user->p2, error); } + if (--(c->lock) == 0 && c->destroy_pending) + content_destroy(c); } diff --git a/content/content.h b/content/content.h index 7235011a4..737942187 100644 --- a/content/content.h +++ b/content/content.h @@ -139,6 +139,10 @@ struct content { struct fetch *fetch; /**< Associated fetch, or 0. */ unsigned long fetch_size; /**< Amount of data fetched so far. */ unsigned long total_size; /**< Total data size, 0 if unknown. */ + + int lock; /**< Content in use, do not destroy. */ + bool destroy_pending; /**< Destroy when lock returns to 0. */ + bool no_error_pages; /**< Used by fetchcache(). */ }; diff --git a/content/fetchcache.c b/content/fetchcache.c index e7e8f2b3a..f2ad43a71 100644 --- a/content/fetchcache.c +++ b/content/fetchcache.c @@ -44,13 +44,28 @@ static void fetchcache_error_page(struct content *c, const char *error); * * If an error occurs immediately, 0 may be returned. Later errors will be * reported via the callback. + * + * \param url address to fetch + * \param referer url of referring page, or 0 if none + * \param callback function to call when anything interesting happens to + * the new content + * \param p1 user parameter for callback + * \param p2 user parameter for callback + * \param width available space + * \param height available space + * \param no_error_pages if an error occurs, send CONTENT_MSG_ERROR instead + * of generating an error page + * \param post_urlenc url encoded post data, or 0 if none + * \param post_multipart multipart post data, or 0 if none + * \param cookies send and accept cookies + * \return a new content, or 0 if an error occurred and no_error_pages is true */ -struct content * fetchcache(const char *url0, char *referer, +struct content * fetchcache(const char *url, char *referer, void (*callback)(content_msg msg, struct content *c, void *p1, void *p2, const char *error), void *p1, void *p2, unsigned long width, unsigned long height, - bool only_2xx + bool no_error_pages #ifdef WITH_POST , char *post_urlenc, struct form_successful_control *post_multipart @@ -61,8 +76,8 @@ struct content * fetchcache(const char *url0, char *referer, ) { struct content *c; - char *url = xstrdup(url0); - char *hash = strchr(url, '#'); + char *url1 = xstrdup(url); + char *hash = strchr(url1, '#'); const char *params[] = { 0 }; char error_message[500]; @@ -70,21 +85,21 @@ struct content * fetchcache(const char *url0, char *referer, if (hash != 0) *hash = 0; - LOG(("url %s", url)); + LOG(("url %s", url1)); #ifdef WITH_POST if (!post_urlenc && !post_multipart) #endif { - c = cache_get(url); + c = cache_get(url1); if (c != 0) { - free(url); + free(url1); content_add_user(c, callback, p1, p2); return c; } } - c = content_create(url); + c = content_create(url1); content_add_user(c, callback, p1, p2); #ifdef WITH_POST @@ -95,7 +110,8 @@ struct content * fetchcache(const char *url0, char *referer, c->fetch_size = 0; c->width = width; c->height = height; - c->fetch = fetch_start(url, referer, fetchcache_callback, c, only_2xx + c->no_error_pages = no_error_pages; + c->fetch = fetch_start(url1, referer, fetchcache_callback, c, no_error_pages #ifdef WITH_POST ,post_urlenc, post_multipart #endif @@ -107,11 +123,16 @@ struct content * fetchcache(const char *url0, char *referer, LOG(("warning: fetch_start failed")); if (c->cache) cache_destroy(c); + if (no_error_pages) { + content_destroy(c); + free(url1); + return 0; + } snprintf(error_message, sizeof error_message, - messages_get("InvalidURL"), url); + messages_get("InvalidURL"), url1); fetchcache_error_page(c, error_message); } - free(url); + free(url1); return c; } @@ -130,6 +151,8 @@ void fetchcache_callback(fetch_msg msg, void *p, char *data, unsigned long size) char **params; unsigned int i; + c->lock++; + switch (msg) { case FETCH_TYPE: c->total_size = size; @@ -167,11 +190,15 @@ void fetchcache_callback(fetch_msg msg, void *p, char *data, unsigned long size) case FETCH_ERROR: LOG(("FETCH_ERROR, '%s'", data)); c->fetch = 0; -/* content_broadcast(c, CONTENT_MSG_ERROR, data); */ if (c->cache) cache_destroy(c); - content_reset(c); - fetchcache_error_page(c, data); + if (c->no_error_pages) { + content_broadcast(c, CONTENT_MSG_ERROR, data); + content_destroy(c); + } else { + content_reset(c); + fetchcache_error_page(c, data); + } break; case FETCH_REDIRECT: @@ -202,6 +229,9 @@ void fetchcache_callback(fetch_msg msg, void *p, char *data, unsigned long size) default: assert(0); } + + if (--(c->lock) == 0 && c->destroy_pending) + content_destroy(c); } diff --git a/content/fetchcache.h b/content/fetchcache.h index cbd447aa1..37d3e1f18 100644 --- a/content/fetchcache.h +++ b/content/fetchcache.h @@ -27,7 +27,7 @@ struct content * fetchcache(const char *url, char *referer, void (*callback)(content_msg msg, struct content *c, void *p1, void *p2, const char *error), void *p1, void *p2, unsigned long width, unsigned long height, - bool only_2xx + bool no_error_pages #ifdef WITH_POST , char *post_urlenc, struct form_successful_control *post_multipart