summaryrefslogtreecommitdiff
path: root/render
diff options
context:
space:
mode:
authorVincent Sanders <vince@netsurf-browser.org>2013-02-25 18:24:04 +0000
committerVincent Sanders <vince@netsurf-browser.org>2013-02-25 18:24:04 +0000
commita35e66ffa16d98bcfdf9608cafbcfd85e18d5f02 (patch)
tree1e21aac859bdeb65a732f198dc3d5f05cd9d7058 /render
parent4e7b4259a44703ecc0994c922315b67df7096c90 (diff)
downloadnetsurf-a35e66ffa16d98bcfdf9608cafbcfd85e18d5f02.tar.gz
netsurf-a35e66ffa16d98bcfdf9608cafbcfd85e18d5f02.tar.bz2
split out object handling from render/html.c
Diffstat (limited to 'render')
-rw-r--r--render/html.c577
-rw-r--r--render/html_internal.h28
-rw-r--r--render/html_object.c616
3 files changed, 651 insertions, 570 deletions
diff --git a/render/html.c b/render/html.c
index 7414ceda0..31571658a 100644
--- a/render/html.c
+++ b/render/html.c
@@ -69,27 +69,6 @@ static const char *html_types[] = {
"text/html"
};
-/* forward declared functions */
-static void html_object_refresh(void *p);
-
-static void html_destroy_objects(html_content *html)
-{
- while (html->object_list != NULL) {
- struct content_html_object *victim = html->object_list;
-
- if (victim->content != NULL) {
- LOG(("object %p", victim->content));
-
- if (content_get_type(victim->content) == CONTENT_HTML)
- schedule_remove(html_object_refresh, victim);
-
- hlcache_handle_release(victim->content);
- }
-
- html->object_list = victim->next;
- free(victim);
- }
-}
/**
* Perform post-box-creation conversion of a document
@@ -107,7 +86,7 @@ static void html_box_convert_done(html_content *c, bool success)
/* Clean up and report error if unsuccessful or aborted */
if ((success == false) || (c->aborted)) {
- html_destroy_objects(c);
+ html_object_free_objects(c);
if (success == false) {
content_broadcast_errorcode(&c->base, NSERROR_BOX_CONVERT);
@@ -130,7 +109,7 @@ static void html_box_convert_done(html_content *c, bool success)
exc = dom_document_get_document_element(c->document, (void *) &html);
if ((exc != DOM_NO_ERR) || (html == NULL)) {
- /** @todo should this call html_destroy_objects(c);
+ /** @todo should this call html_object_free_objects(c);
* like the other error paths
*/
LOG(("error retrieving html element from dom"));
@@ -143,7 +122,7 @@ static void html_box_convert_done(html_content *c, bool success)
err = imagemap_extract(c);
if (err != NSERROR_OK) {
LOG(("imagemap extraction failed"));
- html_destroy_objects(c);
+ html_object_free_objects(c);
content_broadcast_errorcode(&c->base, err);
content_set_error(&c->base);
dom_node_unref(html);
@@ -217,7 +196,7 @@ void html_finish_conversion(html_content *c)
error = dom_to_box(html, c, html_box_convert_done);
if (error != NSERROR_OK) {
dom_node_unref(html);
- html_destroy_objects(c);
+ html_object_free_objects(c);
content_broadcast_errorcode(&c->base, error);
content_set_error(&c->base);
return;
@@ -1092,409 +1071,9 @@ static nserror html_meta_refresh(html_content *c, dom_node *head)
return ns_error;
}
-/**
- * Update a box whose content has completed rendering.
- */
-
-static void
-html_object_done(struct box *box,
- hlcache_handle *object,
- bool background)
-{
- struct box *b;
-
- if (background) {
- box->background = object;
- return;
- }
-
- box->object = object;
-
- if (!(box->flags & REPLACE_DIM)) {
- /* invalidate parent min, max widths */
- for (b = box; b; b = b->parent)
- b->max_width = UNKNOWN_MAX_WIDTH;
-
- /* delete any clones of this box */
- while (box->next && (box->next->flags & CLONE)) {
- /* box_free_box(box->next); */
- box->next = box->next->next;
- }
- }
-}
-
-/**
- * Handle object fetching or loading failure.
- *
- * \param box box containing object which failed to load
- * \param content document of type CONTENT_HTML
- * \param background the object was the background image for the box
- */
-
-static void
-html_object_failed(struct box *box, html_content *content, bool background)
-{
- /* Nothing to do */
- return;
-}
-
-/**
- * Callback for hlcache_handle_retrieve() for objects.
- */
-
-static nserror
-html_object_callback(hlcache_handle *object,
- const hlcache_event *event,
- void *pw)
-{
- struct content_html_object *o = pw;
- html_content *c = (html_content *) o->parent;
- int x, y;
- struct box *box;
-
- assert(c->base.status != CONTENT_STATUS_ERROR);
-
- box = o->box;
-
- switch (event->type) {
- case CONTENT_MSG_LOADING:
- if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL)
- content_open(object,
- c->bw, &c->base,
- box->object_params);
- break;
-
- case CONTENT_MSG_READY:
- if (content_can_reformat(object)) {
- /* TODO: avoid knowledge of box internals here */
- content_reformat(object, false,
- box->max_width != UNKNOWN_MAX_WIDTH ?
- box->width : 0,
- box->max_width != UNKNOWN_MAX_WIDTH ?
- box->height : 0);
-
- /* Adjust parent content for new object size */
- html_object_done(box, object, o->background);
- if (c->base.status == CONTENT_STATUS_READY ||
- c->base.status == CONTENT_STATUS_DONE)
- content__reformat(&c->base, false,
- c->base.available_width,
- c->base.height);
- }
- break;
-
- case CONTENT_MSG_DONE:
- c->base.active--;
- LOG(("%d fetches active", c->base.active));
-
- html_object_done(box, object, o->background);
-
- if (c->base.status != CONTENT_STATUS_LOADING &&
- box->flags & REPLACE_DIM) {
- union content_msg_data data;
-
- if (!box_visible(box))
- break;
-
- box_coords(box, &x, &y);
-
- data.redraw.x = x + box->padding[LEFT];
- data.redraw.y = y + box->padding[TOP];
- data.redraw.width = box->width;
- data.redraw.height = box->height;
- data.redraw.full_redraw = true;
-
- content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
- }
- break;
-
- case CONTENT_MSG_ERROR:
- hlcache_handle_release(object);
-
- o->content = NULL;
-
- c->base.active--;
- LOG(("%d fetches active", c->base.active));
-
- content_add_error(&c->base, "?", 0);
- html_object_failed(box, c, o->background);
- break;
-
- case CONTENT_MSG_STATUS:
- if (event->data.explicit_status_text == NULL) {
- /* Object content's status text updated */
- union content_msg_data data;
- data.explicit_status_text =
- content_get_status_message(object);
- html_set_status(c, data.explicit_status_text);
- content_broadcast(&c->base, CONTENT_MSG_STATUS, data);
- } else {
- /* Object content wants to set explicit message */
- content_broadcast(&c->base, CONTENT_MSG_STATUS,
- event->data);
- }
- break;
-
- case CONTENT_MSG_REFORMAT:
- break;
-
- case CONTENT_MSG_REDRAW:
- if (c->base.status != CONTENT_STATUS_LOADING) {
- union content_msg_data data = event->data;
-
- if (!box_visible(box))
- break;
-
- box_coords(box, &x, &y);
-
- if (hlcache_handle_get_content(object) ==
- event->data.redraw.object) {
- data.redraw.x = data.redraw.x *
- box->width / content_get_width(object);
- data.redraw.y = data.redraw.y *
- box->height /
- content_get_height(object);
- data.redraw.width = data.redraw.width *
- box->width / content_get_width(object);
- data.redraw.height = data.redraw.height *
- box->height /
- content_get_height(object);
- data.redraw.object_width = box->width;
- data.redraw.object_height = box->height;
- }
-
- data.redraw.x += x + box->padding[LEFT];
- data.redraw.y += y + box->padding[TOP];
- data.redraw.object_x += x + box->padding[LEFT];
- data.redraw.object_y += y + box->padding[TOP];
-
- content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
- }
- break;
-
- case CONTENT_MSG_REFRESH:
- if (content_get_type(object) == CONTENT_HTML) {
- /* only for HTML objects */
- schedule(event->data.delay * 100,
- html_object_refresh, o);
- }
-
- break;
-
- case CONTENT_MSG_LINK:
- /* Don't care about favicons that aren't on top level content */
- break;
-
- case CONTENT_MSG_GETCTX:
- *(event->data.jscontext) = NULL;
- break;
-
- case CONTENT_MSG_SCROLL:
- if (box->scroll_x != NULL)
- scrollbar_set(box->scroll_x, event->data.scroll.x0,
- false);
- if (box->scroll_y != NULL)
- scrollbar_set(box->scroll_y, event->data.scroll.y0,
- false);
- break;
-
- case CONTENT_MSG_DRAGSAVE:
- {
- union content_msg_data msg_data;
- if (event->data.dragsave.content == NULL)
- msg_data.dragsave.content = object;
- else
- msg_data.dragsave.content =
- event->data.dragsave.content;
-
- content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data);
- }
- break;
-
- case CONTENT_MSG_SAVELINK:
- case CONTENT_MSG_POINTER:
- /* These messages are for browser window layer.
- * we're not interested, so pass them on. */
- content_broadcast(&c->base, event->type, event->data);
- break;
-
- case CONTENT_MSG_CARET:
- {
- union html_focus_owner focus_owner;
- focus_owner.content = box;
-
- switch (event->data.caret.type) {
- case CONTENT_CARET_REMOVE:
- case CONTENT_CARET_HIDE:
- html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
- true, 0, 0, 0, NULL);
- break;
- case CONTENT_CARET_SET_POS:
- html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
- false, event->data.caret.pos.x,
- event->data.caret.pos.y,
- event->data.caret.pos.height,
- event->data.caret.pos.clip);
- break;
- }
- }
- break;
-
- case CONTENT_MSG_DRAG:
- {
- html_drag_type drag_type = HTML_DRAG_NONE;
- union html_drag_owner drag_owner;
- drag_owner.content = box;
-
- switch (event->data.drag.type) {
- case CONTENT_DRAG_NONE:
- drag_type = HTML_DRAG_NONE;
- drag_owner.no_owner = true;
- break;
- case CONTENT_DRAG_SCROLL:
- drag_type = HTML_DRAG_CONTENT_SCROLL;
- break;
- case CONTENT_DRAG_SELECTION:
- drag_type = HTML_DRAG_CONTENT_SELECTION;
- break;
- }
- html_set_drag_type(c, drag_type, drag_owner,
- event->data.drag.rect);
- }
- break;
-
- case CONTENT_MSG_SELECTION:
- {
- html_selection_type sel_type;
- union html_selection_owner sel_owner;
-
- if (event->data.selection.selection) {
- sel_type = HTML_SELECTION_CONTENT;
- sel_owner.content = box;
- } else {
- sel_type = HTML_SELECTION_NONE;
- sel_owner.none = true;
- }
- html_set_selection(c, sel_type, sel_owner,
- event->data.selection.read_only);
- }
- break;
-
- default:
- assert(0);
- }
-
- if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 &&
- (event->type == CONTENT_MSG_LOADING ||
- event->type == CONTENT_MSG_DONE ||
- event->type == CONTENT_MSG_ERROR)) {
- /* all objects have arrived */
- content__reformat(&c->base, false, c->base.available_width,
- c->base.height);
- html_set_status(c, "");
- content_set_done(&c->base);
- }
-
- /* If 1) the configuration option to reflow pages while objects are
- * fetched is set
- * 2) an object is newly fetched & converted,
- * 3) the box's dimensions need to change due to being replaced
- * 4) the object's parent HTML is ready for reformat,
- * 5) the time since the previous reformat is more than the
- * configured minimum time between reformats
- * then reformat the page to display newly fetched objects */
- else if (nsoption_bool(incremental_reflow) &&
- event->type == CONTENT_MSG_DONE &&
- !(box->flags & REPLACE_DIM) &&
- (c->base.status == CONTENT_STATUS_READY ||
- c->base.status == CONTENT_STATUS_DONE) &&
- (wallclock() > c->base.reformat_time)) {
- content__reformat(&c->base, false, c->base.available_width,
- c->base.height);
- }
-
- return NSERROR_OK;
-}
-
-/**
- * Start a fetch for an object required by a page, replacing an existing object.
- *
- * \param object Object to replace
- * \param url URL of object to fetch (copied)
- * \return true on success, false on memory exhaustion
- */
-static bool html_replace_object(struct content_html_object *object, nsurl *url)
-{
- html_content *c;
- hlcache_child_context child;
- html_content *page;
- nserror error;
-
- assert(object != NULL);
- c = (html_content *) object->parent;
- child.charset = c->encoding;
- child.quirks = c->base.quirks;
-
- if (object->content != NULL) {
- /* remove existing object */
- if (content_get_status(object->content) != CONTENT_STATUS_DONE) {
- c->base.active--;
- LOG(("%d fetches active", c->base.active));
- }
-
- hlcache_handle_release(object->content);
- object->content = NULL;
-
- object->box->object = NULL;
- }
-
- /* initialise fetch */
- error = hlcache_handle_retrieve(url, HLCACHE_RETRIEVE_SNIFF_TYPE,
- content_get_url(&c->base), NULL,
- html_object_callback, object, &child,
- object->permitted_types,
- &object->content);
-
- if (error != NSERROR_OK)
- return false;
-
- for (page = c; page != NULL; page = page->page) {
- page->base.active++;
- LOG(("%d fetches active", c->base.active));
-
- page->base.status = CONTENT_STATUS_READY;
- }
-
- return true;
-}
-
-/**
- * schedule() callback for object refresh
- */
-
-static void html_object_refresh(void *p)
-{
- struct content_html_object *object = p;
- nsurl *refresh_url;
-
- assert(content_get_type(object->content) == CONTENT_HTML);
-
- refresh_url = content_get_refresh_url(object->content);
-
- /* Ignore if refresh URL has gone
- * (may happen if fetch errored) */
- if (refresh_url == NULL)
- return;
-
- content_invalidate_reuse_data(object->content);
-
- if (!html_replace_object(object, refresh_url)) {
- /** \todo handle memory exhaustion */
- }
-}
/**
@@ -1707,74 +1286,6 @@ html_begin_conversion(html_content *htmlc)
}
-
-
-/**
- * Start a fetch for an object required by a page.
- *
- * \param c content of type CONTENT_HTML
- * \param url URL of object to fetch (copied)
- * \param box box that will contain the object
- * \param permitted_types bitmap of acceptable types
- * \param available_width estimate of width of object
- * \param available_height estimate of height of object
- * \param background this is a background image
- * \return true on success, false on memory exhaustion
- */
-
-bool html_fetch_object(html_content *c, nsurl *url, struct box *box,
- content_type permitted_types,
- int available_width, int available_height,
- bool background)
-{
- struct content_html_object *object;
- hlcache_child_context child;
- nserror error;
-
- /* If we've already been aborted, don't bother attempting the fetch */
- if (c->aborted)
- return true;
-
- child.charset = c->encoding;
- child.quirks = c->base.quirks;
-
- object = calloc(1, sizeof(struct content_html_object));
- if (object == NULL) {
- return false;
- }
-
- object->parent = (struct content *) c;
- object->next = NULL;
- object->content = NULL;
- object->box = box;
- object->permitted_types = permitted_types;
- object->background = background;
-
- error = hlcache_handle_retrieve(url,
- HLCACHE_RETRIEVE_SNIFF_TYPE,
- content_get_url(&c->base), NULL,
- html_object_callback, object, &child,
- object->permitted_types, &object->content);
- if (error != NSERROR_OK) {
- free(object);
- return error != NSERROR_NOMEM;
- }
-
- /* add to content object list */
- object->next = c->object_list;
- c->object_list = object;
-
- c->num_objects++;
- c->base.active++;
- LOG(("%d fetches active", c->base.active));
-
- return true;
-}
-
-
-
-
-
/**
* Stop loading a CONTENT_HTML.
*/
@@ -1782,7 +1293,6 @@ bool html_fetch_object(html_content *c, nsurl *url, struct box *box,
static void html_stop(struct content *c)
{
html_content *htmlc = (html_content *) c;
- struct content_html_object *object;
switch (c->status) {
case CONTENT_STATUS_LOADING:
@@ -1790,31 +1300,9 @@ static void html_stop(struct content *c)
* html_convert/html_finish_conversion will do the rest */
htmlc->aborted = true;
break;
- case CONTENT_STATUS_READY:
- for (object = htmlc->object_list; object != NULL;
- object = object->next) {
- if (object->content == NULL)
- continue;
-
- if (content_get_status(object->content) ==
- CONTENT_STATUS_DONE)
- ; /* already loaded: do nothing */
- else if (content_get_status(object->content) ==
- CONTENT_STATUS_READY)
- hlcache_handle_abort(object->content);
- /* Active count will be updated when
- * html_object_callback receives
- * CONTENT_MSG_DONE from this object */
- else {
- hlcache_handle_abort(object->content);
- hlcache_handle_release(object->content);
- object->content = NULL;
-
- c->active--;
- LOG(("%d fetches active", c->active));
- }
- }
+ case CONTENT_STATUS_READY:
+ html_object_abort_objects(htmlc);
/* If there are no further active fetches and we're still
* in the READY state, transition to the DONE state. */
@@ -1824,9 +1312,11 @@ static void html_stop(struct content *c)
}
break;
+
case CONTENT_STATUS_DONE:
/* Nothing to do */
break;
+
default:
LOG(("Unexpected status %d", c->status));
assert(0);
@@ -2038,7 +1528,7 @@ static void html_destroy(struct content *c)
html_free_scripts(html);
/* Free objects */
- html_destroy_objects(html);
+ html_object_free_objects(html);
/* free layout */
html_free_layout(html);
@@ -2078,7 +1568,6 @@ html_open(struct content *c,
struct object_params *params)
{
html_content *html = (html_content *) c;
- struct content_html_object *object, *next;
html->bw = bw;
html->page = (html_content *) page;
@@ -2091,19 +1580,7 @@ html_open(struct content *c,
html->selection_type = HTML_SELECTION_NONE;
html->selection_owner.none = true;
- for (object = html->object_list; object != NULL; object = next) {
- next = object->next;
-
- if (object->content == NULL)
- continue;
-
- if (content_get_type(object->content) == CONTENT_NONE)
- continue;
-
- content_open(object->content,
- bw, c,
- object->box->object_params);
- }
+ html_object_open_objects(html, bw);
}
@@ -2114,7 +1591,6 @@ html_open(struct content *c,
static void html_close(struct content *c)
{
html_content *html = (html_content *) c;
- struct content_html_object *object, *next;
selection_clear(&html->sel, false);
@@ -2123,20 +1599,7 @@ static void html_close(struct content *c)
html->bw = NULL;
- for (object = html->object_list; object != NULL; object = next) {
- next = object->next;
-
- if (object->content == NULL)
- continue;
-
- if (content_get_type(object->content) == CONTENT_NONE)
- continue;
-
- if (content_get_type(object->content) == CONTENT_HTML)
- schedule_remove(html_object_refresh, object);
-
- content_close(object->content);
- }
+ html_object_close_objects(html);
}
@@ -2728,24 +2191,6 @@ const char *html_get_base_target(hlcache_handle *h)
return c->base_target;
}
-/**
- * Retrieve objects used by HTML document
- *
- * \param h Content to retrieve objects from
- * \param n Pointer to location to receive number of objects
- * \return Pointer to list of objects
- */
-struct content_html_object *html_get_objects(hlcache_handle *h, unsigned int *n)
-{
- html_content *c = (html_content *) hlcache_handle_get_content(h);
-
- assert(c != NULL);
- assert(n != NULL);
-
- *n = c->num_objects;
-
- return c->object_list;
-}
/**
* Retrieve layout coordinates of box with given id
diff --git a/render/html_internal.h b/render/html_internal.h
index 4c43345a2..428cd3995 100644
--- a/render/html_internal.h
+++ b/render/html_internal.h
@@ -165,10 +165,6 @@ typedef struct html_content {
} html_content;
-bool html_fetch_object(html_content *c, nsurl *url, struct box *box,
- content_type permitted_types,
- int available_width, int available_height,
- bool background);
void html_set_status(html_content *c, const char *extra);
@@ -274,6 +270,30 @@ bool html_css_update_style(html_content *c, dom_node *style);
nserror html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx);
+/* in render/html_object.c */
+
+/**
+ * Start a fetch for an object required by a page.
+ *
+ * \param c content of type CONTENT_HTML
+ * \param url URL of object to fetch (copied)
+ * \param box box that will contain the object
+ * \param permitted_types bitmap of acceptable types
+ * \param available_width estimate of width of object
+ * \param available_height estimate of height of object
+ * \param background this is a background image
+ * \return true on success, false on memory exhaustion
+ */
+bool html_fetch_object(html_content *c, nsurl *url, struct box *box,
+ content_type permitted_types,
+ int available_width, int available_height,
+ bool background);
+
+nserror html_object_free_objects(html_content *html);
+nserror html_object_close_objects(html_content *html);
+nserror html_object_open_objects(html_content *html, struct browser_window *bw);
+nserror html_object_abort_objects(html_content *html);
+
/* Useful dom_string pointers */
struct dom_string;
diff --git a/render/html_object.c b/render/html_object.c
new file mode 100644
index 000000000..d4d0ff9d2
--- /dev/null
+++ b/render/html_object.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright 2013 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Processing for html content object operations.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include "content/hlcache.h"
+#include "desktop/options.h"
+#include "desktop/scrollbar.h"
+#include "render/box.h"
+#include "render/html_internal.h"
+#include "utils/corestrings.h"
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/schedule.h"
+
+/* break reference loop */
+static void html_object_refresh(void *p);
+
+/**
+ * Retrieve objects used by HTML document
+ *
+ * \param h Content to retrieve objects from
+ * \param n Pointer to location to receive number of objects
+ * \return Pointer to list of objects
+ */
+struct content_html_object *html_get_objects(hlcache_handle *h, unsigned int *n)
+{
+ html_content *c = (html_content *) hlcache_handle_get_content(h);
+
+ assert(c != NULL);
+ assert(n != NULL);
+
+ *n = c->num_objects;
+
+ return c->object_list;
+}
+
+/**
+ * Handle object fetching or loading failure.
+ *
+ * \param box box containing object which failed to load
+ * \param content document of type CONTENT_HTML
+ * \param background the object was the background image for the box
+ */
+
+static void
+html_object_failed(struct box *box, html_content *content, bool background)
+{
+ /* Nothing to do */
+ return;
+}
+
+/**
+ * Update a box whose content has completed rendering.
+ */
+
+static void
+html_object_done(struct box *box,
+ hlcache_handle *object,
+ bool background)
+{
+ struct box *b;
+
+ if (background) {
+ box->background = object;
+ return;
+ }
+
+ box->object = object;
+
+ if (!(box->flags & REPLACE_DIM)) {
+ /* invalidate parent min, max widths */
+ for (b = box; b; b = b->parent)
+ b->max_width = UNKNOWN_MAX_WIDTH;
+
+ /* delete any clones of this box */
+ while (box->next && (box->next->flags & CLONE)) {
+ /* box_free_box(box->next); */
+ box->next = box->next->next;
+ }
+ }
+}
+
+/**
+ * Callback for hlcache_handle_retrieve() for objects.
+ */
+
+static nserror
+html_object_callback(hlcache_handle *object,
+ const hlcache_event *event,
+ void *pw)
+{
+ struct content_html_object *o = pw;
+ html_content *c = (html_content *) o->parent;
+ int x, y;
+ struct box *box;
+
+ assert(c->base.status != CONTENT_STATUS_ERROR);
+
+ box = o->box;
+
+ switch (event->type) {
+ case CONTENT_MSG_LOADING:
+ if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL)
+ content_open(object,
+ c->bw, &c->base,
+ box->object_params);
+ break;
+
+ case CONTENT_MSG_READY:
+ if (content_can_reformat(object)) {
+ /* TODO: avoid knowledge of box internals here */
+ content_reformat(object, false,
+ box->max_width != UNKNOWN_MAX_WIDTH ?
+ box->width : 0,
+ box->max_width != UNKNOWN_MAX_WIDTH ?
+ box->height : 0);
+
+ /* Adjust parent content for new object size */
+ html_object_done(box, object, o->background);
+ if (c->base.status == CONTENT_STATUS_READY ||
+ c->base.status == CONTENT_STATUS_DONE)
+ content__reformat(&c->base, false,
+ c->base.available_width,
+ c->base.height);
+ }
+ break;
+
+ case CONTENT_MSG_DONE:
+ c->base.active--;
+ LOG(("%d fetches active", c->base.active));
+
+ html_object_done(box, object, o->background);
+
+ if (c->base.status != CONTENT_STATUS_LOADING &&
+ box->flags & REPLACE_DIM) {
+ union content_msg_data data;
+
+ if (!box_visible(box))
+ break;
+
+ box_coords(box, &x, &y);
+
+ data.redraw.x = x + box->padding[LEFT];
+ data.redraw.y = y + box->padding[TOP];
+ data.redraw.width = box->width;
+ data.redraw.height = box->height;
+ data.redraw.full_redraw = true;
+
+ content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
+ }
+ break;
+
+ case CONTENT_MSG_ERROR:
+ hlcache_handle_release(object);
+
+ o->content = NULL;
+
+ c->base.active--;
+ LOG(("%d fetches active", c->base.active));
+
+ content_add_error(&c->base, "?", 0);
+ html_object_failed(box, c, o->background);
+ break;
+
+ case CONTENT_MSG_STATUS:
+ if (event->data.explicit_status_text == NULL) {
+ /* Object content's status text updated */
+ union content_msg_data data;
+ data.explicit_status_text =
+ content_get_status_message(object);
+ html_set_status(c, data.explicit_status_text);
+ content_broadcast(&c->base, CONTENT_MSG_STATUS, data);
+ } else {
+ /* Object content wants to set explicit message */
+ content_broadcast(&c->base, CONTENT_MSG_STATUS,
+ event->data);
+ }
+ break;
+
+ case CONTENT_MSG_REFORMAT:
+ break;
+
+ case CONTENT_MSG_REDRAW:
+ if (c->base.status != CONTENT_STATUS_LOADING) {
+ union content_msg_data data = event->data;
+
+ if (!box_visible(box))
+ break;
+
+ box_coords(box, &x, &y);
+
+ if (hlcache_handle_get_content(object) ==
+ event->data.redraw.object) {
+ data.redraw.x = data.redraw.x *
+ box->width / content_get_width(object);
+ data.redraw.y = data.redraw.y *
+ box->height /
+ content_get_height(object);
+ data.redraw.width = data.redraw.width *
+ box->width / content_get_width(object);
+ data.redraw.height = data.redraw.height *
+ box->height /
+ content_get_height(object);
+ data.redraw.object_width = box->width;
+ data.redraw.object_height = box->height;
+ }
+
+ data.redraw.x += x + box->padding[LEFT];
+ data.redraw.y += y + box->padding[TOP];
+ data.redraw.object_x += x + box->padding[LEFT];
+ data.redraw.object_y += y + box->padding[TOP];
+
+ content_broadcast(&c->base, CONTENT_MSG_REDRAW, data);
+ }
+ break;
+
+ case CONTENT_MSG_REFRESH:
+ if (content_get_type(object) == CONTENT_HTML) {
+ /* only for HTML objects */
+ schedule(event->data.delay * 100,
+ html_object_refresh, o);
+ }
+
+ break;
+
+ case CONTENT_MSG_LINK:
+ /* Don't care about favicons that aren't on top level content */
+ break;
+
+ case CONTENT_MSG_GETCTX:
+ *(event->data.jscontext) = NULL;
+ break;
+
+ case CONTENT_MSG_SCROLL:
+ if (box->scroll_x != NULL)
+ scrollbar_set(box->scroll_x, event->data.scroll.x0,
+ false);
+ if (box->scroll_y != NULL)
+ scrollbar_set(box->scroll_y, event->data.scroll.y0,
+ false);
+ break;
+
+ case CONTENT_MSG_DRAGSAVE:
+ {
+ union content_msg_data msg_data;
+ if (event->data.dragsave.content == NULL)
+ msg_data.dragsave.content = object;
+ else
+ msg_data.dragsave.content =
+ event->data.dragsave.content;
+
+ content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data);
+ }
+ break;
+
+ case CONTENT_MSG_SAVELINK:
+ case CONTENT_MSG_POINTER:
+ /* These messages are for browser window layer.
+ * we're not interested, so pass them on. */
+ content_broadcast(&c->base, event->type, event->data);
+ break;
+
+ case CONTENT_MSG_CARET:
+ {
+ union html_focus_owner focus_owner;
+ focus_owner.content = box;
+
+ switch (event->data.caret.type) {
+ case CONTENT_CARET_REMOVE:
+ case CONTENT_CARET_HIDE:
+ html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
+ true, 0, 0, 0, NULL);
+ break;
+ case CONTENT_CARET_SET_POS:
+ html_set_focus(c, HTML_FOCUS_CONTENT, focus_owner,
+ false, event->data.caret.pos.x,
+ event->data.caret.pos.y,
+ event->data.caret.pos.height,
+ event->data.caret.pos.clip);
+ break;
+ }
+ }
+ break;
+
+ case CONTENT_MSG_DRAG:
+ {
+ html_drag_type drag_type = HTML_DRAG_NONE;
+ union html_drag_owner drag_owner;
+ drag_owner.content = box;
+
+ switch (event->data.drag.type) {
+ case CONTENT_DRAG_NONE:
+ drag_type = HTML_DRAG_NONE;
+ drag_owner.no_owner = true;
+ break;
+ case CONTENT_DRAG_SCROLL:
+ drag_type = HTML_DRAG_CONTENT_SCROLL;
+ break;
+ case CONTENT_DRAG_SELECTION:
+ drag_type = HTML_DRAG_CONTENT_SELECTION;
+ break;
+ }
+ html_set_drag_type(c, drag_type, drag_owner,
+ event->data.drag.rect);
+ }
+ break;
+
+ case CONTENT_MSG_SELECTION:
+ {
+ html_selection_type sel_type;
+ union html_selection_owner sel_owner;
+
+ if (event->data.selection.selection) {
+ sel_type = HTML_SELECTION_CONTENT;
+ sel_owner.content = box;
+ } else {
+ sel_type = HTML_SELECTION_NONE;
+ sel_owner.none = true;
+ }
+ html_set_selection(c, sel_type, sel_owner,
+ event->data.selection.read_only);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 &&
+ (event->type == CONTENT_MSG_LOADING ||
+ event->type == CONTENT_MSG_DONE ||
+ event->type == CONTENT_MSG_ERROR)) {
+ /* all objects have arrived */
+ content__reformat(&c->base, false, c->base.available_width,
+ c->base.height);
+ html_set_status(c, "");
+ content_set_done(&c->base);
+ }
+
+ /* If 1) the configuration option to reflow pages while objects are
+ * fetched is set
+ * 2) an object is newly fetched & converted,
+ * 3) the box's dimensions need to change due to being replaced
+ * 4) the object's parent HTML is ready for reformat,
+ * 5) the time since the previous reformat is more than the
+ * configured minimum time between reformats
+ * then reformat the page to display newly fetched objects */
+ else if (nsoption_bool(incremental_reflow) &&
+ event->type == CONTENT_MSG_DONE &&
+ !(box->flags & REPLACE_DIM) &&
+ (c->base.status == CONTENT_STATUS_READY ||
+ c->base.status == CONTENT_STATUS_DONE) &&
+ (wallclock() > c->base.reformat_time)) {
+ content__reformat(&c->base, false, c->base.available_width,
+ c->base.height);
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Start a fetch for an object required by a page, replacing an existing object.
+ *
+ * \param object Object to replace
+ * \param url URL of object to fetch (copied)
+ * \return true on success, false on memory exhaustion
+ */
+
+static bool html_replace_object(struct content_html_object *object, nsurl *url)
+{
+ html_content *c;
+ hlcache_child_context child;
+ html_content *page;
+ nserror error;
+
+ assert(object != NULL);
+
+ c = (html_content *) object->parent;
+
+ child.charset = c->encoding;
+ child.quirks = c->base.quirks;
+
+ if (object->content != NULL) {
+ /* remove existing object */
+ if (content_get_status(object->content) != CONTENT_STATUS_DONE) {
+ c->base.active--;
+ LOG(("%d fetches active", c->base.active));
+ }
+
+ hlcache_handle_release(object->content);
+ object->content = NULL;
+
+ object->box->object = NULL;
+ }
+
+ /* initialise fetch */
+ error = hlcache_handle_retrieve(url, HLCACHE_RETRIEVE_SNIFF_TYPE,
+ content_get_url(&c->base), NULL,
+ html_object_callback, object, &child,
+ object->permitted_types,
+ &object->content);
+
+ if (error != NSERROR_OK)
+ return false;
+
+ for (page = c; page != NULL; page = page->page) {
+ page->base.active++;
+ LOG(("%d fetches active", c->base.active));
+
+ page->base.status = CONTENT_STATUS_READY;
+ }
+
+ return true;
+}
+
+/**
+ * schedule() callback for object refresh
+ */
+
+static void html_object_refresh(void *p)
+{
+ struct content_html_object *object = p;
+ nsurl *refresh_url;
+
+ assert(content_get_type(object->content) == CONTENT_HTML);
+
+ refresh_url = content_get_refresh_url(object->content);
+
+ /* Ignore if refresh URL has gone
+ * (may happen if fetch errored) */
+ if (refresh_url == NULL)
+ return;
+
+ content_invalidate_reuse_data(object->content);
+
+ if (!html_replace_object(object, refresh_url)) {
+ /** \todo handle memory exhaustion */
+ }
+}
+
+nserror html_object_open_objects(html_content *html, struct browser_window *bw)
+{
+ struct content_html_object *object, *next;
+
+ for (object = html->object_list; object != NULL; object = next) {
+ next = object->next;
+
+ if (object->content == NULL)
+ continue;
+
+ if (content_get_type(object->content) == CONTENT_NONE)
+ continue;
+
+ content_open(object->content,
+ bw,
+ &html->base,
+ object->box->object_params);
+ }
+ return NSERROR_OK;
+}
+
+nserror html_object_abort_objects(html_content *htmlc)
+{
+ struct content_html_object *object;
+
+ for (object = htmlc->object_list;
+ object != NULL;
+ object = object->next) {
+ if (object->content == NULL)
+ continue;
+
+ switch (content_get_status(object->content)) {
+ case CONTENT_STATUS_DONE:
+ /* already loaded: do nothing */
+ break;
+
+ case CONTENT_STATUS_READY:
+ hlcache_handle_abort(object->content);
+ /* Active count will be updated when
+ * html_object_callback receives
+ * CONTENT_MSG_DONE from this object
+ */
+ break;
+
+ default:
+ hlcache_handle_abort(object->content);
+ hlcache_handle_release(object->content);
+ object->content = NULL;
+
+ htmlc->base.active--;
+ LOG(("%d fetches active", htmlc->base.active));
+ break;
+
+ }
+ }
+
+ return NSERROR_OK;
+}
+
+nserror html_object_close_objects(html_content *html)
+{
+ struct content_html_object *object, *next;
+
+ for (object = html->object_list; object != NULL; object = next) {
+ next = object->next;
+
+ if (object->content == NULL)
+ continue;
+
+ if (content_get_type(object->content) == CONTENT_NONE)
+ continue;
+
+ if (content_get_type(object->content) == CONTENT_HTML)
+ schedule_remove(html_object_refresh, object);
+
+ content_close(object->content);
+ }
+ return NSERROR_OK;
+}
+
+nserror html_object_free_objects(html_content *html)
+{
+ while (html->object_list != NULL) {
+ struct content_html_object *victim = html->object_list;
+
+ if (victim->content != NULL) {
+ LOG(("object %p", victim->content));
+
+ if (content_get_type(victim->content) == CONTENT_HTML)
+ schedule_remove(html_object_refresh, victim);
+
+ hlcache_handle_release(victim->content);
+ }
+
+ html->object_list = victim->next;
+ free(victim);
+ }
+ return NSERROR_OK;
+}
+
+
+
+/* exported interface documented in render/html_internal.h */
+bool html_fetch_object(html_content *c, nsurl *url, struct box *box,
+ content_type permitted_types,
+ int available_width, int available_height,
+ bool background)
+{
+ struct content_html_object *object;
+ hlcache_child_context child;
+ nserror error;
+
+ /* If we've already been aborted, don't bother attempting the fetch */
+ if (c->aborted)
+ return true;
+
+ child.charset = c->encoding;
+ child.quirks = c->base.quirks;
+
+ object = calloc(1, sizeof(struct content_html_object));
+ if (object == NULL) {
+ return false;
+ }
+
+ object->parent = (struct content *) c;
+ object->next = NULL;
+ object->content = NULL;
+ object->box = box;
+ object->permitted_types = permitted_types;
+ object->background = background;
+
+ error = hlcache_handle_retrieve(url,
+ HLCACHE_RETRIEVE_SNIFF_TYPE,
+ content_get_url(&c->base), NULL,
+ html_object_callback, object, &child,
+ object->permitted_types, &object->content);
+ if (error != NSERROR_OK) {
+ free(object);
+ return error != NSERROR_NOMEM;
+ }
+
+ /* add to content object list */
+ object->next = c->object_list;
+ c->object_list = object;
+
+ c->num_objects++;
+ c->base.active++;
+ LOG(("%d fetches active", c->base.active));
+
+ return true;
+}