/* See hlcache.h for documentation */ nserror hlcache_handle_abort(hlcache_handle *handle) { struct hlcache_entry *entry = handle->entry; struct content *c; 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. */ 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); return NSERROR_OK; }
/* 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); } handle->cb = NULL; handle->pw = NULL; free(handle); return NSERROR_OK; }
/* See download.h for documentation */ void download_context_destroy(download_context *ctx) { llcache_handle_release(ctx->llcache); free(ctx->mime_type); /* Window is not owned by us, so don't attempt to destroy it */ free(ctx); }
/* See download.h for documentation */ void download_context_destroy(download_context *ctx) { llcache_handle_release(ctx->llcache); if (ctx->mime_type != NULL) lwc_string_unref(ctx->mime_type); free(ctx->filename); /* Window is not owned by us, so don't attempt to destroy it */ free(ctx); }
void content_destroy(struct content *c) { struct content_rfc5988_link *link; assert(c); LOG(("content %p %s", c, nsurl_access(llcache_handle_get_url(c->llcache)))); assert(c->locked == false); if (c->handler->destroy != NULL) c->handler->destroy(c); llcache_handle_release(c->llcache); c->llcache = NULL; lwc_string_unref(c->mime_type); /* release metadata links */ link = c->links; while (link != NULL) { link = content__free_rfc5988_link(link); } /* free the user list */ if (c->user_list != NULL) { free(c->user_list); } /* free the title */ if (c->title != NULL) { free(c->title); } /* free the fallback characterset */ if (c->fallback_charset != NULL) { free(c->fallback_charset); } free(c); }
/* See hlcache.h for documentation */ void hlcache_finalise(void) { 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 }; 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)); } } /* 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(); }
/** * 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 */ static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx, lwc_string *effective_type) { content_type type = CONTENT_NONE; nserror error = NSERROR_OK; ctx->migrate_target = true; 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; hlevent.type = CONTENT_MSG_ERROR; hlevent.data.error = messages_get("MiscError"); ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw); } 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; hlevent.type = CONTENT_MSG_DOWNLOAD; hlevent.data.download = ctx->llcache; ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw); } /* Ensure caller knows we need data */ error = NSERROR_NEED_DATA; } else { /* Unacceptable type: report error */ if (ctx->handle->cb != NULL) { hlcache_event hlevent; 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); } ctx->migrate_target = false; /* No longer require retrieval context */ RING_REMOVE(hlcache->retrieval_ctx_ring, ctx); free((char *) ctx->child.charset); free(ctx); return error; }
/** * 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) { hlcache_entry *entry; hlcache_event event; nserror error = NSERROR_OK; /* 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->content == NULL) continue; /* Ignore contents in the error state */ if (content_get_status(&entry_handle) == CONTENT_STATUS_ERROR) continue; /* Ensure that content is shareable */ if (content_is_shareable(entry->content) == false) continue; /* Ensure that quirks mode is acceptable */ if (content_matches_quirks(entry->content, ctx->child.quirks) == false) continue; /* Ensure that content uses same low-level object as * low-level handle */ entry_llcache = content_get_llcache_handle(entry->content); if (llcache_handle_references_same_object(entry_llcache, ctx->llcache)) break; } if (entry == NULL) { /* No existing entry, so need to create one */ entry = malloc(sizeof(hlcache_entry)); if (entry == NULL) 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) { free(entry); return NSERROR_NOMEM; } /* 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; /* 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++; } /* 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); if (ctx->handle->cb != NULL) { event.type = CONTENT_MSG_READY; ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); } if (ctx->handle->cb != NULL) { event.type = CONTENT_MSG_DONE; ctx->handle->cb(ctx->handle, &event, ctx->handle->pw); } } } return error; }