Gitweb links:
...log
http://git.netsurf-browser.org/netsurf.git/shortlog/479d0cb29a74eddc95e3e...
...commit
http://git.netsurf-browser.org/netsurf.git/commit/479d0cb29a74eddc95e3e22...
...tree
http://git.netsurf-browser.org/netsurf.git/tree/479d0cb29a74eddc95e3e2292...
The branch, master has been updated
via 479d0cb29a74eddc95e3e2292d9d49e8dafdf46f (commit)
via 6b6bbad2b7765378b5135a26b3d410d6b0079966 (commit)
from e8d0ba15ad4a91b4644cac0095f40bb3246ec509 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=479d0cb29a74eddc95e...
commit 479d0cb29a74eddc95e3e2292d9d49e8dafdf46f
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
remove unused available width and height parameters from html_fecth_object()
diff --git a/content/handlers/html/box_construct.c
b/content/handlers/html/box_construct.c
index 1637c0f..fba9764 100644
--- a/content/handlers/html/box_construct.c
+++ b/content/handlers/html/box_construct.c
@@ -470,9 +470,11 @@ box_construct_marker(struct box *box,
if (error != NSERROR_OK)
return false;
- if (html_fetch_object(ctx->content, url, marker, image_types,
- ctx->content->base.available_width, 1000, false) ==
- false) {
+ if (html_fetch_object(ctx->content,
+ url,
+ marker,
+ image_types,
+ false) == false) {
nsurl_unref(url);
return false;
}
@@ -705,10 +707,11 @@ box_construct_element(struct box_construct_ctx *ctx, bool
*convert_children)
error = nsurl_create(lwc_string_data(bgimage_uri), &url);
if (error == NSERROR_OK) {
/* Fetch image if we got a valid URL */
- if (html_fetch_object(ctx->content, url, box,
- image_types,
- ctx->content->base.available_width,
- 1000, true) == false) {
+ if (html_fetch_object(ctx->content,
+ url,
+ box,
+ image_types,
+ true) == false) {
nsurl_unref(url);
return false;
}
diff --git a/content/handlers/html/box_special.c b/content/handlers/html/box_special.c
index 38031ce..9f4a512 100644
--- a/content/handlers/html/box_special.c
+++ b/content/handlers/html/box_special.c
@@ -956,8 +956,7 @@ box_embed(dom_node *n,
/* start fetch */
box->flags |= IS_REPLACED;
- return html_fetch_object(content, params->data, box, CONTENT_ANY,
- content->base.available_width, 1000, false);
+ return html_fetch_object(content, params->data, box, CONTENT_ANY, false);
}
@@ -1189,8 +1188,7 @@ box_image(dom_node *n,
/* start fetch */
box->flags |= IS_REPLACED;
- ok = html_fetch_object(content, url, box, image_types,
- content->base.available_width, 1000, false);
+ ok = html_fetch_object(content, url, box, image_types, false);
nsurl_unref(url);
wtype = css_computed_width(box->style, &value, &wunit);
@@ -1332,11 +1330,11 @@ box_input(dom_node *n,
*/
if (nsurl_compare(url, content->base_url,
NSURL_COMPLETE) == false) {
- if (!html_fetch_object(content, url,
- box, image_types,
- content->base.
- available_width,
- 1000, false)) {
+ if (!html_fetch_object(content,
+ url,
+ box,
+ image_types,
+ false)) {
nsurl_unref(url);
goto no_memory;
}
@@ -1613,9 +1611,10 @@ box_object(dom_node *n,
/* start fetch (MIME type is ok or not specified) */
box->flags |= IS_REPLACED;
if (!html_fetch_object(content,
- params->data ? params->data : params->classid,
- box, CONTENT_ANY, content->base.available_width, 1000,
- false))
+ params->data ? params->data : params->classid,
+ box,
+ CONTENT_ANY,
+ false))
return false;
*convert_children = false;
diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c
index 01244cc..7230526 100644
--- a/content/handlers/html/html.c
+++ b/content/handlers/html/html.c
@@ -687,7 +687,7 @@ static bool html_process_img(html_content *c, dom_node *node)
dom_string_unref(src);
/* Speculatively fetch the image */
- success = html_fetch_object(c, url, NULL, CONTENT_IMAGE, 0, 0, false);
+ success = html_fetch_object(c, url, NULL, CONTENT_IMAGE, false);
nsurl_unref(url);
return success;
diff --git a/content/handlers/html/html_internal.h
b/content/handlers/html/html_internal.h
index d981017..6b55334 100644
--- a/content/handlers/html/html_internal.h
+++ b/content/handlers/html/html_internal.h
@@ -376,15 +376,10 @@ nserror html_css_fetcher_add_item(dom_string *data, nsurl
*base_url,
* \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);
+bool html_fetch_object(html_content *c, nsurl *url, struct box *box, content_type
permitted_types, bool background);
nserror html_object_free_objects(html_content *html);
nserror html_object_close_objects(html_content *html);
diff --git a/content/handlers/html/object.c b/content/handlers/html/object.c
index 587c487..c20118d 100644
--- a/content/handlers/html/object.c
+++ b/content/handlers/html/object.c
@@ -677,10 +677,12 @@ nserror html_object_free_objects(html_content *html)
/* exported interface documented in html/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)
+bool
+html_fetch_object(html_content *c,
+ nsurl *url,
+ struct box *box,
+ content_type permitted_types,
+ bool background)
{
struct content_html_object *object;
hlcache_child_context child;
@@ -706,10 +708,14 @@ bool html_fetch_object(html_content *c, nsurl *url, struct box
*box,
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);
+ 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;
commitdiff
http://git.netsurf-browser.org/netsurf.git/commit/?id=6b6bbad2b7765378b51...
commit 6b6bbad2b7765378b5135a26b3d410d6b0079966
Author: Vincent Sanders <vince(a)kyllikki.org>
Commit: Vincent Sanders <vince(a)kyllikki.org>
remove unecessary filename prefixes in the html content handler
diff --git a/content/handlers/html/Makefile b/content/handlers/html/Makefile
index 1976624..bdc0513 100644
--- a/content/handlers/html/Makefile
+++ b/content/handlers/html/Makefile
@@ -6,18 +6,18 @@ S_HTML := box_construct.c \
box_normalise.c \
box_special.c \
box_textarea.c \
+ css.c \
+ css_fetcher.c \
font.c \
form.c \
+ forms.c \
+ html.c \
imagemap.c \
+ interaction.c \
layout.c \
+ object.c \
+ redraw.c \
+ redraw_border.c \
+ script.c \
search.c \
- table.c \
- html.c \
- html_css.c \
- html_css_fetcher.c \
- html_script.c \
- interaction.c \
- html_redraw.c \
- html_redraw_border.c \
- html_forms.c \
- html_object.c
+ table.c
diff --git a/content/handlers/html/css.c b/content/handlers/html/css.c
new file mode 100644
index 0000000..23d51f1
--- /dev/null
+++ b/content/handlers/html/css.c
@@ -0,0 +1,739 @@
+/*
+ * Copyright 2013 Vincent Sanders <vince(a)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 css operations.
+ */
+
+#include "utils/config.h"
+
+#define _GNU_SOURCE /* strcasestr needs this for string.h */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include "utils/nsoption.h"
+#include "utils/corestrings.h"
+#include "utils/log.h"
+#include "netsurf/misc.h"
+#include "netsurf/content.h"
+#include "content/hlcache.h"
+#include "css/css.h"
+#include "desktop/gui_internal.h"
+
+#include "html/html.h"
+#include "html/html_internal.h"
+
+static nsurl *html_default_stylesheet_url;
+static nsurl *html_adblock_stylesheet_url;
+static nsurl *html_quirks_stylesheet_url;
+static nsurl *html_user_stylesheet_url;
+
+static nserror css_error_to_nserror(css_error error)
+{
+ switch (error) {
+ case CSS_OK:
+ return NSERROR_OK;
+
+ case CSS_NOMEM:
+ return NSERROR_NOMEM;
+
+ case CSS_BADPARM:
+ return NSERROR_BAD_PARAMETER;
+
+ case CSS_INVALID:
+ return NSERROR_INVALID;
+
+ case CSS_FILENOTFOUND:
+ return NSERROR_NOT_FOUND;
+
+ case CSS_NEEDDATA:
+ return NSERROR_NEED_DATA;
+
+ case CSS_BADCHARSET:
+ return NSERROR_BAD_ENCODING;
+
+ case CSS_EOF:
+ case CSS_IMPORTS_PENDING:
+ case CSS_PROPERTY_NOT_SET:
+ default:
+ break;
+ }
+ return NSERROR_CSS;
+}
+
+/**
+ * Callback for fetchcache() for stylesheets.
+ */
+
+static nserror
+html_convert_css_callback(hlcache_handle *css,
+ const hlcache_event *event,
+ void *pw)
+{
+ html_content *parent = pw;
+ unsigned int i;
+ struct html_stylesheet *s;
+
+ /* Find sheet */
+ for (i = 0, s = parent->stylesheets;
+ i != parent->stylesheet_count;
+ i++, s++) {
+ if (s->sheet == css)
+ break;
+ }
+
+ assert(i != parent->stylesheet_count);
+
+ switch (event->type) {
+
+ case CONTENT_MSG_DONE:
+ NSLOG(netsurf, INFO, "done stylesheet slot %d '%s'", i,
+ nsurl_access(hlcache_handle_get_url(css)));
+ parent->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+ break;
+
+ case CONTENT_MSG_ERROR:
+ NSLOG(netsurf, INFO, "stylesheet %s failed: %s",
+ nsurl_access(hlcache_handle_get_url(css)),
+ event->data.errordata.errormsg);
+
+ hlcache_handle_release(css);
+ s->sheet = NULL;
+ parent->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+ content_add_error(&parent->base, "?", 0);
+ break;
+
+ case CONTENT_MSG_POINTER:
+ /* Really don't want this to continue after the switch */
+ return NSERROR_OK;
+
+ default:
+ break;
+ }
+
+ if (html_can_begin_conversion(parent)) {
+ html_begin_conversion(parent);
+ }
+
+ return NSERROR_OK;
+}
+
+static nserror
+html_stylesheet_from_domnode(html_content *c,
+ dom_node *node,
+ hlcache_handle **sheet)
+{
+ hlcache_child_context child;
+ dom_string *style;
+ nsurl *url;
+ dom_exception exc;
+ nserror error;
+ uint32_t key;
+ char urlbuf[64];
+
+ child.charset = c->encoding;
+ child.quirks = c->base.quirks;
+
+ exc = dom_node_get_text_content(node, &style);
+ if ((exc != DOM_NO_ERR) || (style == NULL)) {
+ NSLOG(netsurf, INFO, "No text content");
+ return NSERROR_OK;
+ }
+
+ error = html_css_fetcher_add_item(style, c->base_url, &key);
+ if (error != NSERROR_OK) {
+ dom_string_unref(style);
+ return error;
+ }
+
+ dom_string_unref(style);
+
+ snprintf(urlbuf, sizeof(urlbuf), "x-ns-css:%u", key);
+
+ error = nsurl_create(urlbuf, &url);
+ if (error != NSERROR_OK) {
+ return error;
+ }
+
+ error = hlcache_handle_retrieve(url, 0,
+ content_get_url(&c->base), NULL,
+ html_convert_css_callback, c, &child, CONTENT_CSS,
+ sheet);
+ if (error != NSERROR_OK) {
+ nsurl_unref(url);
+ return error;
+ }
+
+ nsurl_unref(url);
+
+ c->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
+
+ return NSERROR_OK;
+}
+
+/**
+ * Process an inline stylesheet in the document.
+ *
+ * \param c content structure
+ * \param style xml node of style element
+ * \return true on success, false if an error occurred
+ */
+
+static struct html_stylesheet *
+html_create_style_element(html_content *c, dom_node *style)
+{
+ dom_string *val;
+ dom_exception exc;
+ struct html_stylesheet *stylesheets;
+
+ /* type='text/css', or not present (invalid but common) */
+ exc = dom_element_get_attribute(style, corestring_dom_type, &val);
+ if (exc == DOM_NO_ERR && val != NULL) {
+ if (!dom_string_caseless_lwc_isequal(val,
+ corestring_lwc_text_css)) {
+ dom_string_unref(val);
+ return NULL;
+ }
+ dom_string_unref(val);
+ }
+
+ /* media contains 'screen' or 'all' or not present */
+ exc = dom_element_get_attribute(style, corestring_dom_media, &val);
+ if (exc == DOM_NO_ERR && val != NULL) {
+ if (strcasestr(dom_string_data(val), "screen") == NULL &&
+ strcasestr(dom_string_data(val),
+ "all") == NULL) {
+ dom_string_unref(val);
+ return NULL;
+ }
+ dom_string_unref(val);
+ }
+
+ /* Extend array */
+ stylesheets = realloc(c->stylesheets,
+ sizeof(struct html_stylesheet) *
+ (c->stylesheet_count + 1));
+ if (stylesheets == NULL) {
+
+ content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
+ return false;
+
+ }
+ c->stylesheets = stylesheets;
+
+ c->stylesheets[c->stylesheet_count].node = dom_node_ref(style);
+ c->stylesheets[c->stylesheet_count].sheet = NULL;
+ c->stylesheets[c->stylesheet_count].modified = false;
+ c->stylesheets[c->stylesheet_count].unused = false;
+ c->stylesheet_count++;
+
+ return c->stylesheets + (c->stylesheet_count - 1);
+}
+
+static bool html_css_process_modified_style(html_content *c,
+ struct html_stylesheet *s)
+{
+ hlcache_handle *sheet = NULL;
+ nserror error;
+
+ error = html_stylesheet_from_domnode(c, s->node, &sheet);
+ if (error != NSERROR_OK) {
+ NSLOG(netsurf, INFO, "Failed to update sheet");
+ content_broadcast_error(&c->base, error, NULL);
+ return false;
+ }
+
+ if (sheet != NULL) {
+ NSLOG(netsurf, INFO, "Updating sheet %p with %p", s->sheet,
+ sheet);
+
+ if (s->sheet != NULL) {
+ switch (content_get_status(s->sheet)) {
+ case CONTENT_STATUS_DONE:
+ break;
+ default:
+ hlcache_handle_abort(s->sheet);
+ c->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active",
+ c->base.active);
+ }
+ hlcache_handle_release(s->sheet);
+ }
+ s->sheet = sheet;
+ }
+
+ s->modified = false;
+
+ return true;
+}
+
+static void html_css_process_modified_styles(void *pw)
+{
+ html_content *c = pw;
+ struct html_stylesheet *s;
+ unsigned int i;
+ bool all_done = true;
+
+ for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
+ if (c->stylesheets[i].modified) {
+ all_done &= html_css_process_modified_style(c, s);
+ }
+ }
+
+ /* If we failed to process any sheet, schedule a retry */
+ if (all_done == false) {
+ guit->misc->schedule(1000, html_css_process_modified_styles, c);
+ }
+}
+
+bool html_css_update_style(html_content *c, dom_node *style)
+{
+ unsigned int i;
+ struct html_stylesheet *s;
+
+ /* Find sheet */
+ for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
+ if (s->node == style)
+ break;
+ }
+ if (i == c->stylesheet_count) {
+ s = html_create_style_element(c, style);
+ }
+ if (s == NULL) {
+ NSLOG(netsurf, INFO,
+ "Could not find or create inline stylesheet for %p",
+ style);
+ return false;
+ }
+
+ s->modified = true;
+
+ guit->misc->schedule(0, html_css_process_modified_styles, c);
+
+ return true;
+}
+
+bool html_css_process_style(html_content *c, dom_node *node)
+{
+ unsigned int i;
+ dom_string *val;
+ dom_exception exc;
+ struct html_stylesheet *s;
+
+ /* Find sheet */
+ for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
+ if (s->node == node)
+ break;
+ }
+
+ /* Should already exist */
+ if (i == c->stylesheet_count) {
+ return false;
+ }
+
+ exc = dom_element_get_attribute(node, corestring_dom_media, &val);
+ if (exc == DOM_NO_ERR && val != NULL) {
+ if (strcasestr(dom_string_data(val), "screen") == NULL &&
+ strcasestr(dom_string_data(val),
+ "all") == NULL) {
+ s->unused = true;
+ }
+ dom_string_unref(val);
+ }
+
+ return true;
+}
+
+bool html_css_process_link(html_content *htmlc, dom_node *node)
+{
+ dom_string *rel, *type_attr, *media, *href;
+ struct html_stylesheet *stylesheets;
+ nsurl *joined;
+ dom_exception exc;
+ nserror ns_error;
+ hlcache_child_context child;
+
+ /* rel=<space separated list, including 'stylesheet'> */
+ exc = dom_element_get_attribute(node, corestring_dom_rel, &rel);
+ if (exc != DOM_NO_ERR || rel == NULL)
+ return true;
+
+ if (strcasestr(dom_string_data(rel), "stylesheet") == 0) {
+ dom_string_unref(rel);
+ return true;
+ } else if (strcasestr(dom_string_data(rel), "alternate") != 0) {
+ /* Ignore alternate stylesheets */
+ dom_string_unref(rel);
+ return true;
+ }
+ dom_string_unref(rel);
+
+ /* type='text/css' or not present */
+ exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr);
+ if (exc == DOM_NO_ERR && type_attr != NULL) {
+ if (!dom_string_caseless_lwc_isequal(type_attr,
+ corestring_lwc_text_css)) {
+ dom_string_unref(type_attr);
+ return true;
+ }
+ dom_string_unref(type_attr);
+ }
+
+ /* media contains 'screen' or 'all' or not present */
+ exc = dom_element_get_attribute(node, corestring_dom_media, &media);
+ if (exc == DOM_NO_ERR && media != NULL) {
+ if (strcasestr(dom_string_data(media), "screen") == NULL &&
+ strcasestr(dom_string_data(media), "all") == NULL) {
+ dom_string_unref(media);
+ return true;
+ }
+ dom_string_unref(media);
+ }
+
+ /* href='...' */
+ exc = dom_element_get_attribute(node, corestring_dom_href, &href);
+ if (exc != DOM_NO_ERR || href == NULL)
+ return true;
+
+ /* TODO: only the first preferred stylesheets (ie.
+ * those with a title attribute) should be loaded
+ * (see HTML4 14.3) */
+
+ ns_error = nsurl_join(htmlc->base_url, dom_string_data(href), &joined);
+ if (ns_error != NSERROR_OK) {
+ dom_string_unref(href);
+ goto no_memory;
+ }
+ dom_string_unref(href);
+
+ NSLOG(netsurf, INFO, "linked stylesheet %i '%s'",
+ htmlc->stylesheet_count, nsurl_access(joined));
+
+ /* extend stylesheets array to allow for new sheet */
+ stylesheets = realloc(htmlc->stylesheets,
+ sizeof(struct html_stylesheet) *
+ (htmlc->stylesheet_count + 1));
+ if (stylesheets == NULL) {
+ nsurl_unref(joined);
+ ns_error = NSERROR_NOMEM;
+ goto no_memory;
+ }
+
+ htmlc->stylesheets = stylesheets;
+ htmlc->stylesheets[htmlc->stylesheet_count].node = NULL;
+ htmlc->stylesheets[htmlc->stylesheet_count].modified = false;
+ htmlc->stylesheets[htmlc->stylesheet_count].unused = false;
+
+ /* start fetch */
+ child.charset = htmlc->encoding;
+ child.quirks = htmlc->base.quirks;
+
+ ns_error = hlcache_handle_retrieve(joined, 0,
+ content_get_url(&htmlc->base),
+ NULL, html_convert_css_callback,
+ htmlc, &child, CONTENT_CSS,
+ &htmlc->stylesheets[htmlc->stylesheet_count].sheet);
+
+ nsurl_unref(joined);
+
+ if (ns_error != NSERROR_OK)
+ goto no_memory;
+
+ htmlc->stylesheet_count++;
+
+ htmlc->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", htmlc->base.active);
+
+ return true;
+
+no_memory:
+ content_broadcast_error(&htmlc->base, ns_error, NULL);
+ return false;
+}
+
+/* exported interface documented in html/html.h */
+struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n)
+{
+ html_content *c = (html_content *) hlcache_handle_get_content(h);
+
+ assert(c != NULL);
+ assert(n != NULL);
+
+ *n = c->stylesheet_count;
+
+ return c->stylesheets;
+}
+
+/* exported interface documented in html/html_internal.h */
+bool html_saw_insecure_stylesheets(html_content *html)
+{
+ struct html_stylesheet *s;
+ unsigned int i;
+
+ for (i = 0, s = html->stylesheets; i < html->stylesheet_count;
+ i++, s++) {
+ if (s->sheet != NULL) {
+ if (content_saw_insecure_objects(s->sheet)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* exported interface documented in html/html_internal.h */
+nserror html_css_free_stylesheets(html_content *html)
+{
+ unsigned int i;
+
+ guit->misc->schedule(-1, html_css_process_modified_styles, html);
+
+ for (i = 0; i != html->stylesheet_count; i++) {
+ if (html->stylesheets[i].sheet != NULL) {
+ hlcache_handle_release(html->stylesheets[i].sheet);
+ }
+ if (html->stylesheets[i].node != NULL) {
+ dom_node_unref(html->stylesheets[i].node);
+ }
+ }
+ free(html->stylesheets);
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in html/html_internal.h */
+nserror html_css_quirks_stylesheets(html_content *c)
+{
+ nserror ns_error = NSERROR_OK;
+ hlcache_child_context child;
+
+ assert(c->stylesheets != NULL);
+
+ if (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL) {
+ child.charset = c->encoding;
+ child.quirks = c->base.quirks;
+
+ ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url,
+ 0, content_get_url(&c->base), NULL,
+ html_convert_css_callback, c, &child,
+ CONTENT_CSS,
+ &c->stylesheets[STYLESHEET_QUIRKS].sheet);
+ if (ns_error != NSERROR_OK) {
+ return ns_error;
+ }
+
+ c->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
+ }
+
+ return ns_error;
+}
+
+/* exported interface documented in html/html_internal.h */
+nserror html_css_new_stylesheets(html_content *c)
+{
+ nserror ns_error;
+ hlcache_child_context child;
+
+ if (c->stylesheets != NULL) {
+ return NSERROR_OK; /* already initialised */
+ }
+
+ /* stylesheet 0 is the base style sheet,
+ * stylesheet 1 is the quirks mode style sheet,
+ * stylesheet 2 is the adblocking stylesheet,
+ * stylesheet 3 is the user stylesheet */
+ c->stylesheets = calloc(STYLESHEET_START,
+ sizeof(struct html_stylesheet));
+ if (c->stylesheets == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ c->stylesheets[STYLESHEET_BASE].sheet = NULL;
+ c->stylesheets[STYLESHEET_QUIRKS].sheet = NULL;
+ c->stylesheets[STYLESHEET_ADBLOCK].sheet = NULL;
+ c->stylesheets[STYLESHEET_USER].sheet = NULL;
+ c->stylesheet_count = STYLESHEET_START;
+
+ child.charset = c->encoding;
+ child.quirks = c->base.quirks;
+
+ ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0,
+ content_get_url(&c->base), NULL,
+ html_convert_css_callback, c, &child, CONTENT_CSS,
+ &c->stylesheets[STYLESHEET_BASE].sheet);
+ if (ns_error != NSERROR_OK) {
+ return ns_error;
+ }
+
+ c->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
+
+
+ if (nsoption_bool(block_advertisements)) {
+ ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url,
+ 0, content_get_url(&c->base), NULL,
+ html_convert_css_callback,
+ c, &child, CONTENT_CSS,
+ &c->stylesheets[STYLESHEET_ADBLOCK].sheet);
+ if (ns_error != NSERROR_OK) {
+ return ns_error;
+ }
+
+ c->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
+
+ }
+
+ ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0,
+ content_get_url(&c->base), NULL,
+ html_convert_css_callback, c, &child, CONTENT_CSS,
+ &c->stylesheets[STYLESHEET_USER].sheet);
+ if (ns_error != NSERROR_OK) {
+ return ns_error;
+ }
+
+ c->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
+
+ return ns_error;
+}
+
+nserror
+html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx)
+{
+ uint32_t i;
+ css_error css_ret;
+ css_select_ctx *select_ctx;
+
+ /* check that the base stylesheet loaded; layout fails without it */
+ if (c->stylesheets[STYLESHEET_BASE].sheet == NULL) {
+ return NSERROR_CSS_BASE;
+ }
+
+ /* Create selection context */
+ css_ret = css_select_ctx_create(&select_ctx);
+ if (css_ret != CSS_OK) {
+ return css_error_to_nserror(css_ret);
+ }
+
+ /* Add sheets to it */
+ for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) {
+ const struct html_stylesheet *hsheet = &c->stylesheets[i];
+ css_stylesheet *sheet = NULL;
+ css_origin origin = CSS_ORIGIN_AUTHOR;
+
+ /* Filter out stylesheets for non-screen media. */
+ /* TODO: We should probably pass the sheet in anyway, and let
+ * libcss handle the filtering.
+ */
+ if (hsheet->unused) {
+ continue;
+ }
+
+ if (i < STYLESHEET_USER) {
+ origin = CSS_ORIGIN_UA;
+ } else if (i < STYLESHEET_START) {
+ origin = CSS_ORIGIN_USER;
+ }
+
+ if (hsheet->sheet != NULL) {
+ sheet = nscss_get_stylesheet(hsheet->sheet);
+ }
+
+ if (sheet != NULL) {
+ /* TODO: Pass the sheet's full media query, instead of
+ * "screen".
+ */
+ css_ret = css_select_ctx_append_sheet(select_ctx,
+ sheet,
+ origin,
+ "screen");
+ if (css_ret != CSS_OK) {
+ css_select_ctx_destroy(select_ctx);
+ return css_error_to_nserror(css_ret);
+ }
+ }
+ }
+
+ /* return new selection context to caller */
+ *ret_select_ctx = select_ctx;
+ return NSERROR_OK;
+}
+
+nserror html_css_init(void)
+{
+ nserror error;
+
+ error = html_css_fetcher_register();
+ if (error != NSERROR_OK)
+ return error;
+
+ error = nsurl_create("resource:default.css",
+ &html_default_stylesheet_url);
+ if (error != NSERROR_OK)
+ return error;
+
+ error = nsurl_create("resource:adblock.css",
+ &html_adblock_stylesheet_url);
+ if (error != NSERROR_OK)
+ return error;
+
+ error = nsurl_create("resource:quirks.css",
+ &html_quirks_stylesheet_url);
+ if (error != NSERROR_OK)
+ return error;
+
+ error = nsurl_create("resource:user.css",
+ &html_user_stylesheet_url);
+
+ return error;
+}
+
+void html_css_fini(void)
+{
+ if (html_user_stylesheet_url != NULL) {
+ nsurl_unref(html_user_stylesheet_url);
+ html_user_stylesheet_url = NULL;
+ }
+
+ if (html_quirks_stylesheet_url != NULL) {
+ nsurl_unref(html_quirks_stylesheet_url);
+ html_quirks_stylesheet_url = NULL;
+ }
+
+ if (html_adblock_stylesheet_url != NULL) {
+ nsurl_unref(html_adblock_stylesheet_url);
+ html_adblock_stylesheet_url = NULL;
+ }
+
+ if (html_default_stylesheet_url != NULL) {
+ nsurl_unref(html_default_stylesheet_url);
+ html_default_stylesheet_url = NULL;
+ }
+}
diff --git a/content/handlers/html/css_fetcher.c b/content/handlers/html/css_fetcher.c
new file mode 100644
index 0000000..ae20879
--- /dev/null
+++ b/content/handlers/html/css_fetcher.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2008 Rob Kendrick <rjek(a)netsurf-browser.org>
+ * Copyright 2013 John-Mark Bell <jmb(a)netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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
+ * HTML fetcher for CSS objects
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dom/dom.h>
+#include <libwapcaplet/libwapcaplet.h>
+
+#include "netsurf/inttypes.h"
+#include "utils/config.h"
+#include "utils/corestrings.h"
+#include "utils/log.h"
+#include "utils/ring.h"
+#include "utils/nsurl.h"
+#include "utils/utils.h"
+#include "content/fetch.h"
+#include "content/fetchers.h"
+
+#include "html/html_internal.h"
+
+typedef struct html_css_fetcher_item {
+ uint32_t key;
+ dom_string *data;
+ nsurl *base_url;
+
+ struct html_css_fetcher_item *r_next, *r_prev;
+} html_css_fetcher_item;
+
+typedef struct html_css_fetcher_context {
+ struct fetch *parent_fetch;
+
+ nsurl *url;
+ html_css_fetcher_item *item;
+
+ bool aborted;
+ bool locked;
+
+ struct html_css_fetcher_context *r_next, *r_prev;
+} html_css_fetcher_context;
+
+static uint32_t current_key = 0;
+static html_css_fetcher_item *items = NULL;
+static html_css_fetcher_context *ring = NULL;
+
+static bool html_css_fetcher_initialise(lwc_string *scheme)
+{
+ NSLOG(netsurf, INFO, "html_css_fetcher_initialise called for %s",
+ lwc_string_data(scheme));
+ return true;
+}
+
+static void html_css_fetcher_finalise(lwc_string *scheme)
+{
+ NSLOG(netsurf, INFO, "html_css_fetcher_finalise called for %s",
+ lwc_string_data(scheme));
+}
+
+static bool html_css_fetcher_can_fetch(const nsurl *url)
+{
+ return true;
+}
+
+static void *html_css_fetcher_setup(struct fetch *parent_fetch, nsurl *url,
+ bool only_2xx, bool downgrade_tls, const char *post_urlenc,
+ const struct fetch_multipart_data *post_multipart,
+ const char **headers)
+{
+ html_css_fetcher_context *ctx;
+ lwc_string *path;
+ uint32_t key;
+ html_css_fetcher_item *item, *found = NULL;
+
+ /* format of a x-ns-css URL is:
+ * x-ns-url:<key>
+ * Where key is an unsigned 32bit integer
+ */
+
+ path = nsurl_get_component(url, NSURL_PATH);
+ /* The path must exist */
+ if (path == NULL) {
+ return NULL;
+ }
+
+ key = strtoul(lwc_string_data(path), NULL, 10);
+
+ lwc_string_unref(path);
+
+ /* There must be at least one item */
+ if (items == NULL) {
+ return NULL;
+ }
+
+ item = items;
+ do {
+ if (item->key == key) {
+ found = item;
+ break;
+ }
+
+ item = item->r_next;
+ } while (item != items);
+
+ /* We must have found the item */
+ if (found == NULL) {
+ return NULL;
+ }
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->parent_fetch = parent_fetch;
+ ctx->url = nsurl_ref(url);
+ ctx->item = found;
+
+ RING_INSERT(ring, ctx);
+
+ return ctx;
+}
+
+static bool html_css_fetcher_start(void *ctx)
+{
+ return true;
+}
+
+static void html_css_fetcher_free(void *ctx)
+{
+ html_css_fetcher_context *c = ctx;
+
+ nsurl_unref(c->url);
+ if (c->item != NULL) {
+ nsurl_unref(c->item->base_url);
+ dom_string_unref(c->item->data);
+ RING_REMOVE(items, c->item);
+ free(c->item);
+ }
+ RING_REMOVE(ring, c);
+ free(ctx);
+}
+
+static void html_css_fetcher_abort(void *ctx)
+{
+ html_css_fetcher_context *c = ctx;
+
+ /* To avoid the poll loop having to deal with the fetch context
+ * disappearing from under it, we simply flag the abort here.
+ * The poll loop itself will perform the appropriate cleanup.
+ */
+ c->aborted = true;
+}
+
+static void html_css_fetcher_send_callback(const fetch_msg *msg,
+ html_css_fetcher_context *c)
+{
+ c->locked = true;
+ fetch_send_callback(msg, c->parent_fetch);
+ c->locked = false;
+}
+
+static void html_css_fetcher_poll(lwc_string *scheme)
+{
+ fetch_msg msg;
+ html_css_fetcher_context *c, *next;
+
+ if (ring == NULL) return;
+
+ /* Iterate over ring, processing each pending fetch */
+ c = ring;
+ do {
+ /* Ignore fetches that have been flagged as locked.
+ * This allows safe re-entrant calls to this function.
+ * Re-entrancy can occur if, as a result of a callback,
+ * the interested party causes fetch_poll() to be called
+ * again.
+ */
+ if (c->locked == true) {
+ next = c->r_next;
+ continue;
+ }
+
+ /* Only process non-aborted fetches */
+ if (c->aborted) {
+ /* Nothing to do */
+ assert(c->locked == false);
+ } else if (c->item != NULL) {
+ char header[4096];
+
+ fetch_set_http_code(c->parent_fetch, 200);
+
+ /* Any callback can result in the fetch being aborted.
+ * Therefore, we _must_ check for this after _every_
+ * call to html_css_fetcher_send_callback().
+ */
+ snprintf(header, sizeof header,
+ "Content-Type: text/css; charset=utf-8");
+ msg.type = FETCH_HEADER;
+ msg.data.header_or_data.buf = (const uint8_t *) header;
+ msg.data.header_or_data.len = strlen(header);
+ html_css_fetcher_send_callback(&msg, c);
+
+ if (c->aborted == false) {
+ snprintf(header, sizeof header,
+ "Content-Length: %"PRIsizet,
+ dom_string_byte_length(c->item->data));
+ msg.type = FETCH_HEADER;
+ msg.data.header_or_data.buf =
+ (const uint8_t *) header;
+ msg.data.header_or_data.len = strlen(header);
+ html_css_fetcher_send_callback(&msg, c);
+ }
+
+ if (c->aborted == false) {
+ snprintf(header, sizeof header,
+ "X-NS-Base: %.*s",
+ (int) nsurl_length(c->item->base_url),
+ nsurl_access(c->item->base_url));
+ msg.type = FETCH_HEADER;
+ msg.data.header_or_data.buf =
+ (const uint8_t *) header;
+ msg.data.header_or_data.len = strlen(header);
+ html_css_fetcher_send_callback(&msg, c);
+ }
+
+ if (c->aborted == false) {
+ msg.type = FETCH_DATA;
+ msg.data.header_or_data.buf =
+ (const uint8_t *)
+ dom_string_data(c->item->data);
+ msg.data.header_or_data.len =
+ dom_string_byte_length(c->item->data);
+ html_css_fetcher_send_callback(&msg, c);
+ }
+
+ if (c->aborted == false) {
+ msg.type = FETCH_FINISHED;
+ html_css_fetcher_send_callback(&msg, c);
+ }
+ } else {
+ NSLOG(netsurf, INFO, "Processing of %s failed!",
+ nsurl_access(c->url));
+
+ /* Ensure that we're unlocked here. If we aren't,
+ * then html_css_fetcher_process() is broken.
+ */
+ assert(c->locked == false);
+ }
+
+ /* Compute next fetch item at the last possible moment as
+ * processing this item may have added to the ring.
+ */
+ next = c->r_next;
+
+ fetch_remove_from_queues(c->parent_fetch);
+ fetch_free(c->parent_fetch);
+
+ /* Advance to next ring entry, exiting if we've reached
+ * the start of the ring or the ring has become empty
+ */
+ } while ( (c = next) != ring && ring != NULL);
+}
+
+/* exported interface documented in html_internal.h */
+nserror html_css_fetcher_register(void)
+{
+ const struct fetcher_operation_table html_css_fetcher_ops = {
+ .initialise = html_css_fetcher_initialise,
+ .acceptable = html_css_fetcher_can_fetch,
+ .setup = html_css_fetcher_setup,
+ .start = html_css_fetcher_start,
+ .abort = html_css_fetcher_abort,
+ .free = html_css_fetcher_free,
+ .poll = html_css_fetcher_poll,
+ .finalise = html_css_fetcher_finalise
+ };
+
+ return fetcher_add(lwc_string_ref(corestring_lwc_x_ns_css),
+ &html_css_fetcher_ops);
+}
+
+/* exported interface documented in html_internal.h */
+nserror
+html_css_fetcher_add_item(dom_string *data, nsurl *base_url, uint32_t *key)
+{
+ html_css_fetcher_item *item = malloc(sizeof(*item));
+
+ if (item == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ *key = item->key = current_key++;
+ item->data = dom_string_ref(data);
+ item->base_url = nsurl_ref(base_url);
+
+ RING_INSERT(items, item);
+
+ return NSERROR_OK;
+}
diff --git a/content/handlers/html/forms.c b/content/handlers/html/forms.c
new file mode 100644
index 0000000..896263d
--- /dev/null
+++ b/content/handlers/html/forms.c
@@ -0,0 +1,590 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince(a)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
+ * HTML form handling implementation
+ */
+
+#include "utils/config.h"
+#include "utils/corestrings.h"
+#include "utils/log.h"
+
+#include "html/form_internal.h"
+#include "html/html_internal.h"
+
+/**
+ * process form element from dom
+ */
+static struct form *
+parse_form_element(const char *docenc, dom_node *node)
+{
+ dom_string *ds_action = NULL;
+ dom_string *ds_charset = NULL;
+ dom_string *ds_target = NULL;
+ dom_string *ds_method = NULL;
+ dom_string *ds_enctype = NULL;
+ char *action = NULL, *charset = NULL, *target = NULL;
+ form_method method;
+ dom_html_form_element *formele = (dom_html_form_element *)(node);
+ struct form * ret = NULL;
+
+ /* Retrieve the attributes from the node */
+ if (dom_html_form_element_get_action(formele,
+ &ds_action) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_form_element_get_accept_charset(formele,
+ &ds_charset) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_form_element_get_target(formele,
+ &ds_target) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_form_element_get_method(formele,
+ &ds_method) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_form_element_get_enctype(formele,
+ &ds_enctype) != DOM_NO_ERR)
+ goto out;
+
+ /* Extract the plain attributes ready for use. We have to do this
+ * because we cannot guarantee that the dom_strings are NULL terminated
+ * and thus we copy them.
+ */
+ if (ds_action != NULL)
+ action = strndup(dom_string_data(ds_action),
+ dom_string_byte_length(ds_action));
+
+ if (ds_charset != NULL)
+ charset = strndup(dom_string_data(ds_charset),
+ dom_string_byte_length(ds_charset));
+
+ if (ds_target != NULL)
+ target = strndup(dom_string_data(ds_target),
+ dom_string_byte_length(ds_target));
+
+ /* Determine the method */
+ method = method_GET;
+ if (ds_method != NULL) {
+ if (dom_string_caseless_lwc_isequal(ds_method,
+ corestring_lwc_post)) {
+ method = method_POST_URLENC;
+ if (ds_enctype != NULL) {
+ if (dom_string_caseless_lwc_isequal(ds_enctype,
+ corestring_lwc_multipart_form_data)) {
+
+ method = method_POST_MULTIPART;
+ }
+ }
+ }
+ }
+
+ /* Construct the form object */
+ ret = form_new(node, action, target, method, charset, docenc);
+
+out:
+ if (ds_action != NULL)
+ dom_string_unref(ds_action);
+ if (ds_charset != NULL)
+ dom_string_unref(ds_charset);
+ if (ds_target != NULL)
+ dom_string_unref(ds_target);
+ if (ds_method != NULL)
+ dom_string_unref(ds_method);
+ if (ds_enctype != NULL)
+ dom_string_unref(ds_enctype);
+ if (action != NULL)
+ free(action);
+ if (charset != NULL)
+ free(charset);
+ if (target != NULL)
+ free(target);
+ return ret;
+}
+
+/* documented in html_internal.h */
+struct form *html_forms_get_forms(const char *docenc, dom_html_document *doc)
+{
+ dom_html_collection *forms;
+ struct form *ret = NULL, *newf;
+ dom_node *node;
+ unsigned long n;
+ uint32_t nforms;
+
+ if (doc == NULL)
+ return NULL;
+
+ /* Attempt to build a set of all the forms */
+ if (dom_html_document_get_forms(doc, &forms) != DOM_NO_ERR)
+ return NULL;
+
+ /* Count the number of forms so we can iterate */
+ if (dom_html_collection_get_length(forms, &nforms) != DOM_NO_ERR)
+ goto out;
+
+ /* Iterate the forms collection, making form structs for returning */
+ for (n = 0; n < nforms; ++n) {
+ if (dom_html_collection_item(forms, n, &node) != DOM_NO_ERR) {
+ goto out;
+ }
+ newf = parse_form_element(docenc, node);
+ dom_node_unref(node);
+ if (newf == NULL) {
+ goto err;
+ }
+ newf->prev = ret;
+ ret = newf;
+ }
+
+ /* All went well */
+ goto out;
+err:
+ while (ret != NULL) {
+ struct form *prev = ret->prev;
+ /* Destroy ret */
+ free(ret);
+ ret = prev;
+ }
+out:
+ /* Finished with the collection, return it */
+ dom_html_collection_unref(forms);
+
+ return ret;
+}
+
+static struct form *
+find_form(struct form *forms, dom_html_form_element *form)
+{
+ while (forms != NULL) {
+ if (forms->node == form)
+ break;
+ forms = forms->prev;
+ }
+
+ return forms;
+}
+
+static struct form_control *
+parse_button_element(struct form *forms, dom_html_button_element *button)
+{
+ struct form_control *control = NULL;
+ dom_exception err;
+ dom_html_form_element *form = NULL;
+ dom_string *ds_type = NULL;
+ dom_string *ds_value = NULL;
+ dom_string *ds_name = NULL;
+
+ err = dom_html_button_element_get_form(button, &form);
+ if (err != DOM_NO_ERR)
+ goto out;
+
+ err = dom_html_button_element_get_type(button, &ds_type);
+ if (err != DOM_NO_ERR)
+ goto out;
+
+ if (ds_type == NULL) {
+ control = form_new_control(button, GADGET_SUBMIT);
+ } else {
+ if (dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_submit)) {
+ control = form_new_control(button, GADGET_SUBMIT);
+ } else if (dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_reset)) {
+ control = form_new_control(button, GADGET_RESET);
+ } else {
+ control = form_new_control(button, GADGET_BUTTON);
+ }
+ }
+
+ if (control == NULL)
+ goto out;
+
+ err = dom_html_button_element_get_value(button, &ds_value);
+ if (err != DOM_NO_ERR)
+ goto out;
+ err = dom_html_button_element_get_name(button, &ds_name);
+ if (err != DOM_NO_ERR)
+ goto out;
+
+ if (ds_value != NULL) {
+ control->value = strndup(
+ dom_string_data(ds_value),
+ dom_string_byte_length(ds_value));
+
+ if (control->value == NULL) {
+ form_free_control(control);
+ control = NULL;
+ goto out;
+ }
+ }
+
+ if (ds_name != NULL) {
+ control->name = strndup(
+ dom_string_data(ds_name),
+ dom_string_byte_length(ds_name));
+
+ if (control->name == NULL) {
+ form_free_control(control);
+ control = NULL;
+ goto out;
+ }
+ }
+
+ if (form != NULL && control != NULL)
+ form_add_control(find_form(forms, form), control);
+
+out:
+ if (form != NULL)
+ dom_node_unref(form);
+ if (ds_type != NULL)
+ dom_string_unref(ds_type);
+ if (ds_value != NULL)
+ dom_string_unref(ds_value);
+ if (ds_name != NULL)
+ dom_string_unref(ds_name);
+
+ return control;
+}
+
+static struct form_control *
+parse_input_element(struct form *forms, dom_html_input_element *input)
+{
+ struct form_control *control = NULL;
+ dom_html_form_element *form = NULL;
+ dom_string *ds_type = NULL;
+ dom_string *ds_name = NULL;
+ dom_string *ds_value = NULL;
+
+ char *name = NULL;
+
+ if (dom_html_input_element_get_form(input, &form) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_input_element_get_type(input, &ds_type) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_input_element_get_name(input, &ds_name) != DOM_NO_ERR)
+ goto out;
+
+ if (ds_name != NULL)
+ name = strndup(dom_string_data(ds_name),
+ dom_string_byte_length(ds_name));
+
+ if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_password)) {
+ control = form_new_control(input, GADGET_PASSWORD);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_file)) {
+ control = form_new_control(input, GADGET_FILE);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_hidden)) {
+ control = form_new_control(input, GADGET_HIDDEN);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_checkbox)) {
+ control = form_new_control(input, GADGET_CHECKBOX);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_radio)) {
+ control = form_new_control(input, GADGET_RADIO);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_submit)) {
+ control = form_new_control(input, GADGET_SUBMIT);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_reset)) {
+ control = form_new_control(input, GADGET_RESET);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_button)) {
+ control = form_new_control(input, GADGET_BUTTON);
+ } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
+ corestring_lwc_image)) {
+ control = form_new_control(input, GADGET_IMAGE);
+ } else {
+ control = form_new_control(input, GADGET_TEXTBOX);
+ }
+
+ if (control == NULL)
+ goto out;
+
+ if (name != NULL) {
+ /* Hand the name string over */
+ control->name = name;
+ name = NULL;
+ }
+
+ if (control->type == GADGET_CHECKBOX || control->type == GADGET_RADIO) {
+ bool selected;
+ if (dom_html_input_element_get_checked(
+ input, &selected) == DOM_NO_ERR) {
+ control->selected = selected;
+ }
+ }
+
+ if (control->type == GADGET_PASSWORD ||
+ control->type == GADGET_TEXTBOX) {
+ int32_t maxlength;
+ if (dom_html_input_element_get_max_length(
+ input, &maxlength) != DOM_NO_ERR) {
+ maxlength = -1;
+ }
+
+ if (maxlength >= 0) {
+ /* Got valid maxlength */
+ control->maxlength = maxlength;
+ } else {
+ /* Input has no maxlength attr, or
+ * dom_html_input_element_get_max_length failed.
+ *
+ * Set it to something insane. */
+ control->maxlength = UINT_MAX;
+ }
+ }
+
+ if (control->type != GADGET_FILE && control->type != GADGET_IMAGE) {
+ if (dom_html_input_element_get_value(
+ input, &ds_value) == DOM_NO_ERR) {
+ if (ds_value != NULL) {
+ control->value = strndup(
+ dom_string_data(ds_value),
+ dom_string_byte_length(ds_value));
+ if (control->value == NULL) {
+ form_free_control(control);
+ control = NULL;
+ goto out;
+ }
+ control->length = strlen(control->value);
+ }
+ }
+
+ if (control->type == GADGET_TEXTBOX ||
+ control->type == GADGET_PASSWORD) {
+ if (control->value == NULL) {
+ control->value = strdup("");
+ if (control->value == NULL) {
+ form_free_control(control);
+ control = NULL;
+ goto out;
+ }
+
+ control->length = 0;
+ }
+
+ control->initial_value = strdup(control->value);
+ if (control->initial_value == NULL) {
+ form_free_control(control);
+ control = NULL;
+ goto out;
+ }
+
+ control->last_synced_value = strdup(control->value);
+ if (control->last_synced_value == NULL) {
+ form_free_control(control);
+ control = NULL;
+ goto out;
+ }
+
+ control->node_value = dom_string_ref(ds_value);
+ }
+ /* Force the gadget and DOM to be in sync */
+ form_gadget_sync_with_dom(control);
+ }
+
+ if (form != NULL && control != NULL)
+ form_add_control(find_form(forms, form), control);
+
+out:
+ if (form != NULL)
+ dom_node_unref(form);
+ if (ds_type != NULL)
+ dom_string_unref(ds_type);
+ if (ds_name != NULL)
+ dom_string_unref(ds_name);
+ if (ds_value != NULL)
+ dom_string_unref(ds_value);
+
+ if (name != NULL)
+ free(name);
+
+ return control;
+}
+
+static struct form_control *
+parse_textarea_element(struct form *forms, dom_html_text_area_element *ta)
+{
+ struct form_control *control = NULL;
+ dom_html_form_element *form = NULL;
+ dom_string *ds_name = NULL;
+
+ char *name = NULL;
+
+ if (dom_html_text_area_element_get_form(ta, &form) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_text_area_element_get_name(ta, &ds_name) != DOM_NO_ERR)
+ goto out;
+
+ if (ds_name != NULL)
+ name = strndup(dom_string_data(ds_name),
+ dom_string_byte_length(ds_name));
+
+ control = form_new_control(ta, GADGET_TEXTAREA);
+
+ if (control == NULL)
+ goto out;
+
+ if (name != NULL) {
+ /* Hand the name string over */
+ control->name = name;
+ name = NULL;
+ }
+
+ if (form != NULL && control != NULL)
+ form_add_control(find_form(forms, form), control);
+
+out:
+ if (form != NULL)
+ dom_node_unref(form);
+ if (ds_name != NULL)
+ dom_string_unref(ds_name);
+
+ if (name != NULL)
+ free(name);
+
+
+ return control;
+}
+
+static struct form_control *
+parse_select_element(struct form *forms, dom_html_select_element *select)
+{
+ struct form_control *control = NULL;
+ dom_html_form_element *form = NULL;
+ dom_string *ds_name = NULL;
+
+ char *name = NULL;
+
+ if (dom_html_select_element_get_form(select, &form) != DOM_NO_ERR)
+ goto out;
+
+ if (dom_html_select_element_get_name(select, &ds_name) != DOM_NO_ERR)
+ goto out;
+
+ if (ds_name != NULL)
+ name = strndup(dom_string_data(ds_name),
+ dom_string_byte_length(ds_name));
+
+ control = form_new_control(select, GADGET_SELECT);
+
+ if (control == NULL)
+ goto out;
+
+ if (name != NULL) {
+ /* Hand the name string over */
+ control->name = name;
+ name = NULL;
+ }
+
+ dom_html_select_element_get_multiple(select,
+ &(control->data.select.multiple));
+
+ if (form != NULL && control != NULL)
+ form_add_control(find_form(forms, form), control);
+
+out:
+ if (form != NULL)
+ dom_node_unref(form);
+ if (ds_name != NULL)
+ dom_string_unref(ds_name);
+
+ if (name != NULL)
+ free(name);
+
+
+ return control;
+}
+
+
+static struct form_control *
+invent_fake_gadget(dom_node *node)
+{
+ struct form_control *ctl = form_new_control(node, GADGET_HIDDEN);
+ if (ctl != NULL) {
+ ctl->value = strdup("");
+ ctl->initial_value = strdup("");
+ ctl->name = strdup("foo");
+
+ if (ctl->value == NULL || ctl->initial_value == NULL ||
+ ctl->name == NULL) {
+ form_free_control(ctl);
+ ctl = NULL;
+ }
+ }
+ return ctl;
+}
+
+/* documented in html_internal.h */
+struct form_control *html_forms_get_control_for_node(struct form *forms,
+ dom_node *node)
+{
+ struct form *f;
+ struct form_control *ctl = NULL;
+ dom_exception err;
+ dom_string *ds_name = NULL;
+
+ /* Step one, see if we already have a control */
+ for (f = forms; f != NULL; f = f->prev) {
+ for (ctl = f->controls; ctl != NULL; ctl = ctl->next) {
+ if (ctl->node == node)
+ return ctl;
+ }
+ }
+
+ /* Step two, extract the node's name so we can construct a gadget. */
+ err = dom_element_get_tag_name(node, &ds_name);
+ if (err == DOM_NO_ERR && ds_name != NULL) {
+
+ /* Step three, attempt to work out what gadget to make */
+ if (dom_string_caseless_lwc_isequal(ds_name,
+ corestring_lwc_button)) {
+ ctl = parse_button_element(forms,
+ (dom_html_button_element *) node);
+ } else if (dom_string_caseless_lwc_isequal(ds_name,
+ corestring_lwc_input)) {
+ ctl = parse_input_element(forms,
+ (dom_html_input_element *) node);
+ } else if (dom_string_caseless_lwc_isequal(ds_name,
+ corestring_lwc_textarea)) {
+ ctl = parse_textarea_element(forms,
+ (dom_html_text_area_element *) node);
+ } else if (dom_string_caseless_lwc_isequal(ds_name,
+ corestring_lwc_select)) {
+ ctl = parse_select_element(forms,
+ (dom_html_select_element *) node);
+ }
+ }
+
+ /* If all else fails, fake gadget time */
+ if (ctl == NULL)
+ ctl = invent_fake_gadget(node);
+
+ if (ds_name != NULL)
+ dom_string_unref(ds_name);
+
+ return ctl;
+}
diff --git a/content/handlers/html/html_css.c b/content/handlers/html/html_css.c
deleted file mode 100644
index 23d51f1..0000000
--- a/content/handlers/html/html_css.c
+++ /dev/null
@@ -1,739 +0,0 @@
-/*
- * Copyright 2013 Vincent Sanders <vince(a)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 css operations.
- */
-
-#include "utils/config.h"
-
-#define _GNU_SOURCE /* strcasestr needs this for string.h */
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdint.h>
-#include <string.h>
-#include <strings.h>
-#include <stdlib.h>
-
-#include "utils/nsoption.h"
-#include "utils/corestrings.h"
-#include "utils/log.h"
-#include "netsurf/misc.h"
-#include "netsurf/content.h"
-#include "content/hlcache.h"
-#include "css/css.h"
-#include "desktop/gui_internal.h"
-
-#include "html/html.h"
-#include "html/html_internal.h"
-
-static nsurl *html_default_stylesheet_url;
-static nsurl *html_adblock_stylesheet_url;
-static nsurl *html_quirks_stylesheet_url;
-static nsurl *html_user_stylesheet_url;
-
-static nserror css_error_to_nserror(css_error error)
-{
- switch (error) {
- case CSS_OK:
- return NSERROR_OK;
-
- case CSS_NOMEM:
- return NSERROR_NOMEM;
-
- case CSS_BADPARM:
- return NSERROR_BAD_PARAMETER;
-
- case CSS_INVALID:
- return NSERROR_INVALID;
-
- case CSS_FILENOTFOUND:
- return NSERROR_NOT_FOUND;
-
- case CSS_NEEDDATA:
- return NSERROR_NEED_DATA;
-
- case CSS_BADCHARSET:
- return NSERROR_BAD_ENCODING;
-
- case CSS_EOF:
- case CSS_IMPORTS_PENDING:
- case CSS_PROPERTY_NOT_SET:
- default:
- break;
- }
- return NSERROR_CSS;
-}
-
-/**
- * Callback for fetchcache() for stylesheets.
- */
-
-static nserror
-html_convert_css_callback(hlcache_handle *css,
- const hlcache_event *event,
- void *pw)
-{
- html_content *parent = pw;
- unsigned int i;
- struct html_stylesheet *s;
-
- /* Find sheet */
- for (i = 0, s = parent->stylesheets;
- i != parent->stylesheet_count;
- i++, s++) {
- if (s->sheet == css)
- break;
- }
-
- assert(i != parent->stylesheet_count);
-
- switch (event->type) {
-
- case CONTENT_MSG_DONE:
- NSLOG(netsurf, INFO, "done stylesheet slot %d '%s'", i,
- nsurl_access(hlcache_handle_get_url(css)));
- parent->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
- break;
-
- case CONTENT_MSG_ERROR:
- NSLOG(netsurf, INFO, "stylesheet %s failed: %s",
- nsurl_access(hlcache_handle_get_url(css)),
- event->data.errordata.errormsg);
-
- hlcache_handle_release(css);
- s->sheet = NULL;
- parent->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
- content_add_error(&parent->base, "?", 0);
- break;
-
- case CONTENT_MSG_POINTER:
- /* Really don't want this to continue after the switch */
- return NSERROR_OK;
-
- default:
- break;
- }
-
- if (html_can_begin_conversion(parent)) {
- html_begin_conversion(parent);
- }
-
- return NSERROR_OK;
-}
-
-static nserror
-html_stylesheet_from_domnode(html_content *c,
- dom_node *node,
- hlcache_handle **sheet)
-{
- hlcache_child_context child;
- dom_string *style;
- nsurl *url;
- dom_exception exc;
- nserror error;
- uint32_t key;
- char urlbuf[64];
-
- child.charset = c->encoding;
- child.quirks = c->base.quirks;
-
- exc = dom_node_get_text_content(node, &style);
- if ((exc != DOM_NO_ERR) || (style == NULL)) {
- NSLOG(netsurf, INFO, "No text content");
- return NSERROR_OK;
- }
-
- error = html_css_fetcher_add_item(style, c->base_url, &key);
- if (error != NSERROR_OK) {
- dom_string_unref(style);
- return error;
- }
-
- dom_string_unref(style);
-
- snprintf(urlbuf, sizeof(urlbuf), "x-ns-css:%u", key);
-
- error = nsurl_create(urlbuf, &url);
- if (error != NSERROR_OK) {
- return error;
- }
-
- error = hlcache_handle_retrieve(url, 0,
- content_get_url(&c->base), NULL,
- html_convert_css_callback, c, &child, CONTENT_CSS,
- sheet);
- if (error != NSERROR_OK) {
- nsurl_unref(url);
- return error;
- }
-
- nsurl_unref(url);
-
- c->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
-
- return NSERROR_OK;
-}
-
-/**
- * Process an inline stylesheet in the document.
- *
- * \param c content structure
- * \param style xml node of style element
- * \return true on success, false if an error occurred
- */
-
-static struct html_stylesheet *
-html_create_style_element(html_content *c, dom_node *style)
-{
- dom_string *val;
- dom_exception exc;
- struct html_stylesheet *stylesheets;
-
- /* type='text/css', or not present (invalid but common) */
- exc = dom_element_get_attribute(style, corestring_dom_type, &val);
- if (exc == DOM_NO_ERR && val != NULL) {
- if (!dom_string_caseless_lwc_isequal(val,
- corestring_lwc_text_css)) {
- dom_string_unref(val);
- return NULL;
- }
- dom_string_unref(val);
- }
-
- /* media contains 'screen' or 'all' or not present */
- exc = dom_element_get_attribute(style, corestring_dom_media, &val);
- if (exc == DOM_NO_ERR && val != NULL) {
- if (strcasestr(dom_string_data(val), "screen") == NULL &&
- strcasestr(dom_string_data(val),
- "all") == NULL) {
- dom_string_unref(val);
- return NULL;
- }
- dom_string_unref(val);
- }
-
- /* Extend array */
- stylesheets = realloc(c->stylesheets,
- sizeof(struct html_stylesheet) *
- (c->stylesheet_count + 1));
- if (stylesheets == NULL) {
-
- content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
- return false;
-
- }
- c->stylesheets = stylesheets;
-
- c->stylesheets[c->stylesheet_count].node = dom_node_ref(style);
- c->stylesheets[c->stylesheet_count].sheet = NULL;
- c->stylesheets[c->stylesheet_count].modified = false;
- c->stylesheets[c->stylesheet_count].unused = false;
- c->stylesheet_count++;
-
- return c->stylesheets + (c->stylesheet_count - 1);
-}
-
-static bool html_css_process_modified_style(html_content *c,
- struct html_stylesheet *s)
-{
- hlcache_handle *sheet = NULL;
- nserror error;
-
- error = html_stylesheet_from_domnode(c, s->node, &sheet);
- if (error != NSERROR_OK) {
- NSLOG(netsurf, INFO, "Failed to update sheet");
- content_broadcast_error(&c->base, error, NULL);
- return false;
- }
-
- if (sheet != NULL) {
- NSLOG(netsurf, INFO, "Updating sheet %p with %p", s->sheet,
- sheet);
-
- if (s->sheet != NULL) {
- switch (content_get_status(s->sheet)) {
- case CONTENT_STATUS_DONE:
- break;
- default:
- hlcache_handle_abort(s->sheet);
- c->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active",
- c->base.active);
- }
- hlcache_handle_release(s->sheet);
- }
- s->sheet = sheet;
- }
-
- s->modified = false;
-
- return true;
-}
-
-static void html_css_process_modified_styles(void *pw)
-{
- html_content *c = pw;
- struct html_stylesheet *s;
- unsigned int i;
- bool all_done = true;
-
- for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
- if (c->stylesheets[i].modified) {
- all_done &= html_css_process_modified_style(c, s);
- }
- }
-
- /* If we failed to process any sheet, schedule a retry */
- if (all_done == false) {
- guit->misc->schedule(1000, html_css_process_modified_styles, c);
- }
-}
-
-bool html_css_update_style(html_content *c, dom_node *style)
-{
- unsigned int i;
- struct html_stylesheet *s;
-
- /* Find sheet */
- for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
- if (s->node == style)
- break;
- }
- if (i == c->stylesheet_count) {
- s = html_create_style_element(c, style);
- }
- if (s == NULL) {
- NSLOG(netsurf, INFO,
- "Could not find or create inline stylesheet for %p",
- style);
- return false;
- }
-
- s->modified = true;
-
- guit->misc->schedule(0, html_css_process_modified_styles, c);
-
- return true;
-}
-
-bool html_css_process_style(html_content *c, dom_node *node)
-{
- unsigned int i;
- dom_string *val;
- dom_exception exc;
- struct html_stylesheet *s;
-
- /* Find sheet */
- for (i = 0, s = c->stylesheets; i != c->stylesheet_count; i++, s++) {
- if (s->node == node)
- break;
- }
-
- /* Should already exist */
- if (i == c->stylesheet_count) {
- return false;
- }
-
- exc = dom_element_get_attribute(node, corestring_dom_media, &val);
- if (exc == DOM_NO_ERR && val != NULL) {
- if (strcasestr(dom_string_data(val), "screen") == NULL &&
- strcasestr(dom_string_data(val),
- "all") == NULL) {
- s->unused = true;
- }
- dom_string_unref(val);
- }
-
- return true;
-}
-
-bool html_css_process_link(html_content *htmlc, dom_node *node)
-{
- dom_string *rel, *type_attr, *media, *href;
- struct html_stylesheet *stylesheets;
- nsurl *joined;
- dom_exception exc;
- nserror ns_error;
- hlcache_child_context child;
-
- /* rel=<space separated list, including 'stylesheet'> */
- exc = dom_element_get_attribute(node, corestring_dom_rel, &rel);
- if (exc != DOM_NO_ERR || rel == NULL)
- return true;
-
- if (strcasestr(dom_string_data(rel), "stylesheet") == 0) {
- dom_string_unref(rel);
- return true;
- } else if (strcasestr(dom_string_data(rel), "alternate") != 0) {
- /* Ignore alternate stylesheets */
- dom_string_unref(rel);
- return true;
- }
- dom_string_unref(rel);
-
- /* type='text/css' or not present */
- exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr);
- if (exc == DOM_NO_ERR && type_attr != NULL) {
- if (!dom_string_caseless_lwc_isequal(type_attr,
- corestring_lwc_text_css)) {
- dom_string_unref(type_attr);
- return true;
- }
- dom_string_unref(type_attr);
- }
-
- /* media contains 'screen' or 'all' or not present */
- exc = dom_element_get_attribute(node, corestring_dom_media, &media);
- if (exc == DOM_NO_ERR && media != NULL) {
- if (strcasestr(dom_string_data(media), "screen") == NULL &&
- strcasestr(dom_string_data(media), "all") == NULL) {
- dom_string_unref(media);
- return true;
- }
- dom_string_unref(media);
- }
-
- /* href='...' */
- exc = dom_element_get_attribute(node, corestring_dom_href, &href);
- if (exc != DOM_NO_ERR || href == NULL)
- return true;
-
- /* TODO: only the first preferred stylesheets (ie.
- * those with a title attribute) should be loaded
- * (see HTML4 14.3) */
-
- ns_error = nsurl_join(htmlc->base_url, dom_string_data(href), &joined);
- if (ns_error != NSERROR_OK) {
- dom_string_unref(href);
- goto no_memory;
- }
- dom_string_unref(href);
-
- NSLOG(netsurf, INFO, "linked stylesheet %i '%s'",
- htmlc->stylesheet_count, nsurl_access(joined));
-
- /* extend stylesheets array to allow for new sheet */
- stylesheets = realloc(htmlc->stylesheets,
- sizeof(struct html_stylesheet) *
- (htmlc->stylesheet_count + 1));
- if (stylesheets == NULL) {
- nsurl_unref(joined);
- ns_error = NSERROR_NOMEM;
- goto no_memory;
- }
-
- htmlc->stylesheets = stylesheets;
- htmlc->stylesheets[htmlc->stylesheet_count].node = NULL;
- htmlc->stylesheets[htmlc->stylesheet_count].modified = false;
- htmlc->stylesheets[htmlc->stylesheet_count].unused = false;
-
- /* start fetch */
- child.charset = htmlc->encoding;
- child.quirks = htmlc->base.quirks;
-
- ns_error = hlcache_handle_retrieve(joined, 0,
- content_get_url(&htmlc->base),
- NULL, html_convert_css_callback,
- htmlc, &child, CONTENT_CSS,
- &htmlc->stylesheets[htmlc->stylesheet_count].sheet);
-
- nsurl_unref(joined);
-
- if (ns_error != NSERROR_OK)
- goto no_memory;
-
- htmlc->stylesheet_count++;
-
- htmlc->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", htmlc->base.active);
-
- return true;
-
-no_memory:
- content_broadcast_error(&htmlc->base, ns_error, NULL);
- return false;
-}
-
-/* exported interface documented in html/html.h */
-struct html_stylesheet *html_get_stylesheets(hlcache_handle *h, unsigned int *n)
-{
- html_content *c = (html_content *) hlcache_handle_get_content(h);
-
- assert(c != NULL);
- assert(n != NULL);
-
- *n = c->stylesheet_count;
-
- return c->stylesheets;
-}
-
-/* exported interface documented in html/html_internal.h */
-bool html_saw_insecure_stylesheets(html_content *html)
-{
- struct html_stylesheet *s;
- unsigned int i;
-
- for (i = 0, s = html->stylesheets; i < html->stylesheet_count;
- i++, s++) {
- if (s->sheet != NULL) {
- if (content_saw_insecure_objects(s->sheet)) {
- return true;
- }
- }
- }
-
- return false;
-}
-
-/* exported interface documented in html/html_internal.h */
-nserror html_css_free_stylesheets(html_content *html)
-{
- unsigned int i;
-
- guit->misc->schedule(-1, html_css_process_modified_styles, html);
-
- for (i = 0; i != html->stylesheet_count; i++) {
- if (html->stylesheets[i].sheet != NULL) {
- hlcache_handle_release(html->stylesheets[i].sheet);
- }
- if (html->stylesheets[i].node != NULL) {
- dom_node_unref(html->stylesheets[i].node);
- }
- }
- free(html->stylesheets);
-
- return NSERROR_OK;
-}
-
-/* exported interface documented in html/html_internal.h */
-nserror html_css_quirks_stylesheets(html_content *c)
-{
- nserror ns_error = NSERROR_OK;
- hlcache_child_context child;
-
- assert(c->stylesheets != NULL);
-
- if (c->quirks == DOM_DOCUMENT_QUIRKS_MODE_FULL) {
- child.charset = c->encoding;
- child.quirks = c->base.quirks;
-
- ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url,
- 0, content_get_url(&c->base), NULL,
- html_convert_css_callback, c, &child,
- CONTENT_CSS,
- &c->stylesheets[STYLESHEET_QUIRKS].sheet);
- if (ns_error != NSERROR_OK) {
- return ns_error;
- }
-
- c->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
- }
-
- return ns_error;
-}
-
-/* exported interface documented in html/html_internal.h */
-nserror html_css_new_stylesheets(html_content *c)
-{
- nserror ns_error;
- hlcache_child_context child;
-
- if (c->stylesheets != NULL) {
- return NSERROR_OK; /* already initialised */
- }
-
- /* stylesheet 0 is the base style sheet,
- * stylesheet 1 is the quirks mode style sheet,
- * stylesheet 2 is the adblocking stylesheet,
- * stylesheet 3 is the user stylesheet */
- c->stylesheets = calloc(STYLESHEET_START,
- sizeof(struct html_stylesheet));
- if (c->stylesheets == NULL) {
- return NSERROR_NOMEM;
- }
-
- c->stylesheets[STYLESHEET_BASE].sheet = NULL;
- c->stylesheets[STYLESHEET_QUIRKS].sheet = NULL;
- c->stylesheets[STYLESHEET_ADBLOCK].sheet = NULL;
- c->stylesheets[STYLESHEET_USER].sheet = NULL;
- c->stylesheet_count = STYLESHEET_START;
-
- child.charset = c->encoding;
- child.quirks = c->base.quirks;
-
- ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0,
- content_get_url(&c->base), NULL,
- html_convert_css_callback, c, &child, CONTENT_CSS,
- &c->stylesheets[STYLESHEET_BASE].sheet);
- if (ns_error != NSERROR_OK) {
- return ns_error;
- }
-
- c->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
-
-
- if (nsoption_bool(block_advertisements)) {
- ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url,
- 0, content_get_url(&c->base), NULL,
- html_convert_css_callback,
- c, &child, CONTENT_CSS,
- &c->stylesheets[STYLESHEET_ADBLOCK].sheet);
- if (ns_error != NSERROR_OK) {
- return ns_error;
- }
-
- c->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
-
- }
-
- ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0,
- content_get_url(&c->base), NULL,
- html_convert_css_callback, c, &child, CONTENT_CSS,
- &c->stylesheets[STYLESHEET_USER].sheet);
- if (ns_error != NSERROR_OK) {
- return ns_error;
- }
-
- c->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
-
- return ns_error;
-}
-
-nserror
-html_css_new_selection_context(html_content *c, css_select_ctx **ret_select_ctx)
-{
- uint32_t i;
- css_error css_ret;
- css_select_ctx *select_ctx;
-
- /* check that the base stylesheet loaded; layout fails without it */
- if (c->stylesheets[STYLESHEET_BASE].sheet == NULL) {
- return NSERROR_CSS_BASE;
- }
-
- /* Create selection context */
- css_ret = css_select_ctx_create(&select_ctx);
- if (css_ret != CSS_OK) {
- return css_error_to_nserror(css_ret);
- }
-
- /* Add sheets to it */
- for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) {
- const struct html_stylesheet *hsheet = &c->stylesheets[i];
- css_stylesheet *sheet = NULL;
- css_origin origin = CSS_ORIGIN_AUTHOR;
-
- /* Filter out stylesheets for non-screen media. */
- /* TODO: We should probably pass the sheet in anyway, and let
- * libcss handle the filtering.
- */
- if (hsheet->unused) {
- continue;
- }
-
- if (i < STYLESHEET_USER) {
- origin = CSS_ORIGIN_UA;
- } else if (i < STYLESHEET_START) {
- origin = CSS_ORIGIN_USER;
- }
-
- if (hsheet->sheet != NULL) {
- sheet = nscss_get_stylesheet(hsheet->sheet);
- }
-
- if (sheet != NULL) {
- /* TODO: Pass the sheet's full media query, instead of
- * "screen".
- */
- css_ret = css_select_ctx_append_sheet(select_ctx,
- sheet,
- origin,
- "screen");
- if (css_ret != CSS_OK) {
- css_select_ctx_destroy(select_ctx);
- return css_error_to_nserror(css_ret);
- }
- }
- }
-
- /* return new selection context to caller */
- *ret_select_ctx = select_ctx;
- return NSERROR_OK;
-}
-
-nserror html_css_init(void)
-{
- nserror error;
-
- error = html_css_fetcher_register();
- if (error != NSERROR_OK)
- return error;
-
- error = nsurl_create("resource:default.css",
- &html_default_stylesheet_url);
- if (error != NSERROR_OK)
- return error;
-
- error = nsurl_create("resource:adblock.css",
- &html_adblock_stylesheet_url);
- if (error != NSERROR_OK)
- return error;
-
- error = nsurl_create("resource:quirks.css",
- &html_quirks_stylesheet_url);
- if (error != NSERROR_OK)
- return error;
-
- error = nsurl_create("resource:user.css",
- &html_user_stylesheet_url);
-
- return error;
-}
-
-void html_css_fini(void)
-{
- if (html_user_stylesheet_url != NULL) {
- nsurl_unref(html_user_stylesheet_url);
- html_user_stylesheet_url = NULL;
- }
-
- if (html_quirks_stylesheet_url != NULL) {
- nsurl_unref(html_quirks_stylesheet_url);
- html_quirks_stylesheet_url = NULL;
- }
-
- if (html_adblock_stylesheet_url != NULL) {
- nsurl_unref(html_adblock_stylesheet_url);
- html_adblock_stylesheet_url = NULL;
- }
-
- if (html_default_stylesheet_url != NULL) {
- nsurl_unref(html_default_stylesheet_url);
- html_default_stylesheet_url = NULL;
- }
-}
diff --git a/content/handlers/html/html_css_fetcher.c
b/content/handlers/html/html_css_fetcher.c
deleted file mode 100644
index ae20879..0000000
--- a/content/handlers/html/html_css_fetcher.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright 2008 Rob Kendrick <rjek(a)netsurf-browser.org>
- * Copyright 2013 John-Mark Bell <jmb(a)netsurf-browser.org>
- *
- * This file is part of NetSurf.
- *
- * 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
- * HTML fetcher for CSS objects
- */
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dom/dom.h>
-#include <libwapcaplet/libwapcaplet.h>
-
-#include "netsurf/inttypes.h"
-#include "utils/config.h"
-#include "utils/corestrings.h"
-#include "utils/log.h"
-#include "utils/ring.h"
-#include "utils/nsurl.h"
-#include "utils/utils.h"
-#include "content/fetch.h"
-#include "content/fetchers.h"
-
-#include "html/html_internal.h"
-
-typedef struct html_css_fetcher_item {
- uint32_t key;
- dom_string *data;
- nsurl *base_url;
-
- struct html_css_fetcher_item *r_next, *r_prev;
-} html_css_fetcher_item;
-
-typedef struct html_css_fetcher_context {
- struct fetch *parent_fetch;
-
- nsurl *url;
- html_css_fetcher_item *item;
-
- bool aborted;
- bool locked;
-
- struct html_css_fetcher_context *r_next, *r_prev;
-} html_css_fetcher_context;
-
-static uint32_t current_key = 0;
-static html_css_fetcher_item *items = NULL;
-static html_css_fetcher_context *ring = NULL;
-
-static bool html_css_fetcher_initialise(lwc_string *scheme)
-{
- NSLOG(netsurf, INFO, "html_css_fetcher_initialise called for %s",
- lwc_string_data(scheme));
- return true;
-}
-
-static void html_css_fetcher_finalise(lwc_string *scheme)
-{
- NSLOG(netsurf, INFO, "html_css_fetcher_finalise called for %s",
- lwc_string_data(scheme));
-}
-
-static bool html_css_fetcher_can_fetch(const nsurl *url)
-{
- return true;
-}
-
-static void *html_css_fetcher_setup(struct fetch *parent_fetch, nsurl *url,
- bool only_2xx, bool downgrade_tls, const char *post_urlenc,
- const struct fetch_multipart_data *post_multipart,
- const char **headers)
-{
- html_css_fetcher_context *ctx;
- lwc_string *path;
- uint32_t key;
- html_css_fetcher_item *item, *found = NULL;
-
- /* format of a x-ns-css URL is:
- * x-ns-url:<key>
- * Where key is an unsigned 32bit integer
- */
-
- path = nsurl_get_component(url, NSURL_PATH);
- /* The path must exist */
- if (path == NULL) {
- return NULL;
- }
-
- key = strtoul(lwc_string_data(path), NULL, 10);
-
- lwc_string_unref(path);
-
- /* There must be at least one item */
- if (items == NULL) {
- return NULL;
- }
-
- item = items;
- do {
- if (item->key == key) {
- found = item;
- break;
- }
-
- item = item->r_next;
- } while (item != items);
-
- /* We must have found the item */
- if (found == NULL) {
- return NULL;
- }
-
- ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL)
- return NULL;
-
- ctx->parent_fetch = parent_fetch;
- ctx->url = nsurl_ref(url);
- ctx->item = found;
-
- RING_INSERT(ring, ctx);
-
- return ctx;
-}
-
-static bool html_css_fetcher_start(void *ctx)
-{
- return true;
-}
-
-static void html_css_fetcher_free(void *ctx)
-{
- html_css_fetcher_context *c = ctx;
-
- nsurl_unref(c->url);
- if (c->item != NULL) {
- nsurl_unref(c->item->base_url);
- dom_string_unref(c->item->data);
- RING_REMOVE(items, c->item);
- free(c->item);
- }
- RING_REMOVE(ring, c);
- free(ctx);
-}
-
-static void html_css_fetcher_abort(void *ctx)
-{
- html_css_fetcher_context *c = ctx;
-
- /* To avoid the poll loop having to deal with the fetch context
- * disappearing from under it, we simply flag the abort here.
- * The poll loop itself will perform the appropriate cleanup.
- */
- c->aborted = true;
-}
-
-static void html_css_fetcher_send_callback(const fetch_msg *msg,
- html_css_fetcher_context *c)
-{
- c->locked = true;
- fetch_send_callback(msg, c->parent_fetch);
- c->locked = false;
-}
-
-static void html_css_fetcher_poll(lwc_string *scheme)
-{
- fetch_msg msg;
- html_css_fetcher_context *c, *next;
-
- if (ring == NULL) return;
-
- /* Iterate over ring, processing each pending fetch */
- c = ring;
- do {
- /* Ignore fetches that have been flagged as locked.
- * This allows safe re-entrant calls to this function.
- * Re-entrancy can occur if, as a result of a callback,
- * the interested party causes fetch_poll() to be called
- * again.
- */
- if (c->locked == true) {
- next = c->r_next;
- continue;
- }
-
- /* Only process non-aborted fetches */
- if (c->aborted) {
- /* Nothing to do */
- assert(c->locked == false);
- } else if (c->item != NULL) {
- char header[4096];
-
- fetch_set_http_code(c->parent_fetch, 200);
-
- /* Any callback can result in the fetch being aborted.
- * Therefore, we _must_ check for this after _every_
- * call to html_css_fetcher_send_callback().
- */
- snprintf(header, sizeof header,
- "Content-Type: text/css; charset=utf-8");
- msg.type = FETCH_HEADER;
- msg.data.header_or_data.buf = (const uint8_t *) header;
- msg.data.header_or_data.len = strlen(header);
- html_css_fetcher_send_callback(&msg, c);
-
- if (c->aborted == false) {
- snprintf(header, sizeof header,
- "Content-Length: %"PRIsizet,
- dom_string_byte_length(c->item->data));
- msg.type = FETCH_HEADER;
- msg.data.header_or_data.buf =
- (const uint8_t *) header;
- msg.data.header_or_data.len = strlen(header);
- html_css_fetcher_send_callback(&msg, c);
- }
-
- if (c->aborted == false) {
- snprintf(header, sizeof header,
- "X-NS-Base: %.*s",
- (int) nsurl_length(c->item->base_url),
- nsurl_access(c->item->base_url));
- msg.type = FETCH_HEADER;
- msg.data.header_or_data.buf =
- (const uint8_t *) header;
- msg.data.header_or_data.len = strlen(header);
- html_css_fetcher_send_callback(&msg, c);
- }
-
- if (c->aborted == false) {
- msg.type = FETCH_DATA;
- msg.data.header_or_data.buf =
- (const uint8_t *)
- dom_string_data(c->item->data);
- msg.data.header_or_data.len =
- dom_string_byte_length(c->item->data);
- html_css_fetcher_send_callback(&msg, c);
- }
-
- if (c->aborted == false) {
- msg.type = FETCH_FINISHED;
- html_css_fetcher_send_callback(&msg, c);
- }
- } else {
- NSLOG(netsurf, INFO, "Processing of %s failed!",
- nsurl_access(c->url));
-
- /* Ensure that we're unlocked here. If we aren't,
- * then html_css_fetcher_process() is broken.
- */
- assert(c->locked == false);
- }
-
- /* Compute next fetch item at the last possible moment as
- * processing this item may have added to the ring.
- */
- next = c->r_next;
-
- fetch_remove_from_queues(c->parent_fetch);
- fetch_free(c->parent_fetch);
-
- /* Advance to next ring entry, exiting if we've reached
- * the start of the ring or the ring has become empty
- */
- } while ( (c = next) != ring && ring != NULL);
-}
-
-/* exported interface documented in html_internal.h */
-nserror html_css_fetcher_register(void)
-{
- const struct fetcher_operation_table html_css_fetcher_ops = {
- .initialise = html_css_fetcher_initialise,
- .acceptable = html_css_fetcher_can_fetch,
- .setup = html_css_fetcher_setup,
- .start = html_css_fetcher_start,
- .abort = html_css_fetcher_abort,
- .free = html_css_fetcher_free,
- .poll = html_css_fetcher_poll,
- .finalise = html_css_fetcher_finalise
- };
-
- return fetcher_add(lwc_string_ref(corestring_lwc_x_ns_css),
- &html_css_fetcher_ops);
-}
-
-/* exported interface documented in html_internal.h */
-nserror
-html_css_fetcher_add_item(dom_string *data, nsurl *base_url, uint32_t *key)
-{
- html_css_fetcher_item *item = malloc(sizeof(*item));
-
- if (item == NULL) {
- return NSERROR_NOMEM;
- }
-
- *key = item->key = current_key++;
- item->data = dom_string_ref(data);
- item->base_url = nsurl_ref(base_url);
-
- RING_INSERT(items, item);
-
- return NSERROR_OK;
-}
diff --git a/content/handlers/html/html_forms.c b/content/handlers/html/html_forms.c
deleted file mode 100644
index 896263d..0000000
--- a/content/handlers/html/html_forms.c
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * Copyright 2011 Vincent Sanders <vince(a)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
- * HTML form handling implementation
- */
-
-#include "utils/config.h"
-#include "utils/corestrings.h"
-#include "utils/log.h"
-
-#include "html/form_internal.h"
-#include "html/html_internal.h"
-
-/**
- * process form element from dom
- */
-static struct form *
-parse_form_element(const char *docenc, dom_node *node)
-{
- dom_string *ds_action = NULL;
- dom_string *ds_charset = NULL;
- dom_string *ds_target = NULL;
- dom_string *ds_method = NULL;
- dom_string *ds_enctype = NULL;
- char *action = NULL, *charset = NULL, *target = NULL;
- form_method method;
- dom_html_form_element *formele = (dom_html_form_element *)(node);
- struct form * ret = NULL;
-
- /* Retrieve the attributes from the node */
- if (dom_html_form_element_get_action(formele,
- &ds_action) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_form_element_get_accept_charset(formele,
- &ds_charset) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_form_element_get_target(formele,
- &ds_target) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_form_element_get_method(formele,
- &ds_method) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_form_element_get_enctype(formele,
- &ds_enctype) != DOM_NO_ERR)
- goto out;
-
- /* Extract the plain attributes ready for use. We have to do this
- * because we cannot guarantee that the dom_strings are NULL terminated
- * and thus we copy them.
- */
- if (ds_action != NULL)
- action = strndup(dom_string_data(ds_action),
- dom_string_byte_length(ds_action));
-
- if (ds_charset != NULL)
- charset = strndup(dom_string_data(ds_charset),
- dom_string_byte_length(ds_charset));
-
- if (ds_target != NULL)
- target = strndup(dom_string_data(ds_target),
- dom_string_byte_length(ds_target));
-
- /* Determine the method */
- method = method_GET;
- if (ds_method != NULL) {
- if (dom_string_caseless_lwc_isequal(ds_method,
- corestring_lwc_post)) {
- method = method_POST_URLENC;
- if (ds_enctype != NULL) {
- if (dom_string_caseless_lwc_isequal(ds_enctype,
- corestring_lwc_multipart_form_data)) {
-
- method = method_POST_MULTIPART;
- }
- }
- }
- }
-
- /* Construct the form object */
- ret = form_new(node, action, target, method, charset, docenc);
-
-out:
- if (ds_action != NULL)
- dom_string_unref(ds_action);
- if (ds_charset != NULL)
- dom_string_unref(ds_charset);
- if (ds_target != NULL)
- dom_string_unref(ds_target);
- if (ds_method != NULL)
- dom_string_unref(ds_method);
- if (ds_enctype != NULL)
- dom_string_unref(ds_enctype);
- if (action != NULL)
- free(action);
- if (charset != NULL)
- free(charset);
- if (target != NULL)
- free(target);
- return ret;
-}
-
-/* documented in html_internal.h */
-struct form *html_forms_get_forms(const char *docenc, dom_html_document *doc)
-{
- dom_html_collection *forms;
- struct form *ret = NULL, *newf;
- dom_node *node;
- unsigned long n;
- uint32_t nforms;
-
- if (doc == NULL)
- return NULL;
-
- /* Attempt to build a set of all the forms */
- if (dom_html_document_get_forms(doc, &forms) != DOM_NO_ERR)
- return NULL;
-
- /* Count the number of forms so we can iterate */
- if (dom_html_collection_get_length(forms, &nforms) != DOM_NO_ERR)
- goto out;
-
- /* Iterate the forms collection, making form structs for returning */
- for (n = 0; n < nforms; ++n) {
- if (dom_html_collection_item(forms, n, &node) != DOM_NO_ERR) {
- goto out;
- }
- newf = parse_form_element(docenc, node);
- dom_node_unref(node);
- if (newf == NULL) {
- goto err;
- }
- newf->prev = ret;
- ret = newf;
- }
-
- /* All went well */
- goto out;
-err:
- while (ret != NULL) {
- struct form *prev = ret->prev;
- /* Destroy ret */
- free(ret);
- ret = prev;
- }
-out:
- /* Finished with the collection, return it */
- dom_html_collection_unref(forms);
-
- return ret;
-}
-
-static struct form *
-find_form(struct form *forms, dom_html_form_element *form)
-{
- while (forms != NULL) {
- if (forms->node == form)
- break;
- forms = forms->prev;
- }
-
- return forms;
-}
-
-static struct form_control *
-parse_button_element(struct form *forms, dom_html_button_element *button)
-{
- struct form_control *control = NULL;
- dom_exception err;
- dom_html_form_element *form = NULL;
- dom_string *ds_type = NULL;
- dom_string *ds_value = NULL;
- dom_string *ds_name = NULL;
-
- err = dom_html_button_element_get_form(button, &form);
- if (err != DOM_NO_ERR)
- goto out;
-
- err = dom_html_button_element_get_type(button, &ds_type);
- if (err != DOM_NO_ERR)
- goto out;
-
- if (ds_type == NULL) {
- control = form_new_control(button, GADGET_SUBMIT);
- } else {
- if (dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_submit)) {
- control = form_new_control(button, GADGET_SUBMIT);
- } else if (dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_reset)) {
- control = form_new_control(button, GADGET_RESET);
- } else {
- control = form_new_control(button, GADGET_BUTTON);
- }
- }
-
- if (control == NULL)
- goto out;
-
- err = dom_html_button_element_get_value(button, &ds_value);
- if (err != DOM_NO_ERR)
- goto out;
- err = dom_html_button_element_get_name(button, &ds_name);
- if (err != DOM_NO_ERR)
- goto out;
-
- if (ds_value != NULL) {
- control->value = strndup(
- dom_string_data(ds_value),
- dom_string_byte_length(ds_value));
-
- if (control->value == NULL) {
- form_free_control(control);
- control = NULL;
- goto out;
- }
- }
-
- if (ds_name != NULL) {
- control->name = strndup(
- dom_string_data(ds_name),
- dom_string_byte_length(ds_name));
-
- if (control->name == NULL) {
- form_free_control(control);
- control = NULL;
- goto out;
- }
- }
-
- if (form != NULL && control != NULL)
- form_add_control(find_form(forms, form), control);
-
-out:
- if (form != NULL)
- dom_node_unref(form);
- if (ds_type != NULL)
- dom_string_unref(ds_type);
- if (ds_value != NULL)
- dom_string_unref(ds_value);
- if (ds_name != NULL)
- dom_string_unref(ds_name);
-
- return control;
-}
-
-static struct form_control *
-parse_input_element(struct form *forms, dom_html_input_element *input)
-{
- struct form_control *control = NULL;
- dom_html_form_element *form = NULL;
- dom_string *ds_type = NULL;
- dom_string *ds_name = NULL;
- dom_string *ds_value = NULL;
-
- char *name = NULL;
-
- if (dom_html_input_element_get_form(input, &form) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_input_element_get_type(input, &ds_type) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_input_element_get_name(input, &ds_name) != DOM_NO_ERR)
- goto out;
-
- if (ds_name != NULL)
- name = strndup(dom_string_data(ds_name),
- dom_string_byte_length(ds_name));
-
- if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_password)) {
- control = form_new_control(input, GADGET_PASSWORD);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_file)) {
- control = form_new_control(input, GADGET_FILE);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_hidden)) {
- control = form_new_control(input, GADGET_HIDDEN);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_checkbox)) {
- control = form_new_control(input, GADGET_CHECKBOX);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_radio)) {
- control = form_new_control(input, GADGET_RADIO);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_submit)) {
- control = form_new_control(input, GADGET_SUBMIT);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_reset)) {
- control = form_new_control(input, GADGET_RESET);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_button)) {
- control = form_new_control(input, GADGET_BUTTON);
- } else if (ds_type != NULL && dom_string_caseless_lwc_isequal(ds_type,
- corestring_lwc_image)) {
- control = form_new_control(input, GADGET_IMAGE);
- } else {
- control = form_new_control(input, GADGET_TEXTBOX);
- }
-
- if (control == NULL)
- goto out;
-
- if (name != NULL) {
- /* Hand the name string over */
- control->name = name;
- name = NULL;
- }
-
- if (control->type == GADGET_CHECKBOX || control->type == GADGET_RADIO) {
- bool selected;
- if (dom_html_input_element_get_checked(
- input, &selected) == DOM_NO_ERR) {
- control->selected = selected;
- }
- }
-
- if (control->type == GADGET_PASSWORD ||
- control->type == GADGET_TEXTBOX) {
- int32_t maxlength;
- if (dom_html_input_element_get_max_length(
- input, &maxlength) != DOM_NO_ERR) {
- maxlength = -1;
- }
-
- if (maxlength >= 0) {
- /* Got valid maxlength */
- control->maxlength = maxlength;
- } else {
- /* Input has no maxlength attr, or
- * dom_html_input_element_get_max_length failed.
- *
- * Set it to something insane. */
- control->maxlength = UINT_MAX;
- }
- }
-
- if (control->type != GADGET_FILE && control->type != GADGET_IMAGE) {
- if (dom_html_input_element_get_value(
- input, &ds_value) == DOM_NO_ERR) {
- if (ds_value != NULL) {
- control->value = strndup(
- dom_string_data(ds_value),
- dom_string_byte_length(ds_value));
- if (control->value == NULL) {
- form_free_control(control);
- control = NULL;
- goto out;
- }
- control->length = strlen(control->value);
- }
- }
-
- if (control->type == GADGET_TEXTBOX ||
- control->type == GADGET_PASSWORD) {
- if (control->value == NULL) {
- control->value = strdup("");
- if (control->value == NULL) {
- form_free_control(control);
- control = NULL;
- goto out;
- }
-
- control->length = 0;
- }
-
- control->initial_value = strdup(control->value);
- if (control->initial_value == NULL) {
- form_free_control(control);
- control = NULL;
- goto out;
- }
-
- control->last_synced_value = strdup(control->value);
- if (control->last_synced_value == NULL) {
- form_free_control(control);
- control = NULL;
- goto out;
- }
-
- control->node_value = dom_string_ref(ds_value);
- }
- /* Force the gadget and DOM to be in sync */
- form_gadget_sync_with_dom(control);
- }
-
- if (form != NULL && control != NULL)
- form_add_control(find_form(forms, form), control);
-
-out:
- if (form != NULL)
- dom_node_unref(form);
- if (ds_type != NULL)
- dom_string_unref(ds_type);
- if (ds_name != NULL)
- dom_string_unref(ds_name);
- if (ds_value != NULL)
- dom_string_unref(ds_value);
-
- if (name != NULL)
- free(name);
-
- return control;
-}
-
-static struct form_control *
-parse_textarea_element(struct form *forms, dom_html_text_area_element *ta)
-{
- struct form_control *control = NULL;
- dom_html_form_element *form = NULL;
- dom_string *ds_name = NULL;
-
- char *name = NULL;
-
- if (dom_html_text_area_element_get_form(ta, &form) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_text_area_element_get_name(ta, &ds_name) != DOM_NO_ERR)
- goto out;
-
- if (ds_name != NULL)
- name = strndup(dom_string_data(ds_name),
- dom_string_byte_length(ds_name));
-
- control = form_new_control(ta, GADGET_TEXTAREA);
-
- if (control == NULL)
- goto out;
-
- if (name != NULL) {
- /* Hand the name string over */
- control->name = name;
- name = NULL;
- }
-
- if (form != NULL && control != NULL)
- form_add_control(find_form(forms, form), control);
-
-out:
- if (form != NULL)
- dom_node_unref(form);
- if (ds_name != NULL)
- dom_string_unref(ds_name);
-
- if (name != NULL)
- free(name);
-
-
- return control;
-}
-
-static struct form_control *
-parse_select_element(struct form *forms, dom_html_select_element *select)
-{
- struct form_control *control = NULL;
- dom_html_form_element *form = NULL;
- dom_string *ds_name = NULL;
-
- char *name = NULL;
-
- if (dom_html_select_element_get_form(select, &form) != DOM_NO_ERR)
- goto out;
-
- if (dom_html_select_element_get_name(select, &ds_name) != DOM_NO_ERR)
- goto out;
-
- if (ds_name != NULL)
- name = strndup(dom_string_data(ds_name),
- dom_string_byte_length(ds_name));
-
- control = form_new_control(select, GADGET_SELECT);
-
- if (control == NULL)
- goto out;
-
- if (name != NULL) {
- /* Hand the name string over */
- control->name = name;
- name = NULL;
- }
-
- dom_html_select_element_get_multiple(select,
- &(control->data.select.multiple));
-
- if (form != NULL && control != NULL)
- form_add_control(find_form(forms, form), control);
-
-out:
- if (form != NULL)
- dom_node_unref(form);
- if (ds_name != NULL)
- dom_string_unref(ds_name);
-
- if (name != NULL)
- free(name);
-
-
- return control;
-}
-
-
-static struct form_control *
-invent_fake_gadget(dom_node *node)
-{
- struct form_control *ctl = form_new_control(node, GADGET_HIDDEN);
- if (ctl != NULL) {
- ctl->value = strdup("");
- ctl->initial_value = strdup("");
- ctl->name = strdup("foo");
-
- if (ctl->value == NULL || ctl->initial_value == NULL ||
- ctl->name == NULL) {
- form_free_control(ctl);
- ctl = NULL;
- }
- }
- return ctl;
-}
-
-/* documented in html_internal.h */
-struct form_control *html_forms_get_control_for_node(struct form *forms,
- dom_node *node)
-{
- struct form *f;
- struct form_control *ctl = NULL;
- dom_exception err;
- dom_string *ds_name = NULL;
-
- /* Step one, see if we already have a control */
- for (f = forms; f != NULL; f = f->prev) {
- for (ctl = f->controls; ctl != NULL; ctl = ctl->next) {
- if (ctl->node == node)
- return ctl;
- }
- }
-
- /* Step two, extract the node's name so we can construct a gadget. */
- err = dom_element_get_tag_name(node, &ds_name);
- if (err == DOM_NO_ERR && ds_name != NULL) {
-
- /* Step three, attempt to work out what gadget to make */
- if (dom_string_caseless_lwc_isequal(ds_name,
- corestring_lwc_button)) {
- ctl = parse_button_element(forms,
- (dom_html_button_element *) node);
- } else if (dom_string_caseless_lwc_isequal(ds_name,
- corestring_lwc_input)) {
- ctl = parse_input_element(forms,
- (dom_html_input_element *) node);
- } else if (dom_string_caseless_lwc_isequal(ds_name,
- corestring_lwc_textarea)) {
- ctl = parse_textarea_element(forms,
- (dom_html_text_area_element *) node);
- } else if (dom_string_caseless_lwc_isequal(ds_name,
- corestring_lwc_select)) {
- ctl = parse_select_element(forms,
- (dom_html_select_element *) node);
- }
- }
-
- /* If all else fails, fake gadget time */
- if (ctl == NULL)
- ctl = invent_fake_gadget(node);
-
- if (ds_name != NULL)
- dom_string_unref(ds_name);
-
- return ctl;
-}
diff --git a/content/handlers/html/html_object.c b/content/handlers/html/html_object.c
deleted file mode 100644
index 587c487..0000000
--- a/content/handlers/html/html_object.c
+++ /dev/null
@@ -1,729 +0,0 @@
-/*
- * Copyright 2013 Vincent Sanders <vince(a)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 <nsutils/time.h>
-
-#include "utils/corestrings.h"
-#include "utils/config.h"
-#include "utils/log.h"
-#include "utils/nsoption.h"
-#include "netsurf/content.h"
-#include "netsurf/misc.h"
-#include "content/hlcache.h"
-#include "css/utils.h"
-#include "desktop/scrollbar.h"
-#include "desktop/gui_internal.h"
-
-#include "html/html.h"
-#include "html/box.h"
-#include "html/box_inspect.h"
-#include "html/html_internal.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;
-
- /* Normalise the box type, now it has been replaced. */
- switch (box->type) {
- case BOX_TABLE:
- box->type = BOX_BLOCK;
- break;
- default:
- /* TODO: Any other box types need mapping? */
- break;
- }
-
- 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;
-
- box = o->box;
- if (box == NULL && event->type != CONTENT_MSG_ERROR) {
- return NSERROR_OK;
- }
-
- 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.available_height);
- }
- break;
-
- case CONTENT_MSG_DONE:
- c->base.active--;
- NSLOG(netsurf, INFO, "%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 (c->had_initial_layout == false) {
- break;
- }
-
- 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;
-
- content_broadcast(&c->base, CONTENT_MSG_REDRAW, &data);
- }
- break;
-
- case CONTENT_MSG_ERROR:
- hlcache_handle_release(object);
-
- o->content = NULL;
-
- if (box != NULL) {
- c->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active",
- c->base.active);
-
- content_add_error(&c->base, "?", 0);
- html_object_failed(box, c, o->background);
- }
- break;
-
- case CONTENT_MSG_REDRAW:
- if (c->base.status != CONTENT_STATUS_LOADING) {
- union content_msg_data data = event->data;
-
- if (c->had_initial_layout == false) {
- break;
- }
-
- if (!box_visible(box))
- break;
-
- box_coords(box, &x, &y);
-
- if (object == box->background) {
- /* Redraw request is for background */
- css_fixed hpos = 0, vpos = 0;
- css_unit hunit = CSS_UNIT_PX;
- css_unit vunit = CSS_UNIT_PX;
- int width = box->padding[LEFT] + box->width +
- box->padding[RIGHT];
- int height = box->padding[TOP] + box->height +
- box->padding[BOTTOM];
- int t, h, l, w;
-
- /* Need to know background-position */
- css_computed_background_position(box->style,
- &hpos, &hunit, &vpos, &vunit);
-
- w = content_get_width(box->background);
- if (hunit == CSS_UNIT_PCT) {
- l = (width - w) * hpos / INTTOFIX(100);
- } else {
- l = FIXTOINT(nscss_len2px(&c->len_ctx,
- hpos, hunit,
- box->style));
- }
-
- h = content_get_height(box->background);
- if (vunit == CSS_UNIT_PCT) {
- t = (height - h) * vpos / INTTOFIX(100);
- } else {
- t = FIXTOINT(nscss_len2px(&c->len_ctx,
- vpos, vunit,
- box->style));
- }
-
- /* Redraw area depends on background-repeat */
- switch (css_computed_background_repeat(
- box->style)) {
- case CSS_BACKGROUND_REPEAT_REPEAT:
- data.redraw.x = 0;
- data.redraw.y = 0;
- data.redraw.width = box->width;
- data.redraw.height = box->height;
- break;
-
- case CSS_BACKGROUND_REPEAT_REPEAT_X:
- data.redraw.x = 0;
- data.redraw.y += t;
- data.redraw.width = box->width;
- break;
-
- case CSS_BACKGROUND_REPEAT_REPEAT_Y:
- data.redraw.x += l;
- data.redraw.y = 0;
- data.redraw.height = box->height;
- break;
-
- case CSS_BACKGROUND_REPEAT_NO_REPEAT:
- data.redraw.x += l;
- data.redraw.y += t;
- break;
-
- default:
- break;
- }
-
- /* Add offset to box */
- data.redraw.x += x;
- data.redraw.y += y;
-
- } else {
- /* Non-background case */
- int w = content_get_width(object);
- int h = content_get_height(object);
-
- if (w != 0 && box->width != w) {
- /* Not showing image at intrinsic
- * width; need to scale the redraw
- * request area. */
- data.redraw.x = data.redraw.x *
- box->width / w;
- data.redraw.width =
- data.redraw.width *
- box->width / w;
- }
-
- if (h != 0 && box->height != w) {
- /* Not showing image at intrinsic
- * height; need to scale the redraw
- * request area. */
- data.redraw.y = data.redraw.y *
- box->height / h;
- data.redraw.height =
- data.redraw.height *
- box->height / h;
- }
-
- data.redraw.x += x + box->padding[LEFT];
- data.redraw.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 */
- guit->misc->schedule(event->data.delay * 1000,
- 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_GETTHREAD:
- /* Objects don't have JS threads */
- *(event->data.jsthread) = NULL;
- break;
-
- case CONTENT_MSG_GETDIMS:
- *(event->data.getdims.viewport_width) =
- content__get_width(&c->base);
- *(event->data.getdims.viewport_height) =
- content__get_height(&c->base);
- 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:
- case CONTENT_MSG_SELECTMENU:
- case CONTENT_MSG_GADGETCLICK:
- /* 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:
- break;
- }
-
- 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.available_height);
- content_set_done(&c->base);
- } else if (nsoption_bool(incremental_reflow) &&
- event->type == CONTENT_MSG_DONE &&
- box != NULL &&
- !(box->flags & REPLACE_DIM) &&
- (c->base.status == CONTENT_STATUS_READY ||
- c->base.status == CONTENT_STATUS_DONE)) {
- /* 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,
- */
- uint64_t ms_now;
- nsu_getmonotonic_ms(&ms_now);
- if (ms_now > c->base.reformat_time) {
- /* The time since the previous reformat is
- * more than the configured minimum time
- * between reformats so reformat the page to
- * display newly fetched objects
- */
- content__reformat(&c->base,
- false,
- c->base.available_width,
- c->base.available_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);
- assert(object->box != 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--;
- NSLOG(netsurf, INFO, "%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++;
- NSLOG(netsurf, INFO, "%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 || object->box == 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;
- if (object->box != NULL) {
- htmlc->base.active--;
- NSLOG(netsurf, INFO, "%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 || object->box == NULL)
- continue;
-
- if (content_get_type(object->content) == CONTENT_NONE)
- continue;
-
- if (content_get_type(object->content) == CONTENT_HTML) {
- guit->misc->schedule(-1, 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) {
- NSLOG(netsurf, INFO, "object %p", victim->content);
-
- if (content_get_type(victim->content) == CONTENT_HTML) {
- guit->misc->schedule(-1, html_object_refresh, victim);
- }
- hlcache_handle_release(victim->content);
- }
-
- html->object_list = victim->next;
- free(victim);
- }
- return NSERROR_OK;
-}
-
-
-
-/* exported interface documented in html/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++;
- if (box != NULL) {
- c->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
- }
-
- return true;
-}
diff --git a/content/handlers/html/html_redraw.c b/content/handlers/html/html_redraw.c
deleted file mode 100644
index ee6fab2..0000000
--- a/content/handlers/html/html_redraw.c
+++ /dev/null
@@ -1,1961 +0,0 @@
-/*
- * Copyright 2004-2008 James Bursa <bursa(a)users.sourceforge.net>
- * Copyright 2004-2007 John M Bell <jmb202(a)ecs.soton.ac.uk>
- * Copyright 2004-2007 Richard Wilson <info(a)tinct.net>
- * Copyright 2005-2006 Adrian Lees <adrianl(a)users.sourceforge.net>
- * Copyright 2006 Rob Kendrick <rjek(a)netsurf-browser.org>
- * Copyright 2008 Michael Drake <tlsa(a)netsurf-browser.org>
- * Copyright 2009 Paul Blokus <paul_pl(a)users.sourceforge.net>
- *
- * 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
- *
- * Redrawing CONTENT_HTML implementation.
- */
-
-#include "utils/config.h"
-#include <assert.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <dom/dom.h>
-
-#include "utils/log.h"
-#include "utils/messages.h"
-#include "utils/utils.h"
-#include "utils/nsoption.h"
-#include "netsurf/content.h"
-#include "netsurf/browser_window.h"
-#include "netsurf/plotters.h"
-#include "netsurf/bitmap.h"
-#include "netsurf/layout.h"
-#include "content/content_protected.h"
-#include "css/utils.h"
-#include "desktop/selection.h"
-#include "desktop/print.h"
-#include "desktop/scrollbar.h"
-#include "desktop/textarea.h"
-#include "desktop/gui_internal.h"
-
-#include "html/box.h"
-#include "html/box_inspect.h"
-#include "html/box_manipulate.h"
-#include "html/font.h"
-#include "html/form_internal.h"
-#include "html/html_internal.h"
-#include "html/layout.h"
-#include "html/search.h"
-
-
-bool html_redraw_debug = false;
-
-/**
- * Determine if a box has a background that needs drawing
- *
- * \param box Box to consider
- * \return True if box has a background, false otherwise.
- */
-static bool html_redraw_box_has_background(struct box *box)
-{
- if (box->background != NULL)
- return true;
-
- if (box->style != NULL) {
- css_color colour;
-
- css_computed_background_color(box->style, &colour);
-
- if (nscss_color_is_transparent(colour) == false)
- return true;
- }
-
- return false;
-}
-
-/**
- * Find the background box for a box
- *
- * \param box Box to find background box for
- * \return Pointer to background box, or NULL if there is none
- */
-static struct box *html_redraw_find_bg_box(struct box *box)
-{
- /* Thanks to backwards compatibility, CSS defines the following:
- *
- * + If the box is for the root element and it has a background,
- * use that (and then process the body box with no special case)
- * + If the box is for the root element and it has no background,
- * then use the background (if any) from the body element as if
- * it were specified on the root. Then, when the box for the body
- * element is processed, ignore the background.
- * + For any other box, just use its own styling.
- */
- if (box->parent == NULL) {
- /* Root box */
- if (html_redraw_box_has_background(box))
- return box;
-
- /* No background on root box: consider body box, if any */
- if (box->children != NULL) {
- if (html_redraw_box_has_background(box->children))
- return box->children;
- }
- } else if (box->parent != NULL && box->parent->parent == NULL) {
- /* Body box: only render background if root has its own */
- if (html_redraw_box_has_background(box) &&
- html_redraw_box_has_background(box->parent))
- return box;
- } else {
- /* Any other box */
- if (html_redraw_box_has_background(box))
- return box;
- }
-
- return NULL;
-}
-
-/**
- * Redraw a short text string, complete with highlighting
- * (for selection/search)
- *
- * \param utf8_text pointer to UTF-8 text string
- * \param utf8_len length of string, in bytes
- * \param offset byte offset within textual representation
- * \param space width of space that follows string (0 = no space)
- * \param fstyle text style to use (pass text size unscaled)
- * \param x x ordinate at which to plot text
- * \param y y ordinate at which to plot text
- * \param clip pointer to current clip rectangle
- * \param height height of text string
- * \param scale current display scale (1.0 = 100%)
- * \param excluded exclude this text string from the selection
- * \param c Content being redrawn.
- * \param sel Selection context
- * \param search Search context
- * \param ctx current redraw context
- * \return true iff successful and redraw should proceed
- */
-
-static bool
-text_redraw(const char *utf8_text,
- size_t utf8_len,
- size_t offset,
- int space,
- const plot_font_style_t *fstyle,
- int x,
- int y,
- const struct rect *clip,
- int height,
- float scale,
- bool excluded,
- struct content *c,
- const struct selection *sel,
- struct search_context *search,
- const struct redraw_context *ctx)
-{
- bool highlighted = false;
- plot_font_style_t plot_fstyle = *fstyle;
- nserror res;
-
- /* Need scaled text size to pass to plotters */
- plot_fstyle.size *= scale;
-
- /* is this box part of a selection? */
- if (!excluded && ctx->interactive == true) {
- unsigned len = utf8_len + (space ? 1 : 0);
- unsigned start_idx;
- unsigned end_idx;
-
- /* first try the browser window's current selection */
- if (selection_defined(sel) && selection_highlighted(sel,
- offset, offset + len,
- &start_idx, &end_idx)) {
- highlighted = true;
- }
-
- /* what about the current search operation, if any? */
- if (!highlighted && (search != NULL) &&
- search_term_highlighted(c,
- offset, offset + len,
- &start_idx, &end_idx,
- search)) {
- highlighted = true;
- }
-
- /* \todo make search terms visible within selected text */
- if (highlighted) {
- struct rect r;
- unsigned endtxt_idx = end_idx;
- bool clip_changed = false;
- bool text_visible = true;
- int startx, endx;
- plot_style_t pstyle_fill_hback = *plot_style_fill_white;
- plot_font_style_t fstyle_hback = plot_fstyle;
-
- if (end_idx > utf8_len) {
- /* adjust for trailing space, not present in
- * utf8_text */
- assert(end_idx == utf8_len + 1);
- endtxt_idx = utf8_len;
- }
-
- res = guit->layout->width(fstyle,
- utf8_text, start_idx,
- &startx);
- if (res != NSERROR_OK) {
- startx = 0;
- }
-
- res = guit->layout->width(fstyle,
- utf8_text, endtxt_idx,
- &endx);
- if (res != NSERROR_OK) {
- endx = 0;
- }
-
- /* is there a trailing space that should be highlighted
- * as well? */
- if (end_idx > utf8_len) {
- endx += space;
- }
-
- if (scale != 1.0) {
- startx *= scale;
- endx *= scale;
- }
-
- /* draw any text preceding highlighted portion */
- if ((start_idx > 0) &&
- (ctx->plot->text(ctx,
- &plot_fstyle,
- x,
- y + (int)(height * 0.75 * scale),
- utf8_text,
- start_idx) != NSERROR_OK))
- return false;
-
- pstyle_fill_hback.fill_colour = fstyle->foreground;
-
- /* highlighted portion */
- r.x0 = x + startx;
- r.y0 = y;
- r.x1 = x + endx;
- r.y1 = y + height * scale;
- res = ctx->plot->rectangle(ctx, &pstyle_fill_hback, &r);
- if (res != NSERROR_OK) {
- return false;
- }
-
- if (start_idx > 0) {
- int px0 = max(x + startx, clip->x0);
- int px1 = min(x + endx, clip->x1);
-
- if (px0 < px1) {
- r.x0 = px0;
- r.y0 = clip->y0;
- r.x1 = px1;
- r.y1 = clip->y1;
- res = ctx->plot->clip(ctx, &r);
- if (res != NSERROR_OK) {
- return false;
- }
-
- clip_changed = true;
- } else {
- text_visible = false;
- }
- }
-
- fstyle_hback.background =
- pstyle_fill_hback.fill_colour;
- fstyle_hback.foreground = colour_to_bw_furthest(
- pstyle_fill_hback.fill_colour);
-
- if (text_visible &&
- (ctx->plot->text(ctx,
- &fstyle_hback,
- x,
- y + (int)(height * 0.75 * scale),
- utf8_text,
- endtxt_idx) != NSERROR_OK)) {
- return false;
- }
-
- /* draw any text succeeding highlighted portion */
- if (endtxt_idx < utf8_len) {
- int px0 = max(x + endx, clip->x0);
- if (px0 < clip->x1) {
-
- r.x0 = px0;
- r.y0 = clip->y0;
- r.x1 = clip->x1;
- r.y1 = clip->y1;
- res = ctx->plot->clip(ctx, &r);
- if (res != NSERROR_OK) {
- return false;
- }
-
- clip_changed = true;
-
- res = ctx->plot->text(ctx,
- &plot_fstyle,
- x,
- y + (int)(height * 0.75 * scale),
- utf8_text,
- utf8_len);
- if (res != NSERROR_OK) {
- return false;
- }
- }
- }
-
- if (clip_changed &&
- (ctx->plot->clip(ctx, clip) != NSERROR_OK)) {
- return false;
- }
- }
- }
-
- if (!highlighted) {
- res = ctx->plot->text(ctx,
- &plot_fstyle,
- x,
- y + (int) (height * 0.75 * scale),
- utf8_text,
- utf8_len);
- if (res != NSERROR_OK) {
- return false;
- }
- }
- return true;
-}
-
-
-/**
- * Plot a checkbox.
- *
- * \param x left coordinate
- * \param y top coordinate
- * \param width dimensions of checkbox
- * \param height dimensions of checkbox
- * \param selected the checkbox is selected
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool html_redraw_checkbox(int x, int y, int width, int height,
- bool selected, const struct redraw_context *ctx)
-{
- double z;
- nserror res;
- struct rect rect;
-
- z = width * 0.15;
- if (z == 0) {
- z = 1;
- }
-
- rect.x0 = x;
- rect.y0 = y ;
- rect.x1 = x + width;
- rect.y1 = y + height;
- res = ctx->plot->rectangle(ctx, plot_style_fill_wbasec, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
-
- /* dark line across top */
- rect.y1 = y;
- res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
-
- /* dark line across left */
- rect.x1 = x;
- rect.y1 = y + height;
- res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
-
- /* light line across right */
- rect.x0 = x + width;
- rect.x1 = x + width;
- res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
-
- /* light line across bottom */
- rect.x0 = x;
- rect.y0 = y + height;
- res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
-
- if (selected) {
- if (width < 12 || height < 12) {
- /* render a solid box instead of a tick */
- rect.x0 = x + z + z;
- rect.y0 = y + z + z;
- rect.x1 = x + width - z;
- rect.y1 = y + height - z;
- res = ctx->plot->rectangle(ctx, plot_style_fill_wblobc, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
- } else {
- /* render a tick, as it'll fit comfortably */
- rect.x0 = x + width - z;
- rect.y0 = y + z;
- rect.x1 = x + (z * 3);
- rect.y1 = y + height - z;
- res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
-
- rect.x0 = x + (z * 3);
- rect.y0 = y + height - z;
- rect.x1 = x + z + z;
- rect.y1 = y + (height / 2);
- res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
- }
- }
- return true;
-}
-
-
-/**
- * Plot a radio icon.
- *
- * \param x left coordinate
- * \param y top coordinate
- * \param width dimensions of radio icon
- * \param height dimensions of radio icon
- * \param selected the radio icon is selected
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-static bool html_redraw_radio(int x, int y, int width, int height,
- bool selected, const struct redraw_context *ctx)
-{
- nserror res;
-
- /* plot background of radio button */
- res = ctx->plot->disc(ctx,
- plot_style_fill_wbasec,
- x + width * 0.5,
- y + height * 0.5,
- width * 0.5 - 1);
- if (res != NSERROR_OK) {
- return false;
- }
-
- /* plot dark arc */
- res = ctx->plot->arc(ctx,
- plot_style_fill_darkwbasec,
- x + width * 0.5,
- y + height * 0.5,
- width * 0.5 - 1,
- 45,
- 225);
- if (res != NSERROR_OK) {
- return false;
- }
-
- /* plot light arc */
- res = ctx->plot->arc(ctx,
- plot_style_fill_lightwbasec,
- x + width * 0.5,
- y + height * 0.5,
- width * 0.5 - 1,
- 225,
- 45);
- if (res != NSERROR_OK) {
- return false;
- }
-
- if (selected) {
- /* plot selection blob */
- res = ctx->plot->disc(ctx,
- plot_style_fill_wblobc,
- x + width * 0.5,
- y + height * 0.5,
- width * 0.3 - 1);
- if (res != NSERROR_OK) {
- return false;
- }
- }
-
- return true;
-}
-
-
-/**
- * Plot a file upload input.
- *
- * \param x left coordinate
- * \param y top coordinate
- * \param width dimensions of input
- * \param height dimensions of input
- * \param box box of input
- * \param scale scale for redraw
- * \param background_colour current background colour
- * \param len_ctx Length conversion context
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool html_redraw_file(int x, int y, int width, int height,
- struct box *box, float scale, colour background_colour,
- const nscss_len_ctx *len_ctx,
- const struct redraw_context *ctx)
-{
- int text_width;
- const char *text;
- size_t length;
- plot_font_style_t fstyle;
- nserror res;
-
- font_plot_style_from_css(len_ctx, box->style, &fstyle);
- fstyle.background = background_colour;
-
- if (box->gadget->value) {
- text = box->gadget->value;
- } else {
- text = messages_get("Form_Drop");
- }
- length = strlen(text);
-
- res = guit->layout->width(&fstyle, text, length, &text_width);
- if (res != NSERROR_OK) {
- return false;
- }
- text_width *= scale;
- if (width < text_width + 8) {
- x = x + width - text_width - 4;
- } else {
- x = x + 4;
- }
-
- res = ctx->plot->text(ctx, &fstyle, x, y + height * 0.75, text, length);
- if (res != NSERROR_OK) {
- return false;
- }
- return true;
-}
-
-
-/**
- * Plot background images.
- *
- * The reason for the presence of \a background is the backwards compatibility
- * mess that is backgrounds on <body>. The background will be drawn
relative
- * to \a box, using the background information contained within \a background.
- *
- * \param x coordinate of box
- * \param y coordinate of box
- * \param box box to draw background image of
- * \param scale scale for redraw
- * \param clip current clip rectangle
- * \param background_colour current background colour
- * \param background box containing background details (usually \a box)
- * \param len_ctx Length conversion context
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool html_redraw_background(int x, int y, struct box *box, float scale,
- const struct rect *clip, colour *background_colour,
- struct box *background,
- const nscss_len_ctx *len_ctx,
- const struct redraw_context *ctx)
-{
- bool repeat_x = false;
- bool repeat_y = false;
- bool plot_colour = true;
- bool plot_content;
- bool clip_to_children = false;
- struct box *clip_box = box;
- int ox = x, oy = y;
- int width, height;
- css_fixed hpos = 0, vpos = 0;
- css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
- struct box *parent;
- struct rect r = *clip;
- css_color bgcol;
- plot_style_t pstyle_fill_bg = {
- .fill_type = PLOT_OP_TYPE_SOLID,
- .fill_colour = *background_colour,
- };
- nserror res;
-
- if (ctx->background_images == false)
- return true;
-
- plot_content = (background->background != NULL);
-
- if (plot_content) {
- if (!box->parent) {
- /* Root element, special case:
- * background origin calc. is based on margin box */
- x -= box->margin[LEFT] * scale;
- y -= box->margin[TOP] * scale;
- width = box->margin[LEFT] + box->padding[LEFT] +
- box->width + box->padding[RIGHT] +
- box->margin[RIGHT];
- height = box->margin[TOP] + box->padding[TOP] +
- box->height + box->padding[BOTTOM] +
- box->margin[BOTTOM];
- } else {
- width = box->padding[LEFT] + box->width +
- box->padding[RIGHT];
- height = box->padding[TOP] + box->height +
- box->padding[BOTTOM];
- }
- /* handle background-repeat */
- switch (css_computed_background_repeat(background->style)) {
- case CSS_BACKGROUND_REPEAT_REPEAT:
- repeat_x = repeat_y = true;
- /* optimisation: only plot the colour if
- * bitmap is not opaque */
- plot_colour = !content_get_opaque(background->background);
- break;
-
- case CSS_BACKGROUND_REPEAT_REPEAT_X:
- repeat_x = true;
- break;
-
- case CSS_BACKGROUND_REPEAT_REPEAT_Y:
- repeat_y = true;
- break;
-
- case CSS_BACKGROUND_REPEAT_NO_REPEAT:
- break;
-
- default:
- break;
- }
-
- /* handle background-position */
- css_computed_background_position(background->style,
- &hpos, &hunit, &vpos, &vunit);
- if (hunit == CSS_UNIT_PCT) {
- x += (width -
- content_get_width(background->background)) *
- scale * FIXTOFLT(hpos) / 100.;
- } else {
- x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit,
- background->style)) * scale);
- }
-
- if (vunit == CSS_UNIT_PCT) {
- y += (height -
- content_get_height(background->background)) *
- scale * FIXTOFLT(vpos) / 100.;
- } else {
- y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit,
- background->style)) * scale);
- }
- }
-
- /* special case for table rows as their background needs
- * to be clipped to all the cells */
- if (box->type == BOX_TABLE_ROW) {
- css_fixed h = 0, v = 0;
- css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX;
-
- for (parent = box->parent;
- ((parent) && (parent->type != BOX_TABLE));
- parent = parent->parent);
- assert(parent && (parent->style));
-
- css_computed_border_spacing(parent->style, &h, &hu, &v, &vu);
-
- clip_to_children = (h > 0) || (v > 0);
-
- if (clip_to_children)
- clip_box = box->children;
- }
-
- for (; clip_box; clip_box = clip_box->next) {
- /* clip to child boxes if needed */
- if (clip_to_children) {
- assert(clip_box->type == BOX_TABLE_CELL);
-
- /* update clip.* to the child cell */
- r.x0 = ox + (clip_box->x * scale);
- r.y0 = oy + (clip_box->y * scale);
- r.x1 = r.x0 + (clip_box->padding[LEFT] +
- clip_box->width +
- clip_box->padding[RIGHT]) * scale;
- r.y1 = r.y0 + (clip_box->padding[TOP] +
- clip_box->height +
- clip_box->padding[BOTTOM]) * scale;
-
- if (r.x0 < clip->x0) r.x0 = clip->x0;
- if (r.y0 < clip->y0) r.y0 = clip->y0;
- if (r.x1 > clip->x1) r.x1 = clip->x1;
- if (r.y1 > clip->y1) r.y1 = clip->y1;
-
- css_computed_background_color(clip_box->style, &bgcol);
-
- /* <td> attributes override <tr> */
- /* if the background content is opaque there
- * is no need to plot underneath it.
- */
- if ((r.x0 >= r.x1) ||
- (r.y0 >= r.y1) ||
- (nscss_color_is_transparent(bgcol) == false) ||
- ((clip_box->background != NULL) &&
- content_get_opaque(clip_box->background)))
- continue;
- }
-
- /* plot the background colour */
- css_computed_background_color(background->style, &bgcol);
-
- if (nscss_color_is_transparent(bgcol) == false) {
- *background_colour = nscss_color_to_ns(bgcol);
- pstyle_fill_bg.fill_colour = *background_colour;
- if (plot_colour) {
- res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r);
- if (res != NSERROR_OK) {
- return false;
- }
- }
- }
- /* and plot the image */
- if (plot_content) {
- width = content_get_width(background->background);
- height = content_get_height(background->background);
-
- /* ensure clip area only as large as required */
- if (!repeat_x) {
- if (r.x0 < x)
- r.x0 = x;
- if (r.x1 > x + width * scale)
- r.x1 = x + width * scale;
- }
- if (!repeat_y) {
- if (r.y0 < y)
- r.y0 = y;
- if (r.y1 > y + height * scale)
- r.y1 = y + height * scale;
- }
- /* valid clipping rectangles only */
- if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
- struct content_redraw_data bg_data;
-
- res = ctx->plot->clip(ctx, &r);
- if (res != NSERROR_OK) {
- return false;
- }
-
- bg_data.x = x;
- bg_data.y = y;
- bg_data.width = ceilf(width * scale);
- bg_data.height = ceilf(height * scale);
- bg_data.background_colour = *background_colour;
- bg_data.scale = scale;
- bg_data.repeat_x = repeat_x;
- bg_data.repeat_y = repeat_y;
-
- /* We just continue if redraw fails */
- content_redraw(background->background,
- &bg_data, &r, ctx);
- }
- }
-
- /* only <tr> rows being clipped to child boxes loop */
- if (!clip_to_children)
- return true;
- }
- return true;
-}
-
-
-/**
- * Plot an inline's background and/or background image.
- *
- * \param x coordinate of box
- * \param y coordinate of box
- * \param box BOX_INLINE which created the background
- * \param scale scale for redraw
- * \param clip coordinates of clip rectangle
- * \param b coordinates of border edge rectangle
- * \param first true if this is the first rectangle associated with the inline
- * \param last true if this is the last rectangle associated with the inline
- * \param background_colour updated to current background colour if plotted
- * \param len_ctx Length conversion context
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool html_redraw_inline_background(int x, int y, struct box *box,
- float scale, const struct rect *clip, struct rect b,
- bool first, bool last, colour *background_colour,
- const nscss_len_ctx *len_ctx,
- const struct redraw_context *ctx)
-{
- struct rect r = *clip;
- bool repeat_x = false;
- bool repeat_y = false;
- bool plot_colour = true;
- bool plot_content;
- css_fixed hpos = 0, vpos = 0;
- css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
- css_color bgcol;
- plot_style_t pstyle_fill_bg = {
- .fill_type = PLOT_OP_TYPE_SOLID,
- .fill_colour = *background_colour,
- };
- nserror res;
-
- plot_content = (box->background != NULL);
-
- if (html_redraw_printing && nsoption_bool(remove_backgrounds))
- return true;
-
- if (plot_content) {
- /* handle background-repeat */
- switch (css_computed_background_repeat(box->style)) {
- case CSS_BACKGROUND_REPEAT_REPEAT:
- repeat_x = repeat_y = true;
- /* optimisation: only plot the colour if
- * bitmap is not opaque
- */
- plot_colour = !content_get_opaque(box->background);
- break;
-
- case CSS_BACKGROUND_REPEAT_REPEAT_X:
- repeat_x = true;
- break;
-
- case CSS_BACKGROUND_REPEAT_REPEAT_Y:
- repeat_y = true;
- break;
-
- case CSS_BACKGROUND_REPEAT_NO_REPEAT:
- break;
-
- default:
- break;
- }
-
- /* handle background-position */
- css_computed_background_position(box->style,
- &hpos, &hunit, &vpos, &vunit);
- if (hunit == CSS_UNIT_PCT) {
- x += (b.x1 - b.x0 -
- content_get_width(box->background) *
- scale) * FIXTOFLT(hpos) / 100.;
-
- if (!repeat_x && ((hpos < 2 && !first) ||
- (hpos > 98 && !last))){
- plot_content = false;
- }
- } else {
- x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit,
- box->style)) * scale);
- }
-
- if (vunit == CSS_UNIT_PCT) {
- y += (b.y1 - b.y0 -
- content_get_height(box->background) *
- scale) * FIXTOFLT(vpos) / 100.;
- } else {
- y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit,
- box->style)) * scale);
- }
- }
-
- /* plot the background colour */
- css_computed_background_color(box->style, &bgcol);
-
- if (nscss_color_is_transparent(bgcol) == false) {
- *background_colour = nscss_color_to_ns(bgcol);
- pstyle_fill_bg.fill_colour = *background_colour;
-
- if (plot_colour) {
- res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r);
- if (res != NSERROR_OK) {
- return false;
- }
- }
- }
- /* and plot the image */
- if (plot_content) {
- int width = content_get_width(box->background);
- int height = content_get_height(box->background);
-
- if (!repeat_x) {
- if (r.x0 < x)
- r.x0 = x;
- if (r.x1 > x + width * scale)
- r.x1 = x + width * scale;
- }
- if (!repeat_y) {
- if (r.y0 < y)
- r.y0 = y;
- if (r.y1 > y + height * scale)
- r.y1 = y + height * scale;
- }
- /* valid clipping rectangles only */
- if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
- struct content_redraw_data bg_data;
-
- res = ctx->plot->clip(ctx, &r);
- if (res != NSERROR_OK) {
- return false;
- }
-
- bg_data.x = x;
- bg_data.y = y;
- bg_data.width = ceilf(width * scale);
- bg_data.height = ceilf(height * scale);
- bg_data.background_colour = *background_colour;
- bg_data.scale = scale;
- bg_data.repeat_x = repeat_x;
- bg_data.repeat_y = repeat_y;
-
- /* We just continue if redraw fails */
- content_redraw(box->background, &bg_data, &r, ctx);
- }
- }
-
- return true;
-}
-
-
-/**
- * Plot text decoration for an inline box.
- *
- * \param box box to plot decorations for, of type BOX_INLINE
- * \param x x coordinate of parent of box
- * \param y y coordinate of parent of box
- * \param scale scale for redraw
- * \param colour colour for decorations
- * \param ratio position of line as a ratio of line height
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool
-html_redraw_text_decoration_inline(struct box *box,
- int x, int y,
- float scale,
- colour colour,
- float ratio,
- const struct redraw_context *ctx)
-{
- struct box *c;
- plot_style_t plot_style_box = {
- .stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_colour = colour,
- };
- nserror res;
- struct rect rect;
-
- for (c = box->next;
- c && c != box->inline_end;
- c = c->next) {
- if (c->type != BOX_TEXT) {
- continue;
- }
- rect.x0 = (x + c->x) * scale;
- rect.y0 = (y + c->y + c->height * ratio) * scale;
- rect.x1 = (x + c->x + c->width) * scale;
- rect.y1 = (y + c->y + c->height * ratio) * scale;
- res = ctx->plot->line(ctx, &plot_style_box, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
- }
- return true;
-}
-
-
-/**
- * Plot text decoration for an non-inline box.
- *
- * \param box box to plot decorations for, of type other than BOX_INLINE
- * \param x x coordinate of box
- * \param y y coordinate of box
- * \param scale scale for redraw
- * \param colour colour for decorations
- * \param ratio position of line as a ratio of line height
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool
-html_redraw_text_decoration_block(struct box *box,
- int x, int y,
- float scale,
- colour colour,
- float ratio,
- const struct redraw_context *ctx)
-{
- struct box *c;
- plot_style_t plot_style_box = {
- .stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_colour = colour,
- };
- nserror res;
- struct rect rect;
-
- /* draw through text descendants */
- for (c = box->children; c; c = c->next) {
- if (c->type == BOX_TEXT) {
- rect.x0 = (x + c->x) * scale;
- rect.y0 = (y + c->y + c->height * ratio) * scale;
- rect.x1 = (x + c->x + c->width) * scale;
- rect.y1 = (y + c->y + c->height * ratio) * scale;
- res = ctx->plot->line(ctx, &plot_style_box, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
- } else if ((c->type == BOX_INLINE_CONTAINER) || (c->type == BOX_BLOCK)) {
- if (!html_redraw_text_decoration_block(c,
- x + c->x, y + c->y,
- scale, colour, ratio, ctx))
- return false;
- }
- }
- return true;
-}
-
-
-/**
- * Plot text decoration for a box.
- *
- * \param box box to plot decorations for
- * \param x_parent x coordinate of parent of box
- * \param y_parent y coordinate of parent of box
- * \param scale scale for redraw
- * \param background_colour current background colour
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool html_redraw_text_decoration(struct box *box,
- int x_parent, int y_parent, float scale,
- colour background_colour, const struct redraw_context *ctx)
-{
- static const enum css_text_decoration_e decoration[] = {
- CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE,
- CSS_TEXT_DECORATION_LINE_THROUGH };
- static const float line_ratio[] = { 0.9, 0.1, 0.5 };
- colour fgcol;
- unsigned int i;
- css_color col;
-
- css_computed_color(box->style, &col);
- fgcol = nscss_color_to_ns(col);
-
- /* antialias colour for under/overline */
- if (html_redraw_printing == false)
- fgcol = blend_colour(background_colour, fgcol);
-
- if (box->type == BOX_INLINE) {
- if (!box->inline_end)
- return true;
- for (i = 0; i != NOF_ELEMENTS(decoration); i++)
- if (css_computed_text_decoration(box->style) &
- decoration[i])
- if (!html_redraw_text_decoration_inline(box,
- x_parent, y_parent, scale,
- fgcol, line_ratio[i], ctx))
- return false;
- } else {
- for (i = 0; i != NOF_ELEMENTS(decoration); i++)
- if (css_computed_text_decoration(box->style) &
- decoration[i])
- if (!html_redraw_text_decoration_block(box,
- x_parent + box->x,
- y_parent + box->y,
- scale,
- fgcol, line_ratio[i], ctx))
- return false;
- }
-
- return true;
-}
-
-
-/**
- * Redraw the text content of a box, possibly partially highlighted
- * because the text has been selected, or matches a search operation.
- *
- * \param html The html content to redraw text within.
- * \param box box with text content
- * \param x x co-ord of box
- * \param y y co-ord of box
- * \param clip current clip rectangle
- * \param scale current scale setting (1.0 = 100%)
- * \param current_background_color
- * \param ctx current redraw context
- * \return true iff successful and redraw should proceed
- */
-
-static bool html_redraw_text_box(const html_content *html, struct box *box,
- int x, int y, const struct rect *clip, float scale,
- colour current_background_color,
- const struct redraw_context *ctx)
-{
- bool excluded = (box->object != NULL);
- plot_font_style_t fstyle;
-
- font_plot_style_from_css(&html->len_ctx, box->style, &fstyle);
- fstyle.background = current_background_color;
-
- if (!text_redraw(box->text, box->length, box->byte_offset,
- box->space, &fstyle, x, y,
- clip, box->height, scale, excluded,
- (struct content *)html, &html->sel,
- html->search, ctx))
- return false;
-
- return true;
-}
-
-bool html_redraw_box(const html_content *html, struct box *box,
- int x_parent, int y_parent,
- const struct rect *clip, float scale,
- colour current_background_color,
- const struct redraw_context *ctx);
-
-/**
- * Draw the various children of a box.
- *
- * \param html html content
- * \param box box to draw children of
- * \param x_parent coordinate of parent box
- * \param y_parent coordinate of parent box
- * \param clip clip rectangle
- * \param scale scale for redraw
- * \param current_background_color background colour under this box
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-
-static bool html_redraw_box_children(const html_content *html, struct box *box,
- int x_parent, int y_parent,
- const struct rect *clip, float scale,
- colour current_background_color,
- const struct redraw_context *ctx)
-{
- struct box *c;
-
- for (c = box->children; c; c = c->next) {
-
- if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT)
- if (!html_redraw_box(html, c,
- x_parent + box->x -
- scrollbar_get_offset(box->scroll_x),
- y_parent + box->y -
- scrollbar_get_offset(box->scroll_y),
- clip, scale, current_background_color,
- ctx))
- return false;
- }
- for (c = box->float_children; c; c = c->next_float)
- if (!html_redraw_box(html, c,
- x_parent + box->x -
- scrollbar_get_offset(box->scroll_x),
- y_parent + box->y -
- scrollbar_get_offset(box->scroll_y),
- clip, scale, current_background_color,
- ctx))
- return false;
-
- return true;
-}
-
-/**
- * Recursively draw a box.
- *
- * \param html html content
- * \param box box to draw
- * \param x_parent coordinate of parent box
- * \param y_parent coordinate of parent box
- * \param clip clip rectangle
- * \param scale scale for redraw
- * \param current_background_color background colour under this box
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- *
- * x, y, clip_[xy][01] are in target coordinates.
- */
-
-bool html_redraw_box(const html_content *html, struct box *box,
- int x_parent, int y_parent,
- const struct rect *clip, const float scale,
- colour current_background_color,
- const struct redraw_context *ctx)
-{
- const struct plotter_table *plot = ctx->plot;
- int x, y;
- int width, height;
- int padding_left, padding_top, padding_width, padding_height;
- int border_left, border_top, border_right, border_bottom;
- struct rect r;
- struct rect rect;
- int x_scrolled, y_scrolled;
- struct box *bg_box = NULL;
- css_computed_clip_rect css_rect;
- enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE;
- enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE;
-
- if (html_redraw_printing && (box->flags & PRINTED))
- return true;
-
- if (box->style != NULL) {
- overflow_x = css_computed_overflow_x(box->style);
- overflow_y = css_computed_overflow_y(box->style);
- }
-
- /* avoid trivial FP maths */
- if (scale == 1.0) {
- x = x_parent + box->x;
- y = y_parent + box->y;
- width = box->width;
- height = box->height;
- padding_left = box->padding[LEFT];
- padding_top = box->padding[TOP];
- padding_width = padding_left + box->width + box->padding[RIGHT];
- padding_height = padding_top + box->height +
- box->padding[BOTTOM];
- border_left = box->border[LEFT].width;
- border_top = box->border[TOP].width;
- border_right = box->border[RIGHT].width;
- border_bottom = box->border[BOTTOM].width;
- } else {
- x = (x_parent + box->x) * scale;
- y = (y_parent + box->y) * scale;
- width = box->width * scale;
- height = box->height * scale;
- /* left and top padding values are normally zero,
- * so avoid trivial FP maths */
- padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale
- : 0;
- padding_top = box->padding[TOP] ? box->padding[TOP] * scale
- : 0;
- padding_width = (box->padding[LEFT] + box->width +
- box->padding[RIGHT]) * scale;
- padding_height = (box->padding[TOP] + box->height +
- box->padding[BOTTOM]) * scale;
- border_left = box->border[LEFT].width * scale;
- border_top = box->border[TOP].width * scale;
- border_right = box->border[RIGHT].width * scale;
- border_bottom = box->border[BOTTOM].width * scale;
- }
-
- /* calculate rectangle covering this box and descendants */
- if (box->style && overflow_x != CSS_OVERFLOW_VISIBLE &&
- box->parent != NULL) {
- /* box contents clipped to box size */
- r.x0 = x - border_left;
- r.x1 = x + padding_width + border_right;
- } else {
- /* box contents can hang out of the box; use descendant box */
- if (scale == 1.0) {
- r.x0 = x + box->descendant_x0;
- r.x1 = x + box->descendant_x1 + 1;
- } else {
- r.x0 = x + box->descendant_x0 * scale;
- r.x1 = x + box->descendant_x1 * scale + 1;
- }
- if (!box->parent) {
- /* root element */
- int margin_left, margin_right;
- if (scale == 1.0) {
- margin_left = box->margin[LEFT];
- margin_right = box->margin[RIGHT];
- } else {
- margin_left = box->margin[LEFT] * scale;
- margin_right = box->margin[RIGHT] * scale;
- }
- r.x0 = x - border_left - margin_left < r.x0 ?
- x - border_left - margin_left : r.x0;
- r.x1 = x + padding_width + border_right +
- margin_right > r.x1 ?
- x + padding_width + border_right +
- margin_right : r.x1;
- }
- }
-
- /* calculate rectangle covering this box and descendants */
- if (box->style && overflow_y != CSS_OVERFLOW_VISIBLE &&
- box->parent != NULL) {
- /* box contents clipped to box size */
- r.y0 = y - border_top;
- r.y1 = y + padding_height + border_bottom;
- } else {
- /* box contents can hang out of the box; use descendant box */
- if (scale == 1.0) {
- r.y0 = y + box->descendant_y0;
- r.y1 = y + box->descendant_y1 + 1;
- } else {
- r.y0 = y + box->descendant_y0 * scale;
- r.y1 = y + box->descendant_y1 * scale + 1;
- }
- if (!box->parent) {
- /* root element */
- int margin_top, margin_bottom;
- if (scale == 1.0) {
- margin_top = box->margin[TOP];
- margin_bottom = box->margin[BOTTOM];
- } else {
- margin_top = box->margin[TOP] * scale;
- margin_bottom = box->margin[BOTTOM] * scale;
- }
- r.y0 = y - border_top - margin_top < r.y0 ?
- y - border_top - margin_top : r.y0;
- r.y1 = y + padding_height + border_bottom +
- margin_bottom > r.y1 ?
- y + padding_height + border_bottom +
- margin_bottom : r.y1;
- }
- }
-
- /* return if the rectangle is completely outside the clip rectangle */
- if (clip->y1 < r.y0 || r.y1 < clip->y0 ||
- clip->x1 < r.x0 || r.x1 < clip->x0)
- return true;
-
- /*if the rectangle is under the page bottom but it can fit in a page,
- don't print it now*/
- if (html_redraw_printing) {
- if (r.y1 > html_redraw_printing_border) {
- if (r.y1 - r.y0 <= html_redraw_printing_border &&
- (box->type == BOX_TEXT ||
- box->type == BOX_TABLE_CELL
- || box->object || box->gadget)) {
- /*remember the highest of all points from the
- not printed elements*/
- if (r.y0 < html_redraw_printing_top_cropped)
- html_redraw_printing_top_cropped = r.y0;
- return true;
- }
- }
- else box->flags |= PRINTED; /*it won't be printed anymore*/
- }
-
- /* if visibility is hidden render children only */
- if (box->style && css_computed_visibility(box->style) ==
- CSS_VISIBILITY_HIDDEN) {
- if ((ctx->plot->group_start) &&
- (ctx->plot->group_start(ctx, "hidden box") != NSERROR_OK))
- return false;
- if (!html_redraw_box_children(html, box, x_parent, y_parent,
- &r, scale, current_background_color, ctx))
- return false;
- return ((!ctx->plot->group_end) || (ctx->plot->group_end(ctx) ==
NSERROR_OK));
- }
-
- if ((ctx->plot->group_start) &&
- (ctx->plot->group_start(ctx,"vis box") != NSERROR_OK)) {
- return false;
- }
-
- if (box->style != NULL &&
- css_computed_position(box->style) ==
- CSS_POSITION_ABSOLUTE &&
- css_computed_clip(box->style, &css_rect) ==
- CSS_CLIP_RECT) {
- /* We have an absolutly positioned box with a clip rect */
- if (css_rect.left_auto == false)
- r.x0 = x - border_left + FIXTOINT(nscss_len2px(
- &html->len_ctx,
- css_rect.left, css_rect.lunit,
- box->style));
-
- if (css_rect.top_auto == false)
- r.y0 = y - border_top + FIXTOINT(nscss_len2px(
- &html->len_ctx,
- css_rect.top, css_rect.tunit,
- box->style));
-
- if (css_rect.right_auto == false)
- r.x1 = x - border_left + FIXTOINT(nscss_len2px(
- &html->len_ctx,
- css_rect.right, css_rect.runit,
- box->style));
-
- if (css_rect.bottom_auto == false)
- r.y1 = y - border_top + FIXTOINT(nscss_len2px(
- &html->len_ctx,
- css_rect.bottom, css_rect.bunit,
- box->style));
-
- /* find intersection of clip rectangle and box */
- if (r.x0 < clip->x0) r.x0 = clip->x0;
- if (r.y0 < clip->y0) r.y0 = clip->y0;
- if (clip->x1 < r.x1) r.x1 = clip->x1;
- if (clip->y1 < r.y1) r.y1 = clip->y1;
- /* Nothing to do for invalid rectangles */
- if (r.x0 >= r.x1 || r.y0 >= r.y1)
- /* not an error */
- return ((!ctx->plot->group_end) ||
- (ctx->plot->group_end(ctx) == NSERROR_OK));
- /* clip to it */
- if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
- return false;
-
- } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
- box->type == BOX_TABLE_CELL || box->object) {
- /* find intersection of clip rectangle and box */
- if (r.x0 < clip->x0) r.x0 = clip->x0;
- if (r.y0 < clip->y0) r.y0 = clip->y0;
- if (clip->x1 < r.x1) r.x1 = clip->x1;
- if (clip->y1 < r.y1) r.y1 = clip->y1;
- /* no point trying to draw 0-width/height boxes */
- if (r.x0 == r.x1 || r.y0 == r.y1)
- /* not an error */
- return ((!ctx->plot->group_end) ||
- (ctx->plot->group_end(ctx) == NSERROR_OK));
- /* clip to it */
- if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
- return false;
- } else {
- /* clip box is fine, clip to it */
- r = *clip;
- if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
- return false;
- }
-
- /* background colour and image for block level content and replaced
- * inlines */
-
- bg_box = html_redraw_find_bg_box(box);
-
- /* bg_box == NULL implies that this box should not have
- * its background rendered. Otherwise filter out linebreaks,
- * optimize away non-differing inlines, only plot background
- * for BOX_TEXT it's in an inline */
- if (bg_box && bg_box->type != BOX_BR &&
- bg_box->type != BOX_TEXT &&
- bg_box->type != BOX_INLINE_END &&
- (bg_box->type != BOX_INLINE || bg_box->object ||
- bg_box->flags & IFRAME || box->flags & REPLACE_DIM ||
- (bg_box->gadget != NULL &&
- (bg_box->gadget->type == GADGET_TEXTAREA ||
- bg_box->gadget->type == GADGET_TEXTBOX ||
- bg_box->gadget->type == GADGET_PASSWORD)))) {
- /* find intersection of clip box and border edge */
- struct rect p;
- p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left;
- p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top;
- p.x1 = x + padding_width + border_right < r.x1 ?
- x + padding_width + border_right : r.x1;
- p.y1 = y + padding_height + border_bottom < r.y1 ?
- y + padding_height + border_bottom : r.y1;
- if (!box->parent) {
- /* Root element, special case:
- * background covers margins too */
- int m_left, m_top, m_right, m_bottom;
- if (scale == 1.0) {
- m_left = box->margin[LEFT];
- m_top = box->margin[TOP];
- m_right = box->margin[RIGHT];
- m_bottom = box->margin[BOTTOM];
- } else {
- m_left = box->margin[LEFT] * scale;
- m_top = box->margin[TOP] * scale;
- m_right = box->margin[RIGHT] * scale;
- m_bottom = box->margin[BOTTOM] * scale;
- }
- p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left;
- p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top;
- p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1;
- p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1;
- }
- /* valid clipping rectangles only */
- if ((p.x0 < p.x1) && (p.y0 < p.y1)) {
- /* plot background */
- if (!html_redraw_background(x, y, box, scale, &p,
- ¤t_background_color, bg_box,
- &html->len_ctx, ctx))
- return false;
- /* restore previous graphics window */
- if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
- return false;
- }
- }
-
- /* borders for block level content and replaced inlines */
- if (box->style &&
- box->type != BOX_TEXT &&
- box->type != BOX_INLINE_END &&
- (box->type != BOX_INLINE || box->object ||
- box->flags & IFRAME || box->flags & REPLACE_DIM ||
- (box->gadget != NULL &&
- (box->gadget->type == GADGET_TEXTAREA ||
- box->gadget->type == GADGET_TEXTBOX ||
- box->gadget->type == GADGET_PASSWORD))) &&
- (border_top || border_right || border_bottom || border_left)) {
- if (!html_redraw_borders(box, x_parent, y_parent,
- padding_width, padding_height, &r,
- scale, ctx))
- return false;
- }
-
- /* backgrounds and borders for non-replaced inlines */
- if (box->style && box->type == BOX_INLINE && box->inline_end
&&
- (html_redraw_box_has_background(box) ||
- border_top || border_right ||
- border_bottom || border_left)) {
- /* inline backgrounds and borders span other boxes and may
- * wrap onto separate lines */
- struct box *ib;
- struct rect b; /* border edge rectangle */
- struct rect p; /* clipped rect */
- bool first = true;
- int ib_x;
- int ib_y = y;
- int ib_p_width;
- int ib_b_left, ib_b_right;
-
- b.x0 = x - border_left;
- b.x1 = x + padding_width + border_right;
- b.y0 = y - border_top;
- b.y1 = y + padding_height + border_bottom;
-
- p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
- p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
- p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
- p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
- for (ib = box; ib; ib = ib->next) {
- /* to get extents of rectangle(s) associated with
- * inline, cycle though all boxes in inline, skipping
- * over floats */
- if (ib->type == BOX_FLOAT_LEFT ||
- ib->type == BOX_FLOAT_RIGHT)
- continue;
- if (scale == 1.0) {
- ib_x = x_parent + ib->x;
- ib_y = y_parent + ib->y;
- ib_p_width = ib->padding[LEFT] + ib->width +
- ib->padding[RIGHT];
- ib_b_left = ib->border[LEFT].width;
- ib_b_right = ib->border[RIGHT].width;
- } else {
- ib_x = (x_parent + ib->x) * scale;
- ib_y = (y_parent + ib->y) * scale;
- ib_p_width = (ib->padding[LEFT] + ib->width +
- ib->padding[RIGHT]) * scale;
- ib_b_left = ib->border[LEFT].width * scale;
- ib_b_right = ib->border[RIGHT].width * scale;
- }
-
- if ((ib->flags & NEW_LINE) && ib != box) {
- /* inline element has wrapped, plot background
- * and borders */
- if (!html_redraw_inline_background(
- x, y, box, scale, &p, b,
- first, false,
- ¤t_background_color,
- &html->len_ctx, ctx))
- return false;
- /* restore previous graphics window */
- if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
- return false;
- if (!html_redraw_inline_borders(box, b, &r,
- scale, first, false, ctx))
- return false;
- /* reset coords */
- b.x0 = ib_x - ib_b_left;
- b.y0 = ib_y - border_top - padding_top;
- b.y1 = ib_y + padding_height - padding_top +
- border_bottom;
-
- p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
- p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
- p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
-
- first = false;
- }
-
- /* increase width for current box */
- b.x1 = ib_x + ib_p_width + ib_b_right;
- p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
-
- if (ib == box->inline_end)
- /* reached end of BOX_INLINE span */
- break;
- }
- /* plot background and borders for last rectangle of
- * the inline */
- if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b,
- first, true, ¤t_background_color,
- &html->len_ctx, ctx))
- return false;
- /* restore previous graphics window */
- if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
- return false;
- if (!html_redraw_inline_borders(box, b, &r, scale, first, true,
- ctx))
- return false;
-
- }
-
- /* Debug outlines */
- if (html_redraw_debug) {
- int margin_left, margin_right;
- int margin_top, margin_bottom;
- if (scale == 1.0) {
- /* avoid trivial fp maths */
- margin_left = box->margin[LEFT];
- margin_top = box->margin[TOP];
- margin_right = box->margin[RIGHT];
- margin_bottom = box->margin[BOTTOM];
- } else {
- margin_left = box->margin[LEFT] * scale;
- margin_top = box->margin[TOP] * scale;
- margin_right = box->margin[RIGHT] * scale;
- margin_bottom = box->margin[BOTTOM] * scale;
- }
- /* Content edge -- blue */
- rect.x0 = x + padding_left;
- rect.y0 = y + padding_top;
- rect.x1 = x + padding_left + width;
- rect.y1 = y + padding_top + height;
- if (ctx->plot->rectangle(ctx, plot_style_content_edge, &rect) != NSERROR_OK)
- return false;
-
- /* Padding edge -- red */
- rect.x0 = x;
- rect.y0 = y;
- rect.x1 = x + padding_width;
- rect.y1 = y + padding_height;
- if (ctx->plot->rectangle(ctx, plot_style_padding_edge, &rect) != NSERROR_OK)
- return false;
-
- /* Margin edge -- yellow */
- rect.x0 = x - border_left - margin_left;
- rect.y0 = y - border_top - margin_top;
- rect.x1 = x + padding_width + border_right + margin_right;
- rect.y1 = y + padding_height + border_bottom + margin_bottom;
- if (ctx->plot->rectangle(ctx, plot_style_margin_edge, &rect) != NSERROR_OK)
- return false;
- }
-
- /* clip to the padding edge for objects, or boxes with overflow hidden
- * or scroll, unless it's the root element */
- if (box->parent != NULL) {
- bool need_clip = false;
- if (box->object || box->flags & IFRAME ||
- (overflow_x != CSS_OVERFLOW_VISIBLE &&
- overflow_y != CSS_OVERFLOW_VISIBLE)) {
- r.x0 = x;
- r.y0 = y;
- r.x1 = x + padding_width;
- r.y1 = y + padding_height;
- if (r.x0 < clip->x0) r.x0 = clip->x0;
- if (r.y0 < clip->y0) r.y0 = clip->y0;
- if (clip->x1 < r.x1) r.x1 = clip->x1;
- if (clip->y1 < r.y1) r.y1 = clip->y1;
- if (r.x1 <= r.x0 || r.y1 <= r.y0) {
- return (!ctx->plot->group_end ||
- (ctx->plot->group_end(ctx) == NSERROR_OK));
- }
- need_clip = true;
-
- } else if (overflow_x != CSS_OVERFLOW_VISIBLE) {
- r.x0 = x;
- r.y0 = clip->y0;
- r.x1 = x + padding_width;
- r.y1 = clip->y1;
- if (r.x0 < clip->x0) r.x0 = clip->x0;
- if (clip->x1 < r.x1) r.x1 = clip->x1;
- if (r.x1 <= r.x0) {
- return (!ctx->plot->group_end ||
- (ctx->plot->group_end(ctx) == NSERROR_OK));
- }
- need_clip = true;
-
- } else if (overflow_y != CSS_OVERFLOW_VISIBLE) {
- r.x0 = clip->x0;
- r.y0 = y;
- r.x1 = clip->x1;
- r.y1 = y + padding_height;
- if (r.y0 < clip->y0) r.y0 = clip->y0;
- if (clip->y1 < r.y1) r.y1 = clip->y1;
- if (r.y1 <= r.y0) {
- return (!ctx->plot->group_end ||
- (ctx->plot->group_end(ctx) == NSERROR_OK));
- }
- need_clip = true;
- }
-
- if (need_clip &&
- (box->type == BOX_BLOCK ||
- box->type == BOX_INLINE_BLOCK ||
- box->type == BOX_TABLE_CELL || box->object)) {
- if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
- return false;
- }
- }
-
- /* text decoration */
- if ((box->type != BOX_TEXT) &&
- box->style &&
- css_computed_text_decoration(box->style) != CSS_TEXT_DECORATION_NONE) {
- if (!html_redraw_text_decoration(box, x_parent, y_parent,
- scale, current_background_color, ctx))
- return false;
- }
-
- if (box->object && width != 0 && height != 0) {
- struct content_redraw_data obj_data;
-
- x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale;
- y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale;
-
- obj_data.x = x_scrolled + padding_left;
- obj_data.y = y_scrolled + padding_top;
- obj_data.width = width;
- obj_data.height = height;
- obj_data.background_colour = current_background_color;
- obj_data.scale = scale;
- obj_data.repeat_x = false;
- obj_data.repeat_y = false;
-
- if (content_get_type(box->object) == CONTENT_HTML) {
- obj_data.x /= scale;
- obj_data.y /= scale;
- }
-
- if (!content_redraw(box->object, &obj_data, &r, ctx)) {
- /* Show image fail */
- /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */
- const char *obj = "\xef\xbf\xbc";
- int obj_width;
- int obj_x = x + padding_left;
- nserror res;
-
- rect.x0 = x + padding_left;
- rect.y0 = y + padding_top;
- rect.x1 = x + padding_left + width - 1;
- rect.y1 = y + padding_top + height - 1;
- res = ctx->plot->rectangle(ctx, plot_style_broken_object, &rect);
- if (res != NSERROR_OK) {
- return false;
- }
-
- res = guit->layout->width(plot_fstyle_broken_object,
- obj,
- sizeof(obj) - 1,
- &obj_width);
- if (res != NSERROR_OK) {
- obj_x += 1;
- } else {
- obj_x += width / 2 - obj_width / 2;
- }
-
- if (ctx->plot->text(ctx,
- plot_fstyle_broken_object,
- obj_x, y + padding_top + (int)(height * 0.75),
- obj, sizeof(obj) - 1) != NSERROR_OK)
- return false;
- }
-
- } else if (box->iframe) {
- /* Offset is passed to browser window redraw unscaled */
- browser_window_redraw(box->iframe,
- x + padding_left,
- y + padding_top, &r, ctx);
-
- } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) {
- if (!html_redraw_checkbox(x + padding_left, y + padding_top,
- width, height, box->gadget->selected, ctx))
- return false;
-
- } else if (box->gadget && box->gadget->type == GADGET_RADIO) {
- if (!html_redraw_radio(x + padding_left, y + padding_top,
- width, height, box->gadget->selected, ctx))
- return false;
-
- } else if (box->gadget && box->gadget->type == GADGET_FILE) {
- if (!html_redraw_file(x + padding_left, y + padding_top,
- width, height, box, scale,
- current_background_color, &html->len_ctx, ctx))
- return false;
-
- } else if (box->gadget &&
- (box->gadget->type == GADGET_TEXTAREA ||
- box->gadget->type == GADGET_PASSWORD ||
- box->gadget->type == GADGET_TEXTBOX)) {
- textarea_redraw(box->gadget->data.text.ta, x, y,
- current_background_color, scale, &r, ctx);
-
- } else if (box->text) {
- if (!html_redraw_text_box(html, box, x, y, &r, scale,
- current_background_color, ctx))
- return false;
-
- } else {
- if (!html_redraw_box_children(html, box, x_parent, y_parent, &r,
- scale, current_background_color, ctx))
- return false;
- }
-
- if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
- box->type == BOX_TABLE_CELL || box->type == BOX_INLINE)
- if (ctx->plot->clip(ctx, clip) != NSERROR_OK)
- return false;
-
- /* list marker */
- if (box->list_marker) {
- if (!html_redraw_box(html, box->list_marker,
- x_parent + box->x -
- scrollbar_get_offset(box->scroll_x),
- y_parent + box->y -
- scrollbar_get_offset(box->scroll_y),
- clip, scale, current_background_color, ctx))
- return false;
- }
-
- /* scrollbars */
- if (((box->style && box->type != BOX_BR &&
- box->type != BOX_TABLE && box->type != BOX_INLINE &&
- (box->gadget == NULL || box->gadget->type != GADGET_TEXTAREA) &&
- (overflow_x == CSS_OVERFLOW_SCROLL ||
- overflow_x == CSS_OVERFLOW_AUTO ||
- overflow_y == CSS_OVERFLOW_SCROLL ||
- overflow_y == CSS_OVERFLOW_AUTO)) ||
- (box->object && content_get_type(box->object) ==
- CONTENT_HTML)) && box->parent != NULL) {
- nserror res;
- bool has_x_scroll = (overflow_x == CSS_OVERFLOW_SCROLL);
- bool has_y_scroll = (overflow_y == CSS_OVERFLOW_SCROLL);
-
- has_x_scroll |= (overflow_x == CSS_OVERFLOW_AUTO) &&
- box_hscrollbar_present(box);
- has_y_scroll |= (overflow_y == CSS_OVERFLOW_AUTO) &&
- box_vscrollbar_present(box);
-
- res = box_handle_scrollbars((struct content *)html,
- box, has_x_scroll, has_y_scroll);
- if (res != NSERROR_OK) {
- NSLOG(netsurf, INFO, "%s", messages_get_errorcode(res));
- return false;
- }
-
- if (box->scroll_x != NULL)
- scrollbar_redraw(box->scroll_x,
- x_parent + box->x,
- y_parent + box->y + box->padding[TOP] +
- box->height + box->padding[BOTTOM] -
- SCROLLBAR_WIDTH, clip, scale, ctx);
- if (box->scroll_y != NULL)
- scrollbar_redraw(box->scroll_y,
- x_parent + box->x + box->padding[LEFT] +
- box->width + box->padding[RIGHT] -
- SCROLLBAR_WIDTH,
- y_parent + box->y, clip, scale, ctx);
- }
-
- if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
- box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) {
- if (ctx->plot->clip(ctx, clip) != NSERROR_OK)
- return false;
- }
-
- return ((!plot->group_end) || (ctx->plot->group_end(ctx) == NSERROR_OK));
-}
-
-/**
- * Draw a CONTENT_HTML using the current set of plotters (plot).
- *
- * \param c content of type CONTENT_HTML
- * \param data redraw data for this content redraw
- * \param clip current clip region
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- *
- * x, y, clip_[xy][01] are in target coordinates.
- */
-
-bool html_redraw(struct content *c, struct content_redraw_data *data,
- const struct rect *clip, const struct redraw_context *ctx)
-{
- html_content *html = (html_content *) c;
- struct box *box;
- bool result = true;
- bool select, select_only;
- plot_style_t pstyle_fill_bg = {
- .fill_type = PLOT_OP_TYPE_SOLID,
- .fill_colour = data->background_colour,
- };
-
- box = html->layout;
- assert(box);
-
- /* The select menu needs special treating because, when opened, it
- * reaches beyond its layout box.
- */
- select = false;
- select_only = false;
- if (ctx->interactive && html->visible_select_menu != NULL) {
- struct form_control *control = html->visible_select_menu;
- select = true;
- /* check if the redraw rectangle is completely inside of the
- select menu */
- select_only = form_clip_inside_select_menu(control,
- data->scale, clip);
- }
-
- if (!select_only) {
- /* clear to background colour */
- result = (ctx->plot->clip(ctx, clip) == NSERROR_OK);
-
- if (html->background_colour != NS_TRANSPARENT)
- pstyle_fill_bg.fill_colour = html->background_colour;
-
- result &= (ctx->plot->rectangle(ctx, &pstyle_fill_bg, clip) ==
NSERROR_OK);
-
- result &= html_redraw_box(html, box, data->x, data->y, clip,
- data->scale, pstyle_fill_bg.fill_colour, ctx);
- }
-
- if (select) {
- int menu_x, menu_y;
- box = html->visible_select_menu->box;
- box_coords(box, &menu_x, &menu_y);
-
- menu_x -= box->border[LEFT].width;
- menu_y += box->height + box->border[BOTTOM].width +
- box->padding[BOTTOM] + box->padding[TOP];
- result &= form_redraw_select_menu(html->visible_select_menu,
- data->x + menu_x, data->y + menu_y,
- data->scale, clip, ctx);
- }
-
- return result;
-
-}
diff --git a/content/handlers/html/html_redraw_border.c
b/content/handlers/html/html_redraw_border.c
deleted file mode 100644
index 0b3d858..0000000
--- a/content/handlers/html/html_redraw_border.c
+++ /dev/null
@@ -1,928 +0,0 @@
-/*
- * Copyright 2017 Vincent Sanders <vince(a)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
- *
- * Redrawing CONTENT_HTML borders implementation.
- */
-
-#include <stdbool.h>
-#include <stdlib.h>
-
-#include "utils/log.h"
-#include "netsurf/plotters.h"
-#include "netsurf/css.h"
-
-#include "html/box.h"
-#include "html/html_internal.h"
-
-
-static plot_style_t plot_style_bdr = {
- .stroke_type = PLOT_OP_TYPE_DASH,
-};
-static plot_style_t plot_style_fillbdr = {
- .fill_type = PLOT_OP_TYPE_SOLID,
-};
-static plot_style_t plot_style_fillbdr_dark = {
- .fill_type = PLOT_OP_TYPE_SOLID,
-};
-static plot_style_t plot_style_fillbdr_light = {
- .fill_type = PLOT_OP_TYPE_SOLID,
-};
-static plot_style_t plot_style_fillbdr_ddark = {
- .fill_type = PLOT_OP_TYPE_SOLID,
-};
-static plot_style_t plot_style_fillbdr_dlight = {
- .fill_type = PLOT_OP_TYPE_SOLID,
-};
-
-
-static inline nserror
-plot_clipped_rectangle(const struct redraw_context *ctx,
- const plot_style_t *style,
- const struct rect *clip,
- struct rect *rect)
-{
- nserror res;
-
- rect->x0 = (clip->x0 > rect->x0) ? clip->x0 : rect->x0;
- rect->y0 = (clip->y0 > rect->y0) ? clip->y0 : rect->y0;
- rect->x1 = (clip->x1 < rect->x1) ? clip->x1 : rect->x1;
- rect->y1 = (clip->y1 < rect->y1) ? clip->y1 : rect->y1;
- if ((rect->x0 < rect->x1) && (rect->y0 < rect->y1)) {
- /* valid clip rectangles only */
- res = ctx->plot->rectangle(ctx, style, rect);
- } else {
- res = NSERROR_OK;
- }
- return res;
-}
-
-
-/**
- * Draw one border.
- *
- * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT)
- * \param p array of precomputed border vertices
- * \param c colour for border
- * \param style border line style
- * \param thickness border thickness
- * \param rectangular whether border is rectangular
- * \param clip cliping area for redrawing border.
- * \param ctx current redraw context
- * \return NSERROR_OK if successful otherwise appropriate error code
- */
-static nserror
-html_redraw_border_plot(const int side,
- const int *p,
- colour c,
- enum css_border_style_e style,
- int thickness,
- bool rectangular,
- const struct rect *clip,
- const struct redraw_context *ctx)
-{
- int z[8]; /* Vertices of border part */
- unsigned int light = side;
- plot_style_t *plot_style_bdr_in;
- plot_style_t *plot_style_bdr_out;
- nserror res = NSERROR_OK;
- struct rect rect;
-
- if (c == NS_TRANSPARENT) {
- return res;
- }
-
- plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH;
- plot_style_bdr.stroke_colour = c;
- plot_style_bdr.stroke_width = (thickness << PLOT_STYLE_RADIX);
- plot_style_fillbdr.fill_colour = c;
- plot_style_fillbdr_dark.fill_colour = darken_colour(c);
- plot_style_fillbdr_light.fill_colour = lighten_colour(c);
- plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c);
- plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c);
-
- switch (style) {
- case CSS_BORDER_STYLE_DOTTED:
- plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT;
- /* fall through */
- case CSS_BORDER_STYLE_DASHED:
- rect.x0 = (p[0] + p[2]) / 2;
- rect.y0 = (p[1] + p[3]) / 2;
- rect.x1 = (p[4] + p[6]) / 2;
- rect.y1 = (p[5] + p[7]) / 2;
- res = ctx->plot->line(ctx, &plot_style_bdr, &rect);
- break;
-
- case CSS_BORDER_STYLE_SOLID:
- /* fall through to default */
- default:
- if (rectangular || thickness == 1) {
-
- if (side == TOP || side == RIGHT) {
- rect.x0 = p[2];
- rect.y0 = p[3];
- if ((side == TOP) &&
- (p[4] - p[6] != 0)) {
- rect.x1 = p[4];
- } else {
- rect.x1 = p[6];
- }
- rect.y1 = p[7];
- } else {
- rect.x0 = p[6];
- rect.y0 = p[7];
- rect.x1 = p[2];
- if ((side == LEFT) &&
- (p[1] - p[3] != 0)) {
- rect.y1 = p[1];
- } else {
- rect.y1 = p[3];
- }
- }
- res = plot_clipped_rectangle(ctx,
- &plot_style_fillbdr,
- clip,
- &rect);
- } else {
- res = ctx->plot->polygon(ctx, &plot_style_fillbdr, p, 4);
- }
- break;
-
- case CSS_BORDER_STYLE_DOUBLE:
- z[0] = p[0];
- z[1] = p[1];
- z[2] = (p[0] * 2 + p[2]) / 3;
- z[3] = (p[1] * 2 + p[3]) / 3;
- z[4] = (p[6] * 2 + p[4]) / 3;
- z[5] = (p[7] * 2 + p[5]) / 3;
- z[6] = p[6];
- z[7] = p[7];
- res = ctx->plot->polygon(ctx, &plot_style_fillbdr, z, 4);
- if (res == NSERROR_OK) {
- z[0] = p[2];
- z[1] = p[3];
- z[2] = (p[2] * 2 + p[0]) / 3;
- z[3] = (p[3] * 2 + p[1]) / 3;
- z[4] = (p[4] * 2 + p[6]) / 3;
- z[5] = (p[5] * 2 + p[7]) / 3;
- z[6] = p[4];
- z[7] = p[5];
- res = ctx->plot->polygon(ctx, &plot_style_fillbdr, z, 4);
- }
- break;
-
- case CSS_BORDER_STYLE_GROOVE:
- light = 3 - light;
- /* fall through */
- case CSS_BORDER_STYLE_RIDGE:
- /* choose correct colours for each part of the border line */
- if (light <= 1) {
- plot_style_bdr_in = &plot_style_fillbdr_dark;
- plot_style_bdr_out = &plot_style_fillbdr_light;
- } else {
- plot_style_bdr_in = &plot_style_fillbdr_light;
- plot_style_bdr_out = &plot_style_fillbdr_dark;
- }
-
- /* Render border */
- if ((rectangular || thickness == 2) && thickness != 1) {
- /* Border made up from two parts and can be
- * plotted with rectangles
- */
-
- /* First part */
- if (side == TOP || side == RIGHT) {
- rect.x0 = (p[0] + p[2]) / 2;
- rect.y0 = (p[1] + p[3]) / 2;
- rect.x1 = p[6];
- rect.y1 = p[7];
- } else {
- rect.x0 = p[6];
- rect.y0 = p[7];
- rect.x1 = (p[0] + p[2]) / 2;
- rect.y1 = (p[1] + p[3]) / 2;
- }
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_in,
- clip,
- &rect);
- if (res != NSERROR_OK) {
- return res;
- }
-
- /* Second part */
- if (side == TOP || side == RIGHT) {
- rect.x0 = p[2];
- rect.y0 = p[3];
- rect.x1 = (p[6] + p[4]) / 2;
- rect.y1 = (p[7] + p[5]) / 2;
- } else {
- rect.x0 = (p[6] + p[4]) / 2;
- rect.y0 = (p[7] + p[5]) / 2;
- rect.x1 = p[2];
- rect.y1 = p[3];
- }
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_out,
- clip,
- &rect);
- } else if (thickness == 1) {
- /* Border made up from one part which can be
- * plotted as a rectangle
- */
-
- if (side == TOP || side == RIGHT) {
- rect.x0 = p[2];
- rect.y0 = p[3];
- rect.x1 = p[6];
- rect.y1 = p[7];
- rect.x1 = ((side == TOP) && (p[4] - p[6] != 0)) ?
- rect.x1 + p[4] - p[6] : rect.x1;
-
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_in,
- clip,
- &rect);
- } else {
- rect.x0 = p[6];
- rect.y0 = p[7];
- rect.x1 = p[2];
- rect.y1 = p[3];
- rect.y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ?
- rect.y1 + p[1] - p[3] : rect.y1;
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_out,
- clip,
- &rect);
- }
- } else {
- /* Border made up from two parts and can't be
- * plotted with rectangles
- */
- z[0] = p[0];
- z[1] = p[1];
- z[2] = (p[0] + p[2]) / 2;
- z[3] = (p[1] + p[3]) / 2;
- z[4] = (p[6] + p[4]) / 2;
- z[5] = (p[7] + p[5]) / 2;
- z[6] = p[6];
- z[7] = p[7];
- res = ctx->plot->polygon(ctx, plot_style_bdr_in, z, 4);
- if (res == NSERROR_OK) {
- z[0] = p[2];
- z[1] = p[3];
- z[6] = p[4];
- z[7] = p[5];
- res = ctx->plot->polygon(ctx,
- plot_style_bdr_out,
- z,
- 4);
- }
- }
- break;
-
- case CSS_BORDER_STYLE_INSET:
- light = (light + 2) % 4;
- /* fall through */
- case CSS_BORDER_STYLE_OUTSET:
- /* choose correct colours for each part of the border line */
- switch (light) {
- case 0:
- plot_style_bdr_in = &plot_style_fillbdr_light;
- plot_style_bdr_out = &plot_style_fillbdr_dlight;
- break;
- case 1:
- plot_style_bdr_in = &plot_style_fillbdr_ddark;
- plot_style_bdr_out = &plot_style_fillbdr_dark;
- break;
- case 2:
- plot_style_bdr_in = &plot_style_fillbdr_dark;
- plot_style_bdr_out = &plot_style_fillbdr_ddark;
- break;
- case 3:
- plot_style_bdr_in = &plot_style_fillbdr_dlight;
- plot_style_bdr_out = &plot_style_fillbdr_light;
- break;
- default:
- plot_style_bdr_in = &plot_style_fillbdr;
- plot_style_bdr_out = &plot_style_fillbdr;
- break;
- }
-
- /* Render border */
- if ((rectangular || thickness == 2) && thickness != 1) {
- /* Border made up from two parts and can be
- * plotted with rectangles
- */
-
- /* First part */
- if (side == TOP || side == RIGHT) {
- rect.x0 = (p[0] + p[2]) / 2;
- rect.y0 = (p[1] + p[3]) / 2;
- rect.x1 = p[6];
- rect.y1 = p[7];
- } else {
- rect.x0 = p[6];
- rect.y0 = p[7];
- rect.x1 = (p[0] + p[2]) / 2;
- rect.y1 = (p[1] + p[3]) / 2;
- }
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_in,
- clip,
- &rect);
- if (res != NSERROR_OK) {
- return res;
- }
-
- /* Second part */
- if (side == TOP || side == RIGHT) {
- rect.x0 = p[2];
- rect.y0 = p[3];
- rect.x1 = (p[6] + p[4]) / 2;
- rect.y1 = (p[7] + p[5]) / 2;
- } else {
- rect.x0 = (p[6] + p[4]) / 2;
- rect.y0 = (p[7] + p[5]) / 2;
- rect.x1 = p[2];
- rect.y1 = p[3];
- }
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_out,
- clip,
- &rect);
- } else if (thickness == 1) {
- /* Border made up from one part which can be
- * plotted as a rectangle
- */
-
- if (side == TOP || side == RIGHT) {
- rect.x0 = p[2];
- rect.y0 = p[3];
- rect.x1 = p[6];
- rect.y1 = p[7];
- rect.x1 = ((side == TOP) && (p[4] - p[6] != 0)) ?
- rect.x1 + p[4] - p[6] : rect.x1;
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_in,
- clip,
- &rect);
- } else {
- rect.x0 = p[6];
- rect.y0 = p[7];
- rect.x1 = p[2];
- rect.y1 = p[3];
- rect.y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ?
- rect.y1 + p[1] - p[3] : rect.y1;
- res = plot_clipped_rectangle(ctx,
- plot_style_bdr_out,
- clip,
- &rect);
- }
- } else {
- /* Border made up from two parts and can't be
- * plotted with rectangles
- */
-
- z[0] = p[0];
- z[1] = p[1];
- z[2] = (p[0] + p[2]) / 2;
- z[3] = (p[1] + p[3]) / 2;
- z[4] = (p[6] + p[4]) / 2;
- z[5] = (p[7] + p[5]) / 2;
- z[6] = p[6];
- z[7] = p[7];
- res = ctx->plot->polygon(ctx, plot_style_bdr_in, z, 4);
- if (res != NSERROR_OK) {
- return res;
- }
- z[0] = p[2];
- z[1] = p[3];
- z[6] = p[4];
- z[7] = p[5];
- res = ctx->plot->polygon(ctx, plot_style_bdr_out, z, 4);
- }
- break;
- }
-
- return res;
-}
-
-
-/**
- * Draw borders for a box.
- *
- * \param box box to draw
- * \param x_parent coordinate of left padding edge of parent of box
- * \param y_parent coordinate of top padding edge of parent of box
- * \param p_width width of padding box
- * \param p_height height of padding box
- * \param clip cliping area for redrawing border.
- * \param scale scale for redraw
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-bool
-html_redraw_borders(struct box *box,
- int x_parent,
- int y_parent,
- int p_width,
- int p_height,
- const struct rect *clip,
- float scale,
- const struct redraw_context *ctx)
-{
- unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM };
- int top = box->border[TOP].width;
- int right = box->border[RIGHT].width;
- int bottom = box->border[BOTTOM].width;
- int left = box->border[LEFT].width;
- int x, y;
- unsigned int i, side;
- int p[8]; /* Box border vertices */
- int z[8]; /* Border vertices */
- bool square_end_1 = false;
- bool square_end_2 = false;
- nserror res;
-
- x = x_parent + box->x;
- y = y_parent + box->y;
-
- if (scale != 1.0) {
- top *= scale;
- right *= scale;
- bottom *= scale;
- left *= scale;
- x *= scale;
- y *= scale;
- }
-
- assert(box->style);
-
- /* Calculate border vertices
- *
- * A----------------------+
- * | \ / |
- * | B--------------+ |
- * | | | |
- * | +--------------C |
- * | / \ |
- * +----------------------D
- */
- p[0] = x - left; p[1] = y - top; /* A */
- p[2] = x; p[3] = y; /* B */
- p[4] = x + p_width; p[5] = y + p_height; /* C */
- p[6] = x + p_width + right; p[7] = y + p_height + bottom; /* D */
-
- for (i = 0; i != 4; i++) {
- colour col = 0;
- side = sides[i]; /* plot order */
-
- if (box->border[side].width == 0 ||
- nscss_color_is_transparent(box->border[side].c)) {
- continue;
- }
-
- switch (side) {
- case LEFT:
- square_end_1 = (top == 0);
- square_end_2 = (bottom == 0);
-
- z[0] = p[0]; z[1] = p[7];
- z[2] = p[2]; z[3] = p[5];
- z[4] = p[2]; z[5] = p[3];
- z[6] = p[0]; z[7] = p[1];
-
- if (nscss_color_is_transparent(box->border[TOP].c) == false &&
- box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang top corner fully,
- * if top border is opaque
- */
- z[5] -= top;
- square_end_1 = true;
- }
- if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
- box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang bottom corner fully,
- * if bottom border is opaque
- */
- z[3] += bottom;
- square_end_2 = true;
- }
-
- col = nscss_color_to_ns(box->border[side].c);
-
- res = html_redraw_border_plot(side,
- z,
- col,
- box->border[side].style,
- box->border[side].width * scale,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- break;
-
- case RIGHT:
- square_end_1 = (top == 0);
- square_end_2 = (bottom == 0);
-
- z[0] = p[6]; z[1] = p[1];
- z[2] = p[4]; z[3] = p[3];
- z[4] = p[4]; z[5] = p[5];
- z[6] = p[6]; z[7] = p[7];
-
- if (nscss_color_is_transparent(box->border[TOP].c) == false &&
- box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang top corner fully,
- * if top border is opaque
- */
- z[3] -= top;
- square_end_1 = true;
- }
- if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
- box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang bottom corner fully,
- * if bottom border is opaque
- */
- z[5] += bottom;
- square_end_2 = true;
- }
-
- col = nscss_color_to_ns(box->border[side].c);
-
- res = html_redraw_border_plot(side,
- z,
- col,
- box->border[side].style,
- box->border[side].width * scale,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- break;
-
- case TOP:
- if (clip->y0 > p[3]) {
- /* clip rectangle is below border; nothing to
- * plot
- */
- continue;
- }
-
- square_end_1 = (left == 0);
- square_end_2 = (right == 0);
-
- z[0] = p[2]; z[1] = p[3];
- z[2] = p[0]; z[3] = p[1];
- z[4] = p[6]; z[5] = p[1];
- z[6] = p[4]; z[7] = p[3];
-
- if (box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
- box->border[TOP].c == box->border[LEFT].c) {
- /* don't bother overlapping left corner if
- * it's the same colour anyway
- */
- z[2] += left;
- square_end_1 = true;
- }
- if (box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
- box->border[TOP].c == box->border[RIGHT].c) {
- /* don't bother overlapping right corner if
- * it's the same colour anyway
- */
- z[4] -= right;
- square_end_2 = true;
- }
-
- col = nscss_color_to_ns(box->border[side].c);
-
- res = html_redraw_border_plot(side,
- z,
- col,
- box->border[side].style,
- box->border[side].width * scale,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- break;
-
- case BOTTOM:
- if (clip->y1 < p[5]) {
- /* clip rectangle is above border; nothing to
- * plot
- */
- continue;
- }
-
- square_end_1 = (left == 0);
- square_end_2 = (right == 0);
-
- z[0] = p[4]; z[1] = p[5];
- z[2] = p[6]; z[3] = p[7];
- z[4] = p[0]; z[5] = p[7];
- z[6] = p[2]; z[7] = p[5];
-
- if (box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
- box->border[BOTTOM].c == box->border[LEFT].c) {
- /* don't bother overlapping left corner if
- * it's the same colour anyway
- */
- z[4] += left;
- square_end_1 = true;
- }
- if (box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
- box->border[BOTTOM].c == box->border[RIGHT].c) {
- /* don't bother overlapping right corner if
- * it's the same colour anyway
- */
- z[2] -= right;
- square_end_2 = true;
- }
-
- col = nscss_color_to_ns(box->border[side].c);
-
- res = html_redraw_border_plot(side,
- z,
- col,
- box->border[side].style,
- box->border[side].width * scale,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- break;
-
- default:
- assert(side == TOP || side == BOTTOM ||
- side == LEFT || side == RIGHT);
- break;
- }
- }
-
- return true;
-}
-
-
-/**
- * Draw an inline's borders.
- *
- * \param box BOX_INLINE which created the border
- * \param b coordinates of border edge rectangle
- * \param clip cliping area for redrawing border.
- * \param scale scale for redraw
- * \param first true if this is the first rectangle associated with the inline
- * \param last true if this is the last rectangle associated with the inline
- * \param ctx current redraw context
- * \return true if successful, false otherwise
- */
-bool
-html_redraw_inline_borders(struct box *box,
- struct rect b,
- const struct rect *clip,
- float scale,
- bool first,
- bool last,
- const struct redraw_context *ctx)
-{
- int top = box->border[TOP].width;
- int right = box->border[RIGHT].width;
- int bottom = box->border[BOTTOM].width;
- int left = box->border[LEFT].width;
- colour col;
- int p[8]; /* Box border vertices */
- int z[8]; /* Border vertices */
- bool square_end_1;
- bool square_end_2;
- nserror res;
-
- if (scale != 1.0) {
- top *= scale;
- right *= scale;
- bottom *= scale;
- left *= scale;
- }
-
- /* Calculate border vertices
- *
- * A----------------------+
- * | \ / |
- * | B--------------+ |
- * | | | |
- * | +--------------C |
- * | / \ |
- * +----------------------D
- */
- p[0] = b.x0; p[1] = b.y0; /* A */
- p[2] = first ? b.x0 + left : b.x0; p[3] = b.y0 + top; /* B */
- p[4] = last ? b.x1 - right : b.x1; p[5] = b.y1 - bottom; /* C */
- p[6] = b.x1; p[7] = b.y1; /* D */
-
- assert(box->style);
-
- /* Left */
- square_end_1 = (top == 0);
- square_end_2 = (bottom == 0);
- if (left != 0 &&
- first &&
- nscss_color_is_transparent(box->border[LEFT].c) == false) {
- col = nscss_color_to_ns(box->border[LEFT].c);
-
- z[0] = p[0]; z[1] = p[7];
- z[2] = p[2]; z[3] = p[5];
- z[4] = p[2]; z[5] = p[3];
- z[6] = p[0]; z[7] = p[1];
-
- if (nscss_color_is_transparent(box->border[TOP].c) == false &&
- box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang top corner fully,
- * if top border is opaque
- */
- z[5] -= top;
- square_end_1 = true;
- }
-
- if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
- box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang bottom corner fully,
- * if bottom border is opaque
- */
- z[3] += bottom;
- square_end_2 = true;
- }
-
- res = html_redraw_border_plot(LEFT,
- z,
- col,
- box->border[LEFT].style,
- left,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- }
-
- /* Right */
- square_end_1 = (top == 0);
- square_end_2 = (bottom == 0);
- if (right != 0 &&
- last &&
- nscss_color_is_transparent(box->border[RIGHT].c) == false) {
- col = nscss_color_to_ns(box->border[RIGHT].c);
-
- z[0] = p[6]; z[1] = p[1];
- z[2] = p[4]; z[3] = p[3];
- z[4] = p[4]; z[5] = p[5];
- z[6] = p[6]; z[7] = p[7];
-
- if (nscss_color_is_transparent(box->border[TOP].c) == false &&
- box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang top corner fully,
- * if top border is opaque
- */
- z[3] -= top;
- square_end_1 = true;
- }
-
- if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
- box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
- /* make border overhang bottom corner fully,
- * if bottom border is opaque
- */
- z[5] += bottom;
- square_end_2 = true;
- }
-
- res = html_redraw_border_plot(RIGHT,
- z,
- col,
- box->border[RIGHT].style,
- right,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- }
-
- /* Top */
- square_end_1 = (left == 0);
- square_end_2 = (right == 0);
- if (top != 0 &&
- nscss_color_is_transparent(box->border[TOP].c) == false) {
- col = nscss_color_to_ns(box->border[TOP].c);
-
- z[0] = p[2]; z[1] = p[3];
- z[2] = p[0]; z[3] = p[1];
- z[4] = p[6]; z[5] = p[1];
- z[6] = p[4]; z[7] = p[3];
-
- if (first &&
- box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
- box->border[TOP].c == box->border[LEFT].c) {
- /* don't bother overlapping left corner if
- * it's the same colour anyway
- */
- z[2] += left;
- square_end_1 = true;
- }
-
- if (last &&
- box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
- box->border[TOP].c == box->border[RIGHT].c) {
- /* don't bother overlapping right corner if
- * it's the same colour anyway
- */
- z[4] -= right;
- square_end_2 = true;
- }
-
- res = html_redraw_border_plot(TOP,
- z,
- col,
- box->border[TOP].style,
- top,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- }
-
- /* Bottom */
- square_end_1 = (left == 0);
- square_end_2 = (right == 0);
- if (bottom != 0 &&
- nscss_color_is_transparent(box->border[BOTTOM].c) == false) {
- col = nscss_color_to_ns(box->border[BOTTOM].c);
-
- z[0] = p[4]; z[1] = p[5];
- z[2] = p[6]; z[3] = p[7];
- z[4] = p[0]; z[5] = p[7];
- z[6] = p[2]; z[7] = p[5];
-
- if (first &&
- box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
- box->border[BOTTOM].c == box->border[LEFT].c) {
- /* don't bother overlapping left corner if
- * it's the same colour anyway
- */
- z[4] += left;
- square_end_1 = true;
- }
-
- if (last &&
- box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
- box->border[BOTTOM].c == box->border[RIGHT].c) {
- /* don't bother overlapping right corner if
- * it's the same colour anyway
- */
- z[2] -= right;
- square_end_2 = true;
- }
-
- res = html_redraw_border_plot(BOTTOM,
- z,
- col,
- box->border[BOTTOM].style,
- bottom,
- square_end_1 && square_end_2,
- clip,
- ctx);
- if (res != NSERROR_OK) {
- return false;
- }
- }
-
- return true;
-}
diff --git a/content/handlers/html/html_script.c b/content/handlers/html/html_script.c
deleted file mode 100644
index 81bdccd..0000000
--- a/content/handlers/html/html_script.c
+++ /dev/null
@@ -1,666 +0,0 @@
-/*
- * Copyright 2012 Vincent Sanders <vince(a)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
- * implementation of content handling for text/html scripts.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <strings.h>
-#include <stdlib.h>
-
-#include "utils/config.h"
-#include "utils/corestrings.h"
-#include "utils/log.h"
-#include "utils/messages.h"
-#include "netsurf/content.h"
-#include "javascript/js.h"
-#include "content/content_protected.h"
-#include "content/fetch.h"
-#include "content/hlcache.h"
-
-#include "html/html.h"
-#include "html/html_internal.h"
-
-typedef bool (script_handler_t)(struct jsthread *jsthread, const uint8_t *data, size_t
size, const char *name);
-
-
-static script_handler_t *select_script_handler(content_type ctype)
-{
- if (ctype == CONTENT_JS) {
- return js_exec;
- }
- return NULL;
-}
-
-
-/* exported internal interface documented in html/html_internal.h */
-nserror html_script_exec(html_content *c, bool allow_defer)
-{
- unsigned int i;
- struct html_script *s;
- script_handler_t *script_handler;
- bool have_run_something = false;
-
- if (c->jsthread == NULL) {
- return NSERROR_BAD_PARAMETER;
- }
-
- for (i = 0, s = c->scripts; i != c->scripts_count; i++, s++) {
- if (s->already_started) {
- continue;
- }
-
- if ((s->type == HTML_SCRIPT_ASYNC) ||
- (allow_defer && (s->type == HTML_SCRIPT_DEFER))) {
- /* ensure script content is present */
- if (s->data.handle == NULL)
- continue;
-
- /* ensure script content fetch status is not an error */
- if (content_get_status(s->data.handle) ==
- CONTENT_STATUS_ERROR)
- continue;
-
- /* ensure script handler for content type */
- script_handler = select_script_handler(
- content_get_type(s->data.handle));
- if (script_handler == NULL)
- continue; /* unsupported type */
-
- if (content_get_status(s->data.handle) ==
- CONTENT_STATUS_DONE) {
- /* external script is now available */
- const uint8_t *data;
- size_t size;
- data = content_get_source_data(
- s->data.handle, &size );
- script_handler(c->jsthread, data, size,
- nsurl_access(hlcache_handle_get_url(s->data.handle)));
- have_run_something = true;
- /* We have to re-acquire this here since the
- * c->scripts array may have been reallocated
- * as a result of executing this script.
- */
- s = &(c->scripts[i]);
-
- s->already_started = true;
-
- }
- }
- }
-
- if (have_run_something) {
- return html_proceed_to_done(c);
- }
-
- return NSERROR_OK;
-}
-
-/* create new html script entry */
-static struct html_script *
-html_process_new_script(html_content *c,
- dom_string *mimetype,
- enum html_script_type type)
-{
- struct html_script *nscript;
- /* add space for new script entry */
- nscript = realloc(c->scripts,
- sizeof(struct html_script) * (c->scripts_count + 1));
- if (nscript == NULL) {
- return NULL;
- }
-
- c->scripts = nscript;
-
- /* increment script entry count */
- nscript = &c->scripts[c->scripts_count];
- c->scripts_count++;
-
- nscript->already_started = false;
- nscript->parser_inserted = false;
- nscript->force_async = true;
- nscript->ready_exec = false;
- nscript->async = false;
- nscript->defer = false;
-
- nscript->type = type;
-
- nscript->mimetype = dom_string_ref(mimetype); /* reference mimetype */
-
- return nscript;
-}
-
-/**
- * Callback for asyncronous scripts
- */
-static nserror
-convert_script_async_cb(hlcache_handle *script,
- const hlcache_event *event,
- void *pw)
-{
- html_content *parent = pw;
- unsigned int i;
- struct html_script *s;
-
- /* Find script */
- for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
- if (s->type == HTML_SCRIPT_ASYNC && s->data.handle == script)
- break;
- }
-
- assert(i != parent->scripts_count);
-
- switch (event->type) {
- case CONTENT_MSG_LOADING:
- break;
-
- case CONTENT_MSG_READY:
- break;
-
- case CONTENT_MSG_DONE:
- NSLOG(netsurf, INFO, "script %d done '%s'", i,
- nsurl_access(hlcache_handle_get_url(script)));
- parent->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
-
- break;
-
- case CONTENT_MSG_ERROR:
- NSLOG(netsurf, INFO, "script %s failed: %s",
- nsurl_access(hlcache_handle_get_url(script)),
- event->data.errordata.errormsg);
-
- hlcache_handle_release(script);
- s->data.handle = NULL;
- parent->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
- content_add_error(&parent->base, "?", 0);
-
- break;
-
- default:
- break;
- }
-
- /* if there are no active fetches remaining begin post parse
- * conversion
- */
- if (html_can_begin_conversion(parent)) {
- html_begin_conversion(parent);
- }
-
- /* if we have already started converting though, then we can handle the
- * scripts as they come in.
- */
- else if (parent->conversion_begun) {
- return html_script_exec(parent, false);
- }
-
- return NSERROR_OK;
-}
-
-/**
- * Callback for defer scripts
- */
-static nserror
-convert_script_defer_cb(hlcache_handle *script,
- const hlcache_event *event,
- void *pw)
-{
- html_content *parent = pw;
- unsigned int i;
- struct html_script *s;
-
- /* Find script */
- for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
- if (s->type == HTML_SCRIPT_DEFER && s->data.handle == script)
- break;
- }
-
- assert(i != parent->scripts_count);
-
- switch (event->type) {
-
- case CONTENT_MSG_DONE:
- NSLOG(netsurf, INFO, "script %d done '%s'", i,
- nsurl_access(hlcache_handle_get_url(script)));
- parent->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
-
- break;
-
- case CONTENT_MSG_ERROR:
- NSLOG(netsurf, INFO, "script %s failed: %s",
- nsurl_access(hlcache_handle_get_url(script)),
- event->data.errordata.errormsg);
-
- hlcache_handle_release(script);
- s->data.handle = NULL;
- parent->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
- content_add_error(&parent->base, "?", 0);
-
- break;
-
- default:
- break;
- }
-
- /* if there are no active fetches remaining begin post parse
- * conversion
- */
- if (html_can_begin_conversion(parent)) {
- html_begin_conversion(parent);
- }
-
- return NSERROR_OK;
-}
-
-/**
- * Callback for syncronous scripts
- */
-static nserror
-convert_script_sync_cb(hlcache_handle *script,
- const hlcache_event *event,
- void *pw)
-{
- html_content *parent = pw;
- unsigned int i;
- struct html_script *s;
- script_handler_t *script_handler;
- dom_hubbub_error err;
- unsigned int active_sync_scripts = 0;
-
- /* Count sync scripts which have yet to complete (other than us) */
- for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
- if (s->type == HTML_SCRIPT_SYNC &&
- s->data.handle != script && s->already_started == false) {
- active_sync_scripts++;
- }
- }
-
- /* Find script */
- for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
- if (s->type == HTML_SCRIPT_SYNC && s->data.handle == script)
- break;
- }
-
- assert(i != parent->scripts_count);
-
- switch (event->type) {
- case CONTENT_MSG_DONE:
- NSLOG(netsurf, INFO, "script %d done '%s'", i,
- nsurl_access(hlcache_handle_get_url(script)));
- parent->base.active--;
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
-
- s->already_started = true;
-
- /* attempt to execute script */
- script_handler = select_script_handler(content_get_type(s->data.handle));
- if (script_handler != NULL && parent->jsthread != NULL) {
- /* script has a handler */
- const uint8_t *data;
- size_t size;
- data = content_get_source_data(s->data.handle, &size );
- script_handler(parent->jsthread, data, size,
- nsurl_access(hlcache_handle_get_url(s->data.handle)));
- }
-
- /* continue parse */
- if (parent->parser != NULL && active_sync_scripts == 0) {
- err = dom_hubbub_parser_pause(parent->parser, false);
- if (err != DOM_HUBBUB_OK) {
- NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
- }
- }
-
- break;
-
- case CONTENT_MSG_ERROR:
- NSLOG(netsurf, INFO, "script %s failed: %s",
- nsurl_access(hlcache_handle_get_url(script)),
- event->data.errordata.errormsg);
-
- hlcache_handle_release(script);
- s->data.handle = NULL;
- parent->base.active--;
-
- NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
- content_add_error(&parent->base, "?", 0);
-
- s->already_started = true;
-
- /* continue parse */
- if (parent->parser != NULL && active_sync_scripts == 0) {
- err = dom_hubbub_parser_pause(parent->parser, false);
- if (err != DOM_HUBBUB_OK) {
- NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
- }
- }
-
- break;
-
- default:
- break;
- }
-
- /* if there are no active fetches remaining begin post parse
- * conversion
- */
- if (html_can_begin_conversion(parent)) {
- html_begin_conversion(parent);
- }
-
- return NSERROR_OK;
-}
-
-/**
- * process a script with a src tag
- */
-static dom_hubbub_error
-exec_src_script(html_content *c,
- dom_node *node,
- dom_string *mimetype,
- dom_string *src)
-{
- nserror ns_error;
- nsurl *joined;
- hlcache_child_context child;
- struct html_script *nscript;
- bool async;
- bool defer;
- enum html_script_type script_type;
- hlcache_handle_callback script_cb;
- dom_hubbub_error ret = DOM_HUBBUB_OK;
- dom_exception exc; /* returned by libdom functions */
-
- /* src url */
- ns_error = nsurl_join(c->base_url, dom_string_data(src), &joined);
- if (ns_error != NSERROR_OK) {
- content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
- return DOM_HUBBUB_NOMEM;
- }
-
- NSLOG(netsurf, INFO, "script %i '%s'", c->scripts_count,
- nsurl_access(joined));
-
- /* there are three ways to process the script tag at this point:
- *
- * Syncronously pause the parent parse and continue after
- * the script has downloaded and executed. (default)
- * Async Start the script downloading and execute it when it
- * becomes available.
- * Defered Start the script downloading and execute it when
- * the page has completed parsing, may be set along
- * with async where it is ignored.
- */
-
- /* we interpret the presence of the async and defer attribute
- * as true and ignore its value, technically only the empty
- * value or the attribute name itself are valid. However
- * various browsers interpret this in various ways the most
- * compatible approach is to be liberal and accept any
- * value. Note setting the values to "false" still makes them true!
- */
- exc = dom_element_has_attribute(node, corestring_dom_async, &async);
- if (exc != DOM_NO_ERR) {
- return DOM_HUBBUB_OK; /* dom error */
- }
-
- if (c->parse_completed) {
- /* After parse completed, all scripts are essentially async */
- async = true;
- defer = false;
- }
-
- if (async) {
- /* asyncronous script */
- script_type = HTML_SCRIPT_ASYNC;
- script_cb = convert_script_async_cb;
-
- } else {
- exc = dom_element_has_attribute(node,
- corestring_dom_defer, &defer);
- if (exc != DOM_NO_ERR) {
- return DOM_HUBBUB_OK; /* dom error */
- }
-
- if (defer) {
- /* defered script */
- script_type = HTML_SCRIPT_DEFER;
- script_cb = convert_script_defer_cb;
- } else {
- /* syncronous script */
- script_type = HTML_SCRIPT_SYNC;
- script_cb = convert_script_sync_cb;
- }
- }
-
- nscript = html_process_new_script(c, mimetype, script_type);
- if (nscript == NULL) {
- nsurl_unref(joined);
- content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
- return DOM_HUBBUB_NOMEM;
- }
-
- /* set up child fetch encoding and quirks */
- child.charset = c->encoding;
- child.quirks = c->base.quirks;
-
- ns_error = hlcache_handle_retrieve(joined,
- 0,
- content_get_url(&c->base),
- NULL,
- script_cb,
- c,
- &child,
- CONTENT_SCRIPT,
- &nscript->data.handle);
-
-
- nsurl_unref(joined);
-
- if (ns_error != NSERROR_OK) {
- /* @todo Deal with fetch error better. currently assume
- * fetch never became active
- */
- /* mark duff script fetch as already started */
- nscript->already_started = true;
- NSLOG(netsurf, INFO, "Fetch failed with error %d", ns_error);
- } else {
- /* update base content active fetch count */
- c->base.active++;
- NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
-
- switch (script_type) {
- case HTML_SCRIPT_SYNC:
- ret = DOM_HUBBUB_HUBBUB_ERR | HUBBUB_PAUSED;
-
- case HTML_SCRIPT_ASYNC:
- break;
-
- case HTML_SCRIPT_DEFER:
- break;
-
- default:
- assert(0);
- }
- }
-
- return ret;
-}
-
-static dom_hubbub_error
-exec_inline_script(html_content *c, dom_node *node, dom_string *mimetype)
-{
- dom_string *script;
- dom_exception exc; /* returned by libdom functions */
- struct lwc_string_s *lwcmimetype;
- script_handler_t *script_handler;
- struct html_script *nscript;
-
- /* does not appear to be a src so script is inline content */
- exc = dom_node_get_text_content(node, &script);
- if ((exc != DOM_NO_ERR) || (script == NULL)) {
- return DOM_HUBBUB_OK; /* no contents, skip */
- }
-
- nscript = html_process_new_script(c, mimetype, HTML_SCRIPT_INLINE);
- if (nscript == NULL) {
- dom_string_unref(script);
-
- content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
- return DOM_HUBBUB_NOMEM;
-
- }
-
- nscript->data.string = script;
- nscript->already_started = true;
-
- /* ensure script handler for content type */
- exc = dom_string_intern(mimetype, &lwcmimetype);
- if (exc != DOM_NO_ERR) {
- return DOM_HUBBUB_DOM;
- }
-
- script_handler =
select_script_handler(content_factory_type_from_mime_type(lwcmimetype));
- lwc_string_unref(lwcmimetype);
-
- if (script_handler != NULL) {
- script_handler(c->jsthread,
- (const uint8_t *)dom_string_data(script),
- dom_string_byte_length(script),
- "?inline script?");
- }
- return DOM_HUBBUB_OK;
-}
-
-
-/**
- * process script node parser callback
- *
- *
- */
-dom_hubbub_error
-html_process_script(void *ctx, dom_node *node)
-{
- html_content *c = (html_content *)ctx;
- dom_exception exc; /* returned by libdom functions */
- dom_string *src, *mimetype;
- dom_hubbub_error err = DOM_HUBBUB_OK;
-
- /* ensure javascript context is available */
- /* We should only ever be here if scripting was enabled for this
- * content so it's correct to make a javascript context if there
- * isn't one already. */
- if (c->jsthread == NULL) {
- union content_msg_data msg_data;
-
- msg_data.jsthread = &c->jsthread;
- content_broadcast(&c->base, CONTENT_MSG_GETTHREAD, &msg_data);
- NSLOG(netsurf, INFO, "javascript context %p ", c->jsthread);
- if (c->jsthread == NULL) {
- /* no context and it could not be created, abort */
- return DOM_HUBBUB_OK;
- }
- }
-
- NSLOG(netsurf, INFO, "content %p parser %p node %p", c, c->parser,
- node);
-
- exc = dom_element_get_attribute(node, corestring_dom_type, &mimetype);
- if (exc != DOM_NO_ERR || mimetype == NULL) {
- mimetype = dom_string_ref(corestring_dom_text_javascript);
- }
-
- exc = dom_element_get_attribute(node, corestring_dom_src, &src);
- if (exc != DOM_NO_ERR || src == NULL) {
- err = exec_inline_script(c, node, mimetype);
- } else {
- err = exec_src_script(c, node, mimetype, src);
- dom_string_unref(src);
- }
-
- dom_string_unref(mimetype);
-
- return err;
-}
-
-/* exported internal interface documented in html/html_internal.h */
-bool html_saw_insecure_scripts(html_content *htmlc)
-{
- struct html_script *s;
- unsigned int i;
-
- for (i = 0, s = htmlc->scripts; i != htmlc->scripts_count; i++, s++) {
- if (s->type == HTML_SCRIPT_INLINE) {
- /* Inline scripts are no less secure than their
- * containing HTML content
- */
- continue;
- }
- if (s->data.handle == NULL) {
- /* We've not begun loading this? */
- continue;
- }
- if (content_saw_insecure_objects(s->data.handle)) {
- return true;
- }
- }
-
- return false;
-}
-
-/* exported internal interface documented in html/html_internal.h */
-nserror html_script_free(html_content *html)
-{
- unsigned int i;
-
- for (i = 0; i != html->scripts_count; i++) {
- if (html->scripts[i].mimetype != NULL) {
- dom_string_unref(html->scripts[i].mimetype);
- }
-
- switch (html->scripts[i].type) {
- case HTML_SCRIPT_INLINE:
- if (html->scripts[i].data.string != NULL) {
- dom_string_unref(html->scripts[i].data.string);
- }
- break;
- case HTML_SCRIPT_SYNC:
- /* fallthrough */
- case HTML_SCRIPT_ASYNC:
- /* fallthrough */
- case HTML_SCRIPT_DEFER:
- if (html->scripts[i].data.handle != NULL) {
- hlcache_handle_release(html->scripts[i].data.handle);
- }
- break;
- }
- }
- free(html->scripts);
-
- return NSERROR_OK;
-}
diff --git a/content/handlers/html/object.c b/content/handlers/html/object.c
new file mode 100644
index 0000000..587c487
--- /dev/null
+++ b/content/handlers/html/object.c
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2013 Vincent Sanders <vince(a)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 <nsutils/time.h>
+
+#include "utils/corestrings.h"
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "netsurf/content.h"
+#include "netsurf/misc.h"
+#include "content/hlcache.h"
+#include "css/utils.h"
+#include "desktop/scrollbar.h"
+#include "desktop/gui_internal.h"
+
+#include "html/html.h"
+#include "html/box.h"
+#include "html/box_inspect.h"
+#include "html/html_internal.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;
+
+ /* Normalise the box type, now it has been replaced. */
+ switch (box->type) {
+ case BOX_TABLE:
+ box->type = BOX_BLOCK;
+ break;
+ default:
+ /* TODO: Any other box types need mapping? */
+ break;
+ }
+
+ 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;
+
+ box = o->box;
+ if (box == NULL && event->type != CONTENT_MSG_ERROR) {
+ return NSERROR_OK;
+ }
+
+ 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.available_height);
+ }
+ break;
+
+ case CONTENT_MSG_DONE:
+ c->base.active--;
+ NSLOG(netsurf, INFO, "%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 (c->had_initial_layout == false) {
+ break;
+ }
+
+ 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;
+
+ content_broadcast(&c->base, CONTENT_MSG_REDRAW, &data);
+ }
+ break;
+
+ case CONTENT_MSG_ERROR:
+ hlcache_handle_release(object);
+
+ o->content = NULL;
+
+ if (box != NULL) {
+ c->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active",
+ c->base.active);
+
+ content_add_error(&c->base, "?", 0);
+ html_object_failed(box, c, o->background);
+ }
+ break;
+
+ case CONTENT_MSG_REDRAW:
+ if (c->base.status != CONTENT_STATUS_LOADING) {
+ union content_msg_data data = event->data;
+
+ if (c->had_initial_layout == false) {
+ break;
+ }
+
+ if (!box_visible(box))
+ break;
+
+ box_coords(box, &x, &y);
+
+ if (object == box->background) {
+ /* Redraw request is for background */
+ css_fixed hpos = 0, vpos = 0;
+ css_unit hunit = CSS_UNIT_PX;
+ css_unit vunit = CSS_UNIT_PX;
+ int width = box->padding[LEFT] + box->width +
+ box->padding[RIGHT];
+ int height = box->padding[TOP] + box->height +
+ box->padding[BOTTOM];
+ int t, h, l, w;
+
+ /* Need to know background-position */
+ css_computed_background_position(box->style,
+ &hpos, &hunit, &vpos, &vunit);
+
+ w = content_get_width(box->background);
+ if (hunit == CSS_UNIT_PCT) {
+ l = (width - w) * hpos / INTTOFIX(100);
+ } else {
+ l = FIXTOINT(nscss_len2px(&c->len_ctx,
+ hpos, hunit,
+ box->style));
+ }
+
+ h = content_get_height(box->background);
+ if (vunit == CSS_UNIT_PCT) {
+ t = (height - h) * vpos / INTTOFIX(100);
+ } else {
+ t = FIXTOINT(nscss_len2px(&c->len_ctx,
+ vpos, vunit,
+ box->style));
+ }
+
+ /* Redraw area depends on background-repeat */
+ switch (css_computed_background_repeat(
+ box->style)) {
+ case CSS_BACKGROUND_REPEAT_REPEAT:
+ data.redraw.x = 0;
+ data.redraw.y = 0;
+ data.redraw.width = box->width;
+ data.redraw.height = box->height;
+ break;
+
+ case CSS_BACKGROUND_REPEAT_REPEAT_X:
+ data.redraw.x = 0;
+ data.redraw.y += t;
+ data.redraw.width = box->width;
+ break;
+
+ case CSS_BACKGROUND_REPEAT_REPEAT_Y:
+ data.redraw.x += l;
+ data.redraw.y = 0;
+ data.redraw.height = box->height;
+ break;
+
+ case CSS_BACKGROUND_REPEAT_NO_REPEAT:
+ data.redraw.x += l;
+ data.redraw.y += t;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Add offset to box */
+ data.redraw.x += x;
+ data.redraw.y += y;
+
+ } else {
+ /* Non-background case */
+ int w = content_get_width(object);
+ int h = content_get_height(object);
+
+ if (w != 0 && box->width != w) {
+ /* Not showing image at intrinsic
+ * width; need to scale the redraw
+ * request area. */
+ data.redraw.x = data.redraw.x *
+ box->width / w;
+ data.redraw.width =
+ data.redraw.width *
+ box->width / w;
+ }
+
+ if (h != 0 && box->height != w) {
+ /* Not showing image at intrinsic
+ * height; need to scale the redraw
+ * request area. */
+ data.redraw.y = data.redraw.y *
+ box->height / h;
+ data.redraw.height =
+ data.redraw.height *
+ box->height / h;
+ }
+
+ data.redraw.x += x + box->padding[LEFT];
+ data.redraw.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 */
+ guit->misc->schedule(event->data.delay * 1000,
+ 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_GETTHREAD:
+ /* Objects don't have JS threads */
+ *(event->data.jsthread) = NULL;
+ break;
+
+ case CONTENT_MSG_GETDIMS:
+ *(event->data.getdims.viewport_width) =
+ content__get_width(&c->base);
+ *(event->data.getdims.viewport_height) =
+ content__get_height(&c->base);
+ 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:
+ case CONTENT_MSG_SELECTMENU:
+ case CONTENT_MSG_GADGETCLICK:
+ /* 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:
+ break;
+ }
+
+ 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.available_height);
+ content_set_done(&c->base);
+ } else if (nsoption_bool(incremental_reflow) &&
+ event->type == CONTENT_MSG_DONE &&
+ box != NULL &&
+ !(box->flags & REPLACE_DIM) &&
+ (c->base.status == CONTENT_STATUS_READY ||
+ c->base.status == CONTENT_STATUS_DONE)) {
+ /* 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,
+ */
+ uint64_t ms_now;
+ nsu_getmonotonic_ms(&ms_now);
+ if (ms_now > c->base.reformat_time) {
+ /* The time since the previous reformat is
+ * more than the configured minimum time
+ * between reformats so reformat the page to
+ * display newly fetched objects
+ */
+ content__reformat(&c->base,
+ false,
+ c->base.available_width,
+ c->base.available_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);
+ assert(object->box != 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--;
+ NSLOG(netsurf, INFO, "%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++;
+ NSLOG(netsurf, INFO, "%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 || object->box == 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;
+ if (object->box != NULL) {
+ htmlc->base.active--;
+ NSLOG(netsurf, INFO, "%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 || object->box == NULL)
+ continue;
+
+ if (content_get_type(object->content) == CONTENT_NONE)
+ continue;
+
+ if (content_get_type(object->content) == CONTENT_HTML) {
+ guit->misc->schedule(-1, 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) {
+ NSLOG(netsurf, INFO, "object %p", victim->content);
+
+ if (content_get_type(victim->content) == CONTENT_HTML) {
+ guit->misc->schedule(-1, html_object_refresh, victim);
+ }
+ hlcache_handle_release(victim->content);
+ }
+
+ html->object_list = victim->next;
+ free(victim);
+ }
+ return NSERROR_OK;
+}
+
+
+
+/* exported interface documented in html/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++;
+ if (box != NULL) {
+ c->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
+ }
+
+ return true;
+}
diff --git a/content/handlers/html/redraw.c b/content/handlers/html/redraw.c
new file mode 100644
index 0000000..ee6fab2
--- /dev/null
+++ b/content/handlers/html/redraw.c
@@ -0,0 +1,1961 @@
+/*
+ * Copyright 2004-2008 James Bursa <bursa(a)users.sourceforge.net>
+ * Copyright 2004-2007 John M Bell <jmb202(a)ecs.soton.ac.uk>
+ * Copyright 2004-2007 Richard Wilson <info(a)tinct.net>
+ * Copyright 2005-2006 Adrian Lees <adrianl(a)users.sourceforge.net>
+ * Copyright 2006 Rob Kendrick <rjek(a)netsurf-browser.org>
+ * Copyright 2008 Michael Drake <tlsa(a)netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl(a)users.sourceforge.net>
+ *
+ * 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
+ *
+ * Redrawing CONTENT_HTML implementation.
+ */
+
+#include "utils/config.h"
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <dom/dom.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "netsurf/content.h"
+#include "netsurf/browser_window.h"
+#include "netsurf/plotters.h"
+#include "netsurf/bitmap.h"
+#include "netsurf/layout.h"
+#include "content/content_protected.h"
+#include "css/utils.h"
+#include "desktop/selection.h"
+#include "desktop/print.h"
+#include "desktop/scrollbar.h"
+#include "desktop/textarea.h"
+#include "desktop/gui_internal.h"
+
+#include "html/box.h"
+#include "html/box_inspect.h"
+#include "html/box_manipulate.h"
+#include "html/font.h"
+#include "html/form_internal.h"
+#include "html/html_internal.h"
+#include "html/layout.h"
+#include "html/search.h"
+
+
+bool html_redraw_debug = false;
+
+/**
+ * Determine if a box has a background that needs drawing
+ *
+ * \param box Box to consider
+ * \return True if box has a background, false otherwise.
+ */
+static bool html_redraw_box_has_background(struct box *box)
+{
+ if (box->background != NULL)
+ return true;
+
+ if (box->style != NULL) {
+ css_color colour;
+
+ css_computed_background_color(box->style, &colour);
+
+ if (nscss_color_is_transparent(colour) == false)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Find the background box for a box
+ *
+ * \param box Box to find background box for
+ * \return Pointer to background box, or NULL if there is none
+ */
+static struct box *html_redraw_find_bg_box(struct box *box)
+{
+ /* Thanks to backwards compatibility, CSS defines the following:
+ *
+ * + If the box is for the root element and it has a background,
+ * use that (and then process the body box with no special case)
+ * + If the box is for the root element and it has no background,
+ * then use the background (if any) from the body element as if
+ * it were specified on the root. Then, when the box for the body
+ * element is processed, ignore the background.
+ * + For any other box, just use its own styling.
+ */
+ if (box->parent == NULL) {
+ /* Root box */
+ if (html_redraw_box_has_background(box))
+ return box;
+
+ /* No background on root box: consider body box, if any */
+ if (box->children != NULL) {
+ if (html_redraw_box_has_background(box->children))
+ return box->children;
+ }
+ } else if (box->parent != NULL && box->parent->parent == NULL) {
+ /* Body box: only render background if root has its own */
+ if (html_redraw_box_has_background(box) &&
+ html_redraw_box_has_background(box->parent))
+ return box;
+ } else {
+ /* Any other box */
+ if (html_redraw_box_has_background(box))
+ return box;
+ }
+
+ return NULL;
+}
+
+/**
+ * Redraw a short text string, complete with highlighting
+ * (for selection/search)
+ *
+ * \param utf8_text pointer to UTF-8 text string
+ * \param utf8_len length of string, in bytes
+ * \param offset byte offset within textual representation
+ * \param space width of space that follows string (0 = no space)
+ * \param fstyle text style to use (pass text size unscaled)
+ * \param x x ordinate at which to plot text
+ * \param y y ordinate at which to plot text
+ * \param clip pointer to current clip rectangle
+ * \param height height of text string
+ * \param scale current display scale (1.0 = 100%)
+ * \param excluded exclude this text string from the selection
+ * \param c Content being redrawn.
+ * \param sel Selection context
+ * \param search Search context
+ * \param ctx current redraw context
+ * \return true iff successful and redraw should proceed
+ */
+
+static bool
+text_redraw(const char *utf8_text,
+ size_t utf8_len,
+ size_t offset,
+ int space,
+ const plot_font_style_t *fstyle,
+ int x,
+ int y,
+ const struct rect *clip,
+ int height,
+ float scale,
+ bool excluded,
+ struct content *c,
+ const struct selection *sel,
+ struct search_context *search,
+ const struct redraw_context *ctx)
+{
+ bool highlighted = false;
+ plot_font_style_t plot_fstyle = *fstyle;
+ nserror res;
+
+ /* Need scaled text size to pass to plotters */
+ plot_fstyle.size *= scale;
+
+ /* is this box part of a selection? */
+ if (!excluded && ctx->interactive == true) {
+ unsigned len = utf8_len + (space ? 1 : 0);
+ unsigned start_idx;
+ unsigned end_idx;
+
+ /* first try the browser window's current selection */
+ if (selection_defined(sel) && selection_highlighted(sel,
+ offset, offset + len,
+ &start_idx, &end_idx)) {
+ highlighted = true;
+ }
+
+ /* what about the current search operation, if any? */
+ if (!highlighted && (search != NULL) &&
+ search_term_highlighted(c,
+ offset, offset + len,
+ &start_idx, &end_idx,
+ search)) {
+ highlighted = true;
+ }
+
+ /* \todo make search terms visible within selected text */
+ if (highlighted) {
+ struct rect r;
+ unsigned endtxt_idx = end_idx;
+ bool clip_changed = false;
+ bool text_visible = true;
+ int startx, endx;
+ plot_style_t pstyle_fill_hback = *plot_style_fill_white;
+ plot_font_style_t fstyle_hback = plot_fstyle;
+
+ if (end_idx > utf8_len) {
+ /* adjust for trailing space, not present in
+ * utf8_text */
+ assert(end_idx == utf8_len + 1);
+ endtxt_idx = utf8_len;
+ }
+
+ res = guit->layout->width(fstyle,
+ utf8_text, start_idx,
+ &startx);
+ if (res != NSERROR_OK) {
+ startx = 0;
+ }
+
+ res = guit->layout->width(fstyle,
+ utf8_text, endtxt_idx,
+ &endx);
+ if (res != NSERROR_OK) {
+ endx = 0;
+ }
+
+ /* is there a trailing space that should be highlighted
+ * as well? */
+ if (end_idx > utf8_len) {
+ endx += space;
+ }
+
+ if (scale != 1.0) {
+ startx *= scale;
+ endx *= scale;
+ }
+
+ /* draw any text preceding highlighted portion */
+ if ((start_idx > 0) &&
+ (ctx->plot->text(ctx,
+ &plot_fstyle,
+ x,
+ y + (int)(height * 0.75 * scale),
+ utf8_text,
+ start_idx) != NSERROR_OK))
+ return false;
+
+ pstyle_fill_hback.fill_colour = fstyle->foreground;
+
+ /* highlighted portion */
+ r.x0 = x + startx;
+ r.y0 = y;
+ r.x1 = x + endx;
+ r.y1 = y + height * scale;
+ res = ctx->plot->rectangle(ctx, &pstyle_fill_hback, &r);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ if (start_idx > 0) {
+ int px0 = max(x + startx, clip->x0);
+ int px1 = min(x + endx, clip->x1);
+
+ if (px0 < px1) {
+ r.x0 = px0;
+ r.y0 = clip->y0;
+ r.x1 = px1;
+ r.y1 = clip->y1;
+ res = ctx->plot->clip(ctx, &r);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ clip_changed = true;
+ } else {
+ text_visible = false;
+ }
+ }
+
+ fstyle_hback.background =
+ pstyle_fill_hback.fill_colour;
+ fstyle_hback.foreground = colour_to_bw_furthest(
+ pstyle_fill_hback.fill_colour);
+
+ if (text_visible &&
+ (ctx->plot->text(ctx,
+ &fstyle_hback,
+ x,
+ y + (int)(height * 0.75 * scale),
+ utf8_text,
+ endtxt_idx) != NSERROR_OK)) {
+ return false;
+ }
+
+ /* draw any text succeeding highlighted portion */
+ if (endtxt_idx < utf8_len) {
+ int px0 = max(x + endx, clip->x0);
+ if (px0 < clip->x1) {
+
+ r.x0 = px0;
+ r.y0 = clip->y0;
+ r.x1 = clip->x1;
+ r.y1 = clip->y1;
+ res = ctx->plot->clip(ctx, &r);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ clip_changed = true;
+
+ res = ctx->plot->text(ctx,
+ &plot_fstyle,
+ x,
+ y + (int)(height * 0.75 * scale),
+ utf8_text,
+ utf8_len);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+ }
+
+ if (clip_changed &&
+ (ctx->plot->clip(ctx, clip) != NSERROR_OK)) {
+ return false;
+ }
+ }
+ }
+
+ if (!highlighted) {
+ res = ctx->plot->text(ctx,
+ &plot_fstyle,
+ x,
+ y + (int) (height * 0.75 * scale),
+ utf8_text,
+ utf8_len);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/**
+ * Plot a checkbox.
+ *
+ * \param x left coordinate
+ * \param y top coordinate
+ * \param width dimensions of checkbox
+ * \param height dimensions of checkbox
+ * \param selected the checkbox is selected
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool html_redraw_checkbox(int x, int y, int width, int height,
+ bool selected, const struct redraw_context *ctx)
+{
+ double z;
+ nserror res;
+ struct rect rect;
+
+ z = width * 0.15;
+ if (z == 0) {
+ z = 1;
+ }
+
+ rect.x0 = x;
+ rect.y0 = y ;
+ rect.x1 = x + width;
+ rect.y1 = y + height;
+ res = ctx->plot->rectangle(ctx, plot_style_fill_wbasec, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ /* dark line across top */
+ rect.y1 = y;
+ res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ /* dark line across left */
+ rect.x1 = x;
+ rect.y1 = y + height;
+ res = ctx->plot->line(ctx, plot_style_stroke_darkwbasec, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ /* light line across right */
+ rect.x0 = x + width;
+ rect.x1 = x + width;
+ res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ /* light line across bottom */
+ rect.x0 = x;
+ rect.y0 = y + height;
+ res = ctx->plot->line(ctx, plot_style_stroke_lightwbasec, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ if (selected) {
+ if (width < 12 || height < 12) {
+ /* render a solid box instead of a tick */
+ rect.x0 = x + z + z;
+ rect.y0 = y + z + z;
+ rect.x1 = x + width - z;
+ rect.y1 = y + height - z;
+ res = ctx->plot->rectangle(ctx, plot_style_fill_wblobc, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ } else {
+ /* render a tick, as it'll fit comfortably */
+ rect.x0 = x + width - z;
+ rect.y0 = y + z;
+ rect.x1 = x + (z * 3);
+ rect.y1 = y + height - z;
+ res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ rect.x0 = x + (z * 3);
+ rect.y0 = y + height - z;
+ rect.x1 = x + z + z;
+ rect.y1 = y + (height / 2);
+ res = ctx->plot->line(ctx, plot_style_stroke_wblobc, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+/**
+ * Plot a radio icon.
+ *
+ * \param x left coordinate
+ * \param y top coordinate
+ * \param width dimensions of radio icon
+ * \param height dimensions of radio icon
+ * \param selected the radio icon is selected
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+static bool html_redraw_radio(int x, int y, int width, int height,
+ bool selected, const struct redraw_context *ctx)
+{
+ nserror res;
+
+ /* plot background of radio button */
+ res = ctx->plot->disc(ctx,
+ plot_style_fill_wbasec,
+ x + width * 0.5,
+ y + height * 0.5,
+ width * 0.5 - 1);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ /* plot dark arc */
+ res = ctx->plot->arc(ctx,
+ plot_style_fill_darkwbasec,
+ x + width * 0.5,
+ y + height * 0.5,
+ width * 0.5 - 1,
+ 45,
+ 225);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ /* plot light arc */
+ res = ctx->plot->arc(ctx,
+ plot_style_fill_lightwbasec,
+ x + width * 0.5,
+ y + height * 0.5,
+ width * 0.5 - 1,
+ 225,
+ 45);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ if (selected) {
+ /* plot selection blob */
+ res = ctx->plot->disc(ctx,
+ plot_style_fill_wblobc,
+ x + width * 0.5,
+ y + height * 0.5,
+ width * 0.3 - 1);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Plot a file upload input.
+ *
+ * \param x left coordinate
+ * \param y top coordinate
+ * \param width dimensions of input
+ * \param height dimensions of input
+ * \param box box of input
+ * \param scale scale for redraw
+ * \param background_colour current background colour
+ * \param len_ctx Length conversion context
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool html_redraw_file(int x, int y, int width, int height,
+ struct box *box, float scale, colour background_colour,
+ const nscss_len_ctx *len_ctx,
+ const struct redraw_context *ctx)
+{
+ int text_width;
+ const char *text;
+ size_t length;
+ plot_font_style_t fstyle;
+ nserror res;
+
+ font_plot_style_from_css(len_ctx, box->style, &fstyle);
+ fstyle.background = background_colour;
+
+ if (box->gadget->value) {
+ text = box->gadget->value;
+ } else {
+ text = messages_get("Form_Drop");
+ }
+ length = strlen(text);
+
+ res = guit->layout->width(&fstyle, text, length, &text_width);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ text_width *= scale;
+ if (width < text_width + 8) {
+ x = x + width - text_width - 4;
+ } else {
+ x = x + 4;
+ }
+
+ res = ctx->plot->text(ctx, &fstyle, x, y + height * 0.75, text, length);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * Plot background images.
+ *
+ * The reason for the presence of \a background is the backwards compatibility
+ * mess that is backgrounds on <body>. The background will be drawn
relative
+ * to \a box, using the background information contained within \a background.
+ *
+ * \param x coordinate of box
+ * \param y coordinate of box
+ * \param box box to draw background image of
+ * \param scale scale for redraw
+ * \param clip current clip rectangle
+ * \param background_colour current background colour
+ * \param background box containing background details (usually \a box)
+ * \param len_ctx Length conversion context
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool html_redraw_background(int x, int y, struct box *box, float scale,
+ const struct rect *clip, colour *background_colour,
+ struct box *background,
+ const nscss_len_ctx *len_ctx,
+ const struct redraw_context *ctx)
+{
+ bool repeat_x = false;
+ bool repeat_y = false;
+ bool plot_colour = true;
+ bool plot_content;
+ bool clip_to_children = false;
+ struct box *clip_box = box;
+ int ox = x, oy = y;
+ int width, height;
+ css_fixed hpos = 0, vpos = 0;
+ css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
+ struct box *parent;
+ struct rect r = *clip;
+ css_color bgcol;
+ plot_style_t pstyle_fill_bg = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+ .fill_colour = *background_colour,
+ };
+ nserror res;
+
+ if (ctx->background_images == false)
+ return true;
+
+ plot_content = (background->background != NULL);
+
+ if (plot_content) {
+ if (!box->parent) {
+ /* Root element, special case:
+ * background origin calc. is based on margin box */
+ x -= box->margin[LEFT] * scale;
+ y -= box->margin[TOP] * scale;
+ width = box->margin[LEFT] + box->padding[LEFT] +
+ box->width + box->padding[RIGHT] +
+ box->margin[RIGHT];
+ height = box->margin[TOP] + box->padding[TOP] +
+ box->height + box->padding[BOTTOM] +
+ box->margin[BOTTOM];
+ } else {
+ width = box->padding[LEFT] + box->width +
+ box->padding[RIGHT];
+ height = box->padding[TOP] + box->height +
+ box->padding[BOTTOM];
+ }
+ /* handle background-repeat */
+ switch (css_computed_background_repeat(background->style)) {
+ case CSS_BACKGROUND_REPEAT_REPEAT:
+ repeat_x = repeat_y = true;
+ /* optimisation: only plot the colour if
+ * bitmap is not opaque */
+ plot_colour = !content_get_opaque(background->background);
+ break;
+
+ case CSS_BACKGROUND_REPEAT_REPEAT_X:
+ repeat_x = true;
+ break;
+
+ case CSS_BACKGROUND_REPEAT_REPEAT_Y:
+ repeat_y = true;
+ break;
+
+ case CSS_BACKGROUND_REPEAT_NO_REPEAT:
+ break;
+
+ default:
+ break;
+ }
+
+ /* handle background-position */
+ css_computed_background_position(background->style,
+ &hpos, &hunit, &vpos, &vunit);
+ if (hunit == CSS_UNIT_PCT) {
+ x += (width -
+ content_get_width(background->background)) *
+ scale * FIXTOFLT(hpos) / 100.;
+ } else {
+ x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit,
+ background->style)) * scale);
+ }
+
+ if (vunit == CSS_UNIT_PCT) {
+ y += (height -
+ content_get_height(background->background)) *
+ scale * FIXTOFLT(vpos) / 100.;
+ } else {
+ y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit,
+ background->style)) * scale);
+ }
+ }
+
+ /* special case for table rows as their background needs
+ * to be clipped to all the cells */
+ if (box->type == BOX_TABLE_ROW) {
+ css_fixed h = 0, v = 0;
+ css_unit hu = CSS_UNIT_PX, vu = CSS_UNIT_PX;
+
+ for (parent = box->parent;
+ ((parent) && (parent->type != BOX_TABLE));
+ parent = parent->parent);
+ assert(parent && (parent->style));
+
+ css_computed_border_spacing(parent->style, &h, &hu, &v, &vu);
+
+ clip_to_children = (h > 0) || (v > 0);
+
+ if (clip_to_children)
+ clip_box = box->children;
+ }
+
+ for (; clip_box; clip_box = clip_box->next) {
+ /* clip to child boxes if needed */
+ if (clip_to_children) {
+ assert(clip_box->type == BOX_TABLE_CELL);
+
+ /* update clip.* to the child cell */
+ r.x0 = ox + (clip_box->x * scale);
+ r.y0 = oy + (clip_box->y * scale);
+ r.x1 = r.x0 + (clip_box->padding[LEFT] +
+ clip_box->width +
+ clip_box->padding[RIGHT]) * scale;
+ r.y1 = r.y0 + (clip_box->padding[TOP] +
+ clip_box->height +
+ clip_box->padding[BOTTOM]) * scale;
+
+ if (r.x0 < clip->x0) r.x0 = clip->x0;
+ if (r.y0 < clip->y0) r.y0 = clip->y0;
+ if (r.x1 > clip->x1) r.x1 = clip->x1;
+ if (r.y1 > clip->y1) r.y1 = clip->y1;
+
+ css_computed_background_color(clip_box->style, &bgcol);
+
+ /* <td> attributes override <tr> */
+ /* if the background content is opaque there
+ * is no need to plot underneath it.
+ */
+ if ((r.x0 >= r.x1) ||
+ (r.y0 >= r.y1) ||
+ (nscss_color_is_transparent(bgcol) == false) ||
+ ((clip_box->background != NULL) &&
+ content_get_opaque(clip_box->background)))
+ continue;
+ }
+
+ /* plot the background colour */
+ css_computed_background_color(background->style, &bgcol);
+
+ if (nscss_color_is_transparent(bgcol) == false) {
+ *background_colour = nscss_color_to_ns(bgcol);
+ pstyle_fill_bg.fill_colour = *background_colour;
+ if (plot_colour) {
+ res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+ }
+ /* and plot the image */
+ if (plot_content) {
+ width = content_get_width(background->background);
+ height = content_get_height(background->background);
+
+ /* ensure clip area only as large as required */
+ if (!repeat_x) {
+ if (r.x0 < x)
+ r.x0 = x;
+ if (r.x1 > x + width * scale)
+ r.x1 = x + width * scale;
+ }
+ if (!repeat_y) {
+ if (r.y0 < y)
+ r.y0 = y;
+ if (r.y1 > y + height * scale)
+ r.y1 = y + height * scale;
+ }
+ /* valid clipping rectangles only */
+ if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
+ struct content_redraw_data bg_data;
+
+ res = ctx->plot->clip(ctx, &r);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ bg_data.x = x;
+ bg_data.y = y;
+ bg_data.width = ceilf(width * scale);
+ bg_data.height = ceilf(height * scale);
+ bg_data.background_colour = *background_colour;
+ bg_data.scale = scale;
+ bg_data.repeat_x = repeat_x;
+ bg_data.repeat_y = repeat_y;
+
+ /* We just continue if redraw fails */
+ content_redraw(background->background,
+ &bg_data, &r, ctx);
+ }
+ }
+
+ /* only <tr> rows being clipped to child boxes loop */
+ if (!clip_to_children)
+ return true;
+ }
+ return true;
+}
+
+
+/**
+ * Plot an inline's background and/or background image.
+ *
+ * \param x coordinate of box
+ * \param y coordinate of box
+ * \param box BOX_INLINE which created the background
+ * \param scale scale for redraw
+ * \param clip coordinates of clip rectangle
+ * \param b coordinates of border edge rectangle
+ * \param first true if this is the first rectangle associated with the inline
+ * \param last true if this is the last rectangle associated with the inline
+ * \param background_colour updated to current background colour if plotted
+ * \param len_ctx Length conversion context
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool html_redraw_inline_background(int x, int y, struct box *box,
+ float scale, const struct rect *clip, struct rect b,
+ bool first, bool last, colour *background_colour,
+ const nscss_len_ctx *len_ctx,
+ const struct redraw_context *ctx)
+{
+ struct rect r = *clip;
+ bool repeat_x = false;
+ bool repeat_y = false;
+ bool plot_colour = true;
+ bool plot_content;
+ css_fixed hpos = 0, vpos = 0;
+ css_unit hunit = CSS_UNIT_PX, vunit = CSS_UNIT_PX;
+ css_color bgcol;
+ plot_style_t pstyle_fill_bg = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+ .fill_colour = *background_colour,
+ };
+ nserror res;
+
+ plot_content = (box->background != NULL);
+
+ if (html_redraw_printing && nsoption_bool(remove_backgrounds))
+ return true;
+
+ if (plot_content) {
+ /* handle background-repeat */
+ switch (css_computed_background_repeat(box->style)) {
+ case CSS_BACKGROUND_REPEAT_REPEAT:
+ repeat_x = repeat_y = true;
+ /* optimisation: only plot the colour if
+ * bitmap is not opaque
+ */
+ plot_colour = !content_get_opaque(box->background);
+ break;
+
+ case CSS_BACKGROUND_REPEAT_REPEAT_X:
+ repeat_x = true;
+ break;
+
+ case CSS_BACKGROUND_REPEAT_REPEAT_Y:
+ repeat_y = true;
+ break;
+
+ case CSS_BACKGROUND_REPEAT_NO_REPEAT:
+ break;
+
+ default:
+ break;
+ }
+
+ /* handle background-position */
+ css_computed_background_position(box->style,
+ &hpos, &hunit, &vpos, &vunit);
+ if (hunit == CSS_UNIT_PCT) {
+ x += (b.x1 - b.x0 -
+ content_get_width(box->background) *
+ scale) * FIXTOFLT(hpos) / 100.;
+
+ if (!repeat_x && ((hpos < 2 && !first) ||
+ (hpos > 98 && !last))){
+ plot_content = false;
+ }
+ } else {
+ x += (int) (FIXTOFLT(nscss_len2px(len_ctx, hpos, hunit,
+ box->style)) * scale);
+ }
+
+ if (vunit == CSS_UNIT_PCT) {
+ y += (b.y1 - b.y0 -
+ content_get_height(box->background) *
+ scale) * FIXTOFLT(vpos) / 100.;
+ } else {
+ y += (int) (FIXTOFLT(nscss_len2px(len_ctx, vpos, vunit,
+ box->style)) * scale);
+ }
+ }
+
+ /* plot the background colour */
+ css_computed_background_color(box->style, &bgcol);
+
+ if (nscss_color_is_transparent(bgcol) == false) {
+ *background_colour = nscss_color_to_ns(bgcol);
+ pstyle_fill_bg.fill_colour = *background_colour;
+
+ if (plot_colour) {
+ res = ctx->plot->rectangle(ctx, &pstyle_fill_bg, &r);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+ }
+ /* and plot the image */
+ if (plot_content) {
+ int width = content_get_width(box->background);
+ int height = content_get_height(box->background);
+
+ if (!repeat_x) {
+ if (r.x0 < x)
+ r.x0 = x;
+ if (r.x1 > x + width * scale)
+ r.x1 = x + width * scale;
+ }
+ if (!repeat_y) {
+ if (r.y0 < y)
+ r.y0 = y;
+ if (r.y1 > y + height * scale)
+ r.y1 = y + height * scale;
+ }
+ /* valid clipping rectangles only */
+ if ((r.x0 < r.x1) && (r.y0 < r.y1)) {
+ struct content_redraw_data bg_data;
+
+ res = ctx->plot->clip(ctx, &r);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ bg_data.x = x;
+ bg_data.y = y;
+ bg_data.width = ceilf(width * scale);
+ bg_data.height = ceilf(height * scale);
+ bg_data.background_colour = *background_colour;
+ bg_data.scale = scale;
+ bg_data.repeat_x = repeat_x;
+ bg_data.repeat_y = repeat_y;
+
+ /* We just continue if redraw fails */
+ content_redraw(box->background, &bg_data, &r, ctx);
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Plot text decoration for an inline box.
+ *
+ * \param box box to plot decorations for, of type BOX_INLINE
+ * \param x x coordinate of parent of box
+ * \param y y coordinate of parent of box
+ * \param scale scale for redraw
+ * \param colour colour for decorations
+ * \param ratio position of line as a ratio of line height
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool
+html_redraw_text_decoration_inline(struct box *box,
+ int x, int y,
+ float scale,
+ colour colour,
+ float ratio,
+ const struct redraw_context *ctx)
+{
+ struct box *c;
+ plot_style_t plot_style_box = {
+ .stroke_type = PLOT_OP_TYPE_SOLID,
+ .stroke_colour = colour,
+ };
+ nserror res;
+ struct rect rect;
+
+ for (c = box->next;
+ c && c != box->inline_end;
+ c = c->next) {
+ if (c->type != BOX_TEXT) {
+ continue;
+ }
+ rect.x0 = (x + c->x) * scale;
+ rect.y0 = (y + c->y + c->height * ratio) * scale;
+ rect.x1 = (x + c->x + c->width) * scale;
+ rect.y1 = (y + c->y + c->height * ratio) * scale;
+ res = ctx->plot->line(ctx, &plot_style_box, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/**
+ * Plot text decoration for an non-inline box.
+ *
+ * \param box box to plot decorations for, of type other than BOX_INLINE
+ * \param x x coordinate of box
+ * \param y y coordinate of box
+ * \param scale scale for redraw
+ * \param colour colour for decorations
+ * \param ratio position of line as a ratio of line height
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool
+html_redraw_text_decoration_block(struct box *box,
+ int x, int y,
+ float scale,
+ colour colour,
+ float ratio,
+ const struct redraw_context *ctx)
+{
+ struct box *c;
+ plot_style_t plot_style_box = {
+ .stroke_type = PLOT_OP_TYPE_SOLID,
+ .stroke_colour = colour,
+ };
+ nserror res;
+ struct rect rect;
+
+ /* draw through text descendants */
+ for (c = box->children; c; c = c->next) {
+ if (c->type == BOX_TEXT) {
+ rect.x0 = (x + c->x) * scale;
+ rect.y0 = (y + c->y + c->height * ratio) * scale;
+ rect.x1 = (x + c->x + c->width) * scale;
+ rect.y1 = (y + c->y + c->height * ratio) * scale;
+ res = ctx->plot->line(ctx, &plot_style_box, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ } else if ((c->type == BOX_INLINE_CONTAINER) || (c->type == BOX_BLOCK)) {
+ if (!html_redraw_text_decoration_block(c,
+ x + c->x, y + c->y,
+ scale, colour, ratio, ctx))
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/**
+ * Plot text decoration for a box.
+ *
+ * \param box box to plot decorations for
+ * \param x_parent x coordinate of parent of box
+ * \param y_parent y coordinate of parent of box
+ * \param scale scale for redraw
+ * \param background_colour current background colour
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool html_redraw_text_decoration(struct box *box,
+ int x_parent, int y_parent, float scale,
+ colour background_colour, const struct redraw_context *ctx)
+{
+ static const enum css_text_decoration_e decoration[] = {
+ CSS_TEXT_DECORATION_UNDERLINE, CSS_TEXT_DECORATION_OVERLINE,
+ CSS_TEXT_DECORATION_LINE_THROUGH };
+ static const float line_ratio[] = { 0.9, 0.1, 0.5 };
+ colour fgcol;
+ unsigned int i;
+ css_color col;
+
+ css_computed_color(box->style, &col);
+ fgcol = nscss_color_to_ns(col);
+
+ /* antialias colour for under/overline */
+ if (html_redraw_printing == false)
+ fgcol = blend_colour(background_colour, fgcol);
+
+ if (box->type == BOX_INLINE) {
+ if (!box->inline_end)
+ return true;
+ for (i = 0; i != NOF_ELEMENTS(decoration); i++)
+ if (css_computed_text_decoration(box->style) &
+ decoration[i])
+ if (!html_redraw_text_decoration_inline(box,
+ x_parent, y_parent, scale,
+ fgcol, line_ratio[i], ctx))
+ return false;
+ } else {
+ for (i = 0; i != NOF_ELEMENTS(decoration); i++)
+ if (css_computed_text_decoration(box->style) &
+ decoration[i])
+ if (!html_redraw_text_decoration_block(box,
+ x_parent + box->x,
+ y_parent + box->y,
+ scale,
+ fgcol, line_ratio[i], ctx))
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Redraw the text content of a box, possibly partially highlighted
+ * because the text has been selected, or matches a search operation.
+ *
+ * \param html The html content to redraw text within.
+ * \param box box with text content
+ * \param x x co-ord of box
+ * \param y y co-ord of box
+ * \param clip current clip rectangle
+ * \param scale current scale setting (1.0 = 100%)
+ * \param current_background_color
+ * \param ctx current redraw context
+ * \return true iff successful and redraw should proceed
+ */
+
+static bool html_redraw_text_box(const html_content *html, struct box *box,
+ int x, int y, const struct rect *clip, float scale,
+ colour current_background_color,
+ const struct redraw_context *ctx)
+{
+ bool excluded = (box->object != NULL);
+ plot_font_style_t fstyle;
+
+ font_plot_style_from_css(&html->len_ctx, box->style, &fstyle);
+ fstyle.background = current_background_color;
+
+ if (!text_redraw(box->text, box->length, box->byte_offset,
+ box->space, &fstyle, x, y,
+ clip, box->height, scale, excluded,
+ (struct content *)html, &html->sel,
+ html->search, ctx))
+ return false;
+
+ return true;
+}
+
+bool html_redraw_box(const html_content *html, struct box *box,
+ int x_parent, int y_parent,
+ const struct rect *clip, float scale,
+ colour current_background_color,
+ const struct redraw_context *ctx);
+
+/**
+ * Draw the various children of a box.
+ *
+ * \param html html content
+ * \param box box to draw children of
+ * \param x_parent coordinate of parent box
+ * \param y_parent coordinate of parent box
+ * \param clip clip rectangle
+ * \param scale scale for redraw
+ * \param current_background_color background colour under this box
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+
+static bool html_redraw_box_children(const html_content *html, struct box *box,
+ int x_parent, int y_parent,
+ const struct rect *clip, float scale,
+ colour current_background_color,
+ const struct redraw_context *ctx)
+{
+ struct box *c;
+
+ for (c = box->children; c; c = c->next) {
+
+ if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT)
+ if (!html_redraw_box(html, c,
+ x_parent + box->x -
+ scrollbar_get_offset(box->scroll_x),
+ y_parent + box->y -
+ scrollbar_get_offset(box->scroll_y),
+ clip, scale, current_background_color,
+ ctx))
+ return false;
+ }
+ for (c = box->float_children; c; c = c->next_float)
+ if (!html_redraw_box(html, c,
+ x_parent + box->x -
+ scrollbar_get_offset(box->scroll_x),
+ y_parent + box->y -
+ scrollbar_get_offset(box->scroll_y),
+ clip, scale, current_background_color,
+ ctx))
+ return false;
+
+ return true;
+}
+
+/**
+ * Recursively draw a box.
+ *
+ * \param html html content
+ * \param box box to draw
+ * \param x_parent coordinate of parent box
+ * \param y_parent coordinate of parent box
+ * \param clip clip rectangle
+ * \param scale scale for redraw
+ * \param current_background_color background colour under this box
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ *
+ * x, y, clip_[xy][01] are in target coordinates.
+ */
+
+bool html_redraw_box(const html_content *html, struct box *box,
+ int x_parent, int y_parent,
+ const struct rect *clip, const float scale,
+ colour current_background_color,
+ const struct redraw_context *ctx)
+{
+ const struct plotter_table *plot = ctx->plot;
+ int x, y;
+ int width, height;
+ int padding_left, padding_top, padding_width, padding_height;
+ int border_left, border_top, border_right, border_bottom;
+ struct rect r;
+ struct rect rect;
+ int x_scrolled, y_scrolled;
+ struct box *bg_box = NULL;
+ css_computed_clip_rect css_rect;
+ enum css_overflow_e overflow_x = CSS_OVERFLOW_VISIBLE;
+ enum css_overflow_e overflow_y = CSS_OVERFLOW_VISIBLE;
+
+ if (html_redraw_printing && (box->flags & PRINTED))
+ return true;
+
+ if (box->style != NULL) {
+ overflow_x = css_computed_overflow_x(box->style);
+ overflow_y = css_computed_overflow_y(box->style);
+ }
+
+ /* avoid trivial FP maths */
+ if (scale == 1.0) {
+ x = x_parent + box->x;
+ y = y_parent + box->y;
+ width = box->width;
+ height = box->height;
+ padding_left = box->padding[LEFT];
+ padding_top = box->padding[TOP];
+ padding_width = padding_left + box->width + box->padding[RIGHT];
+ padding_height = padding_top + box->height +
+ box->padding[BOTTOM];
+ border_left = box->border[LEFT].width;
+ border_top = box->border[TOP].width;
+ border_right = box->border[RIGHT].width;
+ border_bottom = box->border[BOTTOM].width;
+ } else {
+ x = (x_parent + box->x) * scale;
+ y = (y_parent + box->y) * scale;
+ width = box->width * scale;
+ height = box->height * scale;
+ /* left and top padding values are normally zero,
+ * so avoid trivial FP maths */
+ padding_left = box->padding[LEFT] ? box->padding[LEFT] * scale
+ : 0;
+ padding_top = box->padding[TOP] ? box->padding[TOP] * scale
+ : 0;
+ padding_width = (box->padding[LEFT] + box->width +
+ box->padding[RIGHT]) * scale;
+ padding_height = (box->padding[TOP] + box->height +
+ box->padding[BOTTOM]) * scale;
+ border_left = box->border[LEFT].width * scale;
+ border_top = box->border[TOP].width * scale;
+ border_right = box->border[RIGHT].width * scale;
+ border_bottom = box->border[BOTTOM].width * scale;
+ }
+
+ /* calculate rectangle covering this box and descendants */
+ if (box->style && overflow_x != CSS_OVERFLOW_VISIBLE &&
+ box->parent != NULL) {
+ /* box contents clipped to box size */
+ r.x0 = x - border_left;
+ r.x1 = x + padding_width + border_right;
+ } else {
+ /* box contents can hang out of the box; use descendant box */
+ if (scale == 1.0) {
+ r.x0 = x + box->descendant_x0;
+ r.x1 = x + box->descendant_x1 + 1;
+ } else {
+ r.x0 = x + box->descendant_x0 * scale;
+ r.x1 = x + box->descendant_x1 * scale + 1;
+ }
+ if (!box->parent) {
+ /* root element */
+ int margin_left, margin_right;
+ if (scale == 1.0) {
+ margin_left = box->margin[LEFT];
+ margin_right = box->margin[RIGHT];
+ } else {
+ margin_left = box->margin[LEFT] * scale;
+ margin_right = box->margin[RIGHT] * scale;
+ }
+ r.x0 = x - border_left - margin_left < r.x0 ?
+ x - border_left - margin_left : r.x0;
+ r.x1 = x + padding_width + border_right +
+ margin_right > r.x1 ?
+ x + padding_width + border_right +
+ margin_right : r.x1;
+ }
+ }
+
+ /* calculate rectangle covering this box and descendants */
+ if (box->style && overflow_y != CSS_OVERFLOW_VISIBLE &&
+ box->parent != NULL) {
+ /* box contents clipped to box size */
+ r.y0 = y - border_top;
+ r.y1 = y + padding_height + border_bottom;
+ } else {
+ /* box contents can hang out of the box; use descendant box */
+ if (scale == 1.0) {
+ r.y0 = y + box->descendant_y0;
+ r.y1 = y + box->descendant_y1 + 1;
+ } else {
+ r.y0 = y + box->descendant_y0 * scale;
+ r.y1 = y + box->descendant_y1 * scale + 1;
+ }
+ if (!box->parent) {
+ /* root element */
+ int margin_top, margin_bottom;
+ if (scale == 1.0) {
+ margin_top = box->margin[TOP];
+ margin_bottom = box->margin[BOTTOM];
+ } else {
+ margin_top = box->margin[TOP] * scale;
+ margin_bottom = box->margin[BOTTOM] * scale;
+ }
+ r.y0 = y - border_top - margin_top < r.y0 ?
+ y - border_top - margin_top : r.y0;
+ r.y1 = y + padding_height + border_bottom +
+ margin_bottom > r.y1 ?
+ y + padding_height + border_bottom +
+ margin_bottom : r.y1;
+ }
+ }
+
+ /* return if the rectangle is completely outside the clip rectangle */
+ if (clip->y1 < r.y0 || r.y1 < clip->y0 ||
+ clip->x1 < r.x0 || r.x1 < clip->x0)
+ return true;
+
+ /*if the rectangle is under the page bottom but it can fit in a page,
+ don't print it now*/
+ if (html_redraw_printing) {
+ if (r.y1 > html_redraw_printing_border) {
+ if (r.y1 - r.y0 <= html_redraw_printing_border &&
+ (box->type == BOX_TEXT ||
+ box->type == BOX_TABLE_CELL
+ || box->object || box->gadget)) {
+ /*remember the highest of all points from the
+ not printed elements*/
+ if (r.y0 < html_redraw_printing_top_cropped)
+ html_redraw_printing_top_cropped = r.y0;
+ return true;
+ }
+ }
+ else box->flags |= PRINTED; /*it won't be printed anymore*/
+ }
+
+ /* if visibility is hidden render children only */
+ if (box->style && css_computed_visibility(box->style) ==
+ CSS_VISIBILITY_HIDDEN) {
+ if ((ctx->plot->group_start) &&
+ (ctx->plot->group_start(ctx, "hidden box") != NSERROR_OK))
+ return false;
+ if (!html_redraw_box_children(html, box, x_parent, y_parent,
+ &r, scale, current_background_color, ctx))
+ return false;
+ return ((!ctx->plot->group_end) || (ctx->plot->group_end(ctx) ==
NSERROR_OK));
+ }
+
+ if ((ctx->plot->group_start) &&
+ (ctx->plot->group_start(ctx,"vis box") != NSERROR_OK)) {
+ return false;
+ }
+
+ if (box->style != NULL &&
+ css_computed_position(box->style) ==
+ CSS_POSITION_ABSOLUTE &&
+ css_computed_clip(box->style, &css_rect) ==
+ CSS_CLIP_RECT) {
+ /* We have an absolutly positioned box with a clip rect */
+ if (css_rect.left_auto == false)
+ r.x0 = x - border_left + FIXTOINT(nscss_len2px(
+ &html->len_ctx,
+ css_rect.left, css_rect.lunit,
+ box->style));
+
+ if (css_rect.top_auto == false)
+ r.y0 = y - border_top + FIXTOINT(nscss_len2px(
+ &html->len_ctx,
+ css_rect.top, css_rect.tunit,
+ box->style));
+
+ if (css_rect.right_auto == false)
+ r.x1 = x - border_left + FIXTOINT(nscss_len2px(
+ &html->len_ctx,
+ css_rect.right, css_rect.runit,
+ box->style));
+
+ if (css_rect.bottom_auto == false)
+ r.y1 = y - border_top + FIXTOINT(nscss_len2px(
+ &html->len_ctx,
+ css_rect.bottom, css_rect.bunit,
+ box->style));
+
+ /* find intersection of clip rectangle and box */
+ if (r.x0 < clip->x0) r.x0 = clip->x0;
+ if (r.y0 < clip->y0) r.y0 = clip->y0;
+ if (clip->x1 < r.x1) r.x1 = clip->x1;
+ if (clip->y1 < r.y1) r.y1 = clip->y1;
+ /* Nothing to do for invalid rectangles */
+ if (r.x0 >= r.x1 || r.y0 >= r.y1)
+ /* not an error */
+ return ((!ctx->plot->group_end) ||
+ (ctx->plot->group_end(ctx) == NSERROR_OK));
+ /* clip to it */
+ if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
+ return false;
+
+ } else if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
+ box->type == BOX_TABLE_CELL || box->object) {
+ /* find intersection of clip rectangle and box */
+ if (r.x0 < clip->x0) r.x0 = clip->x0;
+ if (r.y0 < clip->y0) r.y0 = clip->y0;
+ if (clip->x1 < r.x1) r.x1 = clip->x1;
+ if (clip->y1 < r.y1) r.y1 = clip->y1;
+ /* no point trying to draw 0-width/height boxes */
+ if (r.x0 == r.x1 || r.y0 == r.y1)
+ /* not an error */
+ return ((!ctx->plot->group_end) ||
+ (ctx->plot->group_end(ctx) == NSERROR_OK));
+ /* clip to it */
+ if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
+ return false;
+ } else {
+ /* clip box is fine, clip to it */
+ r = *clip;
+ if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
+ return false;
+ }
+
+ /* background colour and image for block level content and replaced
+ * inlines */
+
+ bg_box = html_redraw_find_bg_box(box);
+
+ /* bg_box == NULL implies that this box should not have
+ * its background rendered. Otherwise filter out linebreaks,
+ * optimize away non-differing inlines, only plot background
+ * for BOX_TEXT it's in an inline */
+ if (bg_box && bg_box->type != BOX_BR &&
+ bg_box->type != BOX_TEXT &&
+ bg_box->type != BOX_INLINE_END &&
+ (bg_box->type != BOX_INLINE || bg_box->object ||
+ bg_box->flags & IFRAME || box->flags & REPLACE_DIM ||
+ (bg_box->gadget != NULL &&
+ (bg_box->gadget->type == GADGET_TEXTAREA ||
+ bg_box->gadget->type == GADGET_TEXTBOX ||
+ bg_box->gadget->type == GADGET_PASSWORD)))) {
+ /* find intersection of clip box and border edge */
+ struct rect p;
+ p.x0 = x - border_left < r.x0 ? r.x0 : x - border_left;
+ p.y0 = y - border_top < r.y0 ? r.y0 : y - border_top;
+ p.x1 = x + padding_width + border_right < r.x1 ?
+ x + padding_width + border_right : r.x1;
+ p.y1 = y + padding_height + border_bottom < r.y1 ?
+ y + padding_height + border_bottom : r.y1;
+ if (!box->parent) {
+ /* Root element, special case:
+ * background covers margins too */
+ int m_left, m_top, m_right, m_bottom;
+ if (scale == 1.0) {
+ m_left = box->margin[LEFT];
+ m_top = box->margin[TOP];
+ m_right = box->margin[RIGHT];
+ m_bottom = box->margin[BOTTOM];
+ } else {
+ m_left = box->margin[LEFT] * scale;
+ m_top = box->margin[TOP] * scale;
+ m_right = box->margin[RIGHT] * scale;
+ m_bottom = box->margin[BOTTOM] * scale;
+ }
+ p.x0 = p.x0 - m_left < r.x0 ? r.x0 : p.x0 - m_left;
+ p.y0 = p.y0 - m_top < r.y0 ? r.y0 : p.y0 - m_top;
+ p.x1 = p.x1 + m_right < r.x1 ? p.x1 + m_right : r.x1;
+ p.y1 = p.y1 + m_bottom < r.y1 ? p.y1 + m_bottom : r.y1;
+ }
+ /* valid clipping rectangles only */
+ if ((p.x0 < p.x1) && (p.y0 < p.y1)) {
+ /* plot background */
+ if (!html_redraw_background(x, y, box, scale, &p,
+ ¤t_background_color, bg_box,
+ &html->len_ctx, ctx))
+ return false;
+ /* restore previous graphics window */
+ if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
+ return false;
+ }
+ }
+
+ /* borders for block level content and replaced inlines */
+ if (box->style &&
+ box->type != BOX_TEXT &&
+ box->type != BOX_INLINE_END &&
+ (box->type != BOX_INLINE || box->object ||
+ box->flags & IFRAME || box->flags & REPLACE_DIM ||
+ (box->gadget != NULL &&
+ (box->gadget->type == GADGET_TEXTAREA ||
+ box->gadget->type == GADGET_TEXTBOX ||
+ box->gadget->type == GADGET_PASSWORD))) &&
+ (border_top || border_right || border_bottom || border_left)) {
+ if (!html_redraw_borders(box, x_parent, y_parent,
+ padding_width, padding_height, &r,
+ scale, ctx))
+ return false;
+ }
+
+ /* backgrounds and borders for non-replaced inlines */
+ if (box->style && box->type == BOX_INLINE && box->inline_end
&&
+ (html_redraw_box_has_background(box) ||
+ border_top || border_right ||
+ border_bottom || border_left)) {
+ /* inline backgrounds and borders span other boxes and may
+ * wrap onto separate lines */
+ struct box *ib;
+ struct rect b; /* border edge rectangle */
+ struct rect p; /* clipped rect */
+ bool first = true;
+ int ib_x;
+ int ib_y = y;
+ int ib_p_width;
+ int ib_b_left, ib_b_right;
+
+ b.x0 = x - border_left;
+ b.x1 = x + padding_width + border_right;
+ b.y0 = y - border_top;
+ b.y1 = y + padding_height + border_bottom;
+
+ p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
+ p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
+ p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
+ p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
+ for (ib = box; ib; ib = ib->next) {
+ /* to get extents of rectangle(s) associated with
+ * inline, cycle though all boxes in inline, skipping
+ * over floats */
+ if (ib->type == BOX_FLOAT_LEFT ||
+ ib->type == BOX_FLOAT_RIGHT)
+ continue;
+ if (scale == 1.0) {
+ ib_x = x_parent + ib->x;
+ ib_y = y_parent + ib->y;
+ ib_p_width = ib->padding[LEFT] + ib->width +
+ ib->padding[RIGHT];
+ ib_b_left = ib->border[LEFT].width;
+ ib_b_right = ib->border[RIGHT].width;
+ } else {
+ ib_x = (x_parent + ib->x) * scale;
+ ib_y = (y_parent + ib->y) * scale;
+ ib_p_width = (ib->padding[LEFT] + ib->width +
+ ib->padding[RIGHT]) * scale;
+ ib_b_left = ib->border[LEFT].width * scale;
+ ib_b_right = ib->border[RIGHT].width * scale;
+ }
+
+ if ((ib->flags & NEW_LINE) && ib != box) {
+ /* inline element has wrapped, plot background
+ * and borders */
+ if (!html_redraw_inline_background(
+ x, y, box, scale, &p, b,
+ first, false,
+ ¤t_background_color,
+ &html->len_ctx, ctx))
+ return false;
+ /* restore previous graphics window */
+ if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
+ return false;
+ if (!html_redraw_inline_borders(box, b, &r,
+ scale, first, false, ctx))
+ return false;
+ /* reset coords */
+ b.x0 = ib_x - ib_b_left;
+ b.y0 = ib_y - border_top - padding_top;
+ b.y1 = ib_y + padding_height - padding_top +
+ border_bottom;
+
+ p.x0 = b.x0 < r.x0 ? r.x0 : b.x0;
+ p.y0 = b.y0 < r.y0 ? r.y0 : b.y0;
+ p.y1 = b.y1 < r.y1 ? b.y1 : r.y1;
+
+ first = false;
+ }
+
+ /* increase width for current box */
+ b.x1 = ib_x + ib_p_width + ib_b_right;
+ p.x1 = b.x1 < r.x1 ? b.x1 : r.x1;
+
+ if (ib == box->inline_end)
+ /* reached end of BOX_INLINE span */
+ break;
+ }
+ /* plot background and borders for last rectangle of
+ * the inline */
+ if (!html_redraw_inline_background(x, ib_y, box, scale, &p, b,
+ first, true, ¤t_background_color,
+ &html->len_ctx, ctx))
+ return false;
+ /* restore previous graphics window */
+ if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
+ return false;
+ if (!html_redraw_inline_borders(box, b, &r, scale, first, true,
+ ctx))
+ return false;
+
+ }
+
+ /* Debug outlines */
+ if (html_redraw_debug) {
+ int margin_left, margin_right;
+ int margin_top, margin_bottom;
+ if (scale == 1.0) {
+ /* avoid trivial fp maths */
+ margin_left = box->margin[LEFT];
+ margin_top = box->margin[TOP];
+ margin_right = box->margin[RIGHT];
+ margin_bottom = box->margin[BOTTOM];
+ } else {
+ margin_left = box->margin[LEFT] * scale;
+ margin_top = box->margin[TOP] * scale;
+ margin_right = box->margin[RIGHT] * scale;
+ margin_bottom = box->margin[BOTTOM] * scale;
+ }
+ /* Content edge -- blue */
+ rect.x0 = x + padding_left;
+ rect.y0 = y + padding_top;
+ rect.x1 = x + padding_left + width;
+ rect.y1 = y + padding_top + height;
+ if (ctx->plot->rectangle(ctx, plot_style_content_edge, &rect) != NSERROR_OK)
+ return false;
+
+ /* Padding edge -- red */
+ rect.x0 = x;
+ rect.y0 = y;
+ rect.x1 = x + padding_width;
+ rect.y1 = y + padding_height;
+ if (ctx->plot->rectangle(ctx, plot_style_padding_edge, &rect) != NSERROR_OK)
+ return false;
+
+ /* Margin edge -- yellow */
+ rect.x0 = x - border_left - margin_left;
+ rect.y0 = y - border_top - margin_top;
+ rect.x1 = x + padding_width + border_right + margin_right;
+ rect.y1 = y + padding_height + border_bottom + margin_bottom;
+ if (ctx->plot->rectangle(ctx, plot_style_margin_edge, &rect) != NSERROR_OK)
+ return false;
+ }
+
+ /* clip to the padding edge for objects, or boxes with overflow hidden
+ * or scroll, unless it's the root element */
+ if (box->parent != NULL) {
+ bool need_clip = false;
+ if (box->object || box->flags & IFRAME ||
+ (overflow_x != CSS_OVERFLOW_VISIBLE &&
+ overflow_y != CSS_OVERFLOW_VISIBLE)) {
+ r.x0 = x;
+ r.y0 = y;
+ r.x1 = x + padding_width;
+ r.y1 = y + padding_height;
+ if (r.x0 < clip->x0) r.x0 = clip->x0;
+ if (r.y0 < clip->y0) r.y0 = clip->y0;
+ if (clip->x1 < r.x1) r.x1 = clip->x1;
+ if (clip->y1 < r.y1) r.y1 = clip->y1;
+ if (r.x1 <= r.x0 || r.y1 <= r.y0) {
+ return (!ctx->plot->group_end ||
+ (ctx->plot->group_end(ctx) == NSERROR_OK));
+ }
+ need_clip = true;
+
+ } else if (overflow_x != CSS_OVERFLOW_VISIBLE) {
+ r.x0 = x;
+ r.y0 = clip->y0;
+ r.x1 = x + padding_width;
+ r.y1 = clip->y1;
+ if (r.x0 < clip->x0) r.x0 = clip->x0;
+ if (clip->x1 < r.x1) r.x1 = clip->x1;
+ if (r.x1 <= r.x0) {
+ return (!ctx->plot->group_end ||
+ (ctx->plot->group_end(ctx) == NSERROR_OK));
+ }
+ need_clip = true;
+
+ } else if (overflow_y != CSS_OVERFLOW_VISIBLE) {
+ r.x0 = clip->x0;
+ r.y0 = y;
+ r.x1 = clip->x1;
+ r.y1 = y + padding_height;
+ if (r.y0 < clip->y0) r.y0 = clip->y0;
+ if (clip->y1 < r.y1) r.y1 = clip->y1;
+ if (r.y1 <= r.y0) {
+ return (!ctx->plot->group_end ||
+ (ctx->plot->group_end(ctx) == NSERROR_OK));
+ }
+ need_clip = true;
+ }
+
+ if (need_clip &&
+ (box->type == BOX_BLOCK ||
+ box->type == BOX_INLINE_BLOCK ||
+ box->type == BOX_TABLE_CELL || box->object)) {
+ if (ctx->plot->clip(ctx, &r) != NSERROR_OK)
+ return false;
+ }
+ }
+
+ /* text decoration */
+ if ((box->type != BOX_TEXT) &&
+ box->style &&
+ css_computed_text_decoration(box->style) != CSS_TEXT_DECORATION_NONE) {
+ if (!html_redraw_text_decoration(box, x_parent, y_parent,
+ scale, current_background_color, ctx))
+ return false;
+ }
+
+ if (box->object && width != 0 && height != 0) {
+ struct content_redraw_data obj_data;
+
+ x_scrolled = x - scrollbar_get_offset(box->scroll_x) * scale;
+ y_scrolled = y - scrollbar_get_offset(box->scroll_y) * scale;
+
+ obj_data.x = x_scrolled + padding_left;
+ obj_data.y = y_scrolled + padding_top;
+ obj_data.width = width;
+ obj_data.height = height;
+ obj_data.background_colour = current_background_color;
+ obj_data.scale = scale;
+ obj_data.repeat_x = false;
+ obj_data.repeat_y = false;
+
+ if (content_get_type(box->object) == CONTENT_HTML) {
+ obj_data.x /= scale;
+ obj_data.y /= scale;
+ }
+
+ if (!content_redraw(box->object, &obj_data, &r, ctx)) {
+ /* Show image fail */
+ /* Unicode (U+FFFC) 'OBJECT REPLACEMENT CHARACTER' */
+ const char *obj = "\xef\xbf\xbc";
+ int obj_width;
+ int obj_x = x + padding_left;
+ nserror res;
+
+ rect.x0 = x + padding_left;
+ rect.y0 = y + padding_top;
+ rect.x1 = x + padding_left + width - 1;
+ rect.y1 = y + padding_top + height - 1;
+ res = ctx->plot->rectangle(ctx, plot_style_broken_object, &rect);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+
+ res = guit->layout->width(plot_fstyle_broken_object,
+ obj,
+ sizeof(obj) - 1,
+ &obj_width);
+ if (res != NSERROR_OK) {
+ obj_x += 1;
+ } else {
+ obj_x += width / 2 - obj_width / 2;
+ }
+
+ if (ctx->plot->text(ctx,
+ plot_fstyle_broken_object,
+ obj_x, y + padding_top + (int)(height * 0.75),
+ obj, sizeof(obj) - 1) != NSERROR_OK)
+ return false;
+ }
+
+ } else if (box->iframe) {
+ /* Offset is passed to browser window redraw unscaled */
+ browser_window_redraw(box->iframe,
+ x + padding_left,
+ y + padding_top, &r, ctx);
+
+ } else if (box->gadget && box->gadget->type == GADGET_CHECKBOX) {
+ if (!html_redraw_checkbox(x + padding_left, y + padding_top,
+ width, height, box->gadget->selected, ctx))
+ return false;
+
+ } else if (box->gadget && box->gadget->type == GADGET_RADIO) {
+ if (!html_redraw_radio(x + padding_left, y + padding_top,
+ width, height, box->gadget->selected, ctx))
+ return false;
+
+ } else if (box->gadget && box->gadget->type == GADGET_FILE) {
+ if (!html_redraw_file(x + padding_left, y + padding_top,
+ width, height, box, scale,
+ current_background_color, &html->len_ctx, ctx))
+ return false;
+
+ } else if (box->gadget &&
+ (box->gadget->type == GADGET_TEXTAREA ||
+ box->gadget->type == GADGET_PASSWORD ||
+ box->gadget->type == GADGET_TEXTBOX)) {
+ textarea_redraw(box->gadget->data.text.ta, x, y,
+ current_background_color, scale, &r, ctx);
+
+ } else if (box->text) {
+ if (!html_redraw_text_box(html, box, x, y, &r, scale,
+ current_background_color, ctx))
+ return false;
+
+ } else {
+ if (!html_redraw_box_children(html, box, x_parent, y_parent, &r,
+ scale, current_background_color, ctx))
+ return false;
+ }
+
+ if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
+ box->type == BOX_TABLE_CELL || box->type == BOX_INLINE)
+ if (ctx->plot->clip(ctx, clip) != NSERROR_OK)
+ return false;
+
+ /* list marker */
+ if (box->list_marker) {
+ if (!html_redraw_box(html, box->list_marker,
+ x_parent + box->x -
+ scrollbar_get_offset(box->scroll_x),
+ y_parent + box->y -
+ scrollbar_get_offset(box->scroll_y),
+ clip, scale, current_background_color, ctx))
+ return false;
+ }
+
+ /* scrollbars */
+ if (((box->style && box->type != BOX_BR &&
+ box->type != BOX_TABLE && box->type != BOX_INLINE &&
+ (box->gadget == NULL || box->gadget->type != GADGET_TEXTAREA) &&
+ (overflow_x == CSS_OVERFLOW_SCROLL ||
+ overflow_x == CSS_OVERFLOW_AUTO ||
+ overflow_y == CSS_OVERFLOW_SCROLL ||
+ overflow_y == CSS_OVERFLOW_AUTO)) ||
+ (box->object && content_get_type(box->object) ==
+ CONTENT_HTML)) && box->parent != NULL) {
+ nserror res;
+ bool has_x_scroll = (overflow_x == CSS_OVERFLOW_SCROLL);
+ bool has_y_scroll = (overflow_y == CSS_OVERFLOW_SCROLL);
+
+ has_x_scroll |= (overflow_x == CSS_OVERFLOW_AUTO) &&
+ box_hscrollbar_present(box);
+ has_y_scroll |= (overflow_y == CSS_OVERFLOW_AUTO) &&
+ box_vscrollbar_present(box);
+
+ res = box_handle_scrollbars((struct content *)html,
+ box, has_x_scroll, has_y_scroll);
+ if (res != NSERROR_OK) {
+ NSLOG(netsurf, INFO, "%s", messages_get_errorcode(res));
+ return false;
+ }
+
+ if (box->scroll_x != NULL)
+ scrollbar_redraw(box->scroll_x,
+ x_parent + box->x,
+ y_parent + box->y + box->padding[TOP] +
+ box->height + box->padding[BOTTOM] -
+ SCROLLBAR_WIDTH, clip, scale, ctx);
+ if (box->scroll_y != NULL)
+ scrollbar_redraw(box->scroll_y,
+ x_parent + box->x + box->padding[LEFT] +
+ box->width + box->padding[RIGHT] -
+ SCROLLBAR_WIDTH,
+ y_parent + box->y, clip, scale, ctx);
+ }
+
+ if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK ||
+ box->type == BOX_TABLE_CELL || box->type == BOX_INLINE) {
+ if (ctx->plot->clip(ctx, clip) != NSERROR_OK)
+ return false;
+ }
+
+ return ((!plot->group_end) || (ctx->plot->group_end(ctx) == NSERROR_OK));
+}
+
+/**
+ * Draw a CONTENT_HTML using the current set of plotters (plot).
+ *
+ * \param c content of type CONTENT_HTML
+ * \param data redraw data for this content redraw
+ * \param clip current clip region
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ *
+ * x, y, clip_[xy][01] are in target coordinates.
+ */
+
+bool html_redraw(struct content *c, struct content_redraw_data *data,
+ const struct rect *clip, const struct redraw_context *ctx)
+{
+ html_content *html = (html_content *) c;
+ struct box *box;
+ bool result = true;
+ bool select, select_only;
+ plot_style_t pstyle_fill_bg = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+ .fill_colour = data->background_colour,
+ };
+
+ box = html->layout;
+ assert(box);
+
+ /* The select menu needs special treating because, when opened, it
+ * reaches beyond its layout box.
+ */
+ select = false;
+ select_only = false;
+ if (ctx->interactive && html->visible_select_menu != NULL) {
+ struct form_control *control = html->visible_select_menu;
+ select = true;
+ /* check if the redraw rectangle is completely inside of the
+ select menu */
+ select_only = form_clip_inside_select_menu(control,
+ data->scale, clip);
+ }
+
+ if (!select_only) {
+ /* clear to background colour */
+ result = (ctx->plot->clip(ctx, clip) == NSERROR_OK);
+
+ if (html->background_colour != NS_TRANSPARENT)
+ pstyle_fill_bg.fill_colour = html->background_colour;
+
+ result &= (ctx->plot->rectangle(ctx, &pstyle_fill_bg, clip) ==
NSERROR_OK);
+
+ result &= html_redraw_box(html, box, data->x, data->y, clip,
+ data->scale, pstyle_fill_bg.fill_colour, ctx);
+ }
+
+ if (select) {
+ int menu_x, menu_y;
+ box = html->visible_select_menu->box;
+ box_coords(box, &menu_x, &menu_y);
+
+ menu_x -= box->border[LEFT].width;
+ menu_y += box->height + box->border[BOTTOM].width +
+ box->padding[BOTTOM] + box->padding[TOP];
+ result &= form_redraw_select_menu(html->visible_select_menu,
+ data->x + menu_x, data->y + menu_y,
+ data->scale, clip, ctx);
+ }
+
+ return result;
+
+}
diff --git a/content/handlers/html/redraw_border.c
b/content/handlers/html/redraw_border.c
new file mode 100644
index 0000000..0b3d858
--- /dev/null
+++ b/content/handlers/html/redraw_border.c
@@ -0,0 +1,928 @@
+/*
+ * Copyright 2017 Vincent Sanders <vince(a)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
+ *
+ * Redrawing CONTENT_HTML borders implementation.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "utils/log.h"
+#include "netsurf/plotters.h"
+#include "netsurf/css.h"
+
+#include "html/box.h"
+#include "html/html_internal.h"
+
+
+static plot_style_t plot_style_bdr = {
+ .stroke_type = PLOT_OP_TYPE_DASH,
+};
+static plot_style_t plot_style_fillbdr = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+static plot_style_t plot_style_fillbdr_dark = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+static plot_style_t plot_style_fillbdr_light = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+static plot_style_t plot_style_fillbdr_ddark = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+static plot_style_t plot_style_fillbdr_dlight = {
+ .fill_type = PLOT_OP_TYPE_SOLID,
+};
+
+
+static inline nserror
+plot_clipped_rectangle(const struct redraw_context *ctx,
+ const plot_style_t *style,
+ const struct rect *clip,
+ struct rect *rect)
+{
+ nserror res;
+
+ rect->x0 = (clip->x0 > rect->x0) ? clip->x0 : rect->x0;
+ rect->y0 = (clip->y0 > rect->y0) ? clip->y0 : rect->y0;
+ rect->x1 = (clip->x1 < rect->x1) ? clip->x1 : rect->x1;
+ rect->y1 = (clip->y1 < rect->y1) ? clip->y1 : rect->y1;
+ if ((rect->x0 < rect->x1) && (rect->y0 < rect->y1)) {
+ /* valid clip rectangles only */
+ res = ctx->plot->rectangle(ctx, style, rect);
+ } else {
+ res = NSERROR_OK;
+ }
+ return res;
+}
+
+
+/**
+ * Draw one border.
+ *
+ * \param side index of border side (TOP, RIGHT, BOTTOM, LEFT)
+ * \param p array of precomputed border vertices
+ * \param c colour for border
+ * \param style border line style
+ * \param thickness border thickness
+ * \param rectangular whether border is rectangular
+ * \param clip cliping area for redrawing border.
+ * \param ctx current redraw context
+ * \return NSERROR_OK if successful otherwise appropriate error code
+ */
+static nserror
+html_redraw_border_plot(const int side,
+ const int *p,
+ colour c,
+ enum css_border_style_e style,
+ int thickness,
+ bool rectangular,
+ const struct rect *clip,
+ const struct redraw_context *ctx)
+{
+ int z[8]; /* Vertices of border part */
+ unsigned int light = side;
+ plot_style_t *plot_style_bdr_in;
+ plot_style_t *plot_style_bdr_out;
+ nserror res = NSERROR_OK;
+ struct rect rect;
+
+ if (c == NS_TRANSPARENT) {
+ return res;
+ }
+
+ plot_style_bdr.stroke_type = PLOT_OP_TYPE_DASH;
+ plot_style_bdr.stroke_colour = c;
+ plot_style_bdr.stroke_width = (thickness << PLOT_STYLE_RADIX);
+ plot_style_fillbdr.fill_colour = c;
+ plot_style_fillbdr_dark.fill_colour = darken_colour(c);
+ plot_style_fillbdr_light.fill_colour = lighten_colour(c);
+ plot_style_fillbdr_ddark.fill_colour = double_darken_colour(c);
+ plot_style_fillbdr_dlight.fill_colour = double_lighten_colour(c);
+
+ switch (style) {
+ case CSS_BORDER_STYLE_DOTTED:
+ plot_style_bdr.stroke_type = PLOT_OP_TYPE_DOT;
+ /* fall through */
+ case CSS_BORDER_STYLE_DASHED:
+ rect.x0 = (p[0] + p[2]) / 2;
+ rect.y0 = (p[1] + p[3]) / 2;
+ rect.x1 = (p[4] + p[6]) / 2;
+ rect.y1 = (p[5] + p[7]) / 2;
+ res = ctx->plot->line(ctx, &plot_style_bdr, &rect);
+ break;
+
+ case CSS_BORDER_STYLE_SOLID:
+ /* fall through to default */
+ default:
+ if (rectangular || thickness == 1) {
+
+ if (side == TOP || side == RIGHT) {
+ rect.x0 = p[2];
+ rect.y0 = p[3];
+ if ((side == TOP) &&
+ (p[4] - p[6] != 0)) {
+ rect.x1 = p[4];
+ } else {
+ rect.x1 = p[6];
+ }
+ rect.y1 = p[7];
+ } else {
+ rect.x0 = p[6];
+ rect.y0 = p[7];
+ rect.x1 = p[2];
+ if ((side == LEFT) &&
+ (p[1] - p[3] != 0)) {
+ rect.y1 = p[1];
+ } else {
+ rect.y1 = p[3];
+ }
+ }
+ res = plot_clipped_rectangle(ctx,
+ &plot_style_fillbdr,
+ clip,
+ &rect);
+ } else {
+ res = ctx->plot->polygon(ctx, &plot_style_fillbdr, p, 4);
+ }
+ break;
+
+ case CSS_BORDER_STYLE_DOUBLE:
+ z[0] = p[0];
+ z[1] = p[1];
+ z[2] = (p[0] * 2 + p[2]) / 3;
+ z[3] = (p[1] * 2 + p[3]) / 3;
+ z[4] = (p[6] * 2 + p[4]) / 3;
+ z[5] = (p[7] * 2 + p[5]) / 3;
+ z[6] = p[6];
+ z[7] = p[7];
+ res = ctx->plot->polygon(ctx, &plot_style_fillbdr, z, 4);
+ if (res == NSERROR_OK) {
+ z[0] = p[2];
+ z[1] = p[3];
+ z[2] = (p[2] * 2 + p[0]) / 3;
+ z[3] = (p[3] * 2 + p[1]) / 3;
+ z[4] = (p[4] * 2 + p[6]) / 3;
+ z[5] = (p[5] * 2 + p[7]) / 3;
+ z[6] = p[4];
+ z[7] = p[5];
+ res = ctx->plot->polygon(ctx, &plot_style_fillbdr, z, 4);
+ }
+ break;
+
+ case CSS_BORDER_STYLE_GROOVE:
+ light = 3 - light;
+ /* fall through */
+ case CSS_BORDER_STYLE_RIDGE:
+ /* choose correct colours for each part of the border line */
+ if (light <= 1) {
+ plot_style_bdr_in = &plot_style_fillbdr_dark;
+ plot_style_bdr_out = &plot_style_fillbdr_light;
+ } else {
+ plot_style_bdr_in = &plot_style_fillbdr_light;
+ plot_style_bdr_out = &plot_style_fillbdr_dark;
+ }
+
+ /* Render border */
+ if ((rectangular || thickness == 2) && thickness != 1) {
+ /* Border made up from two parts and can be
+ * plotted with rectangles
+ */
+
+ /* First part */
+ if (side == TOP || side == RIGHT) {
+ rect.x0 = (p[0] + p[2]) / 2;
+ rect.y0 = (p[1] + p[3]) / 2;
+ rect.x1 = p[6];
+ rect.y1 = p[7];
+ } else {
+ rect.x0 = p[6];
+ rect.y0 = p[7];
+ rect.x1 = (p[0] + p[2]) / 2;
+ rect.y1 = (p[1] + p[3]) / 2;
+ }
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_in,
+ clip,
+ &rect);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ /* Second part */
+ if (side == TOP || side == RIGHT) {
+ rect.x0 = p[2];
+ rect.y0 = p[3];
+ rect.x1 = (p[6] + p[4]) / 2;
+ rect.y1 = (p[7] + p[5]) / 2;
+ } else {
+ rect.x0 = (p[6] + p[4]) / 2;
+ rect.y0 = (p[7] + p[5]) / 2;
+ rect.x1 = p[2];
+ rect.y1 = p[3];
+ }
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_out,
+ clip,
+ &rect);
+ } else if (thickness == 1) {
+ /* Border made up from one part which can be
+ * plotted as a rectangle
+ */
+
+ if (side == TOP || side == RIGHT) {
+ rect.x0 = p[2];
+ rect.y0 = p[3];
+ rect.x1 = p[6];
+ rect.y1 = p[7];
+ rect.x1 = ((side == TOP) && (p[4] - p[6] != 0)) ?
+ rect.x1 + p[4] - p[6] : rect.x1;
+
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_in,
+ clip,
+ &rect);
+ } else {
+ rect.x0 = p[6];
+ rect.y0 = p[7];
+ rect.x1 = p[2];
+ rect.y1 = p[3];
+ rect.y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ?
+ rect.y1 + p[1] - p[3] : rect.y1;
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_out,
+ clip,
+ &rect);
+ }
+ } else {
+ /* Border made up from two parts and can't be
+ * plotted with rectangles
+ */
+ z[0] = p[0];
+ z[1] = p[1];
+ z[2] = (p[0] + p[2]) / 2;
+ z[3] = (p[1] + p[3]) / 2;
+ z[4] = (p[6] + p[4]) / 2;
+ z[5] = (p[7] + p[5]) / 2;
+ z[6] = p[6];
+ z[7] = p[7];
+ res = ctx->plot->polygon(ctx, plot_style_bdr_in, z, 4);
+ if (res == NSERROR_OK) {
+ z[0] = p[2];
+ z[1] = p[3];
+ z[6] = p[4];
+ z[7] = p[5];
+ res = ctx->plot->polygon(ctx,
+ plot_style_bdr_out,
+ z,
+ 4);
+ }
+ }
+ break;
+
+ case CSS_BORDER_STYLE_INSET:
+ light = (light + 2) % 4;
+ /* fall through */
+ case CSS_BORDER_STYLE_OUTSET:
+ /* choose correct colours for each part of the border line */
+ switch (light) {
+ case 0:
+ plot_style_bdr_in = &plot_style_fillbdr_light;
+ plot_style_bdr_out = &plot_style_fillbdr_dlight;
+ break;
+ case 1:
+ plot_style_bdr_in = &plot_style_fillbdr_ddark;
+ plot_style_bdr_out = &plot_style_fillbdr_dark;
+ break;
+ case 2:
+ plot_style_bdr_in = &plot_style_fillbdr_dark;
+ plot_style_bdr_out = &plot_style_fillbdr_ddark;
+ break;
+ case 3:
+ plot_style_bdr_in = &plot_style_fillbdr_dlight;
+ plot_style_bdr_out = &plot_style_fillbdr_light;
+ break;
+ default:
+ plot_style_bdr_in = &plot_style_fillbdr;
+ plot_style_bdr_out = &plot_style_fillbdr;
+ break;
+ }
+
+ /* Render border */
+ if ((rectangular || thickness == 2) && thickness != 1) {
+ /* Border made up from two parts and can be
+ * plotted with rectangles
+ */
+
+ /* First part */
+ if (side == TOP || side == RIGHT) {
+ rect.x0 = (p[0] + p[2]) / 2;
+ rect.y0 = (p[1] + p[3]) / 2;
+ rect.x1 = p[6];
+ rect.y1 = p[7];
+ } else {
+ rect.x0 = p[6];
+ rect.y0 = p[7];
+ rect.x1 = (p[0] + p[2]) / 2;
+ rect.y1 = (p[1] + p[3]) / 2;
+ }
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_in,
+ clip,
+ &rect);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ /* Second part */
+ if (side == TOP || side == RIGHT) {
+ rect.x0 = p[2];
+ rect.y0 = p[3];
+ rect.x1 = (p[6] + p[4]) / 2;
+ rect.y1 = (p[7] + p[5]) / 2;
+ } else {
+ rect.x0 = (p[6] + p[4]) / 2;
+ rect.y0 = (p[7] + p[5]) / 2;
+ rect.x1 = p[2];
+ rect.y1 = p[3];
+ }
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_out,
+ clip,
+ &rect);
+ } else if (thickness == 1) {
+ /* Border made up from one part which can be
+ * plotted as a rectangle
+ */
+
+ if (side == TOP || side == RIGHT) {
+ rect.x0 = p[2];
+ rect.y0 = p[3];
+ rect.x1 = p[6];
+ rect.y1 = p[7];
+ rect.x1 = ((side == TOP) && (p[4] - p[6] != 0)) ?
+ rect.x1 + p[4] - p[6] : rect.x1;
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_in,
+ clip,
+ &rect);
+ } else {
+ rect.x0 = p[6];
+ rect.y0 = p[7];
+ rect.x1 = p[2];
+ rect.y1 = p[3];
+ rect.y1 = ((side == LEFT) && (p[1] - p[3] != 0)) ?
+ rect.y1 + p[1] - p[3] : rect.y1;
+ res = plot_clipped_rectangle(ctx,
+ plot_style_bdr_out,
+ clip,
+ &rect);
+ }
+ } else {
+ /* Border made up from two parts and can't be
+ * plotted with rectangles
+ */
+
+ z[0] = p[0];
+ z[1] = p[1];
+ z[2] = (p[0] + p[2]) / 2;
+ z[3] = (p[1] + p[3]) / 2;
+ z[4] = (p[6] + p[4]) / 2;
+ z[5] = (p[7] + p[5]) / 2;
+ z[6] = p[6];
+ z[7] = p[7];
+ res = ctx->plot->polygon(ctx, plot_style_bdr_in, z, 4);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ z[0] = p[2];
+ z[1] = p[3];
+ z[6] = p[4];
+ z[7] = p[5];
+ res = ctx->plot->polygon(ctx, plot_style_bdr_out, z, 4);
+ }
+ break;
+ }
+
+ return res;
+}
+
+
+/**
+ * Draw borders for a box.
+ *
+ * \param box box to draw
+ * \param x_parent coordinate of left padding edge of parent of box
+ * \param y_parent coordinate of top padding edge of parent of box
+ * \param p_width width of padding box
+ * \param p_height height of padding box
+ * \param clip cliping area for redrawing border.
+ * \param scale scale for redraw
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+bool
+html_redraw_borders(struct box *box,
+ int x_parent,
+ int y_parent,
+ int p_width,
+ int p_height,
+ const struct rect *clip,
+ float scale,
+ const struct redraw_context *ctx)
+{
+ unsigned int sides[] = { LEFT, RIGHT, TOP, BOTTOM };
+ int top = box->border[TOP].width;
+ int right = box->border[RIGHT].width;
+ int bottom = box->border[BOTTOM].width;
+ int left = box->border[LEFT].width;
+ int x, y;
+ unsigned int i, side;
+ int p[8]; /* Box border vertices */
+ int z[8]; /* Border vertices */
+ bool square_end_1 = false;
+ bool square_end_2 = false;
+ nserror res;
+
+ x = x_parent + box->x;
+ y = y_parent + box->y;
+
+ if (scale != 1.0) {
+ top *= scale;
+ right *= scale;
+ bottom *= scale;
+ left *= scale;
+ x *= scale;
+ y *= scale;
+ }
+
+ assert(box->style);
+
+ /* Calculate border vertices
+ *
+ * A----------------------+
+ * | \ / |
+ * | B--------------+ |
+ * | | | |
+ * | +--------------C |
+ * | / \ |
+ * +----------------------D
+ */
+ p[0] = x - left; p[1] = y - top; /* A */
+ p[2] = x; p[3] = y; /* B */
+ p[4] = x + p_width; p[5] = y + p_height; /* C */
+ p[6] = x + p_width + right; p[7] = y + p_height + bottom; /* D */
+
+ for (i = 0; i != 4; i++) {
+ colour col = 0;
+ side = sides[i]; /* plot order */
+
+ if (box->border[side].width == 0 ||
+ nscss_color_is_transparent(box->border[side].c)) {
+ continue;
+ }
+
+ switch (side) {
+ case LEFT:
+ square_end_1 = (top == 0);
+ square_end_2 = (bottom == 0);
+
+ z[0] = p[0]; z[1] = p[7];
+ z[2] = p[2]; z[3] = p[5];
+ z[4] = p[2]; z[5] = p[3];
+ z[6] = p[0]; z[7] = p[1];
+
+ if (nscss_color_is_transparent(box->border[TOP].c) == false &&
+ box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang top corner fully,
+ * if top border is opaque
+ */
+ z[5] -= top;
+ square_end_1 = true;
+ }
+ if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
+ box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang bottom corner fully,
+ * if bottom border is opaque
+ */
+ z[3] += bottom;
+ square_end_2 = true;
+ }
+
+ col = nscss_color_to_ns(box->border[side].c);
+
+ res = html_redraw_border_plot(side,
+ z,
+ col,
+ box->border[side].style,
+ box->border[side].width * scale,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ break;
+
+ case RIGHT:
+ square_end_1 = (top == 0);
+ square_end_2 = (bottom == 0);
+
+ z[0] = p[6]; z[1] = p[1];
+ z[2] = p[4]; z[3] = p[3];
+ z[4] = p[4]; z[5] = p[5];
+ z[6] = p[6]; z[7] = p[7];
+
+ if (nscss_color_is_transparent(box->border[TOP].c) == false &&
+ box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang top corner fully,
+ * if top border is opaque
+ */
+ z[3] -= top;
+ square_end_1 = true;
+ }
+ if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
+ box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang bottom corner fully,
+ * if bottom border is opaque
+ */
+ z[5] += bottom;
+ square_end_2 = true;
+ }
+
+ col = nscss_color_to_ns(box->border[side].c);
+
+ res = html_redraw_border_plot(side,
+ z,
+ col,
+ box->border[side].style,
+ box->border[side].width * scale,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ break;
+
+ case TOP:
+ if (clip->y0 > p[3]) {
+ /* clip rectangle is below border; nothing to
+ * plot
+ */
+ continue;
+ }
+
+ square_end_1 = (left == 0);
+ square_end_2 = (right == 0);
+
+ z[0] = p[2]; z[1] = p[3];
+ z[2] = p[0]; z[3] = p[1];
+ z[4] = p[6]; z[5] = p[1];
+ z[6] = p[4]; z[7] = p[3];
+
+ if (box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[TOP].c == box->border[LEFT].c) {
+ /* don't bother overlapping left corner if
+ * it's the same colour anyway
+ */
+ z[2] += left;
+ square_end_1 = true;
+ }
+ if (box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[TOP].c == box->border[RIGHT].c) {
+ /* don't bother overlapping right corner if
+ * it's the same colour anyway
+ */
+ z[4] -= right;
+ square_end_2 = true;
+ }
+
+ col = nscss_color_to_ns(box->border[side].c);
+
+ res = html_redraw_border_plot(side,
+ z,
+ col,
+ box->border[side].style,
+ box->border[side].width * scale,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ break;
+
+ case BOTTOM:
+ if (clip->y1 < p[5]) {
+ /* clip rectangle is above border; nothing to
+ * plot
+ */
+ continue;
+ }
+
+ square_end_1 = (left == 0);
+ square_end_2 = (right == 0);
+
+ z[0] = p[4]; z[1] = p[5];
+ z[2] = p[6]; z[3] = p[7];
+ z[4] = p[0]; z[5] = p[7];
+ z[6] = p[2]; z[7] = p[5];
+
+ if (box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[BOTTOM].c == box->border[LEFT].c) {
+ /* don't bother overlapping left corner if
+ * it's the same colour anyway
+ */
+ z[4] += left;
+ square_end_1 = true;
+ }
+ if (box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[BOTTOM].c == box->border[RIGHT].c) {
+ /* don't bother overlapping right corner if
+ * it's the same colour anyway
+ */
+ z[2] -= right;
+ square_end_2 = true;
+ }
+
+ col = nscss_color_to_ns(box->border[side].c);
+
+ res = html_redraw_border_plot(side,
+ z,
+ col,
+ box->border[side].style,
+ box->border[side].width * scale,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ break;
+
+ default:
+ assert(side == TOP || side == BOTTOM ||
+ side == LEFT || side == RIGHT);
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Draw an inline's borders.
+ *
+ * \param box BOX_INLINE which created the border
+ * \param b coordinates of border edge rectangle
+ * \param clip cliping area for redrawing border.
+ * \param scale scale for redraw
+ * \param first true if this is the first rectangle associated with the inline
+ * \param last true if this is the last rectangle associated with the inline
+ * \param ctx current redraw context
+ * \return true if successful, false otherwise
+ */
+bool
+html_redraw_inline_borders(struct box *box,
+ struct rect b,
+ const struct rect *clip,
+ float scale,
+ bool first,
+ bool last,
+ const struct redraw_context *ctx)
+{
+ int top = box->border[TOP].width;
+ int right = box->border[RIGHT].width;
+ int bottom = box->border[BOTTOM].width;
+ int left = box->border[LEFT].width;
+ colour col;
+ int p[8]; /* Box border vertices */
+ int z[8]; /* Border vertices */
+ bool square_end_1;
+ bool square_end_2;
+ nserror res;
+
+ if (scale != 1.0) {
+ top *= scale;
+ right *= scale;
+ bottom *= scale;
+ left *= scale;
+ }
+
+ /* Calculate border vertices
+ *
+ * A----------------------+
+ * | \ / |
+ * | B--------------+ |
+ * | | | |
+ * | +--------------C |
+ * | / \ |
+ * +----------------------D
+ */
+ p[0] = b.x0; p[1] = b.y0; /* A */
+ p[2] = first ? b.x0 + left : b.x0; p[3] = b.y0 + top; /* B */
+ p[4] = last ? b.x1 - right : b.x1; p[5] = b.y1 - bottom; /* C */
+ p[6] = b.x1; p[7] = b.y1; /* D */
+
+ assert(box->style);
+
+ /* Left */
+ square_end_1 = (top == 0);
+ square_end_2 = (bottom == 0);
+ if (left != 0 &&
+ first &&
+ nscss_color_is_transparent(box->border[LEFT].c) == false) {
+ col = nscss_color_to_ns(box->border[LEFT].c);
+
+ z[0] = p[0]; z[1] = p[7];
+ z[2] = p[2]; z[3] = p[5];
+ z[4] = p[2]; z[5] = p[3];
+ z[6] = p[0]; z[7] = p[1];
+
+ if (nscss_color_is_transparent(box->border[TOP].c) == false &&
+ box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang top corner fully,
+ * if top border is opaque
+ */
+ z[5] -= top;
+ square_end_1 = true;
+ }
+
+ if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
+ box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang bottom corner fully,
+ * if bottom border is opaque
+ */
+ z[3] += bottom;
+ square_end_2 = true;
+ }
+
+ res = html_redraw_border_plot(LEFT,
+ z,
+ col,
+ box->border[LEFT].style,
+ left,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+
+ /* Right */
+ square_end_1 = (top == 0);
+ square_end_2 = (bottom == 0);
+ if (right != 0 &&
+ last &&
+ nscss_color_is_transparent(box->border[RIGHT].c) == false) {
+ col = nscss_color_to_ns(box->border[RIGHT].c);
+
+ z[0] = p[6]; z[1] = p[1];
+ z[2] = p[4]; z[3] = p[3];
+ z[4] = p[4]; z[5] = p[5];
+ z[6] = p[6]; z[7] = p[7];
+
+ if (nscss_color_is_transparent(box->border[TOP].c) == false &&
+ box->border[TOP].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang top corner fully,
+ * if top border is opaque
+ */
+ z[3] -= top;
+ square_end_1 = true;
+ }
+
+ if (nscss_color_is_transparent(box->border[BOTTOM].c) == false &&
+ box->border[BOTTOM].style != CSS_BORDER_STYLE_DOUBLE) {
+ /* make border overhang bottom corner fully,
+ * if bottom border is opaque
+ */
+ z[5] += bottom;
+ square_end_2 = true;
+ }
+
+ res = html_redraw_border_plot(RIGHT,
+ z,
+ col,
+ box->border[RIGHT].style,
+ right,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+
+ /* Top */
+ square_end_1 = (left == 0);
+ square_end_2 = (right == 0);
+ if (top != 0 &&
+ nscss_color_is_transparent(box->border[TOP].c) == false) {
+ col = nscss_color_to_ns(box->border[TOP].c);
+
+ z[0] = p[2]; z[1] = p[3];
+ z[2] = p[0]; z[3] = p[1];
+ z[4] = p[6]; z[5] = p[1];
+ z[6] = p[4]; z[7] = p[3];
+
+ if (first &&
+ box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[TOP].c == box->border[LEFT].c) {
+ /* don't bother overlapping left corner if
+ * it's the same colour anyway
+ */
+ z[2] += left;
+ square_end_1 = true;
+ }
+
+ if (last &&
+ box->border[TOP].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[TOP].c == box->border[RIGHT].c) {
+ /* don't bother overlapping right corner if
+ * it's the same colour anyway
+ */
+ z[4] -= right;
+ square_end_2 = true;
+ }
+
+ res = html_redraw_border_plot(TOP,
+ z,
+ col,
+ box->border[TOP].style,
+ top,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+
+ /* Bottom */
+ square_end_1 = (left == 0);
+ square_end_2 = (right == 0);
+ if (bottom != 0 &&
+ nscss_color_is_transparent(box->border[BOTTOM].c) == false) {
+ col = nscss_color_to_ns(box->border[BOTTOM].c);
+
+ z[0] = p[4]; z[1] = p[5];
+ z[2] = p[6]; z[3] = p[7];
+ z[4] = p[0]; z[5] = p[7];
+ z[6] = p[2]; z[7] = p[5];
+
+ if (first &&
+ box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[BOTTOM].c == box->border[LEFT].c) {
+ /* don't bother overlapping left corner if
+ * it's the same colour anyway
+ */
+ z[4] += left;
+ square_end_1 = true;
+ }
+
+ if (last &&
+ box->border[BOTTOM].style == CSS_BORDER_STYLE_SOLID &&
+ box->border[BOTTOM].c == box->border[RIGHT].c) {
+ /* don't bother overlapping right corner if
+ * it's the same colour anyway
+ */
+ z[2] -= right;
+ square_end_2 = true;
+ }
+
+ res = html_redraw_border_plot(BOTTOM,
+ z,
+ col,
+ box->border[BOTTOM].style,
+ bottom,
+ square_end_1 && square_end_2,
+ clip,
+ ctx);
+ if (res != NSERROR_OK) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/content/handlers/html/script.c b/content/handlers/html/script.c
new file mode 100644
index 0000000..81bdccd
--- /dev/null
+++ b/content/handlers/html/script.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince(a)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
+ * implementation of content handling for text/html scripts.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include "utils/config.h"
+#include "utils/corestrings.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "netsurf/content.h"
+#include "javascript/js.h"
+#include "content/content_protected.h"
+#include "content/fetch.h"
+#include "content/hlcache.h"
+
+#include "html/html.h"
+#include "html/html_internal.h"
+
+typedef bool (script_handler_t)(struct jsthread *jsthread, const uint8_t *data, size_t
size, const char *name);
+
+
+static script_handler_t *select_script_handler(content_type ctype)
+{
+ if (ctype == CONTENT_JS) {
+ return js_exec;
+ }
+ return NULL;
+}
+
+
+/* exported internal interface documented in html/html_internal.h */
+nserror html_script_exec(html_content *c, bool allow_defer)
+{
+ unsigned int i;
+ struct html_script *s;
+ script_handler_t *script_handler;
+ bool have_run_something = false;
+
+ if (c->jsthread == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ for (i = 0, s = c->scripts; i != c->scripts_count; i++, s++) {
+ if (s->already_started) {
+ continue;
+ }
+
+ if ((s->type == HTML_SCRIPT_ASYNC) ||
+ (allow_defer && (s->type == HTML_SCRIPT_DEFER))) {
+ /* ensure script content is present */
+ if (s->data.handle == NULL)
+ continue;
+
+ /* ensure script content fetch status is not an error */
+ if (content_get_status(s->data.handle) ==
+ CONTENT_STATUS_ERROR)
+ continue;
+
+ /* ensure script handler for content type */
+ script_handler = select_script_handler(
+ content_get_type(s->data.handle));
+ if (script_handler == NULL)
+ continue; /* unsupported type */
+
+ if (content_get_status(s->data.handle) ==
+ CONTENT_STATUS_DONE) {
+ /* external script is now available */
+ const uint8_t *data;
+ size_t size;
+ data = content_get_source_data(
+ s->data.handle, &size );
+ script_handler(c->jsthread, data, size,
+ nsurl_access(hlcache_handle_get_url(s->data.handle)));
+ have_run_something = true;
+ /* We have to re-acquire this here since the
+ * c->scripts array may have been reallocated
+ * as a result of executing this script.
+ */
+ s = &(c->scripts[i]);
+
+ s->already_started = true;
+
+ }
+ }
+ }
+
+ if (have_run_something) {
+ return html_proceed_to_done(c);
+ }
+
+ return NSERROR_OK;
+}
+
+/* create new html script entry */
+static struct html_script *
+html_process_new_script(html_content *c,
+ dom_string *mimetype,
+ enum html_script_type type)
+{
+ struct html_script *nscript;
+ /* add space for new script entry */
+ nscript = realloc(c->scripts,
+ sizeof(struct html_script) * (c->scripts_count + 1));
+ if (nscript == NULL) {
+ return NULL;
+ }
+
+ c->scripts = nscript;
+
+ /* increment script entry count */
+ nscript = &c->scripts[c->scripts_count];
+ c->scripts_count++;
+
+ nscript->already_started = false;
+ nscript->parser_inserted = false;
+ nscript->force_async = true;
+ nscript->ready_exec = false;
+ nscript->async = false;
+ nscript->defer = false;
+
+ nscript->type = type;
+
+ nscript->mimetype = dom_string_ref(mimetype); /* reference mimetype */
+
+ return nscript;
+}
+
+/**
+ * Callback for asyncronous scripts
+ */
+static nserror
+convert_script_async_cb(hlcache_handle *script,
+ const hlcache_event *event,
+ void *pw)
+{
+ html_content *parent = pw;
+ unsigned int i;
+ struct html_script *s;
+
+ /* Find script */
+ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
+ if (s->type == HTML_SCRIPT_ASYNC && s->data.handle == script)
+ break;
+ }
+
+ assert(i != parent->scripts_count);
+
+ switch (event->type) {
+ case CONTENT_MSG_LOADING:
+ break;
+
+ case CONTENT_MSG_READY:
+ break;
+
+ case CONTENT_MSG_DONE:
+ NSLOG(netsurf, INFO, "script %d done '%s'", i,
+ nsurl_access(hlcache_handle_get_url(script)));
+ parent->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+
+ break;
+
+ case CONTENT_MSG_ERROR:
+ NSLOG(netsurf, INFO, "script %s failed: %s",
+ nsurl_access(hlcache_handle_get_url(script)),
+ event->data.errordata.errormsg);
+
+ hlcache_handle_release(script);
+ s->data.handle = NULL;
+ parent->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+ content_add_error(&parent->base, "?", 0);
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* if there are no active fetches remaining begin post parse
+ * conversion
+ */
+ if (html_can_begin_conversion(parent)) {
+ html_begin_conversion(parent);
+ }
+
+ /* if we have already started converting though, then we can handle the
+ * scripts as they come in.
+ */
+ else if (parent->conversion_begun) {
+ return html_script_exec(parent, false);
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Callback for defer scripts
+ */
+static nserror
+convert_script_defer_cb(hlcache_handle *script,
+ const hlcache_event *event,
+ void *pw)
+{
+ html_content *parent = pw;
+ unsigned int i;
+ struct html_script *s;
+
+ /* Find script */
+ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
+ if (s->type == HTML_SCRIPT_DEFER && s->data.handle == script)
+ break;
+ }
+
+ assert(i != parent->scripts_count);
+
+ switch (event->type) {
+
+ case CONTENT_MSG_DONE:
+ NSLOG(netsurf, INFO, "script %d done '%s'", i,
+ nsurl_access(hlcache_handle_get_url(script)));
+ parent->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+
+ break;
+
+ case CONTENT_MSG_ERROR:
+ NSLOG(netsurf, INFO, "script %s failed: %s",
+ nsurl_access(hlcache_handle_get_url(script)),
+ event->data.errordata.errormsg);
+
+ hlcache_handle_release(script);
+ s->data.handle = NULL;
+ parent->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+ content_add_error(&parent->base, "?", 0);
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* if there are no active fetches remaining begin post parse
+ * conversion
+ */
+ if (html_can_begin_conversion(parent)) {
+ html_begin_conversion(parent);
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * Callback for syncronous scripts
+ */
+static nserror
+convert_script_sync_cb(hlcache_handle *script,
+ const hlcache_event *event,
+ void *pw)
+{
+ html_content *parent = pw;
+ unsigned int i;
+ struct html_script *s;
+ script_handler_t *script_handler;
+ dom_hubbub_error err;
+ unsigned int active_sync_scripts = 0;
+
+ /* Count sync scripts which have yet to complete (other than us) */
+ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
+ if (s->type == HTML_SCRIPT_SYNC &&
+ s->data.handle != script && s->already_started == false) {
+ active_sync_scripts++;
+ }
+ }
+
+ /* Find script */
+ for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) {
+ if (s->type == HTML_SCRIPT_SYNC && s->data.handle == script)
+ break;
+ }
+
+ assert(i != parent->scripts_count);
+
+ switch (event->type) {
+ case CONTENT_MSG_DONE:
+ NSLOG(netsurf, INFO, "script %d done '%s'", i,
+ nsurl_access(hlcache_handle_get_url(script)));
+ parent->base.active--;
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+
+ s->already_started = true;
+
+ /* attempt to execute script */
+ script_handler = select_script_handler(content_get_type(s->data.handle));
+ if (script_handler != NULL && parent->jsthread != NULL) {
+ /* script has a handler */
+ const uint8_t *data;
+ size_t size;
+ data = content_get_source_data(s->data.handle, &size );
+ script_handler(parent->jsthread, data, size,
+ nsurl_access(hlcache_handle_get_url(s->data.handle)));
+ }
+
+ /* continue parse */
+ if (parent->parser != NULL && active_sync_scripts == 0) {
+ err = dom_hubbub_parser_pause(parent->parser, false);
+ if (err != DOM_HUBBUB_OK) {
+ NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
+ }
+ }
+
+ break;
+
+ case CONTENT_MSG_ERROR:
+ NSLOG(netsurf, INFO, "script %s failed: %s",
+ nsurl_access(hlcache_handle_get_url(script)),
+ event->data.errordata.errormsg);
+
+ hlcache_handle_release(script);
+ s->data.handle = NULL;
+ parent->base.active--;
+
+ NSLOG(netsurf, INFO, "%d fetches active", parent->base.active);
+ content_add_error(&parent->base, "?", 0);
+
+ s->already_started = true;
+
+ /* continue parse */
+ if (parent->parser != NULL && active_sync_scripts == 0) {
+ err = dom_hubbub_parser_pause(parent->parser, false);
+ if (err != DOM_HUBBUB_OK) {
+ NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* if there are no active fetches remaining begin post parse
+ * conversion
+ */
+ if (html_can_begin_conversion(parent)) {
+ html_begin_conversion(parent);
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * process a script with a src tag
+ */
+static dom_hubbub_error
+exec_src_script(html_content *c,
+ dom_node *node,
+ dom_string *mimetype,
+ dom_string *src)
+{
+ nserror ns_error;
+ nsurl *joined;
+ hlcache_child_context child;
+ struct html_script *nscript;
+ bool async;
+ bool defer;
+ enum html_script_type script_type;
+ hlcache_handle_callback script_cb;
+ dom_hubbub_error ret = DOM_HUBBUB_OK;
+ dom_exception exc; /* returned by libdom functions */
+
+ /* src url */
+ ns_error = nsurl_join(c->base_url, dom_string_data(src), &joined);
+ if (ns_error != NSERROR_OK) {
+ content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
+ return DOM_HUBBUB_NOMEM;
+ }
+
+ NSLOG(netsurf, INFO, "script %i '%s'", c->scripts_count,
+ nsurl_access(joined));
+
+ /* there are three ways to process the script tag at this point:
+ *
+ * Syncronously pause the parent parse and continue after
+ * the script has downloaded and executed. (default)
+ * Async Start the script downloading and execute it when it
+ * becomes available.
+ * Defered Start the script downloading and execute it when
+ * the page has completed parsing, may be set along
+ * with async where it is ignored.
+ */
+
+ /* we interpret the presence of the async and defer attribute
+ * as true and ignore its value, technically only the empty
+ * value or the attribute name itself are valid. However
+ * various browsers interpret this in various ways the most
+ * compatible approach is to be liberal and accept any
+ * value. Note setting the values to "false" still makes them true!
+ */
+ exc = dom_element_has_attribute(node, corestring_dom_async, &async);
+ if (exc != DOM_NO_ERR) {
+ return DOM_HUBBUB_OK; /* dom error */
+ }
+
+ if (c->parse_completed) {
+ /* After parse completed, all scripts are essentially async */
+ async = true;
+ defer = false;
+ }
+
+ if (async) {
+ /* asyncronous script */
+ script_type = HTML_SCRIPT_ASYNC;
+ script_cb = convert_script_async_cb;
+
+ } else {
+ exc = dom_element_has_attribute(node,
+ corestring_dom_defer, &defer);
+ if (exc != DOM_NO_ERR) {
+ return DOM_HUBBUB_OK; /* dom error */
+ }
+
+ if (defer) {
+ /* defered script */
+ script_type = HTML_SCRIPT_DEFER;
+ script_cb = convert_script_defer_cb;
+ } else {
+ /* syncronous script */
+ script_type = HTML_SCRIPT_SYNC;
+ script_cb = convert_script_sync_cb;
+ }
+ }
+
+ nscript = html_process_new_script(c, mimetype, script_type);
+ if (nscript == NULL) {
+ nsurl_unref(joined);
+ content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
+ return DOM_HUBBUB_NOMEM;
+ }
+
+ /* set up child fetch encoding and quirks */
+ child.charset = c->encoding;
+ child.quirks = c->base.quirks;
+
+ ns_error = hlcache_handle_retrieve(joined,
+ 0,
+ content_get_url(&c->base),
+ NULL,
+ script_cb,
+ c,
+ &child,
+ CONTENT_SCRIPT,
+ &nscript->data.handle);
+
+
+ nsurl_unref(joined);
+
+ if (ns_error != NSERROR_OK) {
+ /* @todo Deal with fetch error better. currently assume
+ * fetch never became active
+ */
+ /* mark duff script fetch as already started */
+ nscript->already_started = true;
+ NSLOG(netsurf, INFO, "Fetch failed with error %d", ns_error);
+ } else {
+ /* update base content active fetch count */
+ c->base.active++;
+ NSLOG(netsurf, INFO, "%d fetches active", c->base.active);
+
+ switch (script_type) {
+ case HTML_SCRIPT_SYNC:
+ ret = DOM_HUBBUB_HUBBUB_ERR | HUBBUB_PAUSED;
+
+ case HTML_SCRIPT_ASYNC:
+ break;
+
+ case HTML_SCRIPT_DEFER:
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+
+ return ret;
+}
+
+static dom_hubbub_error
+exec_inline_script(html_content *c, dom_node *node, dom_string *mimetype)
+{
+ dom_string *script;
+ dom_exception exc; /* returned by libdom functions */
+ struct lwc_string_s *lwcmimetype;
+ script_handler_t *script_handler;
+ struct html_script *nscript;
+
+ /* does not appear to be a src so script is inline content */
+ exc = dom_node_get_text_content(node, &script);
+ if ((exc != DOM_NO_ERR) || (script == NULL)) {
+ return DOM_HUBBUB_OK; /* no contents, skip */
+ }
+
+ nscript = html_process_new_script(c, mimetype, HTML_SCRIPT_INLINE);
+ if (nscript == NULL) {
+ dom_string_unref(script);
+
+ content_broadcast_error(&c->base, NSERROR_NOMEM, NULL);
+ return DOM_HUBBUB_NOMEM;
+
+ }
+
+ nscript->data.string = script;
+ nscript->already_started = true;
+
+ /* ensure script handler for content type */
+ exc = dom_string_intern(mimetype, &lwcmimetype);
+ if (exc != DOM_NO_ERR) {
+ return DOM_HUBBUB_DOM;
+ }
+
+ script_handler =
select_script_handler(content_factory_type_from_mime_type(lwcmimetype));
+ lwc_string_unref(lwcmimetype);
+
+ if (script_handler != NULL) {
+ script_handler(c->jsthread,
+ (const uint8_t *)dom_string_data(script),
+ dom_string_byte_length(script),
+ "?inline script?");
+ }
+ return DOM_HUBBUB_OK;
+}
+
+
+/**
+ * process script node parser callback
+ *
+ *
+ */
+dom_hubbub_error
+html_process_script(void *ctx, dom_node *node)
+{
+ html_content *c = (html_content *)ctx;
+ dom_exception exc; /* returned by libdom functions */
+ dom_string *src, *mimetype;
+ dom_hubbub_error err = DOM_HUBBUB_OK;
+
+ /* ensure javascript context is available */
+ /* We should only ever be here if scripting was enabled for this
+ * content so it's correct to make a javascript context if there
+ * isn't one already. */
+ if (c->jsthread == NULL) {
+ union content_msg_data msg_data;
+
+ msg_data.jsthread = &c->jsthread;
+ content_broadcast(&c->base, CONTENT_MSG_GETTHREAD, &msg_data);
+ NSLOG(netsurf, INFO, "javascript context %p ", c->jsthread);
+ if (c->jsthread == NULL) {
+ /* no context and it could not be created, abort */
+ return DOM_HUBBUB_OK;
+ }
+ }
+
+ NSLOG(netsurf, INFO, "content %p parser %p node %p", c, c->parser,
+ node);
+
+ exc = dom_element_get_attribute(node, corestring_dom_type, &mimetype);
+ if (exc != DOM_NO_ERR || mimetype == NULL) {
+ mimetype = dom_string_ref(corestring_dom_text_javascript);
+ }
+
+ exc = dom_element_get_attribute(node, corestring_dom_src, &src);
+ if (exc != DOM_NO_ERR || src == NULL) {
+ err = exec_inline_script(c, node, mimetype);
+ } else {
+ err = exec_src_script(c, node, mimetype, src);
+ dom_string_unref(src);
+ }
+
+ dom_string_unref(mimetype);
+
+ return err;
+}
+
+/* exported internal interface documented in html/html_internal.h */
+bool html_saw_insecure_scripts(html_content *htmlc)
+{
+ struct html_script *s;
+ unsigned int i;
+
+ for (i = 0, s = htmlc->scripts; i != htmlc->scripts_count; i++, s++) {
+ if (s->type == HTML_SCRIPT_INLINE) {
+ /* Inline scripts are no less secure than their
+ * containing HTML content
+ */
+ continue;
+ }
+ if (s->data.handle == NULL) {
+ /* We've not begun loading this? */
+ continue;
+ }
+ if (content_saw_insecure_objects(s->data.handle)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* exported internal interface documented in html/html_internal.h */
+nserror html_script_free(html_content *html)
+{
+ unsigned int i;
+
+ for (i = 0; i != html->scripts_count; i++) {
+ if (html->scripts[i].mimetype != NULL) {
+ dom_string_unref(html->scripts[i].mimetype);
+ }
+
+ switch (html->scripts[i].type) {
+ case HTML_SCRIPT_INLINE:
+ if (html->scripts[i].data.string != NULL) {
+ dom_string_unref(html->scripts[i].data.string);
+ }
+ break;
+ case HTML_SCRIPT_SYNC:
+ /* fallthrough */
+ case HTML_SCRIPT_ASYNC:
+ /* fallthrough */
+ case HTML_SCRIPT_DEFER:
+ if (html->scripts[i].data.handle != NULL) {
+ hlcache_handle_release(html->scripts[i].data.handle);
+ }
+ break;
+ }
+ }
+ free(html->scripts);
+
+ return NSERROR_OK;
+}
-----------------------------------------------------------------------
Summary of changes:
content/handlers/html/Makefile | 20 ++++++++---------
content/handlers/html/box_construct.c | 17 +++++++++------
content/handlers/html/box_special.c | 23 ++++++++++----------
content/handlers/html/{html_css.c => css.c} | 0
.../html/{html_css_fetcher.c => css_fetcher.c} | 0
content/handlers/html/{html_forms.c => forms.c} | 0
content/handlers/html/html.c | 2 +-
content/handlers/html/html_internal.h | 7 +-----
content/handlers/html/{html_object.c => object.c} | 22 ++++++++++++-------
content/handlers/html/{html_redraw.c => redraw.c} | 0
.../html/{html_redraw_border.c => redraw_border.c} | 0
content/handlers/html/{html_script.c => script.c} | 0
12 files changed, 47 insertions(+), 44 deletions(-)
rename content/handlers/html/{html_css.c => css.c} (100%)
rename content/handlers/html/{html_css_fetcher.c => css_fetcher.c} (100%)
rename content/handlers/html/{html_forms.c => forms.c} (100%)
rename content/handlers/html/{html_object.c => object.c} (98%)
rename content/handlers/html/{html_redraw.c => redraw.c} (100%)
rename content/handlers/html/{html_redraw_border.c => redraw_border.c} (100%)
rename content/handlers/html/{html_script.c => script.c} (100%)
diff --git a/content/handlers/html/Makefile b/content/handlers/html/Makefile
index 1976624..bdc0513 100644
--- a/content/handlers/html/Makefile
+++ b/content/handlers/html/Makefile
@@ -6,18 +6,18 @@ S_HTML := box_construct.c \
box_normalise.c \
box_special.c \
box_textarea.c \
+ css.c \
+ css_fetcher.c \
font.c \
form.c \
+ forms.c \
+ html.c \
imagemap.c \
+ interaction.c \
layout.c \
+ object.c \
+ redraw.c \
+ redraw_border.c \
+ script.c \
search.c \
- table.c \
- html.c \
- html_css.c \
- html_css_fetcher.c \
- html_script.c \
- interaction.c \
- html_redraw.c \
- html_redraw_border.c \
- html_forms.c \
- html_object.c
+ table.c
diff --git a/content/handlers/html/box_construct.c
b/content/handlers/html/box_construct.c
index 1637c0f..fba9764 100644
--- a/content/handlers/html/box_construct.c
+++ b/content/handlers/html/box_construct.c
@@ -470,9 +470,11 @@ box_construct_marker(struct box *box,
if (error != NSERROR_OK)
return false;
- if (html_fetch_object(ctx->content, url, marker, image_types,
- ctx->content->base.available_width, 1000, false) ==
- false) {
+ if (html_fetch_object(ctx->content,
+ url,
+ marker,
+ image_types,
+ false) == false) {
nsurl_unref(url);
return false;
}
@@ -705,10 +707,11 @@ box_construct_element(struct box_construct_ctx *ctx, bool
*convert_children)
error = nsurl_create(lwc_string_data(bgimage_uri), &url);
if (error == NSERROR_OK) {
/* Fetch image if we got a valid URL */
- if (html_fetch_object(ctx->content, url, box,
- image_types,
- ctx->content->base.available_width,
- 1000, true) == false) {
+ if (html_fetch_object(ctx->content,
+ url,
+ box,
+ image_types,
+ true) == false) {
nsurl_unref(url);
return false;
}
diff --git a/content/handlers/html/box_special.c b/content/handlers/html/box_special.c
index 38031ce..9f4a512 100644
--- a/content/handlers/html/box_special.c
+++ b/content/handlers/html/box_special.c
@@ -956,8 +956,7 @@ box_embed(dom_node *n,
/* start fetch */
box->flags |= IS_REPLACED;
- return html_fetch_object(content, params->data, box, CONTENT_ANY,
- content->base.available_width, 1000, false);
+ return html_fetch_object(content, params->data, box, CONTENT_ANY, false);
}
@@ -1189,8 +1188,7 @@ box_image(dom_node *n,
/* start fetch */
box->flags |= IS_REPLACED;
- ok = html_fetch_object(content, url, box, image_types,
- content->base.available_width, 1000, false);
+ ok = html_fetch_object(content, url, box, image_types, false);
nsurl_unref(url);
wtype = css_computed_width(box->style, &value, &wunit);
@@ -1332,11 +1330,11 @@ box_input(dom_node *n,
*/
if (nsurl_compare(url, content->base_url,
NSURL_COMPLETE) == false) {
- if (!html_fetch_object(content, url,
- box, image_types,
- content->base.
- available_width,
- 1000, false)) {
+ if (!html_fetch_object(content,
+ url,
+ box,
+ image_types,
+ false)) {
nsurl_unref(url);
goto no_memory;
}
@@ -1613,9 +1611,10 @@ box_object(dom_node *n,
/* start fetch (MIME type is ok or not specified) */
box->flags |= IS_REPLACED;
if (!html_fetch_object(content,
- params->data ? params->data : params->classid,
- box, CONTENT_ANY, content->base.available_width, 1000,
- false))
+ params->data ? params->data : params->classid,
+ box,
+ CONTENT_ANY,
+ false))
return false;
*convert_children = false;
diff --git a/content/handlers/html/html_css.c b/content/handlers/html/css.c
similarity index 100%
rename from content/handlers/html/html_css.c
rename to content/handlers/html/css.c
diff --git a/content/handlers/html/html_css_fetcher.c
b/content/handlers/html/css_fetcher.c
similarity index 100%
rename from content/handlers/html/html_css_fetcher.c
rename to content/handlers/html/css_fetcher.c
diff --git a/content/handlers/html/html_forms.c b/content/handlers/html/forms.c
similarity index 100%
rename from content/handlers/html/html_forms.c
rename to content/handlers/html/forms.c
diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c
index 01244cc..7230526 100644
--- a/content/handlers/html/html.c
+++ b/content/handlers/html/html.c
@@ -687,7 +687,7 @@ static bool html_process_img(html_content *c, dom_node *node)
dom_string_unref(src);
/* Speculatively fetch the image */
- success = html_fetch_object(c, url, NULL, CONTENT_IMAGE, 0, 0, false);
+ success = html_fetch_object(c, url, NULL, CONTENT_IMAGE, false);
nsurl_unref(url);
return success;
diff --git a/content/handlers/html/html_internal.h
b/content/handlers/html/html_internal.h
index d981017..6b55334 100644
--- a/content/handlers/html/html_internal.h
+++ b/content/handlers/html/html_internal.h
@@ -376,15 +376,10 @@ nserror html_css_fetcher_add_item(dom_string *data, nsurl
*base_url,
* \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);
+bool html_fetch_object(html_content *c, nsurl *url, struct box *box, content_type
permitted_types, bool background);
nserror html_object_free_objects(html_content *html);
nserror html_object_close_objects(html_content *html);
diff --git a/content/handlers/html/html_object.c b/content/handlers/html/object.c
similarity index 98%
rename from content/handlers/html/html_object.c
rename to content/handlers/html/object.c
index 587c487..c20118d 100644
--- a/content/handlers/html/html_object.c
+++ b/content/handlers/html/object.c
@@ -677,10 +677,12 @@ nserror html_object_free_objects(html_content *html)
/* exported interface documented in html/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)
+bool
+html_fetch_object(html_content *c,
+ nsurl *url,
+ struct box *box,
+ content_type permitted_types,
+ bool background)
{
struct content_html_object *object;
hlcache_child_context child;
@@ -706,10 +708,14 @@ bool html_fetch_object(html_content *c, nsurl *url, struct box
*box,
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);
+ 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;
diff --git a/content/handlers/html/html_redraw.c b/content/handlers/html/redraw.c
similarity index 100%
rename from content/handlers/html/html_redraw.c
rename to content/handlers/html/redraw.c
diff --git a/content/handlers/html/html_redraw_border.c
b/content/handlers/html/redraw_border.c
similarity index 100%
rename from content/handlers/html/html_redraw_border.c
rename to content/handlers/html/redraw_border.c
diff --git a/content/handlers/html/html_script.c b/content/handlers/html/script.c
similarity index 100%
rename from content/handlers/html/html_script.c
rename to content/handlers/html/script.c
--
NetSurf Browser