Subversion Repositories Kolibri OS

Rev

Rev 4364 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright 2009 John-Mark Bell <jmb@netsurf-browser.org>
  3.  *
  4.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  5.  *
  6.  * NetSurf is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /** \file
  20.  * High-level resource cache (implementation)
  21.  */
  22.  
  23. #include <assert.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26.  
  27. #include "content/content.h"
  28. #include "content/hlcache.h"
  29. #include "content/mimesniff.h"
  30. #include "utils/http.h"
  31. #include "utils/log.h"
  32. #include "utils/messages.h"
  33. #include "utils/ring.h"
  34. #include "utils/schedule.h"
  35. #include "utils/url.h"
  36. #include "utils/utils.h"
  37.  
  38. typedef struct hlcache_entry hlcache_entry;
  39. typedef struct hlcache_retrieval_ctx hlcache_retrieval_ctx;
  40.  
  41. /** High-level cache retrieval context */
  42. struct hlcache_retrieval_ctx {
  43.         struct hlcache_retrieval_ctx *r_prev; /**< Previous retrieval context in the ring */
  44.         struct hlcache_retrieval_ctx *r_next; /**< Next retrieval context in the ring */
  45.  
  46.         llcache_handle *llcache;        /**< Low-level cache handle */
  47.  
  48.         hlcache_handle *handle;         /**< High-level handle for object */
  49.  
  50.         uint32_t flags;                 /**< Retrieval flags */
  51.  
  52.         content_type accepted_types;    /**< Accepted types */
  53.  
  54.         hlcache_child_context child;    /**< Child context */
  55.  
  56.         bool migrate_target;            /**< Whether this context is the migration target */
  57. };
  58.  
  59. /** High-level cache handle */
  60. struct hlcache_handle {
  61.         hlcache_entry *entry;           /**< Pointer to cache entry */
  62.  
  63.         hlcache_handle_callback cb;     /**< Client callback */
  64.         void *pw;                       /**< Client data */
  65. };
  66.  
  67. /** Entry in high-level cache */
  68. struct hlcache_entry {
  69.         struct content *content;        /**< Pointer to associated content */
  70.  
  71.         hlcache_entry *next;            /**< Next sibling */
  72.         hlcache_entry *prev;            /**< Previous sibling */
  73. };
  74.  
  75. /** Current state of the cache.
  76.  *
  77.  * Global state of the cache.
  78.  */
  79. struct hlcache_s {
  80.         struct hlcache_parameters params;
  81.  
  82.         /** List of cached content objects */
  83.         hlcache_entry *content_list;
  84.  
  85.         /** Ring of retrieval contexts */
  86.         hlcache_retrieval_ctx *retrieval_ctx_ring;
  87.  
  88.         /* statsistics */
  89.         unsigned int hit_count;
  90.         unsigned int miss_count;
  91. };
  92.  
  93. /** high level cache state */
  94. static struct hlcache_s *hlcache = NULL;
  95.  
  96.  
  97. static void hlcache_clean(void *ignored);
  98.  
  99. static nserror hlcache_llcache_callback(llcache_handle *handle,
  100.                 const llcache_event *event, void *pw);
  101. static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
  102.                 lwc_string *effective_type);
  103. static bool hlcache_type_is_acceptable(lwc_string *mime_type,
  104.                 content_type accepted_types, content_type *computed_type);
  105. static nserror hlcache_find_content(hlcache_retrieval_ctx *ctx,
  106.                 lwc_string *effective_type);
  107. static void hlcache_content_callback(struct content *c,
  108.                 content_msg msg, union content_msg_data data, void *pw);
  109.  
  110. /******************************************************************************
  111.  * Public API                                                                 *
  112.  ******************************************************************************/
  113.  
  114. nserror
  115. hlcache_initialise(const struct hlcache_parameters *hlcache_parameters)
  116. {
  117.         nserror ret;
  118.  
  119.         hlcache = calloc(1, sizeof(struct hlcache_s));
  120.         if (hlcache == NULL) {
  121.                 return NSERROR_NOMEM;
  122.         }
  123.  
  124.         ret = llcache_initialise(hlcache_parameters->cb,
  125.                                  hlcache_parameters->cb_ctx,
  126.                                  hlcache_parameters->limit);
  127.         if (ret != NSERROR_OK) {
  128.                 free(hlcache);
  129.                 hlcache = NULL;
  130.                 return ret;
  131.         }
  132.  
  133.         hlcache->params = *hlcache_parameters;
  134.  
  135.         /* Schedule the cache cleanup */
  136.         schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL);
  137.  
  138.         return NSERROR_OK;
  139. }
  140.  
  141. /* See hlcache.h for documentation */
  142. void hlcache_stop(void)
  143. {
  144.         /* Remove the hlcache_clean schedule */
  145.         schedule_remove(hlcache_clean, NULL);
  146. }
  147.  
  148. /* See hlcache.h for documentation */
  149. void hlcache_finalise(void)
  150. {
  151.         uint32_t num_contents, prev_contents;
  152.         hlcache_entry *entry;
  153.         hlcache_retrieval_ctx *ctx, *next;
  154.  
  155.         /* Obtain initial count of contents remaining */
  156.         for (num_contents = 0, entry = hlcache->content_list;
  157.                         entry != NULL; entry = entry->next) {
  158.                 num_contents++;
  159.         }
  160.  
  161.         LOG(("%d contents remain before cache drain", num_contents));
  162.  
  163.         /* Drain cache */
  164.         do {
  165.                 prev_contents = num_contents;
  166.  
  167.                 hlcache_clean(NULL);
  168.  
  169.                 for (num_contents = 0, entry = hlcache->content_list;
  170.                                 entry != NULL; entry = entry->next) {
  171.                         num_contents++;
  172.                 }
  173.         } while (num_contents > 0 && num_contents != prev_contents);
  174.  
  175.         LOG(("%d contents remaining:", num_contents));
  176.         for (entry = hlcache->content_list; entry != NULL; entry = entry->next) {
  177.                 hlcache_handle entry_handle = { entry, NULL, NULL };
  178.  
  179.                 if (entry->content != NULL) {
  180.                         LOG(("  %p : %s (%d users)", entry,
  181.                              nsurl_access(hlcache_handle_get_url(&entry_handle)), content_count_users(entry->content)));
  182.                 } else {
  183.                         LOG(("  %p", entry));
  184.                 }
  185.         }
  186.  
  187.         /* Clean up retrieval contexts */
  188.         if (hlcache->retrieval_ctx_ring != NULL) {
  189.                 ctx = hlcache->retrieval_ctx_ring;
  190.  
  191.                 do {
  192.                         next = ctx->r_next;
  193.  
  194.                         if (ctx->llcache != NULL)
  195.                                 llcache_handle_release(ctx->llcache);
  196.  
  197.                         if (ctx->handle != NULL)
  198.                                 free(ctx->handle);
  199.  
  200.                         if (ctx->child.charset != NULL)
  201.                                 free((char *) ctx->child.charset);
  202.  
  203.                         free(ctx);
  204.  
  205.                         ctx = next;
  206.                 } while (ctx != hlcache->retrieval_ctx_ring);
  207.  
  208.                 hlcache->retrieval_ctx_ring = NULL;
  209.         }
  210.  
  211.         LOG(("hit/miss %d/%d", hlcache->hit_count, hlcache->miss_count));
  212.  
  213.         free(hlcache);
  214.         hlcache = NULL;
  215.  
  216.         LOG(("Finalising low-level cache"));
  217.         llcache_finalise();
  218. }
  219.  
  220. /* See hlcache.h for documentation */
  221. nserror hlcache_poll(void)
  222. {
  223.         llcache_poll();
  224.  
  225.         return NSERROR_OK;
  226. }
  227.  
  228. /* See hlcache.h for documentation */
  229. nserror hlcache_handle_retrieve(nsurl *url, uint32_t flags,
  230.                 nsurl *referer, llcache_post_data *post,
  231.                 hlcache_handle_callback cb, void *pw,
  232.                 hlcache_child_context *child,
  233.                 content_type accepted_types, hlcache_handle **result)
  234. {
  235.         hlcache_retrieval_ctx *ctx;
  236.         nserror error;
  237.  
  238.         assert(cb != NULL);
  239.  
  240.         ctx = calloc(1, sizeof(hlcache_retrieval_ctx));
  241.         if (ctx == NULL)
  242.                 return NSERROR_NOMEM;
  243.  
  244.         ctx->handle = calloc(1, sizeof(hlcache_handle));
  245.         if (ctx->handle == NULL) {
  246.                 free(ctx);
  247.                 return NSERROR_NOMEM;
  248.         }
  249.  
  250.         if (child != NULL) {
  251.                 if (child->charset != NULL) {
  252.                         ctx->child.charset = strdup(child->charset);
  253.                         if (ctx->child.charset == NULL) {
  254.                                 free(ctx->handle);
  255.                                 free(ctx);
  256.                                 return NSERROR_NOMEM;
  257.                         }
  258.                 }
  259.                 ctx->child.quirks = child->quirks;
  260.         }
  261.  
  262.         ctx->flags = flags;
  263.         ctx->accepted_types = accepted_types;
  264.  
  265.         ctx->handle->cb = cb;
  266.         ctx->handle->pw = pw;
  267.  
  268.         error = llcache_handle_retrieve(url, flags, referer, post,
  269.                         hlcache_llcache_callback, ctx,
  270.                         &ctx->llcache);
  271.         if (error != NSERROR_OK) {
  272.                 free((char *) ctx->child.charset);
  273.                 free(ctx->handle);
  274.                 free(ctx);
  275.                 return error;
  276.         }
  277.  
  278.         RING_INSERT(hlcache->retrieval_ctx_ring, ctx);
  279.  
  280.         *result = ctx->handle;
  281.  
  282.         return NSERROR_OK;
  283. }
  284.  
  285. /* See hlcache.h for documentation */
  286. nserror hlcache_handle_release(hlcache_handle *handle)
  287. {
  288.         if (handle->entry != NULL) {
  289.                 content_remove_user(handle->entry->content,
  290.                                 hlcache_content_callback, handle);
  291.         } else {
  292.                 RING_ITERATE_START(struct hlcache_retrieval_ctx,
  293.                                    hlcache->retrieval_ctx_ring,
  294.                                    ictx) {
  295.                         if (ictx->handle == handle &&
  296.                                         ictx->migrate_target == false) {
  297.                                 /* This is the nascent context for us,
  298.                                  * so abort the fetch */
  299.                                 llcache_handle_abort(ictx->llcache);
  300.                                 llcache_handle_release(ictx->llcache);
  301.                                 /* Remove us from the ring */
  302.                                 RING_REMOVE(hlcache->retrieval_ctx_ring, ictx);
  303.                                 /* Throw us away */
  304.                                 free((char *) ictx->child.charset);
  305.                                 free(ictx);
  306.                                 /* And stop */
  307.                                 RING_ITERATE_STOP(hlcache->retrieval_ctx_ring,
  308.                                                 ictx);
  309.                         }
  310.                 } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx);
  311.         }
  312.  
  313.         handle->cb = NULL;
  314.         handle->pw = NULL;
  315.  
  316.         free(handle);
  317.  
  318.         return NSERROR_OK;
  319. }
  320.  
  321. /* See hlcache.h for documentation */
  322. struct content *hlcache_handle_get_content(const hlcache_handle *handle)
  323. {
  324.         assert(handle != NULL);
  325.  
  326.         if (handle->entry != NULL)
  327.                 return handle->entry->content;
  328.  
  329.         return NULL;
  330. }
  331.  
  332. /* See hlcache.h for documentation */
  333. nserror hlcache_handle_abort(hlcache_handle *handle)
  334. {
  335.         struct hlcache_entry *entry = handle->entry;
  336.         struct content *c;
  337.  
  338.         if (entry == NULL) {
  339.                 /* This handle is not yet associated with a cache entry.
  340.                  * The implication is that the fetch for the handle has
  341.                  * not progressed to the point where the entry can be
  342.                  * created. */
  343.  
  344.                 RING_ITERATE_START(struct hlcache_retrieval_ctx,
  345.                                    hlcache->retrieval_ctx_ring,
  346.                                    ictx) {
  347.                         if (ictx->handle == handle &&
  348.                                         ictx->migrate_target == false) {
  349.                                 /* This is the nascent context for us,
  350.                                  * so abort the fetch */
  351.                                 llcache_handle_abort(ictx->llcache);
  352.                                 llcache_handle_release(ictx->llcache);
  353.                                 /* Remove us from the ring */
  354.                                 RING_REMOVE(hlcache->retrieval_ctx_ring, ictx);
  355.                                 /* Throw us away */
  356.                                 free((char *) ictx->child.charset);
  357.                                 free(ictx);
  358.                                 /* And stop */
  359.                                 RING_ITERATE_STOP(hlcache->retrieval_ctx_ring,
  360.                                                 ictx);
  361.                         }
  362.                 } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx);
  363.  
  364.                 return NSERROR_OK;
  365.         }
  366.  
  367.         c = entry->content;
  368.  
  369.         if (content_count_users(c) > 1) {
  370.                 /* We are not the only user of 'c' so clone it. */
  371.                 struct content *clone = content_clone(c);
  372.  
  373.                 if (clone == NULL)
  374.                         return NSERROR_NOMEM;
  375.  
  376.                 entry = calloc(sizeof(struct hlcache_entry), 1);
  377.  
  378.                 if (entry == NULL) {
  379.                         content_destroy(clone);
  380.                         return NSERROR_NOMEM;
  381.                 }
  382.  
  383.                 if (content_add_user(clone,
  384.                                 hlcache_content_callback, handle) == false) {
  385.                         content_destroy(clone);
  386.                         free(entry);
  387.                         return NSERROR_NOMEM;
  388.                 }
  389.  
  390.                 content_remove_user(c, hlcache_content_callback, handle);
  391.  
  392.                 entry->content = clone;
  393.                 handle->entry = entry;
  394.                 entry->prev = NULL;
  395.                 entry->next = hlcache->content_list;
  396.                 if (hlcache->content_list != NULL)
  397.                         hlcache->content_list->prev = entry;
  398.                 hlcache->content_list = entry;
  399.  
  400.                 c = clone;
  401.         }
  402.  
  403.         return content_abort(c);
  404. }
  405.  
  406. /* See hlcache.h for documentation */
  407. nserror hlcache_handle_replace_callback(hlcache_handle *handle,
  408.                 hlcache_handle_callback cb, void *pw)
  409. {
  410.         handle->cb = cb;
  411.         handle->pw = pw;
  412.  
  413.         return NSERROR_OK;
  414. }
  415.  
  416. nserror hlcache_handle_clone(hlcache_handle *handle, hlcache_handle **result)
  417. {
  418.         *result = NULL;
  419.         return NSERROR_CLONE_FAILED;
  420. }
  421.  
  422. /* See hlcache.h for documentation */
  423. nsurl *hlcache_handle_get_url(const hlcache_handle *handle)
  424. {
  425.         nsurl *result = NULL;
  426.  
  427.         assert(handle != NULL);
  428.  
  429.         if (handle->entry != NULL) {
  430.                 result = content_get_url(handle->entry->content);
  431.         } else {
  432.                 RING_ITERATE_START(struct hlcache_retrieval_ctx,
  433.                                    hlcache->retrieval_ctx_ring,
  434.                                    ictx) {
  435.                         if (ictx->handle == handle) {
  436.                                 /* This is the nascent context for us */
  437.                                 result = llcache_handle_get_url(ictx->llcache);
  438.  
  439.                                 /* And stop */
  440.                                 RING_ITERATE_STOP(hlcache->retrieval_ctx_ring,
  441.                                                 ictx);
  442.                         }
  443.                 } RING_ITERATE_END(hlcache->retrieval_ctx_ring, ictx);
  444.         }
  445.  
  446.         return result;
  447. }
  448.  
  449. /******************************************************************************
  450.  * High-level cache internals                                                 *
  451.  ******************************************************************************/
  452.  
  453. /**
  454.  * Attempt to clean the cache
  455.  */
  456. void hlcache_clean(void *ignored)
  457. {
  458.         hlcache_entry *entry, *next;
  459.  
  460.         for (entry = hlcache->content_list; entry != NULL; entry = next) {
  461.                 next = entry->next;
  462.  
  463.                 if (entry->content == NULL)
  464.                         continue;
  465.  
  466.                 if (content__get_status(entry->content) ==
  467.                                 CONTENT_STATUS_LOADING)
  468.                         continue;
  469.  
  470.                 if (content_count_users(entry->content) != 0)
  471.                         continue;
  472.  
  473.                 /** \todo This is over-zealous: all unused contents
  474.                  * will be immediately destroyed. Ideally, we want to
  475.                  * purge all unused contents that are using stale
  476.                  * source data, and enough fresh contents such that
  477.                  * the cache fits in the configured cache size limit.
  478.                  */
  479.  
  480.                 /* Remove entry from cache */
  481.                 if (entry->prev == NULL)
  482.                         hlcache->content_list = entry->next;
  483.                 else
  484.                         entry->prev->next = entry->next;
  485.  
  486.                 if (entry->next != NULL)
  487.                         entry->next->prev = entry->prev;
  488.  
  489.                 /* Destroy content */
  490.                 content_destroy(entry->content);
  491.  
  492.                 /* Destroy entry */
  493.                 free(entry);
  494.         }
  495.  
  496.         /* Attempt to clean the llcache */
  497.         llcache_clean();
  498.  
  499.         /* Re-schedule ourselves */
  500.         schedule(hlcache->params.bg_clean_time / 10, hlcache_clean, NULL);
  501. }
  502.  
  503. /**
  504.  * Handler for low-level cache events
  505.  *
  506.  * \param handle  Handle for which event is issued
  507.  * \param event   Event data
  508.  * \param pw      Pointer to client-specific data
  509.  * \return NSERROR_OK on success, appropriate error otherwise
  510.  */
  511. nserror hlcache_llcache_callback(llcache_handle *handle,
  512.                 const llcache_event *event, void *pw)
  513. {
  514.         hlcache_retrieval_ctx *ctx = pw;
  515.         lwc_string *effective_type = NULL;
  516.         nserror error;
  517.         /* LOG(("Asserting ctx->llcache == handle")); */
  518.         assert(ctx->llcache == handle);
  519.         /* LOG(("After Asserting ctx->llcache == handle")); */
  520.  
  521.         switch (event->type) {
  522.         case LLCACHE_EVENT_HAD_HEADERS:
  523.           /* LOG(("LLCACHE_EVENT_HAD_HEADERS")); */
  524.                 error = mimesniff_compute_effective_type(handle, NULL, 0,
  525.                                 ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
  526.                                 ctx->accepted_types == CONTENT_IMAGE,
  527.                                 &effective_type);
  528.                 if (error == NSERROR_OK || error == NSERROR_NOT_FOUND) {
  529.                         /* If the sniffer was successful or failed to find
  530.                          * a Content-Type header when sniffing was
  531.                          * prohibited, we must migrate the retrieval context. */
  532.                   /* LOG(("hlcache_migraet_ctx from LLCACHE_HAD_HEADERS")); */
  533.                         error = hlcache_migrate_ctx(ctx, effective_type);
  534.  
  535.                         if (effective_type != NULL)
  536.                                 lwc_string_unref(effective_type);
  537.                 }
  538.  
  539.                 /* No need to report that we need data:
  540.                  * we'll get some anyway if there is any */
  541.                 if (error == NSERROR_NEED_DATA)
  542.                         error = NSERROR_OK;
  543.  
  544.                 return error;
  545.  
  546.                 break;
  547.         case LLCACHE_EVENT_HAD_DATA:
  548.                   /* LOG(("LLCACHE_EVENT_HAD_DATA")); */
  549.                 error = mimesniff_compute_effective_type(handle,
  550.                                 event->data.data.buf, event->data.data.len,
  551.                                 ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE,
  552.                                 ctx->accepted_types == CONTENT_IMAGE,
  553.                                 &effective_type);
  554.                 if (error != NSERROR_OK) {
  555.                         assert(0 && "MIME sniff failed with data");
  556.                 }
  557.                 /* LOG(("hlcache_migraet_ctx from LLCACHE_HAD_DATA")); */
  558.                 error = hlcache_migrate_ctx(ctx, effective_type);
  559.  
  560.                 lwc_string_unref(effective_type);
  561.  
  562.                 return error;
  563.  
  564.                 break;
  565.         case LLCACHE_EVENT_DONE:
  566.                   /* LOG(("LLCACHE_EVENT_DONE")); */
  567.                 /* DONE event before we could determine the effective MIME type.
  568.                  */
  569.                 error = mimesniff_compute_effective_type(handle,
  570.                                 NULL, 0, false, false, &effective_type);
  571.                 if (error == NSERROR_OK) {
  572.                   /* LOG(("hlcache_migrate_ctx in LLCACHE_EVENT_DONE")); */
  573.                         error = hlcache_migrate_ctx(ctx, effective_type);
  574.  
  575.                         lwc_string_unref(effective_type);
  576.  
  577.                         return error;
  578.                 }
  579.  
  580.                 if (ctx->handle->cb != NULL) {
  581.                         hlcache_event hlevent;
  582.                        
  583.                         hlevent.type = CONTENT_MSG_ERROR;
  584.                         hlevent.data.error = messages_get("BadType");
  585.                         ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
  586.                 }
  587.                 break;
  588.         case LLCACHE_EVENT_ERROR:
  589.           /* LOG(("LLCACHE_EVENT_ERROR")); */
  590.                 if (ctx->handle->cb != NULL) {
  591.                         hlcache_event hlevent;
  592.  
  593.                         hlevent.type = CONTENT_MSG_ERROR;
  594.                         hlevent.data.error = event->data.msg;
  595.  
  596.                         ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
  597.                 }
  598.                 break;         
  599.         case LLCACHE_EVENT_PROGRESS:
  600.                   /* LOG(("LLCACHE_EVENT_PROGRESS")); */
  601.                 break;
  602.         }
  603.         /* LOG(("Returning OK from hlcache_llcache_callback")); */
  604.         return NSERROR_OK;
  605. }
  606.  
  607. /**
  608.  * Migrate a retrieval context into its final destination content
  609.  *
  610.  * \param ctx             Context to migrate
  611.  * \param effective_type  The effective MIME type of the content, or NULL
  612.  * \return NSERROR_OK on success,
  613.  *         NSERROR_NEED_DATA on success where data is needed,
  614.  *         appropriate error otherwise
  615.  */
  616. nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
  617.                 lwc_string *effective_type)
  618. {
  619.         content_type type = CONTENT_NONE;
  620.         nserror error = NSERROR_OK;
  621.  
  622.         ctx->migrate_target = true;
  623.  
  624.         if (effective_type != NULL &&
  625.                         hlcache_type_is_acceptable(effective_type,
  626.                         ctx->accepted_types, &type)) {
  627.                 error = hlcache_find_content(ctx, effective_type);
  628.                 if (error != NSERROR_OK && error != NSERROR_NEED_DATA) {
  629.                         if (ctx->handle->cb != NULL) {
  630.                                 hlcache_event hlevent;
  631.  
  632.                                 hlevent.type = CONTENT_MSG_ERROR;
  633.                                 hlevent.data.error = messages_get("MiscError");
  634.  
  635.                                 ctx->handle->cb(ctx->handle, &hlevent,
  636.                                                 ctx->handle->pw);
  637.                         }
  638.  
  639.                         llcache_handle_abort(ctx->llcache);
  640.                         llcache_handle_release(ctx->llcache);
  641.                 }
  642.         } else if (type == CONTENT_NONE &&
  643.                         (ctx->flags & HLCACHE_RETRIEVE_MAY_DOWNLOAD)) {
  644.                 /* Unknown type, and we can download, so convert */
  645.                 llcache_handle_force_stream(ctx->llcache);
  646.  
  647.                 if (ctx->handle->cb != NULL) {
  648.                         hlcache_event hlevent;
  649.  
  650.                         hlevent.type = CONTENT_MSG_DOWNLOAD;
  651.                         hlevent.data.download = ctx->llcache;
  652.  
  653.                         ctx->handle->cb(ctx->handle, &hlevent,
  654.                                         ctx->handle->pw);
  655.                 }
  656.  
  657.                 /* Ensure caller knows we need data */
  658.                 error = NSERROR_NEED_DATA;
  659.         } else {
  660.                 /* Unacceptable type: report error */
  661.                 if (ctx->handle->cb != NULL) {
  662.                         hlcache_event hlevent;
  663.                         /* LOG(("Unacceptable Type! CONTENT_MSG_ERROR set")); */
  664.                         hlevent.type = CONTENT_MSG_ERROR;
  665.                         hlevent.data.error = messages_get("UnacceptableType");
  666.  
  667.                         ctx->handle->cb(ctx->handle, &hlevent,
  668.                                         ctx->handle->pw);
  669.                 }
  670.  
  671.                 llcache_handle_abort(ctx->llcache);
  672.                 llcache_handle_release(ctx->llcache);
  673.         }
  674.  
  675.         ctx->migrate_target = false;
  676.  
  677.         /* No longer require retrieval context */
  678.         RING_REMOVE(hlcache->retrieval_ctx_ring, ctx);
  679.         free((char *) ctx->child.charset);
  680.         free(ctx);
  681.  
  682.         return error;
  683. }
  684.  
  685. /**
  686.  * Determine if the specified MIME type is acceptable
  687.  *
  688.  * \param mime_type       MIME type to consider
  689.  * \param accepted_types  Array of acceptable types, or NULL for any
  690.  * \param computed_type   Pointer to location to receive computed type of object
  691.  * \return True if the type is acceptable, false otherwise
  692.  */
  693. bool hlcache_type_is_acceptable(lwc_string *mime_type,
  694.                 content_type accepted_types, content_type *computed_type)
  695. {
  696.         content_type type;
  697.  
  698.         type = content_factory_type_from_mime_type(mime_type);
  699.  
  700.         *computed_type = type;
  701.  
  702.         return ((accepted_types & type) != 0);
  703. }
  704.  
  705. /**
  706.  * Find a content for the high-level cache handle
  707.  *
  708.  * \param ctx             High-level cache retrieval context
  709.  * \param effective_type  Effective MIME type of content
  710.  * \return NSERROR_OK on success,
  711.  *         NSERROR_NEED_DATA on success where data is needed,
  712.  *         appropriate error otherwise
  713.  *
  714.  * \pre handle::state == HLCACHE_HANDLE_NEW
  715.  * \pre Headers must have been received for associated low-level handle
  716.  * \post Low-level handle is either released, or associated with new content
  717.  * \post High-level handle is registered with content
  718.  */
  719. nserror hlcache_find_content(hlcache_retrieval_ctx *ctx,
  720.                 lwc_string *effective_type)
  721. {
  722.         hlcache_entry *entry;
  723.         hlcache_event event;
  724.         nserror error = NSERROR_OK;
  725.  
  726.         /* Search list of cached contents for a suitable one */
  727.         for (entry = hlcache->content_list; entry != NULL; entry = entry->next) {
  728.                 hlcache_handle entry_handle = { entry, NULL, NULL };
  729.                 const llcache_handle *entry_llcache;
  730.  
  731.                 if (entry->content == NULL)
  732.                         continue;
  733.  
  734.                 /* Ignore contents in the error state */
  735.                 if (content_get_status(&entry_handle) == CONTENT_STATUS_ERROR)
  736.                         continue;
  737.  
  738.                 /* Ensure that content is shareable */
  739.                 if (content_is_shareable(entry->content) == false)
  740.                         continue;
  741.  
  742.                 /* Ensure that quirks mode is acceptable */
  743.                 if (content_matches_quirks(entry->content,
  744.                                 ctx->child.quirks) == false)
  745.                         continue;
  746.  
  747.                 /* Ensure that content uses same low-level object as
  748.                  * low-level handle */
  749.                 entry_llcache = content_get_llcache_handle(entry->content);
  750.  
  751.                 if (llcache_handle_references_same_object(entry_llcache,
  752.                                 ctx->llcache))
  753.                         break;
  754.         }
  755.  
  756.         if (entry == NULL) {
  757.                 /* No existing entry, so need to create one */
  758.                 entry = malloc(sizeof(hlcache_entry));
  759.                 if (entry == NULL)
  760.                         return NSERROR_NOMEM;
  761.  
  762.                 /* Create content using llhandle */
  763.                 entry->content = content_factory_create_content(ctx->llcache,
  764.                                 ctx->child.charset, ctx->child.quirks,
  765.                                 effective_type);
  766.                 if (entry->content == NULL) {
  767.                         free(entry);
  768.                         return NSERROR_NOMEM;
  769.                 }
  770.  
  771.                 /* Insert into cache */
  772.                 entry->prev = NULL;
  773.                 entry->next = hlcache->content_list;
  774.                 if (hlcache->content_list != NULL)
  775.                         hlcache->content_list->prev = entry;
  776.                 hlcache->content_list = entry;
  777.  
  778.                 /* Signal to caller that we created a content */
  779.                 error = NSERROR_NEED_DATA;
  780.  
  781.                 hlcache->miss_count++;
  782.         } else {
  783.                 /* Found a suitable content: no longer need low-level handle */
  784.                 llcache_handle_release(ctx->llcache);
  785.                 hlcache->hit_count++;
  786.         }
  787.  
  788.         /* Associate handle with content */
  789.         if (content_add_user(entry->content,
  790.                         hlcache_content_callback, ctx->handle) == false)
  791.                 return NSERROR_NOMEM;
  792.  
  793.         /* Associate cache entry with handle */
  794.         ctx->handle->entry = entry;
  795.  
  796.         /* Catch handle up with state of content */
  797.         if (ctx->handle->cb != NULL) {
  798.                 content_status status = content_get_status(ctx->handle);
  799.  
  800.                 if (status == CONTENT_STATUS_LOADING) {
  801.                         event.type = CONTENT_MSG_LOADING;
  802.                         ctx->handle->cb(ctx->handle, &event, ctx->handle->pw);
  803.                 } else if (status == CONTENT_STATUS_READY) {
  804.                         event.type = CONTENT_MSG_LOADING;
  805.                         ctx->handle->cb(ctx->handle, &event, ctx->handle->pw);
  806.  
  807.                         if (ctx->handle->cb != NULL) {
  808.                                 event.type = CONTENT_MSG_READY;
  809.                                 ctx->handle->cb(ctx->handle, &event,
  810.                                                 ctx->handle->pw);
  811.                         }
  812.                 } else if (status == CONTENT_STATUS_DONE) {
  813.                         event.type = CONTENT_MSG_LOADING;
  814.                         ctx->handle->cb(ctx->handle, &event, ctx->handle->pw);
  815.  
  816.                         if (ctx->handle->cb != NULL) {
  817.                                 event.type = CONTENT_MSG_READY;
  818.                                 ctx->handle->cb(ctx->handle, &event,
  819.                                                 ctx->handle->pw);
  820.                         }
  821.  
  822.                         if (ctx->handle->cb != NULL) {
  823.                           /* LOG(("Calling with CONTENT_MSG_DONE")); */
  824.                                 event.type = CONTENT_MSG_DONE;
  825.                                 ctx->handle->cb(ctx->handle, &event,
  826.                                                 ctx->handle->pw);
  827.                         }
  828.                 }
  829.         }
  830.         /* LOG(("Returning from "));  */
  831.         return error;
  832. }
  833.  
  834. /**
  835.  * Veneer between content callback API and hlcache callback API
  836.  *
  837.  * \param c     Content to emit message for
  838.  * \param msg   Message to emit
  839.  * \param data  Data for message
  840.  * \param pw    Pointer to private data (hlcache_handle)
  841.  */
  842. void hlcache_content_callback(struct content *c, content_msg msg,
  843.                 union content_msg_data data, void *pw)
  844. {
  845.         hlcache_handle *handle = pw;
  846.         hlcache_event event;
  847.         nserror error = NSERROR_OK;
  848.  
  849.         event.type = msg;
  850.         event.data = data;
  851.  
  852.         if (handle->cb != NULL)
  853.                 error = handle->cb(handle, &event, handle->pw);
  854.  
  855.         if (error != NSERROR_OK)
  856.                 LOG(("Error in callback: %d", error));
  857. }
  858.