From a35e66ffa16d98bcfdf9608cafbcfd85e18d5f02 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Mon, 25 Feb 2013 18:24:04 +0000 Subject: split out object handling from render/html.c --- Makefile.sources | 8 +- render/html.c | 577 +-------------------------------------------- render/html_internal.h | 28 ++- render/html_object.c | 616 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 655 insertions(+), 574 deletions(-) create mode 100644 render/html_object.c diff --git a/Makefile.sources b/Makefile.sources index 6810915f9..b53d7c50a 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -12,10 +12,10 @@ S_FETCHERS := curl.c data.c file.c about.c resource.c S_CSS := css.c dump.c internal.c select.c utils.c S_RENDER := box.c box_construct.c box_normalise.c box_textarea.c \ - font.c form.c \ - html.c html_css.c html_script.c html_interaction.c \ - html_redraw.c html_forms.c imagemap.c layout.c list.c \ - search.c table.c textplain.c + font.c form.c imagemap.c layout.c list.c search.c table.c \ + textplain.c \ + html.c html_css.c html_script.c html_interaction.c \ + html_redraw.c html_forms.c html_object.c S_UTILS := base64.c corestrings.c filename.c filepath.c hashtable.c \ libdom.c locale.c log.c messages.c nsurl.c talloc.c url.c \ 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 + * + * 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 . + */ + +/** \file + * Processing for html content object operations. + */ + +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3