From 977ae8efc6cc543d98ac8c5261b32cb348dbb47e Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sun, 19 Jan 2014 10:51:14 +0000 Subject: remove all forward declarations from hlcache.c --- content/hlcache.c | 1028 ++++++++++++++++++++++++++--------------------------- 1 file changed, 509 insertions(+), 519 deletions(-) diff --git a/content/hlcache.c b/content/hlcache.c index e8b2836db..618f4fd1e 100644 --- a/content/hlcache.c +++ b/content/hlcache.c @@ -94,413 +94,310 @@ struct hlcache_s { static struct hlcache_s *hlcache = NULL; -static void hlcache_clean(void *ignored); - -static nserror hlcache_llcache_callback(llcache_handle *handle, - const llcache_event *event, void *pw); -static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx, - lwc_string *effective_type); -static bool hlcache_type_is_acceptable(lwc_string *mime_type, - content_type accepted_types, content_type *computed_type); -static nserror hlcache_find_content(hlcache_retrieval_ctx *ctx, - lwc_string *effective_type); -static void hlcache_content_callback(struct content *c, - content_msg msg, union content_msg_data data, void *pw); - /****************************************************************************** - * Public API * + * High-level cache internals * ******************************************************************************/ -nserror -hlcache_initialise(const struct hlcache_parameters *hlcache_parameters) -{ - nserror ret; - - hlcache = calloc(1, sizeof(struct hlcache_s)); - if (hlcache == NULL) { - return NSERROR_NOMEM; - } - - ret = llcache_initialise(hlcache_parameters->cb, - hlcache_parameters->cb_ctx, - hlcache_parameters->limit); - if (ret != NSERROR_OK) { - free(hlcache); - hlcache = NULL; - return ret; - } - - hlcache->params = *hlcache_parameters; - - /* Schedule the cache cleanup */ - schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL); - return NSERROR_OK; -} - -/* See hlcache.h for documentation */ -void hlcache_stop(void) -{ - /* Remove the hlcache_clean schedule */ - schedule_remove(hlcache_clean, NULL); -} - -/* See hlcache.h for documentation */ -void hlcache_finalise(void) +/** + * Attempt to clean the cache + */ +static void hlcache_clean(void *ignored) { - uint32_t num_contents, prev_contents; - hlcache_entry *entry; - hlcache_retrieval_ctx *ctx, *next; - - /* Obtain initial count of contents remaining */ - for (num_contents = 0, entry = hlcache->content_list; - entry != NULL; entry = entry->next) { - num_contents++; - } - - LOG(("%d contents remain before cache drain", num_contents)); - - /* Drain cache */ - do { - prev_contents = num_contents; - - hlcache_clean(NULL); - - for (num_contents = 0, entry = hlcache->content_list; - entry != NULL; entry = entry->next) { - num_contents++; - } - } while (num_contents > 0 && num_contents != prev_contents); - - LOG(("%d contents remaining:", num_contents)); - for (entry = hlcache->content_list; entry != NULL; entry = entry->next) { - hlcache_handle entry_handle = { entry, NULL, NULL }; + hlcache_entry *entry, *next; - if (entry->content != NULL) { - LOG((" %p : %s (%d users)", entry, - nsurl_access( - hlcache_handle_get_url(&entry_handle)), - content_count_users(entry->content))); - } else { - LOG((" %p", entry)); - } - } + for (entry = hlcache->content_list; entry != NULL; entry = next) { + next = entry->next; - /* Clean up retrieval contexts */ - if (hlcache->retrieval_ctx_ring != NULL) { - ctx = hlcache->retrieval_ctx_ring; + if (entry->content == NULL) + continue; - do { - next = ctx->r_next; + if (content__get_status(entry->content) == + CONTENT_STATUS_LOADING) + continue; - if (ctx->llcache != NULL) - llcache_handle_release(ctx->llcache); + if (content_count_users(entry->content) != 0) + continue; - if (ctx->handle != NULL) - free(ctx->handle); + /** \todo This is over-zealous: all unused contents + * will be immediately destroyed. Ideally, we want to + * purge all unused contents that are using stale + * source data, and enough fresh contents such that + * the cache fits in the configured cache size limit. + */ - if (ctx->child.charset != NULL) - free((char *) ctx->child.charset); + /* Remove entry from cache */ + if (entry->prev == NULL) + hlcache->content_list = entry->next; + else + entry->prev->next = entry->next; - free(ctx); + if (entry->next != NULL) + entry->next->prev = entry->prev; - ctx = next; - } while (ctx != hlcache->retrieval_ctx_ring); + /* Destroy content */ + content_destroy(entry->content); - hlcache->retrieval_ctx_ring = NULL; + /* Destroy entry */ + free(entry); } - LOG(("hit/miss %d/%d", hlcache->hit_count, hlcache->miss_count)); - - free(hlcache); - hlcache = NULL; - - LOG(("Finalising low-level cache")); - llcache_finalise(); -} - -/* See hlcache.h for documentation */ -nserror hlcache_poll(void) -{ - - llcache_poll(); + /* Attempt to clean the llcache */ + llcache_clean(); - return NSERROR_OK; + /* Re-schedule ourselves */ + schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL); } -/* See hlcache.h for documentation */ -nserror hlcache_handle_retrieve(nsurl *url, uint32_t flags, - nsurl *referer, llcache_post_data *post, - hlcache_handle_callback cb, void *pw, - hlcache_child_context *child, - content_type accepted_types, hlcache_handle **result) +/** + * Determine if the specified MIME type is acceptable + * + * \param mime_type MIME type to consider + * \param accepted_types Array of acceptable types, or NULL for any + * \param computed_type Pointer to location to receive computed type of object + * \return True if the type is acceptable, false otherwise + */ +static bool hlcache_type_is_acceptable(lwc_string *mime_type, + content_type accepted_types, content_type *computed_type) { - hlcache_retrieval_ctx *ctx; - nserror error; - - assert(cb != NULL); - - ctx = calloc(1, sizeof(hlcache_retrieval_ctx)); - if (ctx == NULL) - return NSERROR_NOMEM; - - ctx->handle = calloc(1, sizeof(hlcache_handle)); - if (ctx->handle == NULL) { - free(ctx); - return NSERROR_NOMEM; - } - - if (child != NULL) { - if (child->charset != NULL) { - ctx->child.charset = strdup(child->charset); - if (ctx->child.charset == NULL) { - free(ctx->handle); - free(ctx); - return NSERROR_NOMEM; - } - } - ctx->child.quirks = child->quirks; - } - - ctx->flags = flags; - ctx->accepted_types = accepted_types; - - ctx->handle->cb = cb; - ctx->handle->pw = pw; - - error = llcache_handle_retrieve(url, flags, referer, post, - hlcache_llcache_callback, ctx, - &ctx->llcache); - if (error != NSERROR_OK) { - free((char *) ctx->child.charset); - free(ctx->handle); - free(ctx); - return error; - } + content_type type; - RING_INSERT(hlcache->retrieval_ctx_ring, ctx); + type = content_factory_type_from_mime_type(mime_type); - *result = ctx->handle; + *computed_type = type; - return NSERROR_OK; + return ((accepted_types & type) != 0); } -/* See hlcache.h for documentation */ -nserror hlcache_handle_release(hlcache_handle *handle) +/** + * Veneer between content callback API and hlcache callback API + * + * \param c Content to emit message for + * \param msg Message to emit + * \param data Data for message + * \param pw Pointer to private data (hlcache_handle) + */ +static void hlcache_content_callback(struct content *c, content_msg msg, + union content_msg_data data, void *pw) { - if (handle->entry != NULL) { - content_remove_user(handle->entry->content, - hlcache_content_callback, handle); - } else { - RING_ITERATE_START(struct hlcache_retrieval_ctx, - hlcache->retrieval_ctx_ring, - ictx) { - if (ictx->handle == handle && - ictx->migrate_target == false) { - /* This is the nascent context for us, - * so abort the fetch */ - llcache_handle_abort(ictx->llcache); - llcache_handle_release(ictx->llcache); - /* Remove us from the ring */ - RING_REMOVE(hlcache->retrieval_ctx_ring, ictx); - /* Throw us away */ - free((char *) ictx->child.charset); - free(ictx); - /* And stop */ - RING_ITERATE_STOP(hlcache->retrieval_ctx_ring, - ictx); - } - } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx); - } - - handle->cb = NULL; - handle->pw = NULL; - - free(handle); - - return NSERROR_OK; -} + hlcache_handle *handle = pw; + hlcache_event event; + nserror error = NSERROR_OK; -/* See hlcache.h for documentation */ -struct content *hlcache_handle_get_content(const hlcache_handle *handle) -{ - assert(handle != NULL); + event.type = msg; + event.data = data; - if (handle->entry != NULL) - return handle->entry->content; + if (handle->cb != NULL) + error = handle->cb(handle, &event, handle->pw); - return NULL; + if (error != NSERROR_OK) + LOG(("Error in callback: %d", error)); } -/* See hlcache.h for documentation */ -nserror hlcache_handle_abort(hlcache_handle *handle) +/** + * Find a content for the high-level cache handle + * + * \param ctx High-level cache retrieval context + * \param effective_type Effective MIME type of content + * \return NSERROR_OK on success, + * NSERROR_NEED_DATA on success where data is needed, + * appropriate error otherwise + * + * \pre handle::state == HLCACHE_HANDLE_NEW + * \pre Headers must have been received for associated low-level handle + * \post Low-level handle is either released, or associated with new content + * \post High-level handle is registered with content + */ +static nserror hlcache_find_content(hlcache_retrieval_ctx *ctx, + lwc_string *effective_type) { - struct hlcache_entry *entry = handle->entry; - struct content *c; + hlcache_entry *entry; + hlcache_event event; + nserror error = NSERROR_OK; - if (entry == NULL) { - /* This handle is not yet associated with a cache entry. - * The implication is that the fetch for the handle has - * not progressed to the point where the entry can be - * created. */ + /* Search list of cached contents for a suitable one */ + for (entry = hlcache->content_list; entry != NULL; entry = entry->next) { + hlcache_handle entry_handle = { entry, NULL, NULL }; + const llcache_handle *entry_llcache; - RING_ITERATE_START(struct hlcache_retrieval_ctx, - hlcache->retrieval_ctx_ring, - ictx) { - if (ictx->handle == handle && - ictx->migrate_target == false) { - /* This is the nascent context for us, - * so abort the fetch */ - llcache_handle_abort(ictx->llcache); - llcache_handle_release(ictx->llcache); - /* Remove us from the ring */ - RING_REMOVE(hlcache->retrieval_ctx_ring, ictx); - /* Throw us away */ - free((char *) ictx->child.charset); - free(ictx); - /* And stop */ - RING_ITERATE_STOP(hlcache->retrieval_ctx_ring, - ictx); - } - } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx); + if (entry->content == NULL) + continue; - return NSERROR_OK; - } + /* Ignore contents in the error state */ + if (content_get_status(&entry_handle) == CONTENT_STATUS_ERROR) + continue; - c = entry->content; + /* Ensure that content is shareable */ + if (content_is_shareable(entry->content) == false) + continue; - if (content_count_users(c) > 1) { - /* We are not the only user of 'c' so clone it. */ - struct content *clone = content_clone(c); + /* Ensure that quirks mode is acceptable */ + if (content_matches_quirks(entry->content, + ctx->child.quirks) == false) + continue; - if (clone == NULL) - return NSERROR_NOMEM; + /* Ensure that content uses same low-level object as + * low-level handle */ + entry_llcache = content_get_llcache_handle(entry->content); - entry = calloc(sizeof(struct hlcache_entry), 1); + if (llcache_handle_references_same_object(entry_llcache, + ctx->llcache)) + break; + } - if (entry == NULL) { - content_destroy(clone); + if (entry == NULL) { + /* No existing entry, so need to create one */ + entry = malloc(sizeof(hlcache_entry)); + if (entry == NULL) return NSERROR_NOMEM; - } - if (content_add_user(clone, - hlcache_content_callback, handle) == false) { - content_destroy(clone); + /* Create content using llhandle */ + entry->content = content_factory_create_content(ctx->llcache, + ctx->child.charset, ctx->child.quirks, + effective_type); + if (entry->content == NULL) { free(entry); return NSERROR_NOMEM; } - content_remove_user(c, hlcache_content_callback, handle); - - entry->content = clone; - handle->entry = entry; + /* Insert into cache */ entry->prev = NULL; entry->next = hlcache->content_list; if (hlcache->content_list != NULL) hlcache->content_list->prev = entry; hlcache->content_list = entry; - c = clone; - } + /* Signal to caller that we created a content */ + error = NSERROR_NEED_DATA; - return content_abort(c); -} + hlcache->miss_count++; + } else { + /* Found a suitable content: no longer need low-level handle */ + llcache_handle_release(ctx->llcache); + hlcache->hit_count++; + } -/* See hlcache.h for documentation */ -nserror hlcache_handle_replace_callback(hlcache_handle *handle, - hlcache_handle_callback cb, void *pw) -{ - handle->cb = cb; - handle->pw = pw; + /* Associate handle with content */ + if (content_add_user(entry->content, + hlcache_content_callback, ctx->handle) == false) + return NSERROR_NOMEM; - return NSERROR_OK; -} + /* Associate cache entry with handle */ + ctx->handle->entry = entry; -nserror hlcache_handle_clone(hlcache_handle *handle, hlcache_handle **result) -{ - *result = NULL; - return NSERROR_CLONE_FAILED; -} + /* Catch handle up with state of content */ + if (ctx->handle->cb != NULL) { + content_status status = content_get_status(ctx->handle); -/* See hlcache.h for documentation */ -nsurl *hlcache_handle_get_url(const hlcache_handle *handle) -{ - nsurl *result = NULL; + if (status == CONTENT_STATUS_LOADING) { + event.type = CONTENT_MSG_LOADING; + ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); + } else if (status == CONTENT_STATUS_READY) { + event.type = CONTENT_MSG_LOADING; + ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); - assert(handle != NULL); + if (ctx->handle->cb != NULL) { + event.type = CONTENT_MSG_READY; + ctx->handle->cb(ctx->handle, &event, + ctx->handle->pw); + } + } else if (status == CONTENT_STATUS_DONE) { + event.type = CONTENT_MSG_LOADING; + ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); - if (handle->entry != NULL) { - result = content_get_url(handle->entry->content); - } else { - RING_ITERATE_START(struct hlcache_retrieval_ctx, - hlcache->retrieval_ctx_ring, - ictx) { - if (ictx->handle == handle) { - /* This is the nascent context for us */ - result = llcache_handle_get_url(ictx->llcache); + if (ctx->handle->cb != NULL) { + event.type = CONTENT_MSG_READY; + ctx->handle->cb(ctx->handle, &event, + ctx->handle->pw); + } - /* And stop */ - RING_ITERATE_STOP(hlcache->retrieval_ctx_ring, - ictx); + if (ctx->handle->cb != NULL) { + event.type = CONTENT_MSG_DONE; + ctx->handle->cb(ctx->handle, &event, + ctx->handle->pw); } - } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx); + } } - return result; + return error; } -/****************************************************************************** - * High-level cache internals * - ******************************************************************************/ - /** - * Attempt to clean the cache + * Migrate a retrieval context into its final destination content + * + * \param ctx Context to migrate + * \param effective_type The effective MIME type of the content, or NULL + * \return NSERROR_OK on success, + * NSERROR_NEED_DATA on success where data is needed, + * appropriate error otherwise */ -void hlcache_clean(void *ignored) +static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx, + lwc_string *effective_type) { - hlcache_entry *entry, *next; + content_type type = CONTENT_NONE; + nserror error = NSERROR_OK; - for (entry = hlcache->content_list; entry != NULL; entry = next) { - next = entry->next; + ctx->migrate_target = true; - if (entry->content == NULL) - continue; + if (effective_type != NULL && + hlcache_type_is_acceptable(effective_type, + ctx->accepted_types, &type)) { + error = hlcache_find_content(ctx, effective_type); + if (error != NSERROR_OK && error != NSERROR_NEED_DATA) { + if (ctx->handle->cb != NULL) { + hlcache_event hlevent; - if (content__get_status(entry->content) == - CONTENT_STATUS_LOADING) - continue; + hlevent.type = CONTENT_MSG_ERROR; + hlevent.data.error = messages_get("MiscError"); - if (content_count_users(entry->content) != 0) - continue; + ctx->handle->cb(ctx->handle, &hlevent, + ctx->handle->pw); + } - /** \todo This is over-zealous: all unused contents - * will be immediately destroyed. Ideally, we want to - * purge all unused contents that are using stale - * source data, and enough fresh contents such that - * the cache fits in the configured cache size limit. - */ + llcache_handle_abort(ctx->llcache); + llcache_handle_release(ctx->llcache); + } + } else if (type == CONTENT_NONE && + (ctx->flags & HLCACHE_RETRIEVE_MAY_DOWNLOAD)) { + /* Unknown type, and we can download, so convert */ + llcache_handle_force_stream(ctx->llcache); + + if (ctx->handle->cb != NULL) { + hlcache_event hlevent; - /* Remove entry from cache */ - if (entry->prev == NULL) - hlcache->content_list = entry->next; - else - entry->prev->next = entry->next; + hlevent.type = CONTENT_MSG_DOWNLOAD; + hlevent.data.download = ctx->llcache; - if (entry->next != NULL) - entry->next->prev = entry->prev; + ctx->handle->cb(ctx->handle, &hlevent, + ctx->handle->pw); + } - /* Destroy content */ - content_destroy(entry->content); + /* Ensure caller knows we need data */ + error = NSERROR_NEED_DATA; + } else { + /* Unacceptable type: report error */ + if (ctx->handle->cb != NULL) { + hlcache_event hlevent; - /* Destroy entry */ - free(entry); + hlevent.type = CONTENT_MSG_ERROR; + hlevent.data.error = messages_get("UnacceptableType"); + + ctx->handle->cb(ctx->handle, &hlevent, + ctx->handle->pw); + } + + llcache_handle_abort(ctx->llcache); + llcache_handle_release(ctx->llcache); } - /* Attempt to clean the llcache */ - llcache_clean(); + ctx->migrate_target = false; - /* Re-schedule ourselves */ - schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL); + /* No longer require retrieval context */ + RING_REMOVE(hlcache->retrieval_ctx_ring, ctx); + free((char *) ctx->child.charset); + free(ctx); + + return error; } /** @@ -511,7 +408,7 @@ void hlcache_clean(void *ignored) * \param pw Pointer to client-specific data * \return NSERROR_OK on success, appropriate error otherwise */ -nserror hlcache_llcache_callback(llcache_handle *handle, +static nserror hlcache_llcache_callback(llcache_handle *handle, const llcache_event *event, void *pw) { hlcache_retrieval_ctx *ctx = pw; @@ -611,253 +508,346 @@ nserror hlcache_llcache_callback(llcache_handle *handle, return NSERROR_OK; } -/** - * Migrate a retrieval context into its final destination content - * - * \param ctx Context to migrate - * \param effective_type The effective MIME type of the content, or NULL - * \return NSERROR_OK on success, - * NSERROR_NEED_DATA on success where data is needed, - * appropriate error otherwise - */ -nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx, - lwc_string *effective_type) + +/****************************************************************************** + * Public API * + ******************************************************************************/ + + +nserror +hlcache_initialise(const struct hlcache_parameters *hlcache_parameters) { - content_type type = CONTENT_NONE; - nserror error = NSERROR_OK; + nserror ret; - ctx->migrate_target = true; + hlcache = calloc(1, sizeof(struct hlcache_s)); + if (hlcache == NULL) { + return NSERROR_NOMEM; + } - if (effective_type != NULL && - hlcache_type_is_acceptable(effective_type, - ctx->accepted_types, &type)) { - error = hlcache_find_content(ctx, effective_type); - if (error != NSERROR_OK && error != NSERROR_NEED_DATA) { - if (ctx->handle->cb != NULL) { - hlcache_event hlevent; + ret = llcache_initialise(hlcache_parameters->cb, + hlcache_parameters->cb_ctx, + hlcache_parameters->limit); + if (ret != NSERROR_OK) { + free(hlcache); + hlcache = NULL; + return ret; + } - hlevent.type = CONTENT_MSG_ERROR; - hlevent.data.error = messages_get("MiscError"); + hlcache->params = *hlcache_parameters; - ctx->handle->cb(ctx->handle, &hlevent, - ctx->handle->pw); - } + /* Schedule the cache cleanup */ + schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL); - llcache_handle_abort(ctx->llcache); - llcache_handle_release(ctx->llcache); - } - } else if (type == CONTENT_NONE && - (ctx->flags & HLCACHE_RETRIEVE_MAY_DOWNLOAD)) { - /* Unknown type, and we can download, so convert */ - llcache_handle_force_stream(ctx->llcache); + return NSERROR_OK; +} - if (ctx->handle->cb != NULL) { - hlcache_event hlevent; +/* See hlcache.h for documentation */ +void hlcache_stop(void) +{ + /* Remove the hlcache_clean schedule */ + schedule_remove(hlcache_clean, NULL); +} - hlevent.type = CONTENT_MSG_DOWNLOAD; - hlevent.data.download = ctx->llcache; +/* See hlcache.h for documentation */ +void hlcache_finalise(void) +{ + uint32_t num_contents, prev_contents; + hlcache_entry *entry; + hlcache_retrieval_ctx *ctx, *next; - ctx->handle->cb(ctx->handle, &hlevent, - ctx->handle->pw); + /* Obtain initial count of contents remaining */ + for (num_contents = 0, entry = hlcache->content_list; + entry != NULL; entry = entry->next) { + num_contents++; + } + + LOG(("%d contents remain before cache drain", num_contents)); + + /* Drain cache */ + do { + prev_contents = num_contents; + + hlcache_clean(NULL); + + for (num_contents = 0, entry = hlcache->content_list; + entry != NULL; entry = entry->next) { + num_contents++; } + } while (num_contents > 0 && num_contents != prev_contents); - /* Ensure caller knows we need data */ - error = NSERROR_NEED_DATA; - } else { - /* Unacceptable type: report error */ - if (ctx->handle->cb != NULL) { - hlcache_event hlevent; + LOG(("%d contents remaining:", num_contents)); + for (entry = hlcache->content_list; entry != NULL; entry = entry->next) { + hlcache_handle entry_handle = { entry, NULL, NULL }; - hlevent.type = CONTENT_MSG_ERROR; - hlevent.data.error = messages_get("UnacceptableType"); + if (entry->content != NULL) { + LOG((" %p : %s (%d users)", entry, + nsurl_access( + hlcache_handle_get_url(&entry_handle)), + content_count_users(entry->content))); + } else { + LOG((" %p", entry)); + } + } - ctx->handle->cb(ctx->handle, &hlevent, - ctx->handle->pw); + /* Clean up retrieval contexts */ + if (hlcache->retrieval_ctx_ring != NULL) { + ctx = hlcache->retrieval_ctx_ring; + + do { + next = ctx->r_next; + + if (ctx->llcache != NULL) + llcache_handle_release(ctx->llcache); + + if (ctx->handle != NULL) + free(ctx->handle); + + if (ctx->child.charset != NULL) + free((char *) ctx->child.charset); + + free(ctx); + + ctx = next; + } while (ctx != hlcache->retrieval_ctx_ring); + + hlcache->retrieval_ctx_ring = NULL; + } + + LOG(("hit/miss %d/%d", hlcache->hit_count, hlcache->miss_count)); + + free(hlcache); + hlcache = NULL; + + LOG(("Finalising low-level cache")); + llcache_finalise(); +} + +/* See hlcache.h for documentation */ +nserror hlcache_poll(void) +{ + + llcache_poll(); + + return NSERROR_OK; +} + +/* See hlcache.h for documentation */ +nserror hlcache_handle_retrieve(nsurl *url, uint32_t flags, + nsurl *referer, llcache_post_data *post, + hlcache_handle_callback cb, void *pw, + hlcache_child_context *child, + content_type accepted_types, hlcache_handle **result) +{ + hlcache_retrieval_ctx *ctx; + nserror error; + + assert(cb != NULL); + + ctx = calloc(1, sizeof(hlcache_retrieval_ctx)); + if (ctx == NULL) + return NSERROR_NOMEM; + + ctx->handle = calloc(1, sizeof(hlcache_handle)); + if (ctx->handle == NULL) { + free(ctx); + return NSERROR_NOMEM; + } + + if (child != NULL) { + if (child->charset != NULL) { + ctx->child.charset = strdup(child->charset); + if (ctx->child.charset == NULL) { + free(ctx->handle); + free(ctx); + return NSERROR_NOMEM; + } } + ctx->child.quirks = child->quirks; + } + + ctx->flags = flags; + ctx->accepted_types = accepted_types; + + ctx->handle->cb = cb; + ctx->handle->pw = pw; + + error = llcache_handle_retrieve(url, flags, referer, post, + hlcache_llcache_callback, ctx, + &ctx->llcache); + if (error != NSERROR_OK) { + free((char *) ctx->child.charset); + free(ctx->handle); + free(ctx); + return error; + } + + RING_INSERT(hlcache->retrieval_ctx_ring, ctx); + + *result = ctx->handle; + + return NSERROR_OK; +} - llcache_handle_abort(ctx->llcache); - llcache_handle_release(ctx->llcache); +/* See hlcache.h for documentation */ +nserror hlcache_handle_release(hlcache_handle *handle) +{ + if (handle->entry != NULL) { + content_remove_user(handle->entry->content, + hlcache_content_callback, handle); + } else { + RING_ITERATE_START(struct hlcache_retrieval_ctx, + hlcache->retrieval_ctx_ring, + ictx) { + if (ictx->handle == handle && + ictx->migrate_target == false) { + /* This is the nascent context for us, + * so abort the fetch */ + llcache_handle_abort(ictx->llcache); + llcache_handle_release(ictx->llcache); + /* Remove us from the ring */ + RING_REMOVE(hlcache->retrieval_ctx_ring, ictx); + /* Throw us away */ + free((char *) ictx->child.charset); + free(ictx); + /* And stop */ + RING_ITERATE_STOP(hlcache->retrieval_ctx_ring, + ictx); + } + } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx); } - ctx->migrate_target = false; + handle->cb = NULL; + handle->pw = NULL; - /* No longer require retrieval context */ - RING_REMOVE(hlcache->retrieval_ctx_ring, ctx); - free((char *) ctx->child.charset); - free(ctx); + free(handle); - return error; + return NSERROR_OK; } -/** - * Determine if the specified MIME type is acceptable - * - * \param mime_type MIME type to consider - * \param accepted_types Array of acceptable types, or NULL for any - * \param computed_type Pointer to location to receive computed type of object - * \return True if the type is acceptable, false otherwise - */ -bool hlcache_type_is_acceptable(lwc_string *mime_type, - content_type accepted_types, content_type *computed_type) +/* See hlcache.h for documentation */ +struct content *hlcache_handle_get_content(const hlcache_handle *handle) { - content_type type; - - type = content_factory_type_from_mime_type(mime_type); + assert(handle != NULL); - *computed_type = type; + if (handle->entry != NULL) + return handle->entry->content; - return ((accepted_types & type) != 0); + return NULL; } -/** - * Find a content for the high-level cache handle - * - * \param ctx High-level cache retrieval context - * \param effective_type Effective MIME type of content - * \return NSERROR_OK on success, - * NSERROR_NEED_DATA on success where data is needed, - * appropriate error otherwise - * - * \pre handle::state == HLCACHE_HANDLE_NEW - * \pre Headers must have been received for associated low-level handle - * \post Low-level handle is either released, or associated with new content - * \post High-level handle is registered with content - */ -nserror hlcache_find_content(hlcache_retrieval_ctx *ctx, - lwc_string *effective_type) +/* See hlcache.h for documentation */ +nserror hlcache_handle_abort(hlcache_handle *handle) { - hlcache_entry *entry; - hlcache_event event; - nserror error = NSERROR_OK; + struct hlcache_entry *entry = handle->entry; + struct content *c; - /* Search list of cached contents for a suitable one */ - for (entry = hlcache->content_list; entry != NULL; entry = entry->next) { - hlcache_handle entry_handle = { entry, NULL, NULL }; - const llcache_handle *entry_llcache; + if (entry == NULL) { + /* This handle is not yet associated with a cache entry. + * The implication is that the fetch for the handle has + * not progressed to the point where the entry can be + * created. */ - if (entry->content == NULL) - continue; + RING_ITERATE_START(struct hlcache_retrieval_ctx, + hlcache->retrieval_ctx_ring, + ictx) { + if (ictx->handle == handle && + ictx->migrate_target == false) { + /* This is the nascent context for us, + * so abort the fetch */ + llcache_handle_abort(ictx->llcache); + llcache_handle_release(ictx->llcache); + /* Remove us from the ring */ + RING_REMOVE(hlcache->retrieval_ctx_ring, ictx); + /* Throw us away */ + free((char *) ictx->child.charset); + free(ictx); + /* And stop */ + RING_ITERATE_STOP(hlcache->retrieval_ctx_ring, + ictx); + } + } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx); - /* Ignore contents in the error state */ - if (content_get_status(&entry_handle) == CONTENT_STATUS_ERROR) - continue; + return NSERROR_OK; + } - /* Ensure that content is shareable */ - if (content_is_shareable(entry->content) == false) - continue; + c = entry->content; - /* Ensure that quirks mode is acceptable */ - if (content_matches_quirks(entry->content, - ctx->child.quirks) == false) - continue; + if (content_count_users(c) > 1) { + /* We are not the only user of 'c' so clone it. */ + struct content *clone = content_clone(c); - /* Ensure that content uses same low-level object as - * low-level handle */ - entry_llcache = content_get_llcache_handle(entry->content); + if (clone == NULL) + return NSERROR_NOMEM; - if (llcache_handle_references_same_object(entry_llcache, - ctx->llcache)) - break; - } + entry = calloc(sizeof(struct hlcache_entry), 1); - if (entry == NULL) { - /* No existing entry, so need to create one */ - entry = malloc(sizeof(hlcache_entry)); - if (entry == NULL) + if (entry == NULL) { + content_destroy(clone); return NSERROR_NOMEM; + } - /* Create content using llhandle */ - entry->content = content_factory_create_content(ctx->llcache, - ctx->child.charset, ctx->child.quirks, - effective_type); - if (entry->content == NULL) { + if (content_add_user(clone, + hlcache_content_callback, handle) == false) { + content_destroy(clone); free(entry); return NSERROR_NOMEM; } - /* Insert into cache */ + content_remove_user(c, hlcache_content_callback, handle); + + entry->content = clone; + handle->entry = entry; entry->prev = NULL; entry->next = hlcache->content_list; if (hlcache->content_list != NULL) hlcache->content_list->prev = entry; hlcache->content_list = entry; - /* Signal to caller that we created a content */ - error = NSERROR_NEED_DATA; - - hlcache->miss_count++; - } else { - /* Found a suitable content: no longer need low-level handle */ - llcache_handle_release(ctx->llcache); - hlcache->hit_count++; + c = clone; } - /* Associate handle with content */ - if (content_add_user(entry->content, - hlcache_content_callback, ctx->handle) == false) - return NSERROR_NOMEM; - - /* Associate cache entry with handle */ - ctx->handle->entry = entry; - - /* Catch handle up with state of content */ - if (ctx->handle->cb != NULL) { - content_status status = content_get_status(ctx->handle); - - if (status == CONTENT_STATUS_LOADING) { - event.type = CONTENT_MSG_LOADING; - ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); - } else if (status == CONTENT_STATUS_READY) { - event.type = CONTENT_MSG_LOADING; - ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); - - if (ctx->handle->cb != NULL) { - event.type = CONTENT_MSG_READY; - ctx->handle->cb(ctx->handle, &event, - ctx->handle->pw); - } - } else if (status == CONTENT_STATUS_DONE) { - event.type = CONTENT_MSG_LOADING; - ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); + return content_abort(c); +} - if (ctx->handle->cb != NULL) { - event.type = CONTENT_MSG_READY; - ctx->handle->cb(ctx->handle, &event, - ctx->handle->pw); - } +/* See hlcache.h for documentation */ +nserror hlcache_handle_replace_callback(hlcache_handle *handle, + hlcache_handle_callback cb, void *pw) +{ + handle->cb = cb; + handle->pw = pw; - if (ctx->handle->cb != NULL) { - event.type = CONTENT_MSG_DONE; - ctx->handle->cb(ctx->handle, &event, - ctx->handle->pw); - } - } - } + return NSERROR_OK; +} - return error; +nserror hlcache_handle_clone(hlcache_handle *handle, hlcache_handle **result) +{ + *result = NULL; + return NSERROR_CLONE_FAILED; } -/** - * Veneer between content callback API and hlcache callback API - * - * \param c Content to emit message for - * \param msg Message to emit - * \param data Data for message - * \param pw Pointer to private data (hlcache_handle) - */ -void hlcache_content_callback(struct content *c, content_msg msg, - union content_msg_data data, void *pw) +/* See hlcache.h for documentation */ +nsurl *hlcache_handle_get_url(const hlcache_handle *handle) { - hlcache_handle *handle = pw; - hlcache_event event; - nserror error = NSERROR_OK; + nsurl *result = NULL; - event.type = msg; - event.data = data; + assert(handle != NULL); - if (handle->cb != NULL) - error = handle->cb(handle, &event, handle->pw); + if (handle->entry != NULL) { + result = content_get_url(handle->entry->content); + } else { + RING_ITERATE_START(struct hlcache_retrieval_ctx, + hlcache->retrieval_ctx_ring, + ictx) { + if (ictx->handle == handle) { + /* This is the nascent context for us */ + result = llcache_handle_get_url(ictx->llcache); - if (error != NSERROR_OK) - LOG(("Error in callback: %d", error)); + /* And stop */ + RING_ITERATE_STOP(hlcache->retrieval_ctx_ring, + ictx); + } + } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx); + } + + return result; } -- cgit v1.2.3