Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2005-2007 James Bursa <bursa@users.sourceforge.net>
  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.  * Content handling (implementation).
  21.  *
  22.  * This implementation is based on the ::handler_map array, which maps
  23.  * ::content_type to the functions which implement that type.
  24.  */
  25.  
  26. #include <assert.h>
  27. #include <inttypes.h>
  28. #include <stdarg.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <strings.h>
  33. #include <time.h>
  34. #include "utils/config.h"
  35. #include "content/content_protected.h"
  36. #include "content/hlcache.h"
  37. #include "css/css.h"
  38. #include "image/bitmap.h"
  39. #include "desktop/browser.h"
  40. #include "desktop/options.h"
  41. #include "render/html.h"
  42. #include "render/textplain.h"
  43.  
  44. #include "utils/http.h"
  45. #include "utils/log.h"
  46. #include "utils/messages.h"
  47. #include "utils/utils.h"
  48.  
  49. #define URL_FMT_SPC "%.140s"
  50.  
  51. const char * const content_status_name[] = {
  52.         "LOADING",
  53.         "READY",
  54.         "DONE",
  55.         "ERROR"
  56. };
  57.  
  58. static nserror content_llcache_callback(llcache_handle *llcache,
  59.                 const llcache_event *event, void *pw);
  60. static void content_convert(struct content *c);
  61.  
  62.  
  63. /**
  64.  * Initialise a new content structure.
  65.  *
  66.  * \param c                 Content to initialise
  67.  * \param handler           Content handler
  68.  * \param imime_type        MIME type of content
  69.  * \param params            HTTP parameters
  70.  * \param llcache           Source data handle
  71.  * \param fallback_charset  Fallback charset
  72.  * \param quirks            Quirkiness of content
  73.  * \return NSERROR_OK on success, appropriate error otherwise
  74.  */
  75.  
  76. nserror content__init(struct content *c, const content_handler *handler,
  77.                 lwc_string *imime_type, const http_parameter *params,
  78.                 llcache_handle *llcache, const char *fallback_charset,
  79.                 bool quirks)
  80. {
  81.         struct content_user *user_sentinel;
  82.         nserror error;
  83.        
  84.         LOG(("url "URL_FMT_SPC" -> %p",
  85.              nsurl_access(llcache_handle_get_url(llcache)), c));
  86.  
  87.         user_sentinel = calloc(1, sizeof(struct content_user));
  88.         if (user_sentinel == NULL) {
  89.                 return NSERROR_NOMEM;
  90.         }
  91.  
  92.         if (fallback_charset != NULL) {
  93.                 c->fallback_charset = strdup(fallback_charset);
  94.                 if (c->fallback_charset == NULL) {
  95.                         return NSERROR_NOMEM;
  96.                 }
  97.         }
  98.  
  99.         c->llcache = llcache;
  100.         c->mime_type = lwc_string_ref(imime_type);
  101.         c->handler = handler;
  102.         c->status = CONTENT_STATUS_LOADING;
  103.         c->width = 0;
  104.         c->height = 0;
  105.         c->available_width = 0;
  106.         c->quirks = quirks;
  107.         c->refresh = 0;
  108.         c->time = wallclock();
  109.         c->size = 0;
  110.         c->title = NULL;
  111.         c->active = 0;
  112.         user_sentinel->callback = NULL;
  113.         user_sentinel->pw = NULL;
  114.         user_sentinel->next = NULL;
  115.         c->user_list = user_sentinel;
  116.         c->sub_status[0] = 0;
  117.         c->locked = false;
  118.         c->total_size = 0;
  119.         c->http_code = 0;
  120.         c->error_count = 0;
  121.  
  122.         content_set_status(c, messages_get("Loading"));
  123.  
  124.         /* Finally, claim low-level cache events */
  125.         error = llcache_handle_change_callback(llcache,
  126.                         content_llcache_callback, c);
  127.         if (error != NSERROR_OK) {
  128.                 lwc_string_unref(c->mime_type);
  129.                 return error;
  130.         }
  131.  
  132.         return NSERROR_OK;
  133. }
  134.  
  135. /**
  136.  * Handler for low-level cache events
  137.  *
  138.  * \param llcache  Low-level cache handle
  139.  * \param event    Event details
  140.  * \param pw       Pointer to our context
  141.  * \return NSERROR_OK on success, appropriate error otherwise
  142.  */
  143. nserror content_llcache_callback(llcache_handle *llcache,
  144.                 const llcache_event *event, void *pw)
  145. {
  146.         struct content *c = pw;
  147.         union content_msg_data msg_data;
  148.         nserror error = NSERROR_OK;
  149.  
  150.         switch (event->type) {
  151.         case LLCACHE_EVENT_HAD_HEADERS:
  152.                 /* Will never happen: handled in hlcache */
  153.                 break;
  154.         case LLCACHE_EVENT_HAD_DATA:
  155.                 if (c->handler->process_data != NULL) {
  156.                         if (c->handler->process_data(c,
  157.                                         (const char *) event->data.data.buf,
  158.                                         event->data.data.len) == false) {
  159.                                 llcache_handle_abort(c->llcache);
  160.                                 c->status = CONTENT_STATUS_ERROR;
  161.                                 /** \todo It's not clear what error this is */
  162.                                 error = NSERROR_NOMEM;
  163.                         }
  164.                 }
  165.                 break;
  166.         case LLCACHE_EVENT_DONE:
  167.         {
  168.                 size_t source_size;
  169.  
  170.                 (void) llcache_handle_get_source_data(llcache, &source_size);
  171.  
  172.                 content_set_status(c, messages_get("Processing"));
  173.                 msg_data.explicit_status_text = NULL;
  174.                 content_broadcast(c, CONTENT_MSG_STATUS, msg_data);
  175.  
  176.                 content_convert(c);
  177.         }
  178.                 break;
  179.         case LLCACHE_EVENT_ERROR:
  180.                 /** \todo Error page? */
  181.                 c->status = CONTENT_STATUS_ERROR;
  182.                 msg_data.error = event->data.msg;
  183.                 content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
  184.                 break;
  185.         case LLCACHE_EVENT_PROGRESS:
  186.                 content_set_status(c, event->data.msg);
  187.                 msg_data.explicit_status_text = NULL;
  188.                 content_broadcast(c, CONTENT_MSG_STATUS, msg_data);
  189.                 break;
  190.         }
  191.  
  192.         return error;
  193. }
  194.  
  195. /**
  196.  * Get whether a content can reformat
  197.  *
  198.  * \param h  content to check
  199.  * \return whether the content can reformat
  200.  */
  201. bool content_can_reformat(hlcache_handle *h)
  202. {
  203.         struct content *c = hlcache_handle_get_content(h);
  204.  
  205.         if (c == NULL)
  206.                 return false;
  207.  
  208.         return (c->handler->reformat != NULL);
  209. }
  210.  
  211.  
  212. static void content_update_status(struct content *c)
  213. {
  214.         if (c->status == CONTENT_STATUS_LOADING ||
  215.                         c->status == CONTENT_STATUS_READY) {
  216.                 /* Not done yet */
  217.                 snprintf(c->status_message, sizeof (c->status_message),
  218.                                 "%s%s%s", messages_get("Fetching"),
  219.                                 c->sub_status ? ", " : " ", c->sub_status);
  220.         } else {
  221.                 unsigned int time = c->time;
  222.                 snprintf(c->status_message, sizeof (c->status_message),
  223.                                 "%s (%.1fs)", messages_get("Done"),
  224.                                 (float) time / 100);
  225.         }
  226. }
  227.  
  228.  
  229. /**
  230.  * Updates content with new status.
  231.  *
  232.  * The textual status contained in the content is updated with given string.
  233.  *
  234.  * \param status_message  new textual status
  235.  */
  236.  
  237. void content_set_status(struct content *c, const char *status_message)
  238. {
  239.         size_t len = strlen(status_message);
  240.  
  241.         if (len >= sizeof(c->sub_status)) {
  242.                 len = sizeof(c->sub_status) - 1;
  243.         }
  244.         memcpy(c->sub_status, status_message, len);
  245.         c->sub_status[len] = '\0';
  246.  
  247.         content_update_status(c);
  248. }
  249.  
  250.  
  251. /**
  252.  * All data has arrived, convert for display.
  253.  *
  254.  * Calls the convert function for the content.
  255.  *
  256.  * - If the conversion succeeds, but there is still some processing required
  257.  *   (eg. loading images), the content gets status CONTENT_STATUS_READY, and a
  258.  *   CONTENT_MSG_READY is sent to all users.
  259.  * - If the conversion succeeds and is complete, the content gets status
  260.  *   CONTENT_STATUS_DONE, and CONTENT_MSG_READY then CONTENT_MSG_DONE are sent.
  261.  * - If the conversion fails, CONTENT_MSG_ERROR is sent. The content will soon
  262.  *   be destroyed and must no longer be used.
  263.  */
  264.  
  265. void content_convert(struct content *c)
  266. {
  267.         assert(c);
  268.         assert(c->status == CONTENT_STATUS_LOADING ||
  269.                         c->status == CONTENT_STATUS_ERROR);
  270.  
  271.         if (c->status != CONTENT_STATUS_LOADING)
  272.                 return;
  273.  
  274.         if (c->locked == true)
  275.                 return;
  276.        
  277.         LOG(("content "URL_FMT_SPC" (%p)",
  278.                         nsurl_access(llcache_handle_get_url(c->llcache)), c));
  279.  
  280.         if (c->handler->data_complete != NULL) {
  281.                 c->locked = true;
  282.                 if (c->handler->data_complete(c) == false) {
  283.                         content_set_error(c);
  284.                 }
  285.                 /* Conversion to the READY state will unlock the content */
  286.         } else {
  287.                 content_set_ready(c);
  288.                 content_set_done(c);
  289.         }
  290. }
  291.  
  292. /**
  293.  * Put a content in status CONTENT_STATUS_READY and unlock the content.
  294.  */
  295.  
  296. void content_set_ready(struct content *c)
  297. {
  298.         union content_msg_data msg_data;
  299.  
  300.         /* The content must be locked at this point, as it can only
  301.          * become READY after conversion. */
  302.         assert(c->locked);
  303.         c->locked = false;
  304.  
  305.         c->status = CONTENT_STATUS_READY;
  306.         content_update_status(c);
  307.         content_broadcast(c, CONTENT_MSG_READY, msg_data);
  308. }
  309.  
  310. /**
  311.  * Put a content in status CONTENT_STATUS_DONE.
  312.  */
  313.  
  314. void content_set_done(struct content *c)
  315. {
  316.         union content_msg_data msg_data;
  317.  
  318.         c->status = CONTENT_STATUS_DONE;
  319.         c->time = wallclock() - c->time;
  320.         content_update_status(c);
  321.         content_broadcast(c, CONTENT_MSG_DONE, msg_data);
  322. }
  323.  
  324. /**
  325.  * Put a content in status CONTENT_STATUS_ERROR and unlock the content.
  326.  *
  327.  * \note We expect the caller to broadcast an error report if needed.
  328.  */
  329.  
  330. void content_set_error(struct content *c)
  331. {
  332.         c->locked = false;
  333.         c->status = CONTENT_STATUS_ERROR;
  334. }
  335.  
  336. /**
  337.  * Reformat to new size.
  338.  *
  339.  * Calls the reformat function for the content.
  340.  */
  341.  
  342. void content_reformat(hlcache_handle *h, bool background,
  343.                 int width, int height)
  344. {
  345.         content__reformat(hlcache_handle_get_content(h), background,
  346.                         width, height);
  347. }
  348.  
  349. void content__reformat(struct content *c, bool background,
  350.                 int width, int height)
  351. {
  352.         union content_msg_data data;
  353.         assert(c != 0);
  354.         assert(c->status == CONTENT_STATUS_READY ||
  355.                         c->status == CONTENT_STATUS_DONE);
  356.         assert(c->locked == false);
  357.         LOG(("%p %s", c, nsurl_access(llcache_handle_get_url(c->llcache))));
  358.         c->available_width = width;
  359.         if (c->handler->reformat != NULL) {
  360.  
  361.                 c->locked = true;
  362.                 c->handler->reformat(c, width, height);
  363.                 c->locked = false;
  364.  
  365.                 data.background = background;
  366.                 content_broadcast(c, CONTENT_MSG_REFORMAT, data);
  367.         }
  368. }
  369.  
  370.  
  371. /**
  372.  * Destroy and free a content.
  373.  *
  374.  * Calls the destroy function for the content, and frees the structure.
  375.  */
  376.  
  377. void content_destroy(struct content *c)
  378. {
  379.         struct content_rfc5988_link *link;
  380.  
  381.         assert(c);
  382.         LOG(("content %p %s", c,
  383.                         nsurl_access(llcache_handle_get_url(c->llcache))));
  384.         assert(c->locked == false);
  385.  
  386.         if (c->handler->destroy != NULL)
  387.                 c->handler->destroy(c);
  388.  
  389.         llcache_handle_release(c->llcache);
  390.         c->llcache = NULL;
  391.  
  392.         lwc_string_unref(c->mime_type);
  393.  
  394.         /* release metadata links */
  395.         link = c->links;
  396.         while (link != NULL) {
  397.                 link = content__free_rfc5988_link(link);
  398.         }
  399.  
  400.         /* free the user list */
  401.         if (c->user_list != NULL) {
  402.                 free(c->user_list);
  403.         }
  404.  
  405.         /* free the title */
  406.         if (c->title != NULL) {
  407.                 free(c->title);
  408.         }
  409.  
  410.         /* free the fallback characterset */
  411.         if (c->fallback_charset != NULL) {
  412.                 free(c->fallback_charset);
  413.         }
  414.  
  415.         free(c);
  416. }
  417.  
  418.  
  419. /**
  420.  * Handle mouse movements in a content window.
  421.  *
  422.  * \param  h      Content handle
  423.  * \param  bw     browser window
  424.  * \param  mouse  state of mouse buttons and modifier keys
  425.  * \param  x      coordinate of mouse
  426.  * \param  y      coordinate of mouse
  427.  */
  428.  
  429. void content_mouse_track(hlcache_handle *h, struct browser_window *bw,
  430.                 browser_mouse_state mouse, int x, int y)
  431. {
  432.         struct content *c = hlcache_handle_get_content(h);
  433.         assert(c != NULL);
  434.  
  435.         if (c->handler->mouse_track != NULL) {
  436.                 c->handler->mouse_track(c, bw, mouse, x, y);
  437.         } else {
  438.                 union content_msg_data msg_data;
  439.                 msg_data.pointer = BROWSER_POINTER_AUTO;
  440.                 content_broadcast(c, CONTENT_MSG_POINTER, msg_data);
  441.         }
  442.  
  443.  
  444.         return;
  445. }
  446.  
  447.  
  448. /**
  449.  * Handle mouse clicks and movements in a content window.
  450.  *
  451.  * \param  h      Content handle
  452.  * \param  bw     browser window
  453.  * \param  mouse  state of mouse buttons and modifier keys
  454.  * \param  x      coordinate of mouse
  455.  * \param  y      coordinate of mouse
  456.  *
  457.  * This function handles both hovering and clicking. It is important that the
  458.  * code path is identical (except that hovering doesn't carry out the action),
  459.  * so that the status bar reflects exactly what will happen. Having separate
  460.  * code paths opens the possibility that an attacker will make the status bar
  461.  * show some harmless action where clicking will be harmful.
  462.  */
  463.  
  464. void content_mouse_action(hlcache_handle *h, struct browser_window *bw,
  465.                 browser_mouse_state mouse, int x, int y)
  466. {
  467.         struct content *c = hlcache_handle_get_content(h);
  468.         assert(c != NULL);
  469.  
  470.         if (c->handler->mouse_action != NULL)
  471.                 c->handler->mouse_action(c, bw, mouse, x, y);
  472.  
  473.         return;
  474. }
  475.  
  476.  
  477. /**
  478.  * Request a redraw of an area of a content
  479.  *
  480.  * \param h       high-level cache handle
  481.  * \param x       x co-ord of left edge
  482.  * \param y       y co-ord of top edge
  483.  * \param width   Width of rectangle
  484.  * \param height  Height of rectangle
  485.  */
  486. void content_request_redraw(struct hlcache_handle *h,
  487.                 int x, int y, int width, int height)
  488. {
  489.         content__request_redraw(hlcache_handle_get_content(h),
  490.                         x, y, width, height);
  491. }
  492.  
  493.  
  494. /**
  495.  * Request a redraw of an area of a content
  496.  *
  497.  * \param c       Content
  498.  * \param x       x co-ord of left edge
  499.  * \param y       y co-ord of top edge
  500.  * \param width   Width of rectangle
  501.  * \param height  Height of rectangle
  502.  */
  503. void content__request_redraw(struct content *c,
  504.                 int x, int y, int width, int height)
  505. {
  506.         union content_msg_data data;
  507.  
  508.         if (c == NULL)
  509.                 return;
  510.  
  511.         data.redraw.x = x;
  512.         data.redraw.y = y;
  513.         data.redraw.width = width;
  514.         data.redraw.height = height;
  515.  
  516.         data.redraw.full_redraw = true;
  517.  
  518.         data.redraw.object = c;
  519.         data.redraw.object_x = 0;
  520.         data.redraw.object_y = 0;
  521.         data.redraw.object_width = c->width;
  522.         data.redraw.object_height = c->height;
  523.  
  524.         content_broadcast(c, CONTENT_MSG_REDRAW, data);
  525. }
  526.  
  527. /**
  528.  * Display content on screen with optional tiling.
  529.  *
  530.  * Calls the redraw_tile function for the content, or emulates it with the
  531.  * redraw function if it doesn't exist.
  532.  */
  533.  
  534. bool content_redraw(hlcache_handle *h, struct content_redraw_data *data,
  535.                 const struct rect *clip, const struct redraw_context *ctx)
  536. {
  537.         struct content *c = hlcache_handle_get_content(h);
  538.  
  539.         assert(c != NULL);
  540.  
  541.         if (c->locked) {
  542.                 /* not safe to attempt redraw */
  543.                 return true;
  544.         }
  545.  
  546.         /* ensure we have a redrawable content */
  547.         if (c->handler->redraw == NULL) {
  548.                 return true;
  549.         }
  550.  
  551.         return c->handler->redraw(c, data, clip, ctx);
  552. }
  553.  
  554.  
  555. /**
  556.  * Register a user for callbacks.
  557.  *
  558.  * \param  c         the content to register
  559.  * \param  callback  the callback function
  560.  * \param  pw        callback private data
  561.  * \return true on success, false otherwise on memory exhaustion
  562.  *
  563.  * The callback will be called when content_broadcast() is
  564.  * called with the content.
  565.  */
  566.  
  567. bool content_add_user(struct content *c,
  568.                 void (*callback)(struct content *c, content_msg msg,
  569.                         union content_msg_data data, void *pw),
  570.                 void *pw)
  571. {
  572.         struct content_user *user;
  573.  
  574.         LOG(("content "URL_FMT_SPC" (%p), user %p %p",
  575.                         nsurl_access(llcache_handle_get_url(c->llcache)),
  576.                         c, callback, pw));
  577.         user = malloc(sizeof(struct content_user));
  578.         if (!user)
  579.                 return false;
  580.         user->callback = callback;
  581.         user->pw = pw;
  582.         user->next = c->user_list->next;
  583.         c->user_list->next = user;
  584.  
  585.         return true;
  586. }
  587.  
  588.  
  589. /**
  590.  * Remove a callback user.
  591.  *
  592.  * The callback function and pw must be identical to those passed to
  593.  * content_add_user().
  594.  */
  595.  
  596. void content_remove_user(struct content *c,
  597.                 void (*callback)(struct content *c, content_msg msg,
  598.                         union content_msg_data data, void *pw),
  599.                 void *pw)
  600. {
  601.         struct content_user *user, *next;
  602.         LOG(("content "URL_FMT_SPC" (%p), user %p %p",
  603.                         nsurl_access(llcache_handle_get_url(c->llcache)), c,
  604.                         callback, pw));
  605.  
  606.         /* user_list starts with a sentinel */
  607.         for (user = c->user_list; user->next != 0 &&
  608.                         !(user->next->callback == callback &&
  609.                                 user->next->pw == pw); user = user->next)
  610.                 ;
  611.         if (user->next == 0) {
  612.                 LOG(("user not found in list"));
  613.                 assert(0);
  614.                 return;
  615.         }
  616.         next = user->next;
  617.         user->next = next->next;
  618.         free(next);
  619. }
  620.  
  621. /**
  622.  * Count users for the content.
  623.  */
  624.  
  625. uint32_t content_count_users(struct content *c)
  626. {
  627.         struct content_user *user;
  628.         uint32_t counter = 0;
  629.  
  630.         assert(c != NULL);
  631.        
  632.         for (user = c->user_list; user != NULL; user = user->next)
  633.                 counter += 1;
  634.        
  635.         return counter - 1; /* Subtract 1 for the sentinel */
  636. }
  637.  
  638. /**
  639.  * Determine if quirks mode matches
  640.  *
  641.  * \param c       Content to consider
  642.  * \param quirks  Quirks mode to match
  643.  * \return True if quirks match, false otherwise
  644.  */
  645. bool content_matches_quirks(struct content *c, bool quirks)
  646. {
  647.         if (c->handler->matches_quirks == NULL)
  648.                 return true;
  649.  
  650.         return c->handler->matches_quirks(c, quirks);
  651. }
  652.  
  653. /**
  654.  * Determine if a content is shareable
  655.  *
  656.  * \param c  Content to consider
  657.  * \return True if content is shareable, false otherwise
  658.  */
  659. bool content_is_shareable(struct content *c)
  660. {
  661.         return c->handler->no_share == false;
  662. }
  663.  
  664. /**
  665.  * Send a message to all users.
  666.  */
  667.  
  668. void content_broadcast(struct content *c, content_msg msg,
  669.                 union content_msg_data data)
  670. {
  671.         struct content_user *user, *next;
  672.         assert(c);
  673. //      LOG(("%p %s -> %d", c, c->url, msg));
  674.         for (user = c->user_list->next; user != 0; user = next) {
  675.                 next = user->next;  /* user may be destroyed during callback */
  676.                 if (user->callback != 0)
  677.                         user->callback(c, msg, data, user->pw);
  678.         }
  679. }
  680.  
  681. /* exported interface documented in content_protected.h */
  682. void content_broadcast_errorcode(struct content *c, nserror errorcode)
  683. {
  684.         struct content_user *user, *next;
  685.         union content_msg_data data;
  686.  
  687.         assert(c);
  688.  
  689.         data.errorcode = errorcode;
  690.  
  691.         for (user = c->user_list->next; user != 0; user = next) {
  692.                 next = user->next;  /* user may be destroyed during callback */
  693.                 if (user->callback != 0)
  694.                         user->callback(c, CONTENT_MSG_ERRORCODE, data, user->pw);
  695.         }
  696. }
  697.  
  698.  
  699. /**
  700.  * A window containing the content has been opened.
  701.  *
  702.  * \param  c       content that has been opened
  703.  * \param  bw      browser window containing the content
  704.  * \param  page    content of type CONTENT_HTML containing c, or 0 if not an
  705.  *                 object within a page
  706.  * \param  params  object parameters, or 0 if not an object
  707.  *
  708.  * Calls the open function for the content.
  709.  */
  710.  
  711. void content_open(hlcache_handle *h, struct browser_window *bw,
  712.                 struct content *page, struct object_params *params)
  713. {
  714.         struct content *c = hlcache_handle_get_content(h);
  715.         assert(c != 0);
  716.         LOG(("content %p %s", c,
  717.                         nsurl_access(llcache_handle_get_url(c->llcache))));
  718.         if (c->handler->open != NULL)
  719.                 c->handler->open(c, bw, page, params);
  720. }
  721.  
  722.  
  723. /**
  724.  * The window containing the content has been closed.
  725.  *
  726.  * Calls the close function for the content.
  727.  */
  728.  
  729. void content_close(hlcache_handle *h)
  730. {
  731.         struct content *c = hlcache_handle_get_content(h);
  732.         assert(c != 0);
  733.         LOG(("content %p %s", c,
  734.                         nsurl_access(llcache_handle_get_url(c->llcache))));
  735.         if (c->handler->close != NULL)
  736.                 c->handler->close(c);
  737. }
  738.  
  739.  
  740. /**
  741.  * Find this content's selection context, if it has one.
  742.  */
  743.  
  744. struct selection *content_get_selection(hlcache_handle *h)
  745. {
  746.         struct content *c = hlcache_handle_get_content(h);
  747.         assert(c != 0);
  748.  
  749.         if (c->handler->get_selection != NULL)
  750.                 return c->handler->get_selection(c);
  751.         else
  752.                 return NULL;
  753. }
  754.  
  755.  
  756. void content_get_contextual_content(struct hlcache_handle *h,
  757.                 int x, int y, struct contextual_content *data)
  758. {
  759.         struct content *c = hlcache_handle_get_content(h);
  760.         assert(c != 0);
  761.  
  762.         if (c->handler->get_contextual_content != NULL) {
  763.                 c->handler->get_contextual_content(c, x, y, data);
  764.                 return;
  765.         } else {
  766.                 data->object = h;
  767.                 return;
  768.         }
  769. }
  770.  
  771.  
  772. bool content_scroll_at_point(struct hlcache_handle *h,
  773.                 int x, int y, int scrx, int scry)
  774. {
  775.         struct content *c = hlcache_handle_get_content(h);
  776.         assert(c != 0);
  777.  
  778.         if (c->handler->scroll_at_point != NULL)
  779.                 return c->handler->scroll_at_point(c, x, y, scrx, scry);
  780.  
  781.         return false;
  782. }
  783.  
  784.  
  785. bool content_drop_file_at_point(struct hlcache_handle *h,
  786.                 int x, int y, char *file)
  787. {
  788.         struct content *c = hlcache_handle_get_content(h);
  789.         assert(c != 0);
  790.  
  791.         if (c->handler->drop_file_at_point != NULL)
  792.                 return c->handler->drop_file_at_point(c, x, y, file);
  793.  
  794.         return false;
  795. }
  796.  
  797.  
  798. void content_debug_dump(struct hlcache_handle *h, FILE *f)
  799. {
  800.         struct content *c = hlcache_handle_get_content(h);
  801.         assert(c != 0);
  802.  
  803.         if (c->handler->debug_dump != NULL)
  804.                 c->handler->debug_dump(c, f);
  805. }
  806.  
  807.  
  808. void content_add_error(struct content *c, const char *token,
  809.                 unsigned int line)
  810. {
  811. }
  812.  
  813. bool content__set_title(struct content *c, const char *title)
  814. {
  815.         char *new_title = strdup(title);
  816.         if (new_title == NULL)
  817.                 return false;
  818.  
  819.         if (c->title != NULL)
  820.                 free(c->title);
  821.  
  822.         c->title = new_title;
  823.  
  824.         return true;
  825. }
  826.  
  827. struct content_rfc5988_link *
  828. content_find_rfc5988_link(hlcache_handle *h, lwc_string *rel)
  829. {
  830.         struct content *c = hlcache_handle_get_content(h);
  831.         struct content_rfc5988_link *link = c->links;
  832.         bool rel_match = false;
  833.  
  834.         while (link != NULL) {
  835.                 if (lwc_string_caseless_isequal(link->rel, rel,
  836.                                 &rel_match) == lwc_error_ok && rel_match) {
  837.                         break;
  838.                 }
  839.                 link = link->next;
  840.         }
  841.         return link;
  842. }
  843.  
  844. struct content_rfc5988_link *
  845. content__free_rfc5988_link(struct content_rfc5988_link *link)
  846. {
  847.         struct content_rfc5988_link *next;
  848.  
  849.         next = link->next;
  850.  
  851.         lwc_string_unref(link->rel);
  852.         nsurl_unref(link->href);
  853.         if (link->hreflang != NULL) {
  854.                 lwc_string_unref(link->hreflang);
  855.         }
  856.         if (link->type != NULL) {
  857.                 lwc_string_unref(link->type);
  858.         }
  859.         if (link->media != NULL) {
  860.                 lwc_string_unref(link->media);
  861.         }
  862.         if (link->sizes != NULL) {
  863.                 lwc_string_unref(link->sizes);
  864.         }
  865.         free(link);
  866.  
  867.         return next;
  868. }
  869.  
  870. bool content__add_rfc5988_link(struct content *c,
  871.                 const struct content_rfc5988_link *link)
  872. {
  873.         struct content_rfc5988_link *newlink;  
  874.         union content_msg_data msg_data;
  875.  
  876.         /* a link relation must be present for it to be a link */
  877.         if (link->rel == NULL) {
  878.                 return false;
  879.         }
  880.  
  881.         /* a link href must be present for it to be a link */
  882.         if (link->href == NULL) {
  883.                 return false;
  884.         }
  885.  
  886.         newlink = calloc(1, sizeof(struct content_rfc5988_link));
  887.         if (newlink == NULL) {
  888.                 return false;
  889.         }
  890.  
  891.         /* copy values */
  892.         newlink->rel = lwc_string_ref(link->rel);
  893.         newlink->href = nsurl_ref(link->href);
  894.         if (link->hreflang != NULL) {
  895.                 newlink->hreflang = lwc_string_ref(link->hreflang);
  896.         }
  897.         if (link->type != NULL) {
  898.                 newlink->type = lwc_string_ref(link->type);
  899.         }
  900.         if (link->media != NULL) {
  901.                 newlink->media = lwc_string_ref(link->media);
  902.         }
  903.         if (link->sizes != NULL) {
  904.                 newlink->sizes = lwc_string_ref(link->sizes);
  905.         }
  906.  
  907.         /* add to metadata link to list */
  908.         newlink->next = c->links;
  909.         c->links = newlink;
  910.  
  911.         /* broadcast the data */
  912.         msg_data.rfc5988_link = newlink;
  913.         content_broadcast(c, CONTENT_MSG_LINK, msg_data);
  914.  
  915.         return true;
  916. }
  917.  
  918. /**
  919.  * Retrieve computed type of content
  920.  *
  921.  * \param c  Content to retrieve type of
  922.  * \return Computed content type
  923.  */
  924. content_type content_get_type(hlcache_handle *h)
  925. {
  926.         struct content *c = hlcache_handle_get_content(h);
  927.  
  928.         if (c == NULL)
  929.                 return CONTENT_NONE;
  930.  
  931.         return c->handler->type();
  932. }
  933.  
  934. /**
  935.  * Retrieve mime-type of content
  936.  *
  937.  * \param c  Content to retrieve mime-type of
  938.  * \return Pointer to referenced mime-type, or NULL if not found.
  939.  */
  940. lwc_string *content_get_mime_type(hlcache_handle *h)
  941. {
  942.         return content__get_mime_type(hlcache_handle_get_content(h));
  943. }
  944.  
  945. lwc_string *content__get_mime_type(struct content *c)
  946. {
  947.         if (c == NULL)
  948.                 return NULL;
  949.  
  950.         return lwc_string_ref(c->mime_type);
  951. }
  952.  
  953. /**
  954.  * Retrieve URL associated with content
  955.  *
  956.  * \param c  Content to retrieve URL from
  957.  * \return Pointer to URL, or NULL if not found.
  958.  */
  959. nsurl *content_get_url(struct content *c)
  960. {
  961.         if (c == NULL)
  962.                 return NULL;
  963.  
  964.         return llcache_handle_get_url(c->llcache);
  965. }
  966.  
  967. /**
  968.  * Retrieve title associated with content
  969.  *
  970.  * \param c  Content to retrieve title from
  971.  * \return Pointer to title, or NULL if not found.
  972.  */
  973. const char *content_get_title(hlcache_handle *h)
  974. {
  975.         return content__get_title(hlcache_handle_get_content(h));
  976. }
  977.  
  978. const char *content__get_title(struct content *c)
  979. {
  980.         if (c == NULL)
  981.                 return NULL;
  982.  
  983.         return c->title != NULL ? c->title :
  984.                         nsurl_access(llcache_handle_get_url(c->llcache));
  985. }
  986.  
  987. /**
  988.  * Retrieve status of content
  989.  *
  990.  * \param c  Content to retrieve status of
  991.  * \return Content status
  992.  */
  993. content_status content_get_status(hlcache_handle *h)
  994. {
  995.         return content__get_status(hlcache_handle_get_content(h));
  996. }
  997.  
  998. content_status content__get_status(struct content *c)
  999. {
  1000.         if (c == NULL)
  1001.                 return CONTENT_STATUS_ERROR;
  1002.  
  1003.         return c->status;
  1004. }
  1005.  
  1006. /**
  1007.  * Retrieve status message associated with content
  1008.  *
  1009.  * \param c  Content to retrieve status message from
  1010.  * \return Pointer to status message, or NULL if not found.
  1011.  */
  1012. const char *content_get_status_message(hlcache_handle *h)
  1013. {
  1014.         return content__get_status_message(hlcache_handle_get_content(h));
  1015. }
  1016.  
  1017. const char *content__get_status_message(struct content *c)
  1018. {
  1019.         if (c == NULL)
  1020.                 return NULL;
  1021.  
  1022.         return c->status_message;
  1023. }
  1024.  
  1025. /**
  1026.  * Retrieve width of content
  1027.  *
  1028.  * \param c  Content to retrieve width of
  1029.  * \return Content width
  1030.  */
  1031. int content_get_width(hlcache_handle *h)
  1032. {
  1033.         return content__get_width(hlcache_handle_get_content(h));
  1034. }
  1035.  
  1036. int content__get_width(struct content *c)
  1037. {
  1038.         if (c == NULL)
  1039.                 return 0;
  1040.  
  1041.         return c->width;
  1042. }
  1043.  
  1044. /**
  1045.  * Retrieve height of content
  1046.  *
  1047.  * \param c  Content to retrieve height of
  1048.  * \return Content height
  1049.  */
  1050. int content_get_height(hlcache_handle *h)
  1051. {
  1052.         return content__get_height(hlcache_handle_get_content(h));
  1053. }
  1054.  
  1055. int content__get_height(struct content *c)
  1056. {
  1057.         if (c == NULL)
  1058.                 return 0;
  1059.  
  1060.         return c->height;
  1061. }
  1062.  
  1063. /**
  1064.  * Retrieve available width of content
  1065.  *
  1066.  * \param c  Content to retrieve available width of
  1067.  * \return Available width of content
  1068.  */
  1069. int content_get_available_width(hlcache_handle *h)
  1070. {
  1071.         return content__get_available_width(hlcache_handle_get_content(h));
  1072. }
  1073.  
  1074. int content__get_available_width(struct content *c)
  1075. {
  1076.         if (c == NULL)
  1077.                 return 0;
  1078.  
  1079.         return c->available_width;
  1080. }
  1081.  
  1082.  
  1083. /**
  1084.  * Retrieve source of content
  1085.  *
  1086.  * \param c     Content to retrieve source of
  1087.  * \param size  Pointer to location to receive byte size of source
  1088.  * \return Pointer to source data
  1089.  */
  1090. const char *content_get_source_data(hlcache_handle *h, unsigned long *size)
  1091. {
  1092.         return content__get_source_data(hlcache_handle_get_content(h), size);
  1093. }
  1094.  
  1095. const char *content__get_source_data(struct content *c, unsigned long *size)
  1096. {
  1097.         const uint8_t *data;
  1098.         size_t len;
  1099.  
  1100.         assert(size != NULL);
  1101.  
  1102.         if (c == NULL)
  1103.                 return NULL;
  1104.  
  1105.         data = llcache_handle_get_source_data(c->llcache, &len);
  1106.  
  1107.         *size = (unsigned long) len;
  1108.  
  1109.         return (const char *) data;
  1110. }
  1111.  
  1112. /**
  1113.  * Invalidate content reuse data: causes subsequent requests for content URL
  1114.  * to query server to determine if content can be reused. This is required
  1115.  * behaviour for forced reloads etc.
  1116.  *
  1117.  * \param c  Content to invalidate
  1118.  */
  1119. void content_invalidate_reuse_data(hlcache_handle *h)
  1120. {
  1121.         content__invalidate_reuse_data(hlcache_handle_get_content(h));
  1122. }
  1123.  
  1124. void content__invalidate_reuse_data(struct content *c)
  1125. {
  1126.         if (c == NULL || c->llcache == NULL)
  1127.                 return;
  1128.  
  1129.         /* Invalidate low-level cache data */
  1130.         llcache_handle_invalidate_cache_data(c->llcache);
  1131. }
  1132.  
  1133. /**
  1134.  * Retrieve the refresh URL for a content
  1135.  *
  1136.  * \param c  Content to retrieve refresh URL from
  1137.  * \return Pointer to URL, or NULL if none
  1138.  */
  1139. nsurl *content_get_refresh_url(hlcache_handle *h)
  1140. {
  1141.         return content__get_refresh_url(hlcache_handle_get_content(h));
  1142. }
  1143.  
  1144. nsurl *content__get_refresh_url(struct content *c)
  1145. {
  1146.         if (c == NULL)
  1147.                 return NULL;
  1148.  
  1149.         return c->refresh;
  1150. }
  1151.  
  1152.  
  1153. /**
  1154.  * Retrieve the bitmap contained in an image content
  1155.  *
  1156.  * \param c  Content to retrieve bitmap from
  1157.  * \return Pointer to bitmap, or NULL if none.
  1158.  */
  1159. struct bitmap *content_get_bitmap(hlcache_handle *h)
  1160. {
  1161.         return content__get_bitmap(hlcache_handle_get_content(h));
  1162. }
  1163.  
  1164. struct bitmap *content__get_bitmap(struct content *c)
  1165. {
  1166.         struct bitmap *bitmap = NULL;
  1167.  
  1168.         if ((c != NULL) &&
  1169.             (c->handler != NULL) &&
  1170.             (c->handler->type != NULL) &&
  1171.             (c->handler->type() == CONTENT_IMAGE) &&
  1172.             (c->handler->get_internal != NULL) ) {
  1173.                 bitmap = c->handler->get_internal(c, NULL);
  1174.         }
  1175.  
  1176.         return bitmap;
  1177. }
  1178.  
  1179.  
  1180. /**
  1181.  * Determine if a content is opaque from handle
  1182.  *
  1183.  * \param h high level cache handle to retrieve opacity from.
  1184.  * \return false if the content is not opaque or information is not
  1185.  *         known else true.
  1186.  */
  1187. bool content_get_opaque(hlcache_handle *h)
  1188. {
  1189.         return content__get_opaque(hlcache_handle_get_content(h));
  1190. }
  1191.  
  1192. /**
  1193.  * Determine if a content is opaque
  1194.  *
  1195.  * \param c Content to retrieve opacity from
  1196.  * \return false if the content is not opaque or information is not
  1197.  *         known else true.
  1198.  */
  1199. bool content__get_opaque(struct content *c)
  1200. {
  1201.         bool opaque = false;
  1202.  
  1203.         if ((c != NULL) &&
  1204.             (c->handler != NULL) &&
  1205.             (c->handler->type != NULL) &&
  1206.             (c->handler->type() == CONTENT_IMAGE) &&
  1207.             (c->handler->get_internal != NULL) ) {
  1208.                 struct bitmap *bitmap = NULL;
  1209.                 bitmap = c->handler->get_internal(c, NULL);
  1210.                 if (bitmap != NULL) {
  1211.                         opaque = bitmap_get_opaque(bitmap);
  1212.                 }
  1213.         }
  1214.  
  1215.         return opaque;
  1216. }
  1217.  
  1218.  
  1219. /**
  1220.  * Retrieve quirkiness of a content
  1221.  *
  1222.  * \param h  Content to examine
  1223.  * \return True if content is quirky, false otherwise
  1224.  */
  1225. bool content_get_quirks(hlcache_handle *h)
  1226. {
  1227.         struct content *c = hlcache_handle_get_content(h);
  1228.  
  1229.         if (c == NULL)
  1230.                 return false;
  1231.  
  1232.         return c->quirks;
  1233. }
  1234.  
  1235.  
  1236. /**
  1237.  * Return whether a content is currently locked
  1238.  *
  1239.  * \param c  Content to test
  1240.  * \return true iff locked, else false
  1241.  */
  1242.  
  1243. bool content_is_locked(hlcache_handle *h)
  1244. {
  1245.         return content__is_locked(hlcache_handle_get_content(h));
  1246. }
  1247.  
  1248. bool content__is_locked(struct content *c)
  1249. {
  1250.         return c->locked;
  1251. }
  1252.  
  1253. /**
  1254.  * Retrieve the low-level cache handle for a content
  1255.  *
  1256.  * \param h  Content to retrieve from
  1257.  * \return Low-level cache handle
  1258.  */
  1259. const llcache_handle *content_get_llcache_handle(struct content *c)
  1260. {
  1261.         if (c == NULL)
  1262.                 return NULL;
  1263.  
  1264.         return c->llcache;
  1265. }
  1266.  
  1267. /**
  1268.  * Clone a content object in its current state.
  1269.  *
  1270.  * \param c  Content to clone
  1271.  * \return Clone of \a c
  1272.  */
  1273. struct content *content_clone(struct content *c)
  1274. {
  1275.         struct content *nc;
  1276.         nserror error;
  1277.  
  1278.         error = c->handler->clone(c, &nc);
  1279.         if (error != NSERROR_OK)
  1280.                 return NULL;
  1281.  
  1282.         return nc;
  1283. };
  1284.  
  1285. /**
  1286.  * Clone a content's data members
  1287.  *
  1288.  * \param c   Content to clone
  1289.  * \param nc  Content to populate
  1290.  * \return NSERROR_OK on success, appropriate error otherwise
  1291.  */
  1292. nserror content__clone(const struct content *c, struct content *nc)
  1293. {
  1294.         struct content_user *user_sentinel;
  1295.         nserror error;
  1296.  
  1297.         user_sentinel = calloc(1, sizeof(struct content_user));
  1298.         if (user_sentinel == NULL) {
  1299.                 return NSERROR_NOMEM;
  1300.         }
  1301.  
  1302.         error = llcache_handle_clone(c->llcache, &(nc->llcache));
  1303.         if (error != NSERROR_OK) {
  1304.                 return error;
  1305.         }
  1306.        
  1307.         llcache_handle_change_callback(nc->llcache,
  1308.                         content_llcache_callback, nc);
  1309.  
  1310.         nc->mime_type = lwc_string_ref(c->mime_type);
  1311.         nc->handler = c->handler;
  1312.  
  1313.         nc->status = c->status;
  1314.        
  1315.         nc->width = c->width;
  1316.         nc->height = c->height;
  1317.         nc->available_width = c->available_width;
  1318.         nc->quirks = c->quirks;
  1319.        
  1320.         if (c->fallback_charset != NULL) {
  1321.                 nc->fallback_charset = strdup(c->fallback_charset);
  1322.                 if (nc->fallback_charset == NULL) {
  1323.                         return NSERROR_NOMEM;
  1324.                 }
  1325.         }
  1326.        
  1327.         if (c->refresh != NULL) {
  1328.                 nc->refresh = nsurl_ref(c->refresh);
  1329.                 if (nc->refresh == NULL) {
  1330.                         return NSERROR_NOMEM;
  1331.                 }
  1332.         }
  1333.  
  1334.         nc->time = c->time;
  1335.         nc->reformat_time = c->reformat_time;
  1336.         nc->size = c->size;
  1337.        
  1338.         if (c->title != NULL) {
  1339.                 nc->title = strdup(c->title);
  1340.                 if (nc->title == NULL) {
  1341.                         return NSERROR_NOMEM;
  1342.                 }
  1343.         }
  1344.        
  1345.         nc->active = c->active;
  1346.  
  1347.         nc->user_list = user_sentinel;
  1348.        
  1349.         memcpy(&(nc->status_message), &(c->status_message), 120);
  1350.         memcpy(&(nc->sub_status), &(c->sub_status), 80);
  1351.        
  1352.         nc->locked = c->locked;
  1353.         nc->total_size = c->total_size;
  1354.         nc->http_code = c->http_code;
  1355.        
  1356.         return NSERROR_OK;
  1357. }
  1358.  
  1359. /**
  1360.  * Abort a content object
  1361.  *
  1362.  * \param c The content object to abort
  1363.  * \return NSERROR_OK on success, otherwise appropriate error
  1364.  */
  1365. nserror content_abort(struct content *c)
  1366. {
  1367.         LOG(("Aborting %p", c));
  1368.        
  1369.         if (c->handler->stop != NULL)
  1370.                 c->handler->stop(c);
  1371.        
  1372.         /* And for now, abort our llcache object */
  1373.         return llcache_handle_abort(c->llcache);
  1374. }
  1375.  
  1376.