remove all forward declarations from hlcache.c
This commit is contained in:
parent
e43fbb3914
commit
977ae8efc6
|
@ -94,23 +94,426 @@ struct hlcache_s {
|
||||||
static struct hlcache_s *hlcache = NULL;
|
static struct hlcache_s *hlcache = NULL;
|
||||||
|
|
||||||
|
|
||||||
static void hlcache_clean(void *ignored);
|
/******************************************************************************
|
||||||
|
* High-level cache internals *
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
static nserror hlcache_llcache_callback(llcache_handle *handle,
|
|
||||||
const llcache_event *event, void *pw);
|
/**
|
||||||
static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
|
* Attempt to clean the cache
|
||||||
lwc_string *effective_type);
|
*/
|
||||||
|
static void hlcache_clean(void *ignored)
|
||||||
|
{
|
||||||
|
hlcache_entry *entry, *next;
|
||||||
|
|
||||||
|
for (entry = hlcache->content_list; entry != NULL; entry = next) {
|
||||||
|
next = entry->next;
|
||||||
|
|
||||||
|
if (entry->content == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (content__get_status(entry->content) ==
|
||||||
|
CONTENT_STATUS_LOADING)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (content_count_users(entry->content) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/** \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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Remove entry from cache */
|
||||||
|
if (entry->prev == NULL)
|
||||||
|
hlcache->content_list = entry->next;
|
||||||
|
else
|
||||||
|
entry->prev->next = entry->next;
|
||||||
|
|
||||||
|
if (entry->next != NULL)
|
||||||
|
entry->next->prev = entry->prev;
|
||||||
|
|
||||||
|
/* Destroy content */
|
||||||
|
content_destroy(entry->content);
|
||||||
|
|
||||||
|
/* Destroy entry */
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt to clean the llcache */
|
||||||
|
llcache_clean();
|
||||||
|
|
||||||
|
/* Re-schedule ourselves */
|
||||||
|
schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
static bool hlcache_type_is_acceptable(lwc_string *mime_type,
|
||||||
content_type accepted_types, content_type *computed_type);
|
content_type accepted_types, content_type *computed_type)
|
||||||
|
{
|
||||||
|
content_type type;
|
||||||
|
|
||||||
|
type = content_factory_type_from_mime_type(mime_type);
|
||||||
|
|
||||||
|
*computed_type = type;
|
||||||
|
|
||||||
|
return ((accepted_types & type) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
hlcache_handle *handle = pw;
|
||||||
|
hlcache_event event;
|
||||||
|
nserror error = NSERROR_OK;
|
||||||
|
|
||||||
|
event.type = msg;
|
||||||
|
event.data = data;
|
||||||
|
|
||||||
|
if (handle->cb != NULL)
|
||||||
|
error = handle->cb(handle, &event, handle->pw);
|
||||||
|
|
||||||
|
if (error != NSERROR_OK)
|
||||||
|
LOG(("Error in callback: %d", 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,
|
static nserror hlcache_find_content(hlcache_retrieval_ctx *ctx,
|
||||||
lwc_string *effective_type);
|
lwc_string *effective_type)
|
||||||
static void hlcache_content_callback(struct content *c,
|
{
|
||||||
content_msg msg, union content_msg_data data, void *pw);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for low-level cache events
|
||||||
|
*
|
||||||
|
* \param handle Handle for which event is issued
|
||||||
|
* \param event Event data
|
||||||
|
* \param pw Pointer to client-specific data
|
||||||
|
* \return NSERROR_OK on success, appropriate error otherwise
|
||||||
|
*/
|
||||||
|
static nserror hlcache_llcache_callback(llcache_handle *handle,
|
||||||
|
const llcache_event *event, void *pw)
|
||||||
|
{
|
||||||
|
hlcache_retrieval_ctx *ctx = pw;
|
||||||
|
lwc_string *effective_type = NULL;
|
||||||
|
nserror error;
|
||||||
|
|
||||||
|
assert(ctx->llcache == handle);
|
||||||
|
|
||||||
|
switch (event->type) {
|
||||||
|
case LLCACHE_EVENT_HAD_HEADERS:
|
||||||
|
error = mimesniff_compute_effective_type(handle, NULL, 0,
|
||||||
|
ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
|
||||||
|
ctx->accepted_types == CONTENT_IMAGE,
|
||||||
|
&effective_type);
|
||||||
|
if (error == NSERROR_OK || error == NSERROR_NOT_FOUND) {
|
||||||
|
/* If the sniffer was successful or failed to find
|
||||||
|
* a Content-Type header when sniffing was
|
||||||
|
* prohibited, we must migrate the retrieval context. */
|
||||||
|
error = hlcache_migrate_ctx(ctx, effective_type);
|
||||||
|
|
||||||
|
if (effective_type != NULL)
|
||||||
|
lwc_string_unref(effective_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No need to report that we need data:
|
||||||
|
* we'll get some anyway if there is any */
|
||||||
|
if (error == NSERROR_NEED_DATA)
|
||||||
|
error = NSERROR_OK;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case LLCACHE_EVENT_HAD_DATA:
|
||||||
|
error = mimesniff_compute_effective_type(handle,
|
||||||
|
event->data.data.buf, event->data.data.len,
|
||||||
|
ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
|
||||||
|
ctx->accepted_types == CONTENT_IMAGE,
|
||||||
|
&effective_type);
|
||||||
|
if (error != NSERROR_OK) {
|
||||||
|
assert(0 && "MIME sniff failed with data");
|
||||||
|
}
|
||||||
|
|
||||||
|
error = hlcache_migrate_ctx(ctx, effective_type);
|
||||||
|
|
||||||
|
lwc_string_unref(effective_type);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case LLCACHE_EVENT_DONE:
|
||||||
|
/* DONE event before we could determine the effective MIME type.
|
||||||
|
*/
|
||||||
|
error = mimesniff_compute_effective_type(handle,
|
||||||
|
NULL, 0, false, false, &effective_type);
|
||||||
|
if (error == NSERROR_OK) {
|
||||||
|
error = hlcache_migrate_ctx(ctx, effective_type);
|
||||||
|
|
||||||
|
lwc_string_unref(effective_type);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->handle->cb != NULL) {
|
||||||
|
hlcache_event hlevent;
|
||||||
|
|
||||||
|
hlevent.type = CONTENT_MSG_ERROR;
|
||||||
|
hlevent.data.error = messages_get("BadType");
|
||||||
|
|
||||||
|
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLCACHE_EVENT_ERROR:
|
||||||
|
if (ctx->handle->cb != NULL) {
|
||||||
|
hlcache_event hlevent;
|
||||||
|
|
||||||
|
hlevent.type = CONTENT_MSG_ERROR;
|
||||||
|
hlevent.data.error = event->data.msg;
|
||||||
|
|
||||||
|
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LLCACHE_EVENT_PROGRESS:
|
||||||
|
break;
|
||||||
|
case LLCACHE_EVENT_REDIRECT:
|
||||||
|
if (ctx->handle->cb != NULL) {
|
||||||
|
hlcache_event hlevent;
|
||||||
|
|
||||||
|
hlevent.type = CONTENT_MSG_REDIRECT;
|
||||||
|
hlevent.data.redirect.from = event->data.redirect.from;
|
||||||
|
hlevent.data.redirect.to = event->data.redirect.to;
|
||||||
|
|
||||||
|
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NSERROR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Public API *
|
* Public API *
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
nserror
|
nserror
|
||||||
hlcache_initialise(const struct hlcache_parameters *hlcache_parameters)
|
hlcache_initialise(const struct hlcache_parameters *hlcache_parameters)
|
||||||
{
|
{
|
||||||
|
@ -295,7 +698,7 @@ nserror hlcache_handle_release(hlcache_handle *handle)
|
||||||
RING_ITERATE_START(struct hlcache_retrieval_ctx,
|
RING_ITERATE_START(struct hlcache_retrieval_ctx,
|
||||||
hlcache->retrieval_ctx_ring,
|
hlcache->retrieval_ctx_ring,
|
||||||
ictx) {
|
ictx) {
|
||||||
if (ictx->handle == handle &&
|
if (ictx->handle == handle &&
|
||||||
ictx->migrate_target == false) {
|
ictx->migrate_target == false) {
|
||||||
/* This is the nascent context for us,
|
/* This is the nascent context for us,
|
||||||
* so abort the fetch */
|
* so abort the fetch */
|
||||||
|
@ -347,7 +750,7 @@ nserror hlcache_handle_abort(hlcache_handle *handle)
|
||||||
RING_ITERATE_START(struct hlcache_retrieval_ctx,
|
RING_ITERATE_START(struct hlcache_retrieval_ctx,
|
||||||
hlcache->retrieval_ctx_ring,
|
hlcache->retrieval_ctx_ring,
|
||||||
ictx) {
|
ictx) {
|
||||||
if (ictx->handle == handle &&
|
if (ictx->handle == handle &&
|
||||||
ictx->migrate_target == false) {
|
ictx->migrate_target == false) {
|
||||||
/* This is the nascent context for us,
|
/* This is the nascent context for us,
|
||||||
* so abort the fetch */
|
* so abort the fetch */
|
||||||
|
@ -448,416 +851,3 @@ nsurl *hlcache_handle_get_url(const hlcache_handle *handle)
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* High-level cache internals *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to clean the cache
|
|
||||||
*/
|
|
||||||
void hlcache_clean(void *ignored)
|
|
||||||
{
|
|
||||||
hlcache_entry *entry, *next;
|
|
||||||
|
|
||||||
for (entry = hlcache->content_list; entry != NULL; entry = next) {
|
|
||||||
next = entry->next;
|
|
||||||
|
|
||||||
if (entry->content == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (content__get_status(entry->content) ==
|
|
||||||
CONTENT_STATUS_LOADING)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (content_count_users(entry->content) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/** \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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Remove entry from cache */
|
|
||||||
if (entry->prev == NULL)
|
|
||||||
hlcache->content_list = entry->next;
|
|
||||||
else
|
|
||||||
entry->prev->next = entry->next;
|
|
||||||
|
|
||||||
if (entry->next != NULL)
|
|
||||||
entry->next->prev = entry->prev;
|
|
||||||
|
|
||||||
/* Destroy content */
|
|
||||||
content_destroy(entry->content);
|
|
||||||
|
|
||||||
/* Destroy entry */
|
|
||||||
free(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Attempt to clean the llcache */
|
|
||||||
llcache_clean();
|
|
||||||
|
|
||||||
/* Re-schedule ourselves */
|
|
||||||
schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for low-level cache events
|
|
||||||
*
|
|
||||||
* \param handle Handle for which event is issued
|
|
||||||
* \param event Event data
|
|
||||||
* \param pw Pointer to client-specific data
|
|
||||||
* \return NSERROR_OK on success, appropriate error otherwise
|
|
||||||
*/
|
|
||||||
nserror hlcache_llcache_callback(llcache_handle *handle,
|
|
||||||
const llcache_event *event, void *pw)
|
|
||||||
{
|
|
||||||
hlcache_retrieval_ctx *ctx = pw;
|
|
||||||
lwc_string *effective_type = NULL;
|
|
||||||
nserror error;
|
|
||||||
|
|
||||||
assert(ctx->llcache == handle);
|
|
||||||
|
|
||||||
switch (event->type) {
|
|
||||||
case LLCACHE_EVENT_HAD_HEADERS:
|
|
||||||
error = mimesniff_compute_effective_type(handle, NULL, 0,
|
|
||||||
ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
|
|
||||||
ctx->accepted_types == CONTENT_IMAGE,
|
|
||||||
&effective_type);
|
|
||||||
if (error == NSERROR_OK || error == NSERROR_NOT_FOUND) {
|
|
||||||
/* If the sniffer was successful or failed to find
|
|
||||||
* a Content-Type header when sniffing was
|
|
||||||
* prohibited, we must migrate the retrieval context. */
|
|
||||||
error = hlcache_migrate_ctx(ctx, effective_type);
|
|
||||||
|
|
||||||
if (effective_type != NULL)
|
|
||||||
lwc_string_unref(effective_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No need to report that we need data:
|
|
||||||
* we'll get some anyway if there is any */
|
|
||||||
if (error == NSERROR_NEED_DATA)
|
|
||||||
error = NSERROR_OK;
|
|
||||||
|
|
||||||
return error;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case LLCACHE_EVENT_HAD_DATA:
|
|
||||||
error = mimesniff_compute_effective_type(handle,
|
|
||||||
event->data.data.buf, event->data.data.len,
|
|
||||||
ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
|
|
||||||
ctx->accepted_types == CONTENT_IMAGE,
|
|
||||||
&effective_type);
|
|
||||||
if (error != NSERROR_OK) {
|
|
||||||
assert(0 && "MIME sniff failed with data");
|
|
||||||
}
|
|
||||||
|
|
||||||
error = hlcache_migrate_ctx(ctx, effective_type);
|
|
||||||
|
|
||||||
lwc_string_unref(effective_type);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case LLCACHE_EVENT_DONE:
|
|
||||||
/* DONE event before we could determine the effective MIME type.
|
|
||||||
*/
|
|
||||||
error = mimesniff_compute_effective_type(handle,
|
|
||||||
NULL, 0, false, false, &effective_type);
|
|
||||||
if (error == NSERROR_OK) {
|
|
||||||
error = hlcache_migrate_ctx(ctx, effective_type);
|
|
||||||
|
|
||||||
lwc_string_unref(effective_type);
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->handle->cb != NULL) {
|
|
||||||
hlcache_event hlevent;
|
|
||||||
|
|
||||||
hlevent.type = CONTENT_MSG_ERROR;
|
|
||||||
hlevent.data.error = messages_get("BadType");
|
|
||||||
|
|
||||||
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LLCACHE_EVENT_ERROR:
|
|
||||||
if (ctx->handle->cb != NULL) {
|
|
||||||
hlcache_event hlevent;
|
|
||||||
|
|
||||||
hlevent.type = CONTENT_MSG_ERROR;
|
|
||||||
hlevent.data.error = event->data.msg;
|
|
||||||
|
|
||||||
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LLCACHE_EVENT_PROGRESS:
|
|
||||||
break;
|
|
||||||
case LLCACHE_EVENT_REDIRECT:
|
|
||||||
if (ctx->handle->cb != NULL) {
|
|
||||||
hlcache_event hlevent;
|
|
||||||
|
|
||||||
hlevent.type = CONTENT_MSG_REDIRECT;
|
|
||||||
hlevent.data.redirect.from = event->data.redirect.from;
|
|
||||||
hlevent.data.redirect.to = event->data.redirect.to;
|
|
||||||
|
|
||||||
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
content_type type;
|
|
||||||
|
|
||||||
type = content_factory_type_from_mime_type(mime_type);
|
|
||||||
|
|
||||||
*computed_type = type;
|
|
||||||
|
|
||||||
return ((accepted_types & type) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
hlcache_handle *handle = pw;
|
|
||||||
hlcache_event event;
|
|
||||||
nserror error = NSERROR_OK;
|
|
||||||
|
|
||||||
event.type = msg;
|
|
||||||
event.data = data;
|
|
||||||
|
|
||||||
if (handle->cb != NULL)
|
|
||||||
error = handle->cb(handle, &event, handle->pw);
|
|
||||||
|
|
||||||
if (error != NSERROR_OK)
|
|
||||||
LOG(("Error in callback: %d", error));
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue