netsurf/content/handlers/html/html.c

2386 lines
57 KiB
C
Raw Normal View History

/*
* Copyright 2007 James Bursa <bursa@users.sourceforge.net>
* Copyright 2010 Michael Drake <tlsa@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 HTML content handling.
*/
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <nsutils/time.h>
#include "utils/utils.h"
#include "utils/config.h"
#include "utils/corestrings.h"
#include "utils/http.h"
#include "utils/libdom.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/talloc.h"
#include "utils/utf8.h"
#include "utils/nsoption.h"
#include "utils/string.h"
#include "utils/ascii.h"
#include "netsurf/content.h"
#include "netsurf/browser_window.h"
#include "netsurf/utf8.h"
#include "netsurf/keypress.h"
#include "netsurf/layout.h"
#include "netsurf/misc.h"
#include "content/hlcache.h"
2020-05-07 22:55:44 +03:00
#include "content/content_factory.h"
#include "content/textsearch.h"
#include "desktop/selection.h"
#include "desktop/scrollbar.h"
2013-02-08 19:26:24 +04:00
#include "desktop/textarea.h"
#include "netsurf/bitmap.h"
#include "javascript/js.h"
#include "desktop/gui_internal.h"
#include "html/html.h"
#include "html/private.h"
#include "html/dom_event.h"
#include "html/css.h"
2020-05-01 23:36:50 +03:00
#include "html/object.h"
#include "html/html_save.h"
#include "html/interaction.h"
#include "html/box.h"
#include "html/box_construct.h"
#include "html/box_inspect.h"
#include "html/form_internal.h"
#include "html/imagemap.h"
#include "html/layout.h"
#define CHUNK 4096
/* Change these to 1 to cause a dump to stderr of the frameset or box
* when the trees have been built.
*/
#define ALWAYS_DUMP_FRAMESET 0
#define ALWAYS_DUMP_BOX 0
static const char *html_types[] = {
"application/xhtml+xml",
"text/html"
};
/**
* Fire an event at the DOM
*
* Helper that swallows DOM errors.
*
* \param[in] event the event to fire at the DOM
* \param[in] target the event target
* \return true on success
*/
static bool fire_dom_event(dom_event *event, dom_node *target)
{
dom_exception exc;
bool result;
exc = dom_event_target_dispatch_event(target, event, &result);
if (exc != DOM_NO_ERR) {
return false;
}
return result;
}
/* Exported interface, see html_internal.h */
bool fire_generic_dom_event(dom_string *type, dom_node *target,
bool bubbles, bool cancelable)
{
dom_exception exc;
dom_event *evt;
bool result;
exc = dom_event_create(&evt);
if (exc != DOM_NO_ERR) return false;
exc = dom_event_init(evt, type, bubbles, cancelable);
if (exc != DOM_NO_ERR) {
dom_event_unref(evt);
return false;
}
NSLOG(netsurf, INFO, "Dispatching '%*s' against %p",
dom_string_length(type), dom_string_data(type), target);
result = fire_dom_event(evt, target);
dom_event_unref(evt);
return result;
}
/* Exported interface, see html_internal.h */
bool fire_dom_keyboard_event(dom_string *type, dom_node *target,
bool bubbles, bool cancelable, uint32_t key)
{
bool is_special = key <= 0x001F || (0x007F <= key && key <= 0x009F);
dom_string *dom_key = NULL;
dom_keyboard_event *evt;
dom_exception exc;
bool result;
if (is_special) {
switch (key) {
case NS_KEY_ESCAPE:
dom_key = dom_string_ref(corestring_dom_Escape);
break;
case NS_KEY_LEFT:
dom_key = dom_string_ref(corestring_dom_ArrowLeft);
break;
case NS_KEY_RIGHT:
dom_key = dom_string_ref(corestring_dom_ArrowRight);
break;
case NS_KEY_UP:
dom_key = dom_string_ref(corestring_dom_ArrowUp);
break;
case NS_KEY_DOWN:
dom_key = dom_string_ref(corestring_dom_ArrowDown);
break;
case NS_KEY_PAGE_UP:
dom_key = dom_string_ref(corestring_dom_PageUp);
break;
case NS_KEY_PAGE_DOWN:
dom_key = dom_string_ref(corestring_dom_PageDown);
break;
case NS_KEY_TEXT_START:
dom_key = dom_string_ref(corestring_dom_Home);
break;
case NS_KEY_TEXT_END:
dom_key = dom_string_ref(corestring_dom_End);
break;
default:
dom_key = NULL;
break;
}
} else {
char utf8[6];
size_t length = utf8_from_ucs4(key, utf8);
utf8[length] = '\0';
exc = dom_string_create((const uint8_t *)utf8, strlen(utf8),
&dom_key);
if (exc != DOM_NO_ERR) {
return exc;
}
}
exc = dom_keyboard_event_create(&evt);
if (exc != DOM_NO_ERR) {
dom_string_unref(dom_key);
return false;
}
exc = dom_keyboard_event_init(evt, type, bubbles, cancelable, NULL,
dom_key, NULL, DOM_KEY_LOCATION_STANDARD, false,
false, false, false, false, false);
dom_string_unref(dom_key);
if (exc != DOM_NO_ERR) {
dom_event_unref(evt);
return false;
}
NSLOG(netsurf, INFO, "Dispatching '%*s' against %p",
dom_string_length(type), dom_string_data(type), target);
result = fire_dom_event((dom_event *) evt, target);
dom_event_unref(evt);
return result;
}
/**
2012-07-06 23:00:32 +04:00
* Perform post-box-creation conversion of a document
*
* \param c HTML content to complete conversion of
* \param success Whether box tree construction was successful
*/
2012-07-06 23:00:32 +04:00
static void html_box_convert_done(html_content *c, bool success)
{
2012-11-26 21:23:03 +04:00
nserror err;
2012-07-06 23:00:32 +04:00
dom_exception exc; /* returned by libdom functions */
dom_node *html;
NSLOG(netsurf, INFO, "DOM to box conversion complete (content %p)", c);
c->box_conversion_context = NULL;
2012-07-06 23:00:32 +04:00
/* Clean up and report error if unsuccessful or aborted */
2012-10-18 00:52:43 +04:00
if ((success == false) || (c->aborted)) {
html_object_free_objects(c);
2012-11-26 21:23:03 +04:00
2012-10-18 00:52:43 +04:00
if (success == false) {
content_broadcast_error(&c->base, NSERROR_BOX_CONVERT, NULL);
2012-10-18 00:52:43 +04:00
} else {
content_broadcast_error(&c->base, NSERROR_STOPPED, NULL);
2012-10-18 00:52:43 +04:00
}
2012-11-26 21:23:03 +04:00
2012-07-06 23:00:32 +04:00
content_set_error(&c->base);
return;
Merged revisions 4859-5013,5016-5018 via svnmerge from svn://source.netsurf-browser.org/branches/takkaria/netsurf-hubbub ........ r4860 | takkaria | 2008-08-02 03:51:35 +0100 (Sat, 02 Aug 2008) | 2 lines A really horribly rough first go at integrating hubbub with NetSurf. Segfaults, but I'm not sure what I've done wrong. ........ r4861 | jmb | 2008-08-02 05:01:19 +0100 (Sat, 02 Aug 2008) | 3 lines Fix segfault caused by mismatched struct layout expectations. Fix warnings, too. ........ r4862 | jmb | 2008-08-02 05:11:02 +0100 (Sat, 02 Aug 2008) | 2 lines Destroy parser as soon as it's no longer needed, and flag this so that html_destroy doesn't cause things to trample all over the heap. ........ r4863 | takkaria | 2008-08-02 15:30:34 +0100 (Sat, 02 Aug 2008) | 2 lines Remove deugging printf()s, add Aliases file, add script { display: none; } block. ........ r4868 | takkaria | 2008-08-02 22:14:55 +0100 (Sat, 02 Aug 2008) | 2 lines Fix a segfault bug. ........ r4869 | takkaria | 2008-08-02 22:17:58 +0100 (Sat, 02 Aug 2008) | 2 lines Add gtk/res/Aliases as a symlink to the one in !NS/Resources ........ r4870 | takkaria | 2008-08-02 22:26:31 +0100 (Sat, 02 Aug 2008) | 2 lines Fix symlink. ........ r4885 | jmb | 2008-08-03 23:26:54 +0100 (Sun, 03 Aug 2008) | 2 lines Make multiple parallel parser instances work correctly. ........ r4886 | tlsa | 2008-08-04 00:21:29 +0100 (Mon, 04 Aug 2008) | 1 line Don't display contents of STYLE. ........ r4891 | jmb | 2008-08-04 01:18:07 +0100 (Mon, 04 Aug 2008) | 5 lines Fix reparent_children to actually work Make get_parent_node pay attention to the element_only flag Fixup node referencing when appending a text child Make clone_node clone attributes and namespace information in the non-deep case ........ r4918 | jmb | 2008-08-05 15:27:03 +0100 (Tue, 05 Aug 2008) | 2 lines Fix debug target ........ r4944 | takkaria | 2008-08-07 12:56:50 +0100 (Thu, 07 Aug 2008) | 2 lines Use talloc to allocate space for Hubbub. ........ r4993 | takkaria | 2008-08-10 17:49:47 +0100 (Sun, 10 Aug 2008) | 2 lines Stub out the encoding change callback so NS-hubbub compiles again. ........ r4994 | takkaria | 2008-08-10 18:02:33 +0100 (Sun, 10 Aug 2008) | 2 lines Tell Hubbub the encoding that HTTP gives us, if we have one. ........ r5001 | takkaria | 2008-08-11 02:53:24 +0100 (Mon, 11 Aug 2008) | 2 lines First go at implementing proper <meta charset> support in NetSurf, amongst some refactoring. Probably works, but I have no pages around to test it on. ........ r5002 | takkaria | 2008-08-11 02:56:35 +0100 (Mon, 11 Aug 2008) | 2 lines Fix (I hope) a 64-bit compiler warning. ........ r5012 | takkaria | 2008-08-11 08:40:28 +0100 (Mon, 11 Aug 2008) | 2 lines Fix some nits from jmb. (Remove const from html->encoding, set encoding_source to something about right.) ........ r5013 | takkaria | 2008-08-11 08:48:50 +0100 (Mon, 11 Aug 2008) | 2 lines Properly fix <meta charset> handling, by passing in the right thing as the context. No idea how this worked before. :) (credit: jmb) ........ r5017 | jmb | 2008-08-11 09:13:22 +0100 (Mon, 11 Aug 2008) | 7 lines Pedantic typo fix. Make Hubbub YES when building for RISC OS. Make Hubbub AUTO when building for GTK Ignore the presence of Hubbub on other platforms. Remove the explicit libparserutils pkg-config stuff from the makefile (see r5016) Add some logic that means Hubbub gets enabled correctly when building on RISC OS and when cross-compiling for it. (This is ugly and will go away when pkg-config is available on RO) ........ svn path=/trunk/netsurf/; revision=5019
2008-08-11 12:17:48 +04:00
}
2012-11-26 21:23:03 +04:00
2012-07-06 23:00:32 +04:00
#if ALWAYS_DUMP_BOX
box_dump(stderr, c->layout->children, 0, true);
2012-07-06 23:00:32 +04:00
#endif
#if ALWAYS_DUMP_FRAMESET
if (c->frameset)
html_dump_frameset(c->frameset, 0);
#endif
Merged revisions 4859-5013,5016-5018 via svnmerge from svn://source.netsurf-browser.org/branches/takkaria/netsurf-hubbub ........ r4860 | takkaria | 2008-08-02 03:51:35 +0100 (Sat, 02 Aug 2008) | 2 lines A really horribly rough first go at integrating hubbub with NetSurf. Segfaults, but I'm not sure what I've done wrong. ........ r4861 | jmb | 2008-08-02 05:01:19 +0100 (Sat, 02 Aug 2008) | 3 lines Fix segfault caused by mismatched struct layout expectations. Fix warnings, too. ........ r4862 | jmb | 2008-08-02 05:11:02 +0100 (Sat, 02 Aug 2008) | 2 lines Destroy parser as soon as it's no longer needed, and flag this so that html_destroy doesn't cause things to trample all over the heap. ........ r4863 | takkaria | 2008-08-02 15:30:34 +0100 (Sat, 02 Aug 2008) | 2 lines Remove deugging printf()s, add Aliases file, add script { display: none; } block. ........ r4868 | takkaria | 2008-08-02 22:14:55 +0100 (Sat, 02 Aug 2008) | 2 lines Fix a segfault bug. ........ r4869 | takkaria | 2008-08-02 22:17:58 +0100 (Sat, 02 Aug 2008) | 2 lines Add gtk/res/Aliases as a symlink to the one in !NS/Resources ........ r4870 | takkaria | 2008-08-02 22:26:31 +0100 (Sat, 02 Aug 2008) | 2 lines Fix symlink. ........ r4885 | jmb | 2008-08-03 23:26:54 +0100 (Sun, 03 Aug 2008) | 2 lines Make multiple parallel parser instances work correctly. ........ r4886 | tlsa | 2008-08-04 00:21:29 +0100 (Mon, 04 Aug 2008) | 1 line Don't display contents of STYLE. ........ r4891 | jmb | 2008-08-04 01:18:07 +0100 (Mon, 04 Aug 2008) | 5 lines Fix reparent_children to actually work Make get_parent_node pay attention to the element_only flag Fixup node referencing when appending a text child Make clone_node clone attributes and namespace information in the non-deep case ........ r4918 | jmb | 2008-08-05 15:27:03 +0100 (Tue, 05 Aug 2008) | 2 lines Fix debug target ........ r4944 | takkaria | 2008-08-07 12:56:50 +0100 (Thu, 07 Aug 2008) | 2 lines Use talloc to allocate space for Hubbub. ........ r4993 | takkaria | 2008-08-10 17:49:47 +0100 (Sun, 10 Aug 2008) | 2 lines Stub out the encoding change callback so NS-hubbub compiles again. ........ r4994 | takkaria | 2008-08-10 18:02:33 +0100 (Sun, 10 Aug 2008) | 2 lines Tell Hubbub the encoding that HTTP gives us, if we have one. ........ r5001 | takkaria | 2008-08-11 02:53:24 +0100 (Mon, 11 Aug 2008) | 2 lines First go at implementing proper <meta charset> support in NetSurf, amongst some refactoring. Probably works, but I have no pages around to test it on. ........ r5002 | takkaria | 2008-08-11 02:56:35 +0100 (Mon, 11 Aug 2008) | 2 lines Fix (I hope) a 64-bit compiler warning. ........ r5012 | takkaria | 2008-08-11 08:40:28 +0100 (Mon, 11 Aug 2008) | 2 lines Fix some nits from jmb. (Remove const from html->encoding, set encoding_source to something about right.) ........ r5013 | takkaria | 2008-08-11 08:48:50 +0100 (Mon, 11 Aug 2008) | 2 lines Properly fix <meta charset> handling, by passing in the right thing as the context. No idea how this worked before. :) (credit: jmb) ........ r5017 | jmb | 2008-08-11 09:13:22 +0100 (Mon, 11 Aug 2008) | 7 lines Pedantic typo fix. Make Hubbub YES when building for RISC OS. Make Hubbub AUTO when building for GTK Ignore the presence of Hubbub on other platforms. Remove the explicit libparserutils pkg-config stuff from the makefile (see r5016) Add some logic that means Hubbub gets enabled correctly when building on RISC OS and when cross-compiling for it. (This is ugly and will go away when pkg-config is available on RO) ........ svn path=/trunk/netsurf/; revision=5019
2008-08-11 12:17:48 +04:00
2012-07-06 23:00:32 +04:00
exc = dom_document_get_document_element(c->document, (void *) &html);
if ((exc != DOM_NO_ERR) || (html == NULL)) {
/** @todo should this call html_object_free_objects(c);
* like the other error paths
2012-10-18 00:52:43 +04:00
*/
NSLOG(netsurf, INFO, "error retrieving html element from dom");
content_broadcast_error(&c->base, NSERROR_DOM, NULL);
2012-07-06 23:00:32 +04:00
content_set_error(&c->base);
return;
}
Merged revisions 4859-5013,5016-5018 via svnmerge from svn://source.netsurf-browser.org/branches/takkaria/netsurf-hubbub ........ r4860 | takkaria | 2008-08-02 03:51:35 +0100 (Sat, 02 Aug 2008) | 2 lines A really horribly rough first go at integrating hubbub with NetSurf. Segfaults, but I'm not sure what I've done wrong. ........ r4861 | jmb | 2008-08-02 05:01:19 +0100 (Sat, 02 Aug 2008) | 3 lines Fix segfault caused by mismatched struct layout expectations. Fix warnings, too. ........ r4862 | jmb | 2008-08-02 05:11:02 +0100 (Sat, 02 Aug 2008) | 2 lines Destroy parser as soon as it's no longer needed, and flag this so that html_destroy doesn't cause things to trample all over the heap. ........ r4863 | takkaria | 2008-08-02 15:30:34 +0100 (Sat, 02 Aug 2008) | 2 lines Remove deugging printf()s, add Aliases file, add script { display: none; } block. ........ r4868 | takkaria | 2008-08-02 22:14:55 +0100 (Sat, 02 Aug 2008) | 2 lines Fix a segfault bug. ........ r4869 | takkaria | 2008-08-02 22:17:58 +0100 (Sat, 02 Aug 2008) | 2 lines Add gtk/res/Aliases as a symlink to the one in !NS/Resources ........ r4870 | takkaria | 2008-08-02 22:26:31 +0100 (Sat, 02 Aug 2008) | 2 lines Fix symlink. ........ r4885 | jmb | 2008-08-03 23:26:54 +0100 (Sun, 03 Aug 2008) | 2 lines Make multiple parallel parser instances work correctly. ........ r4886 | tlsa | 2008-08-04 00:21:29 +0100 (Mon, 04 Aug 2008) | 1 line Don't display contents of STYLE. ........ r4891 | jmb | 2008-08-04 01:18:07 +0100 (Mon, 04 Aug 2008) | 5 lines Fix reparent_children to actually work Make get_parent_node pay attention to the element_only flag Fixup node referencing when appending a text child Make clone_node clone attributes and namespace information in the non-deep case ........ r4918 | jmb | 2008-08-05 15:27:03 +0100 (Tue, 05 Aug 2008) | 2 lines Fix debug target ........ r4944 | takkaria | 2008-08-07 12:56:50 +0100 (Thu, 07 Aug 2008) | 2 lines Use talloc to allocate space for Hubbub. ........ r4993 | takkaria | 2008-08-10 17:49:47 +0100 (Sun, 10 Aug 2008) | 2 lines Stub out the encoding change callback so NS-hubbub compiles again. ........ r4994 | takkaria | 2008-08-10 18:02:33 +0100 (Sun, 10 Aug 2008) | 2 lines Tell Hubbub the encoding that HTTP gives us, if we have one. ........ r5001 | takkaria | 2008-08-11 02:53:24 +0100 (Mon, 11 Aug 2008) | 2 lines First go at implementing proper <meta charset> support in NetSurf, amongst some refactoring. Probably works, but I have no pages around to test it on. ........ r5002 | takkaria | 2008-08-11 02:56:35 +0100 (Mon, 11 Aug 2008) | 2 lines Fix (I hope) a 64-bit compiler warning. ........ r5012 | takkaria | 2008-08-11 08:40:28 +0100 (Mon, 11 Aug 2008) | 2 lines Fix some nits from jmb. (Remove const from html->encoding, set encoding_source to something about right.) ........ r5013 | takkaria | 2008-08-11 08:48:50 +0100 (Mon, 11 Aug 2008) | 2 lines Properly fix <meta charset> handling, by passing in the right thing as the context. No idea how this worked before. :) (credit: jmb) ........ r5017 | jmb | 2008-08-11 09:13:22 +0100 (Mon, 11 Aug 2008) | 7 lines Pedantic typo fix. Make Hubbub YES when building for RISC OS. Make Hubbub AUTO when building for GTK Ignore the presence of Hubbub on other platforms. Remove the explicit libparserutils pkg-config stuff from the makefile (see r5016) Add some logic that means Hubbub gets enabled correctly when building on RISC OS and when cross-compiling for it. (This is ugly and will go away when pkg-config is available on RO) ........ svn path=/trunk/netsurf/; revision=5019
2008-08-11 12:17:48 +04:00
2012-11-26 21:23:03 +04:00
/* extract image maps - can't do this sensibly in dom_to_box */
err = imagemap_extract(c);
if (err != NSERROR_OK) {
NSLOG(netsurf, INFO, "imagemap extraction failed");
html_object_free_objects(c);
content_broadcast_error(&c->base, err, NULL);
2012-07-06 23:00:32 +04:00
content_set_error(&c->base);
dom_node_unref(html);
2012-07-06 23:00:32 +04:00
return;
Merged revisions 4859-5013,5016-5018 via svnmerge from svn://source.netsurf-browser.org/branches/takkaria/netsurf-hubbub ........ r4860 | takkaria | 2008-08-02 03:51:35 +0100 (Sat, 02 Aug 2008) | 2 lines A really horribly rough first go at integrating hubbub with NetSurf. Segfaults, but I'm not sure what I've done wrong. ........ r4861 | jmb | 2008-08-02 05:01:19 +0100 (Sat, 02 Aug 2008) | 3 lines Fix segfault caused by mismatched struct layout expectations. Fix warnings, too. ........ r4862 | jmb | 2008-08-02 05:11:02 +0100 (Sat, 02 Aug 2008) | 2 lines Destroy parser as soon as it's no longer needed, and flag this so that html_destroy doesn't cause things to trample all over the heap. ........ r4863 | takkaria | 2008-08-02 15:30:34 +0100 (Sat, 02 Aug 2008) | 2 lines Remove deugging printf()s, add Aliases file, add script { display: none; } block. ........ r4868 | takkaria | 2008-08-02 22:14:55 +0100 (Sat, 02 Aug 2008) | 2 lines Fix a segfault bug. ........ r4869 | takkaria | 2008-08-02 22:17:58 +0100 (Sat, 02 Aug 2008) | 2 lines Add gtk/res/Aliases as a symlink to the one in !NS/Resources ........ r4870 | takkaria | 2008-08-02 22:26:31 +0100 (Sat, 02 Aug 2008) | 2 lines Fix symlink. ........ r4885 | jmb | 2008-08-03 23:26:54 +0100 (Sun, 03 Aug 2008) | 2 lines Make multiple parallel parser instances work correctly. ........ r4886 | tlsa | 2008-08-04 00:21:29 +0100 (Mon, 04 Aug 2008) | 1 line Don't display contents of STYLE. ........ r4891 | jmb | 2008-08-04 01:18:07 +0100 (Mon, 04 Aug 2008) | 5 lines Fix reparent_children to actually work Make get_parent_node pay attention to the element_only flag Fixup node referencing when appending a text child Make clone_node clone attributes and namespace information in the non-deep case ........ r4918 | jmb | 2008-08-05 15:27:03 +0100 (Tue, 05 Aug 2008) | 2 lines Fix debug target ........ r4944 | takkaria | 2008-08-07 12:56:50 +0100 (Thu, 07 Aug 2008) | 2 lines Use talloc to allocate space for Hubbub. ........ r4993 | takkaria | 2008-08-10 17:49:47 +0100 (Sun, 10 Aug 2008) | 2 lines Stub out the encoding change callback so NS-hubbub compiles again. ........ r4994 | takkaria | 2008-08-10 18:02:33 +0100 (Sun, 10 Aug 2008) | 2 lines Tell Hubbub the encoding that HTTP gives us, if we have one. ........ r5001 | takkaria | 2008-08-11 02:53:24 +0100 (Mon, 11 Aug 2008) | 2 lines First go at implementing proper <meta charset> support in NetSurf, amongst some refactoring. Probably works, but I have no pages around to test it on. ........ r5002 | takkaria | 2008-08-11 02:56:35 +0100 (Mon, 11 Aug 2008) | 2 lines Fix (I hope) a 64-bit compiler warning. ........ r5012 | takkaria | 2008-08-11 08:40:28 +0100 (Mon, 11 Aug 2008) | 2 lines Fix some nits from jmb. (Remove const from html->encoding, set encoding_source to something about right.) ........ r5013 | takkaria | 2008-08-11 08:48:50 +0100 (Mon, 11 Aug 2008) | 2 lines Properly fix <meta charset> handling, by passing in the right thing as the context. No idea how this worked before. :) (credit: jmb) ........ r5017 | jmb | 2008-08-11 09:13:22 +0100 (Mon, 11 Aug 2008) | 7 lines Pedantic typo fix. Make Hubbub YES when building for RISC OS. Make Hubbub AUTO when building for GTK Ignore the presence of Hubbub on other platforms. Remove the explicit libparserutils pkg-config stuff from the makefile (see r5016) Add some logic that means Hubbub gets enabled correctly when building on RISC OS and when cross-compiling for it. (This is ugly and will go away when pkg-config is available on RO) ........ svn path=/trunk/netsurf/; revision=5019
2008-08-11 12:17:48 +04:00
}
2012-07-06 23:00:32 +04:00
/*imagemap_dump(c);*/
Merged revisions 4859-5013,5016-5018 via svnmerge from svn://source.netsurf-browser.org/branches/takkaria/netsurf-hubbub ........ r4860 | takkaria | 2008-08-02 03:51:35 +0100 (Sat, 02 Aug 2008) | 2 lines A really horribly rough first go at integrating hubbub with NetSurf. Segfaults, but I'm not sure what I've done wrong. ........ r4861 | jmb | 2008-08-02 05:01:19 +0100 (Sat, 02 Aug 2008) | 3 lines Fix segfault caused by mismatched struct layout expectations. Fix warnings, too. ........ r4862 | jmb | 2008-08-02 05:11:02 +0100 (Sat, 02 Aug 2008) | 2 lines Destroy parser as soon as it's no longer needed, and flag this so that html_destroy doesn't cause things to trample all over the heap. ........ r4863 | takkaria | 2008-08-02 15:30:34 +0100 (Sat, 02 Aug 2008) | 2 lines Remove deugging printf()s, add Aliases file, add script { display: none; } block. ........ r4868 | takkaria | 2008-08-02 22:14:55 +0100 (Sat, 02 Aug 2008) | 2 lines Fix a segfault bug. ........ r4869 | takkaria | 2008-08-02 22:17:58 +0100 (Sat, 02 Aug 2008) | 2 lines Add gtk/res/Aliases as a symlink to the one in !NS/Resources ........ r4870 | takkaria | 2008-08-02 22:26:31 +0100 (Sat, 02 Aug 2008) | 2 lines Fix symlink. ........ r4885 | jmb | 2008-08-03 23:26:54 +0100 (Sun, 03 Aug 2008) | 2 lines Make multiple parallel parser instances work correctly. ........ r4886 | tlsa | 2008-08-04 00:21:29 +0100 (Mon, 04 Aug 2008) | 1 line Don't display contents of STYLE. ........ r4891 | jmb | 2008-08-04 01:18:07 +0100 (Mon, 04 Aug 2008) | 5 lines Fix reparent_children to actually work Make get_parent_node pay attention to the element_only flag Fixup node referencing when appending a text child Make clone_node clone attributes and namespace information in the non-deep case ........ r4918 | jmb | 2008-08-05 15:27:03 +0100 (Tue, 05 Aug 2008) | 2 lines Fix debug target ........ r4944 | takkaria | 2008-08-07 12:56:50 +0100 (Thu, 07 Aug 2008) | 2 lines Use talloc to allocate space for Hubbub. ........ r4993 | takkaria | 2008-08-10 17:49:47 +0100 (Sun, 10 Aug 2008) | 2 lines Stub out the encoding change callback so NS-hubbub compiles again. ........ r4994 | takkaria | 2008-08-10 18:02:33 +0100 (Sun, 10 Aug 2008) | 2 lines Tell Hubbub the encoding that HTTP gives us, if we have one. ........ r5001 | takkaria | 2008-08-11 02:53:24 +0100 (Mon, 11 Aug 2008) | 2 lines First go at implementing proper <meta charset> support in NetSurf, amongst some refactoring. Probably works, but I have no pages around to test it on. ........ r5002 | takkaria | 2008-08-11 02:56:35 +0100 (Mon, 11 Aug 2008) | 2 lines Fix (I hope) a 64-bit compiler warning. ........ r5012 | takkaria | 2008-08-11 08:40:28 +0100 (Mon, 11 Aug 2008) | 2 lines Fix some nits from jmb. (Remove const from html->encoding, set encoding_source to something about right.) ........ r5013 | takkaria | 2008-08-11 08:48:50 +0100 (Mon, 11 Aug 2008) | 2 lines Properly fix <meta charset> handling, by passing in the right thing as the context. No idea how this worked before. :) (credit: jmb) ........ r5017 | jmb | 2008-08-11 09:13:22 +0100 (Mon, 11 Aug 2008) | 7 lines Pedantic typo fix. Make Hubbub YES when building for RISC OS. Make Hubbub AUTO when building for GTK Ignore the presence of Hubbub on other platforms. Remove the explicit libparserutils pkg-config stuff from the makefile (see r5016) Add some logic that means Hubbub gets enabled correctly when building on RISC OS and when cross-compiling for it. (This is ugly and will go away when pkg-config is available on RO) ........ svn path=/trunk/netsurf/; revision=5019
2008-08-11 12:17:48 +04:00
2012-07-06 23:00:32 +04:00
/* Destroy the parser binding */
2012-07-14 03:33:15 +04:00
dom_hubbub_parser_destroy(c->parser);
c->parser = NULL;
2012-07-06 23:00:32 +04:00
content_set_ready(&c->base);
html_proceed_to_done(c);
dom_node_unref(html);
2012-07-06 23:00:32 +04:00
}
/* Documented in html_internal.h */
nserror
html_proceed_to_done(html_content *html)
{
switch (content__get_status(&html->base)) {
case CONTENT_STATUS_READY:
if (html->base.active == 0) {
content_set_done(&html->base);
return NSERROR_OK;
}
break;
case CONTENT_STATUS_DONE:
/* fallthrough */
case CONTENT_STATUS_LOADING:
return NSERROR_OK;
default:
NSLOG(netsurf, ERROR, "Content status unexpectedly not LOADING/READY/DONE");
break;
}
return NSERROR_UNKNOWN;
}
static void html_get_dimensions(html_content *htmlc)
{
unsigned w;
unsigned h;
union content_msg_data msg_data = {
.getdims = {
.viewport_width = &w,
.viewport_height = &h,
},
};
content_broadcast(&htmlc->base, CONTENT_MSG_GETDIMS, &msg_data);
htmlc->media.width = nscss_pixels_physical_to_css(INTTOFIX(w));
htmlc->media.height = nscss_pixels_physical_to_css(INTTOFIX(h));
htmlc->media.client_font_size =
FDIV(INTTOFIX(nsoption_int(font_size)), F_10);
htmlc->media.client_line_height =
FMUL(nscss_len2px(NULL, htmlc->media.client_font_size,
CSS_UNIT_PT, NULL), FLTTOFIX(1.33));
}
/* exported function documented in html/html_internal.h */
2014-11-12 22:31:39 +03:00
void html_finish_conversion(html_content *htmlc)
2012-07-06 23:00:32 +04:00
{
union content_msg_data msg_data;
dom_exception exc; /* returned by libdom functions */
dom_node *html;
2012-11-26 21:23:03 +04:00
nserror error;
2012-07-06 23:00:32 +04:00
/* Bail out if we've been aborted */
2014-11-12 22:31:39 +03:00
if (htmlc->aborted) {
content_broadcast_error(&htmlc->base, NSERROR_STOPPED, NULL);
2014-11-12 22:31:39 +03:00
content_set_error(&htmlc->base);
2012-07-06 23:00:32 +04:00
return;
}
/* If we already have a selection context, then we have already
* "finished" conversion. We can get here twice if e.g. some JS
* adds a new stylesheet, and the stylesheet gets added after
* the HTML content is initially finished.
*
* If we didn't do this, the HTML content would try to rebuild the
* box tree for the html content when this new stylesheet is ready.
* NetSurf has no concept of dynamically changing documents, so this
* would break badly.
*/
if (htmlc->select_ctx != NULL) {
NSLOG(netsurf, INFO,
"Ignoring style change: NS layout is static.");
return;
}
/* create new css selection context */
2014-11-12 22:31:39 +03:00
error = html_css_new_selection_context(htmlc, &htmlc->select_ctx);
if (error != NSERROR_OK) {
content_broadcast_error(&htmlc->base, error, NULL);
2014-11-12 22:31:39 +03:00
content_set_error(&htmlc->base);
2012-07-06 23:00:32 +04:00
return;
}
2012-07-06 23:00:32 +04:00
2012-11-28 22:07:36 +04:00
/* fire a simple event named load at the Document's Window
* object, but with its target set to the Document object (and
* the currentTarget set to the Window object)
*/
if (htmlc->jsthread != NULL) {
js_fire_event(htmlc->jsthread, "load", htmlc->document, NULL);
}
2012-11-28 22:07:36 +04:00
2012-11-26 21:23:03 +04:00
/* convert dom tree to box tree */
NSLOG(netsurf, INFO, "DOM to box (%p)", htmlc);
2014-11-12 22:31:39 +03:00
content_set_status(&htmlc->base, messages_get("Processing"));
msg_data.explicit_status_text = NULL;
content_broadcast(&htmlc->base, CONTENT_MSG_STATUS, &msg_data);
2012-07-06 23:00:32 +04:00
2014-11-12 22:31:39 +03:00
exc = dom_document_get_document_element(htmlc->document, (void *) &html);
2012-07-06 23:00:32 +04:00
if ((exc != DOM_NO_ERR) || (html == NULL)) {
NSLOG(netsurf, INFO, "error retrieving html element from dom");
content_broadcast_error(&htmlc->base, NSERROR_DOM, NULL);
2014-11-12 22:31:39 +03:00
content_set_error(&htmlc->base);
2012-07-06 23:00:32 +04:00
return;
}
html_get_dimensions(htmlc);
error = dom_to_box(html, htmlc, html_box_convert_done, &htmlc->box_conversion_context);
2012-11-26 21:23:03 +04:00
if (error != NSERROR_OK) {
NSLOG(netsurf, INFO, "box conversion failed");
2012-07-22 16:49:10 +04:00
dom_node_unref(html);
2014-11-12 22:31:39 +03:00
html_object_free_objects(htmlc);
content_broadcast_error(&htmlc->base, error, NULL);
2014-11-12 22:31:39 +03:00
content_set_error(&htmlc->base);
2012-07-06 23:00:32 +04:00
return;
}
2012-07-22 16:49:10 +04:00
dom_node_unref(html);
2012-07-06 23:00:32 +04:00
}
Merged revisions 4859-5013,5016-5018 via svnmerge from svn://source.netsurf-browser.org/branches/takkaria/netsurf-hubbub ........ r4860 | takkaria | 2008-08-02 03:51:35 +0100 (Sat, 02 Aug 2008) | 2 lines A really horribly rough first go at integrating hubbub with NetSurf. Segfaults, but I'm not sure what I've done wrong. ........ r4861 | jmb | 2008-08-02 05:01:19 +0100 (Sat, 02 Aug 2008) | 3 lines Fix segfault caused by mismatched struct layout expectations. Fix warnings, too. ........ r4862 | jmb | 2008-08-02 05:11:02 +0100 (Sat, 02 Aug 2008) | 2 lines Destroy parser as soon as it's no longer needed, and flag this so that html_destroy doesn't cause things to trample all over the heap. ........ r4863 | takkaria | 2008-08-02 15:30:34 +0100 (Sat, 02 Aug 2008) | 2 lines Remove deugging printf()s, add Aliases file, add script { display: none; } block. ........ r4868 | takkaria | 2008-08-02 22:14:55 +0100 (Sat, 02 Aug 2008) | 2 lines Fix a segfault bug. ........ r4869 | takkaria | 2008-08-02 22:17:58 +0100 (Sat, 02 Aug 2008) | 2 lines Add gtk/res/Aliases as a symlink to the one in !NS/Resources ........ r4870 | takkaria | 2008-08-02 22:26:31 +0100 (Sat, 02 Aug 2008) | 2 lines Fix symlink. ........ r4885 | jmb | 2008-08-03 23:26:54 +0100 (Sun, 03 Aug 2008) | 2 lines Make multiple parallel parser instances work correctly. ........ r4886 | tlsa | 2008-08-04 00:21:29 +0100 (Mon, 04 Aug 2008) | 1 line Don't display contents of STYLE. ........ r4891 | jmb | 2008-08-04 01:18:07 +0100 (Mon, 04 Aug 2008) | 5 lines Fix reparent_children to actually work Make get_parent_node pay attention to the element_only flag Fixup node referencing when appending a text child Make clone_node clone attributes and namespace information in the non-deep case ........ r4918 | jmb | 2008-08-05 15:27:03 +0100 (Tue, 05 Aug 2008) | 2 lines Fix debug target ........ r4944 | takkaria | 2008-08-07 12:56:50 +0100 (Thu, 07 Aug 2008) | 2 lines Use talloc to allocate space for Hubbub. ........ r4993 | takkaria | 2008-08-10 17:49:47 +0100 (Sun, 10 Aug 2008) | 2 lines Stub out the encoding change callback so NS-hubbub compiles again. ........ r4994 | takkaria | 2008-08-10 18:02:33 +0100 (Sun, 10 Aug 2008) | 2 lines Tell Hubbub the encoding that HTTP gives us, if we have one. ........ r5001 | takkaria | 2008-08-11 02:53:24 +0100 (Mon, 11 Aug 2008) | 2 lines First go at implementing proper <meta charset> support in NetSurf, amongst some refactoring. Probably works, but I have no pages around to test it on. ........ r5002 | takkaria | 2008-08-11 02:56:35 +0100 (Mon, 11 Aug 2008) | 2 lines Fix (I hope) a 64-bit compiler warning. ........ r5012 | takkaria | 2008-08-11 08:40:28 +0100 (Mon, 11 Aug 2008) | 2 lines Fix some nits from jmb. (Remove const from html->encoding, set encoding_source to something about right.) ........ r5013 | takkaria | 2008-08-11 08:48:50 +0100 (Mon, 11 Aug 2008) | 2 lines Properly fix <meta charset> handling, by passing in the right thing as the context. No idea how this worked before. :) (credit: jmb) ........ r5017 | jmb | 2008-08-11 09:13:22 +0100 (Mon, 11 Aug 2008) | 7 lines Pedantic typo fix. Make Hubbub YES when building for RISC OS. Make Hubbub AUTO when building for GTK Ignore the presence of Hubbub on other platforms. Remove the explicit libparserutils pkg-config stuff from the makefile (see r5016) Add some logic that means Hubbub gets enabled correctly when building on RISC OS and when cross-compiling for it. (This is ugly and will go away when pkg-config is available on RO) ........ svn path=/trunk/netsurf/; revision=5019
2008-08-11 12:17:48 +04:00
static void
html_document_user_data_handler(dom_node_operation operation,
dom_string *key, void *data,
struct dom_node *src,
struct dom_node *dst)
{
if (dom_string_isequal(corestring_dom___ns_key_html_content_data,
key) == false || data == NULL) {
return;
}
switch (operation) {
case DOM_NODE_CLONED:
NSLOG(netsurf, INFO, "Cloned");
break;
case DOM_NODE_RENAMED:
NSLOG(netsurf, INFO, "Renamed");
break;
case DOM_NODE_IMPORTED:
NSLOG(netsurf, INFO, "imported");
break;
case DOM_NODE_ADOPTED:
NSLOG(netsurf, INFO, "Adopted");
break;
case DOM_NODE_DELETED:
/* This is the only path I expect */
break;
default:
NSLOG(netsurf, INFO, "User data operation not handled.");
assert(0);
}
}
static nserror
html_create_html_data(html_content *c, const http_parameter *params)
2012-07-06 23:00:32 +04:00
{
lwc_string *charset;
nserror nerror;
dom_hubbub_parser_params parse_params;
dom_hubbub_error error;
dom_exception err;
void *old_node_data;
c->parser = NULL;
c->parse_completed = false;
c->conversion_begun = false;
c->document = NULL;
c->quirks = DOM_DOCUMENT_QUIRKS_MODE_NONE;
c->encoding = NULL;
c->base_url = nsurl_ref(content_get_url(&c->base));
c->base_target = NULL;
c->aborted = false;
c->refresh = false;
c->reflowing = false;
c->title = NULL;
c->bctx = NULL;
c->layout = NULL;
c->background_colour = NS_TRANSPARENT;
c->stylesheet_count = 0;
c->stylesheets = NULL;
c->select_ctx = NULL;
c->media.type = CSS_MEDIA_SCREEN;
c->universal = NULL;
c->num_objects = 0;
c->object_list = NULL;
c->forms = NULL;
c->imagemaps = NULL;
c->bw = NULL;
c->frameset = NULL;
c->iframe = NULL;
c->page = NULL;
c->font_func = guit->layout;
c->drag_type = HTML_DRAG_NONE;
c->drag_owner.no_owner = true;
c->selection_type = HTML_SELECTION_NONE;
c->selection_owner.none = true;
c->focus_type = HTML_FOCUS_SELF;
c->focus_owner.self = true;
c->scripts_count = 0;
c->scripts = NULL;
c->jsthread = NULL;
c->enable_scripting = nsoption_bool(enable_javascript);
c->base.active = 1; /* The html content itself is active */
if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) {
return NSERROR_NOMEM;
}
selection_prepare(&c->sel, (struct content *)c, true);
nerror = http_parameter_list_find_item(params, corestring_lwc_charset, &charset);
if (nerror == NSERROR_OK) {
c->encoding = strdup(lwc_string_data(charset));
lwc_string_unref(charset);
if (c->encoding == NULL) {
lwc_string_unref(c->universal);
c->universal = NULL;
return NSERROR_NOMEM;
}
c->encoding_source = DOM_HUBBUB_ENCODING_SOURCE_HEADER;
}
/* Create the parser binding */
parse_params.enc = c->encoding;
parse_params.fix_enc = true;
parse_params.enable_script = c->enable_scripting;
parse_params.msg = NULL;
parse_params.script = html_process_script;
parse_params.ctx = c;
parse_params.daf = html_dom_event_fetcher;
error = dom_hubbub_parser_create(&parse_params,
&c->parser,
&c->document);
if ((error != DOM_HUBBUB_OK) && (c->encoding != NULL)) {
/* Ok, we don't support the declared encoding. Bailing out
* isn't exactly user-friendly, so fall back to autodetect */
free(c->encoding);
c->encoding = NULL;
parse_params.enc = c->encoding;
error = dom_hubbub_parser_create(&parse_params,
&c->parser,
&c->document);
2012-11-26 21:23:03 +04:00
}
if (error != DOM_HUBBUB_OK) {
nsurl_unref(c->base_url);
c->base_url = NULL;
lwc_string_unref(c->universal);
c->universal = NULL;
return libdom_hubbub_error_to_nserror(error);
2012-07-06 23:00:32 +04:00
}
err = dom_node_set_user_data(c->document,
corestring_dom___ns_key_html_content_data,
c, html_document_user_data_handler,
(void *) &old_node_data);
if (err != DOM_NO_ERR) {
dom_hubbub_parser_destroy(c->parser);
c->parser = NULL;
nsurl_unref(c->base_url);
c->base_url = NULL;
lwc_string_unref(c->universal);
c->universal = NULL;
NSLOG(netsurf, INFO, "Unable to set user data.");
2015-08-12 12:00:22 +03:00
return NSERROR_DOM;
}
assert(old_node_data == NULL);
return NSERROR_OK;
}
/**
* Create a CONTENT_HTML.
*
* The content_html_data structure is initialized and the HTML parser is
* created.
*/
static nserror
html_create(const content_handler *handler,
lwc_string *imime_type,
const http_parameter *params,
llcache_handle *llcache,
const char *fallback_charset,
bool quirks,
struct content **c)
{
html_content *html;
nserror error;
html = calloc(1, sizeof(html_content));
if (html == NULL)
return NSERROR_NOMEM;
2012-11-26 21:23:03 +04:00
error = content__init(&html->base, handler, imime_type, params,
llcache, fallback_charset, quirks);
if (error != NSERROR_OK) {
free(html);
return error;
2012-07-06 23:00:32 +04:00
}
error = html_create_html_data(html, params);
if (error != NSERROR_OK) {
content_broadcast_error(&html->base, error, NULL);
free(html);
return error;
2012-07-06 23:00:32 +04:00
}
error = html_css_new_stylesheets(html);
if (error != NSERROR_OK) {
content_broadcast_error(&html->base, error, NULL);
free(html);
return error;
}
*c = (struct content *) html;
2012-07-06 23:00:32 +04:00
return NSERROR_OK;
}
static nserror
html_process_encoding_change(struct content *c,
const char *data,
unsigned int size)
{
html_content *html = (html_content *) c;
dom_hubbub_parser_params parse_params;
dom_hubbub_error error;
const char *encoding;
const uint8_t *source_data;
size_t source_size;
/* Retrieve new encoding */
encoding = dom_hubbub_parser_get_encoding(html->parser,
&html->encoding_source);
if (encoding == NULL) {
return NSERROR_NOMEM;
}
if (html->encoding != NULL) {
free(html->encoding);
2013-10-23 21:55:51 +04:00
html->encoding = NULL;
2012-07-06 23:00:32 +04:00
}
html->encoding = strdup(encoding);
if (html->encoding == NULL) {
return NSERROR_NOMEM;
2012-07-06 23:00:32 +04:00
}
/* Destroy binding */
dom_hubbub_parser_destroy(html->parser);
html->parser = NULL;
if (html->document != NULL) {
dom_node_unref(html->document);
2012-07-06 23:00:32 +04:00
}
parse_params.enc = html->encoding;
parse_params.fix_enc = true;
parse_params.enable_script = html->enable_scripting;
parse_params.msg = NULL;
parse_params.script = html_process_script;
parse_params.ctx = html;
parse_params.daf = html_dom_event_fetcher;
/* Create new binding, using the new encoding */
error = dom_hubbub_parser_create(&parse_params,
&html->parser,
&html->document);
if (error != DOM_HUBBUB_OK) {
/* Ok, we don't support the declared encoding. Bailing out
* isn't exactly user-friendly, so fall back to Windows-1252 */
free(html->encoding);
html->encoding = strdup("Windows-1252");
if (html->encoding == NULL) {
2012-11-26 21:23:03 +04:00
return NSERROR_NOMEM;
2012-07-06 23:00:32 +04:00
}
parse_params.enc = html->encoding;
error = dom_hubbub_parser_create(&parse_params,
&html->parser,
&html->document);
if (error != DOM_HUBBUB_OK) {
return libdom_hubbub_error_to_nserror(error);
}
}
2012-07-06 23:00:32 +04:00
source_data = content__get_source_data(c, &source_size);
2012-07-06 23:00:32 +04:00
/* Reprocess all the data. This is safe because
* the encoding is now specified at parser start which means
* it cannot be changed again.
*/
error = dom_hubbub_parser_parse_chunk(html->parser,
source_data,
source_size);
return libdom_hubbub_error_to_nserror(error);
}
/**
* Process data for CONTENT_HTML.
*/
2012-07-06 23:00:32 +04:00
static bool
html_process_data(struct content *c, const char *data, unsigned int size)
{
html_content *html = (html_content *) c;
dom_hubbub_error dom_ret;
nserror err = NSERROR_OK; /* assume its all going to be ok */
dom_ret = dom_hubbub_parser_parse_chunk(html->parser,
(const uint8_t *) data,
size);
err = libdom_hubbub_error_to_nserror(dom_ret);
/* deal with encoding change */
if (err == NSERROR_ENCODING_CHANGE) {
err = html_process_encoding_change(c, data, size);
}
/* broadcast the error if necessary */
if (err != NSERROR_OK) {
content_broadcast_error(c, err, NULL);
return false;
}
return true;
}
/**
* Convert a CONTENT_HTML for display.
*
* The following steps are carried out in order:
*
* - parsing to an XML tree is completed
* - stylesheets are fetched
* - the XML tree is converted to a box tree and object fetches are started
*
* On exit, the content status will be either CONTENT_STATUS_DONE if the
* document is completely loaded or CONTENT_STATUS_READY if objects are still
* being fetched.
*/
static bool html_convert(struct content *c)
{
html_content *htmlc = (html_content *) c;
2013-02-25 19:00:50 +04:00
dom_exception exc; /* returned by libdom functions */
/* The quirk check and associated stylesheet fetch is "safe"
* once the root node has been inserted into the document
* which must have happened by this point in the parse.
*
* faliure to retrive the quirk mode or to start the
* stylesheet fetch is non fatal as this "only" affects the
* render and it would annoy the user to fail the entire
* render for want of a quirks stylesheet.
*/
exc = dom_document_get_quirks_mode(htmlc->document, &htmlc->quirks);
if (exc == DOM_NO_ERR) {
html_css_quirks_stylesheets(htmlc);
NSLOG(netsurf, INFO, "quirks set to %d", htmlc->quirks);
2013-02-25 19:00:50 +04:00
}
htmlc->base.active--; /* the html fetch is no longer active */
NSLOG(netsurf, INFO, "%d fetches active (%p)", htmlc->base.active, c);
2013-02-25 19:00:50 +04:00
/* The parse cannot be completed here because it may be paused
* untill all the resources being fetched have completed.
*/
/* if there are no active fetches in progress no scripts are
* being fetched or they completed already.
*/
if (html_can_begin_conversion(htmlc)) {
return html_begin_conversion(htmlc);
}
return true;
}
/* Exported interface documented in html_internal.h */
bool html_can_begin_conversion(html_content *htmlc)
{
unsigned int i;
2013-05-20 22:51:15 +04:00
/* Cannot begin conversion if we're still fetching stuff */
2013-05-20 22:51:15 +04:00
if (htmlc->base.active != 0)
return false;
for (i = 0; i != htmlc->stylesheet_count; i++) {
/* Cannot begin conversion if the stylesheets are modified */
2013-05-20 22:51:15 +04:00
if (htmlc->stylesheets[i].modified)
return false;
}
/* All is good, begin */
return true;
}
bool
html_begin_conversion(html_content *htmlc)
{
dom_node *html;
2012-11-26 21:23:03 +04:00
nserror ns_error;
struct form *f;
dom_exception exc; /* returned by libdom functions */
dom_string *node_name = NULL;
dom_hubbub_error error;
/* The act of completing the parse can result in additional data
* being flushed through the parser. This may result in new style or
* script nodes, upon which the conversion depends. Thus, once we
* have completed the parse, we must check again to see if we can
* begin the conversion. If we can't, we must stop and wait for the
* new styles/scripts to be processed. Once they have been processed,
* we will be called again to begin the conversion for real. Thus,
* we must also ensure that we don't attempt to complete the parse
* multiple times, so store a flag to indicate that parsing is
* complete to avoid repeating the completion pointlessly.
*/
if (htmlc->parse_completed == false) {
NSLOG(netsurf, INFO, "Completing parse (%p)", htmlc);
/* complete parsing */
error = dom_hubbub_parser_completed(htmlc->parser);
if (error == DOM_HUBBUB_HUBBUB_ERR_PAUSED && htmlc->base.active > 0) {
/* The act of completing the parse failed because we've
* encountered a sync script which needs to run
*/
NSLOG(netsurf, INFO, "Completing parse brought synchronous JS to light, cannot complete yet");
return true;
}
if (error != DOM_HUBBUB_OK) {
NSLOG(netsurf, INFO, "Parsing failed");
content_broadcast_error(&htmlc->base,
libdom_hubbub_error_to_nserror(error),
NULL);
2012-11-26 21:23:03 +04:00
return false;
}
htmlc->parse_completed = true;
}
if (html_can_begin_conversion(htmlc) == false) {
NSLOG(netsurf, INFO, "Can't begin conversion (%p)", htmlc);
/* We can't proceed (see commentary above) */
return true;
}
2012-11-26 21:23:03 +04:00
/* Give up processing if we've been aborted */
if (htmlc->aborted) {
NSLOG(netsurf, INFO, "Conversion aborted (%p) (active: %u)",
htmlc, htmlc->base.active);
content_set_error(&htmlc->base);
content_broadcast_error(&htmlc->base, NSERROR_STOPPED, NULL);
2012-11-26 21:23:03 +04:00
return false;
}
/* Conversion begins proper at this point */
htmlc->conversion_begun = true;
/* complete script execution, including deferred scripts */
html_script_exec(htmlc, true);
2012-08-01 01:33:08 +04:00
2012-11-26 21:23:03 +04:00
/* fire a simple event that bubbles named DOMContentLoaded at
* the Document.
*/
/* get encoding */
if (htmlc->encoding == NULL) {
2012-07-14 03:33:15 +04:00
const char *encoding;
2012-07-14 03:33:15 +04:00
encoding = dom_hubbub_parser_get_encoding(htmlc->parser,
&htmlc->encoding_source);
if (encoding == NULL) {
content_broadcast_error(&htmlc->base,
NSERROR_NOMEM,
NULL);
return false;
}
htmlc->encoding = strdup(encoding);
if (htmlc->encoding == NULL) {
content_broadcast_error(&htmlc->base,
NSERROR_NOMEM,
NULL);
return false;
}
}
/* locate root element and ensure it is html */
exc = dom_document_get_document_element(htmlc->document, (void *) &html);
2012-07-06 23:00:32 +04:00
if ((exc != DOM_NO_ERR) || (html == NULL)) {
NSLOG(netsurf, INFO, "error retrieving html element from dom");
content_broadcast_error(&htmlc->base, NSERROR_DOM, NULL);
return false;
2012-07-06 23:00:32 +04:00
}
exc = dom_node_get_node_name(html, &node_name);
2012-07-06 23:00:32 +04:00
if ((exc != DOM_NO_ERR) ||
(node_name == NULL) ||
(!dom_string_caseless_lwc_isequal(node_name,
corestring_lwc_html))) {
NSLOG(netsurf, INFO, "root element not html");
content_broadcast_error(&htmlc->base, NSERROR_DOM, NULL);
dom_node_unref(html);
return false;
2012-07-06 23:00:32 +04:00
}
dom_string_unref(node_name);
/* Retrieve forms from parser */
2012-07-15 04:02:33 +04:00
htmlc->forms = html_forms_get_forms(htmlc->encoding,
(dom_html_document *) htmlc->document);
for (f = htmlc->forms; f != NULL; f = f->prev) {
nsurl *action;
/* Make all actions absolute */
if (f->action == NULL || f->action[0] == '\0') {
/* HTML5 4.10.22.3 step 9 */
nsurl *doc_addr = content_get_url(&htmlc->base);
2012-11-26 21:23:03 +04:00
ns_error = nsurl_join(htmlc->base_url,
nsurl_access(doc_addr),
2012-11-26 21:23:03 +04:00
&action);
} else {
ns_error = nsurl_join(htmlc->base_url,
f->action,
2012-11-26 21:23:03 +04:00
&action);
}
2012-11-26 21:23:03 +04:00
if (ns_error != NSERROR_OK) {
content_broadcast_error(&htmlc->base, ns_error, NULL);
2012-11-26 21:23:03 +04:00
dom_node_unref(html);
return false;
}
free(f->action);
f->action = strdup(nsurl_access(action));
nsurl_unref(action);
if (f->action == NULL) {
content_broadcast_error(&htmlc->base,
NSERROR_NOMEM,
NULL);
2012-11-26 21:23:03 +04:00
dom_node_unref(html);
return false;
}
/* Ensure each form has a document encoding */
if (f->document_charset == NULL) {
f->document_charset = strdup(htmlc->encoding);
if (f->document_charset == NULL) {
content_broadcast_error(&htmlc->base,
NSERROR_NOMEM,
NULL);
dom_node_unref(html);
return false;
}
}
}
dom_node_unref(html);
if (htmlc->base.active == 0) {
html_finish_conversion(htmlc);
}
return true;
}
/**
* Stop loading a CONTENT_HTML.
*
* called when the content is aborted. This must clean up any state
* created during the fetch.
*/
static void html_stop(struct content *c)
{
html_content *htmlc = (html_content *) c;
switch (c->status) {
case CONTENT_STATUS_LOADING:
/* Still loading; simply flag that we've been aborted
* html_convert/html_finish_conversion will do the rest */
htmlc->aborted = true;
if (htmlc->jsthread != NULL) {
/* Close the JS thread to cancel out any callbacks */
js_closethread(htmlc->jsthread);
}
break;
case CONTENT_STATUS_READY:
html_object_abort_objects(htmlc);
/* If there are no further active fetches and we're still
* in the READY state, transition to the DONE state. */
if (c->status == CONTENT_STATUS_READY && c->active == 0) {
content_set_done(c);
}
break;
case CONTENT_STATUS_DONE:
/* Nothing to do */
break;
default:
NSLOG(netsurf, INFO, "Unexpected status %d (%p)", c->status,
c);
assert(0);
}
}
/**
* Reformat a CONTENT_HTML to a new width.
*/
static void html_reformat(struct content *c, int width, int height)
{
html_content *htmlc = (html_content *) c;
struct box *layout;
uint64_t ms_before;
uint64_t ms_after;
uint64_t ms_interval;
nsu_getmonotonic_ms(&ms_before);
htmlc->reflowing = true;
htmlc->len_ctx.vw = nscss_pixels_physical_to_css(INTTOFIX(width));
htmlc->len_ctx.vh = nscss_pixels_physical_to_css(INTTOFIX(height));
htmlc->len_ctx.root_style = htmlc->layout->style;
layout_document(htmlc, width, height);
layout = htmlc->layout;
/* width and height are at least margin box of document */
c->width = layout->x + layout->padding[LEFT] + layout->width +
layout->padding[RIGHT] + layout->border[RIGHT].width +
layout->margin[RIGHT];
c->height = layout->y + layout->padding[TOP] + layout->height +
layout->padding[BOTTOM] + layout->border[BOTTOM].width +
layout->margin[BOTTOM];
/* if boxes overflow right or bottom edge, expand to contain it */
if (c->width < layout->x + layout->descendant_x1)
c->width = layout->x + layout->descendant_x1;
if (c->height < layout->y + layout->descendant_y1)
c->height = layout->y + layout->descendant_y1;
selection_reinit(&htmlc->sel, htmlc->layout);
htmlc->reflowing = false;
htmlc->had_initial_layout = true;
/* calculate next reflow time at three times what it took to reflow */
nsu_getmonotonic_ms(&ms_after);
ms_interval = (ms_after - ms_before) * 3;
if (ms_interval < (nsoption_uint(min_reflow_period) * 10)) {
ms_interval = nsoption_uint(min_reflow_period) * 10;
}
c->reformat_time = ms_after + ms_interval;
}
/**
* Redraw a box.
*
* \param h content containing the box, of type CONTENT_HTML
* \param box box to redraw
*/
void html_redraw_a_box(hlcache_handle *h, struct box *box)
{
int x, y;
box_coords(box, &x, &y);
content_request_redraw(h, x, y,
box->padding[LEFT] + box->width + box->padding[RIGHT],
box->padding[TOP] + box->height + box->padding[BOTTOM]);
}
/**
* Redraw a box.
*
2014-11-12 19:21:23 +03:00
* \param html content containing the box, of type CONTENT_HTML
* \param box box to redraw.
*/
void html__redraw_a_box(struct html_content *html, struct box *box)
{
int x, y;
box_coords(box, &x, &y);
content__request_redraw((struct content *)html, x, y,
box->padding[LEFT] + box->width + box->padding[RIGHT],
box->padding[TOP] + box->height + box->padding[BOTTOM]);
}
static void html_destroy_frameset(struct content_html_frames *frameset)
{
int i;
if (frameset->name) {
talloc_free(frameset->name);
frameset->name = NULL;
}
if (frameset->url) {
talloc_free(frameset->url);
frameset->url = NULL;
}
if (frameset->children) {
for (i = 0; i < (frameset->rows * frameset->cols); i++) {
if (frameset->children[i].name) {
talloc_free(frameset->children[i].name);
frameset->children[i].name = NULL;
}
if (frameset->children[i].url) {
nsurl_unref(frameset->children[i].url);
frameset->children[i].url = NULL;
}
if (frameset->children[i].children)
html_destroy_frameset(&frameset->children[i]);
}
talloc_free(frameset->children);
frameset->children = NULL;
}
}
static void html_destroy_iframe(struct content_html_iframe *iframe)
{
struct content_html_iframe *next;
next = iframe;
while ((iframe = next) != NULL) {
next = iframe->next;
if (iframe->name)
talloc_free(iframe->name);
if (iframe->url) {
nsurl_unref(iframe->url);
iframe->url = NULL;
}
talloc_free(iframe);
}
}
static void html_free_layout(html_content *htmlc)
{
if (htmlc->bctx != NULL) {
/* freeing talloc context should let the entire box
* set be destroyed
*/
talloc_free(htmlc->bctx);
}
}
/**
* Destroy a CONTENT_HTML and free all resources it owns.
*/
static void html_destroy(struct content *c)
{
html_content *html = (html_content *) c;
struct form *f, *g;
NSLOG(netsurf, INFO, "content %p", c);
/* If we're still converting a layout, cancel it */
if (html->box_conversion_context != NULL) {
if (cancel_dom_to_box(html->box_conversion_context) != NSERROR_OK) {
NSLOG(netsurf, CRITICAL, "WARNING, Unable to cancel conversion context, browser may crash");
}
}
/* Destroy forms */
for (f = html->forms; f != NULL; f = g) {
g = f->prev;
form_free(f);
}
imagemap_destroy(html);
if (c->refresh)
nsurl_unref(c->refresh);
if (html->base_url)
nsurl_unref(html->base_url);
/* At this point we can be moderately confident the JS is offline
* so we destroy the JS thread.
*/
if (html->jsthread != NULL) {
js_destroythread(html->jsthread);
html->jsthread = NULL;
}
2012-07-14 03:33:15 +04:00
if (html->parser != NULL) {
dom_hubbub_parser_destroy(html->parser);
html->parser = NULL;
}
2012-07-14 03:33:15 +04:00
if (html->document != NULL) {
dom_node_unref(html->document);
html->document = NULL;
}
if (html->title != NULL) {
dom_node_unref(html->title);
html->title = NULL;
2012-07-14 03:33:15 +04:00
}
2013-10-23 21:55:51 +04:00
/* Free encoding */
if (html->encoding != NULL) {
free(html->encoding);
html->encoding = NULL;
}
/* Free base target */
if (html->base_target != NULL) {
free(html->base_target);
html->base_target = NULL;
}
/* Free frameset */
if (html->frameset != NULL) {
html_destroy_frameset(html->frameset);
talloc_free(html->frameset);
html->frameset = NULL;
}
/* Free iframes */
if (html->iframe != NULL) {
html_destroy_iframe(html->iframe);
html->iframe = NULL;
}
/* Destroy selection context */
if (html->select_ctx != NULL) {
css_select_ctx_destroy(html->select_ctx);
html->select_ctx = NULL;
}
if (html->universal != NULL) {
lwc_string_unref(html->universal);
html->universal = NULL;
}
/* Free stylesheets */
html_css_free_stylesheets(html);
/* Free scripts */
html_script_free(html);
/* Free objects */
html_object_free_objects(html);
/* free layout */
html_free_layout(html);
}
static nserror html_clone(const struct content *old, struct content **newc)
{
/** \todo Clone HTML specifics */
2012-07-06 23:00:32 +04:00
/* In the meantime, we should never be called, as HTML contents
* cannot be shared and we're not intending to fix printing's
* cloning of documents. */
assert(0 && "html_clone should never be called");
return true;
}
/**
* Handle a window containing a CONTENT_HTML being opened.
*/
static nserror
2012-07-06 23:00:32 +04:00
html_open(struct content *c,
struct browser_window *bw,
2012-07-06 23:00:32 +04:00
struct content *page,
struct object_params *params)
{
html_content *html = (html_content *) c;
html->bw = bw;
html->page = (html_content *) page;
html->drag_type = HTML_DRAG_NONE;
html->drag_owner.no_owner = true;
/* text selection */
selection_init(&html->sel, html->layout, &html->len_ctx);
html->selection_type = HTML_SELECTION_NONE;
html->selection_owner.none = true;
html_object_open_objects(html, bw);
return NSERROR_OK;
}
/**
* Handle a window containing a CONTENT_HTML being closed.
*/
static nserror html_close(struct content *c)
{
html_content *htmlc = (html_content *) c;
nserror ret = NSERROR_OK;
selection_clear(&htmlc->sel, false);
/* clear the html content reference to the browser window */
htmlc->bw = NULL;
/* remove all object references from the html content */
html_object_close_objects(htmlc);
if (htmlc->jsthread != NULL) {
/* Close, but do not destroy (yet) the JS thread */
ret = js_closethread(htmlc->jsthread);
}
return ret;
}
/**
* Return an HTML content's selection context
*/
static void html_clear_selection(struct content *c)
{
html_content *html = (html_content *) c;
switch (html->selection_type) {
case HTML_SELECTION_NONE:
/* Nothing to do */
assert(html->selection_owner.none == true);
break;
case HTML_SELECTION_TEXTAREA:
textarea_clear_selection(html->selection_owner.textarea->
gadget->data.text.ta);
break;
case HTML_SELECTION_SELF:
assert(html->selection_owner.none == false);
selection_clear(&html->sel, true);
break;
case HTML_SELECTION_CONTENT:
content_clear_selection(html->selection_owner.content->object);
break;
default:
break;
}
/* There is no selection now. */
html->selection_type = HTML_SELECTION_NONE;
html->selection_owner.none = true;
}
/**
* Return an HTML content's selection context
*/
static char *html_get_selection(struct content *c)
{
html_content *html = (html_content *) c;
switch (html->selection_type) {
case HTML_SELECTION_TEXTAREA:
return textarea_get_selection(html->selection_owner.textarea->
gadget->data.text.ta);
case HTML_SELECTION_SELF:
assert(html->selection_owner.none == false);
return selection_get_copy(&html->sel);
case HTML_SELECTION_CONTENT:
return content_get_selection(
html->selection_owner.content->object);
case HTML_SELECTION_NONE:
/* Nothing to do */
assert(html->selection_owner.none == true);
break;
default:
break;
}
return NULL;
}
/**
* Get access to any content, link URLs and objects (images) currently
* at the given (x, y) coordinates.
*
* \param[in] c html content to look inside
* \param[in] x x-coordinate of point of interest
* \param[in] y y-coordinate of point of interest
* \param[out] data Positional features struct to be updated with any
* relevent content, or set to NULL if none.
* \return NSERROR_OK on success else appropriate error code.
*/
static nserror
html_get_contextual_content(struct content *c, int x, int y,
struct browser_window_features *data)
{
html_content *html = (html_content *) c;
struct box *box = html->layout;
struct box *next;
int box_x = 0, box_y = 0;
while ((next = box_at_point(&html->len_ctx, box, x, y,
&box_x, &box_y)) != NULL) {
box = next;
/* hidden boxes are ignored */
if ((box->style != NULL) &&
css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) {
continue;
}
if (box->iframe) {
float scale = browser_window_get_scale(box->iframe);
browser_window_get_features(box->iframe,
(x - box_x) * scale,
(y - box_y) * scale,
data);
}
if (box->object)
content_get_contextual_content(box->object,
x - box_x, y - box_y, data);
if (box->object)
data->object = box->object;
if (box->href)
data->link = box->href;
if (box->usemap) {
const char *target = NULL;
nsurl *url = imagemap_get(html, box->usemap, box_x,
box_y, x, y, &target);
/* Box might have imagemap, but no actual link area
* at point */
if (url != NULL)
data->link = url;
}
if (box->gadget) {
switch (box->gadget->type) {
case GADGET_TEXTBOX:
case GADGET_TEXTAREA:
case GADGET_PASSWORD:
data->form_features = CTX_FORM_TEXT;
break;
case GADGET_FILE:
data->form_features = CTX_FORM_FILE;
break;
default:
data->form_features = CTX_FORM_NONE;
break;
}
}
}
return NSERROR_OK;
}
/**
* Scroll deepest thing within the content which can be scrolled at given point
*
* \param c html content to look inside
* \param x x-coordinate of point of interest
* \param y y-coordinate of point of interest
2013-02-08 19:10:39 +04:00
* \param scrx number of px try to scroll something in x direction
* \param scry number of px try to scroll something in y direction
* \return true iff scroll was consumed by something in the content
*/
2012-07-06 23:00:32 +04:00
static bool
html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry)
{
html_content *html = (html_content *) c;
struct box *box = html->layout;
struct box *next;
int box_x = 0, box_y = 0;
bool handled_scroll = false;
/* TODO: invert order; visit deepest box first */
while ((next = box_at_point(&html->len_ctx, box, x, y,
&box_x, &box_y)) != NULL) {
box = next;
2012-07-06 23:00:32 +04:00
if (box->style && css_computed_visibility(box->style) ==
CSS_VISIBILITY_HIDDEN)
continue;
/* Pass into iframe */
if (box->iframe) {
float scale = browser_window_get_scale(box->iframe);
if (browser_window_scroll_at_point(box->iframe,
(x - box_x) * scale,
(y - box_y) * scale,
scrx, scry) == true)
return true;
}
2013-02-08 19:26:24 +04:00
/* Pass into textarea widget */
if (box->gadget && (box->gadget->type == GADGET_TEXTAREA ||
box->gadget->type == GADGET_PASSWORD ||
box->gadget->type == GADGET_TEXTBOX) &&
textarea_scroll(box->gadget->data.text.ta,
scrx, scry) == true)
return true;
/* Pass into object */
if (box->object != NULL && content_scroll_at_point(
box->object, x - box_x, y - box_y,
scrx, scry) == true)
return true;
/* Handle box scrollbars */
if (box->scroll_y && scrollbar_scroll(box->scroll_y, scry))
handled_scroll = true;
if (box->scroll_x && scrollbar_scroll(box->scroll_x, scrx))
handled_scroll = true;
if (handled_scroll == true)
return true;
}
return false;
}
/** Helper for file gadgets to store their filename unencoded on the
* dom node associated with the gadget.
*
* \todo Get rid of this crap eventually
*/
static void html__dom_user_data_handler(dom_node_operation operation,
dom_string *key, void *_data, struct dom_node *src,
struct dom_node *dst)
{
char *oldfile;
char *data = (char *)_data;
2014-01-05 20:14:17 +04:00
if (!dom_string_isequal(corestring_dom___ns_key_file_name_node_data,
key) || data == NULL) {
return;
}
switch (operation) {
case DOM_NODE_CLONED:
if (dom_node_set_user_data(dst,
corestring_dom___ns_key_file_name_node_data,
strdup(data), html__dom_user_data_handler,
&oldfile) == DOM_NO_ERR) {
if (oldfile != NULL)
free(oldfile);
}
break;
case DOM_NODE_RENAMED:
case DOM_NODE_IMPORTED:
case DOM_NODE_ADOPTED:
break;
case DOM_NODE_DELETED:
free(data);
break;
default:
NSLOG(netsurf, INFO, "User data operation not handled.");
assert(0);
}
}
static void html__set_file_gadget_filename(struct content *c,
struct form_control *gadget, const char *fn)
{
nserror ret;
char *utf8_fn, *oldfile = NULL;
html_content *html = (html_content *)c;
struct box *file_box = gadget->box;
2014-03-11 02:19:23 +04:00
ret = guit->utf8->local_to_utf8(fn, 0, &utf8_fn);
if (ret != NSERROR_OK) {
assert(ret != NSERROR_BAD_ENCODING);
NSLOG(netsurf, INFO,
"utf8 to local encoding conversion failed");
/* Load was for us - just no memory */
return;
}
form_gadget_update_value(gadget, utf8_fn);
/* corestring_dom___ns_key_file_name_node_data */
if (dom_node_set_user_data((dom_node *)file_box->gadget->node,
corestring_dom___ns_key_file_name_node_data,
strdup(fn), html__dom_user_data_handler,
&oldfile) == DOM_NO_ERR) {
if (oldfile != NULL)
free(oldfile);
}
/* Redraw box. */
html__redraw_a_box(html, file_box);
}
void html_set_file_gadget_filename(struct hlcache_handle *hl,
struct form_control *gadget, const char *fn)
{
return html__set_file_gadget_filename(hlcache_handle_get_content(hl),
gadget, fn);
}
/**
* Drop a file onto a content at a particular point, or determine if a file
* may be dropped onto the content at given point.
*
* \param c html content to look inside
* \param x x-coordinate of point of interest
* \param y y-coordinate of point of interest
* \param file path to file to be dropped, or NULL to know if drop allowed
* \return true iff file drop has been handled, or if drop possible (NULL file)
*/
static bool html_drop_file_at_point(struct content *c, int x, int y, char *file)
{
html_content *html = (html_content *) c;
struct box *box = html->layout;
struct box *next;
struct box *file_box = NULL;
struct box *text_box = NULL;
int box_x = 0, box_y = 0;
/* Scan box tree for boxes that can handle drop */
while ((next = box_at_point(&html->len_ctx, box, x, y,
&box_x, &box_y)) != NULL) {
box = next;
if (box->style &&
css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN)
continue;
if (box->iframe) {
float scale = browser_window_get_scale(box->iframe);
return browser_window_drop_file_at_point(
box->iframe,
(x - box_x) * scale,
(y - box_y) * scale,
file);
}
if (box->object &&
content_drop_file_at_point(box->object,
x - box_x, y - box_y, file) == true)
return true;
if (box->gadget) {
switch (box->gadget->type) {
case GADGET_FILE:
file_box = box;
break;
case GADGET_TEXTBOX:
case GADGET_TEXTAREA:
case GADGET_PASSWORD:
text_box = box;
break;
default: /* appease compiler */
break;
}
}
}
if (!file_box && !text_box)
/* No box capable of handling drop */
return false;
if (file == NULL)
/* There is a box capable of handling drop here */
return true;
/* Handle the drop */
if (file_box) {
/* File dropped on file input */
2014-03-11 02:25:36 +04:00
html__set_file_gadget_filename(c, file_box->gadget, file);
} else {
/* File dropped on text input */
size_t file_len;
FILE *fp = NULL;
char *buffer;
char *utf8_buff;
nserror ret;
unsigned int size;
int bx, by;
/* Open file */
fp = fopen(file, "rb");
if (fp == NULL) {
/* Couldn't open file, but drop was for us */
return true;
}
/* Get filesize */
fseek(fp, 0, SEEK_END);
file_len = ftell(fp);
fseek(fp, 0, SEEK_SET);
if ((long)file_len == -1) {
/* unable to get file length, but drop was for us */
fclose(fp);
return true;
}
/* Allocate buffer for file data */
buffer = malloc(file_len + 1);
if (buffer == NULL) {
/* No memory, but drop was for us */
fclose(fp);
return true;
}
/* Stick file into buffer */
if (file_len != fread(buffer, 1, file_len, fp)) {
/* Failed, but drop was for us */
free(buffer);
fclose(fp);
return true;
}
/* Done with file */
fclose(fp);
/* Ensure buffer's string termination */
buffer[file_len] = '\0';
/* TODO: Sniff for text? */
/* Convert to UTF-8 */
ret = guit->utf8->local_to_utf8(buffer, file_len, &utf8_buff);
if (ret != NSERROR_OK) {
/* bad encoding shouldn't happen */
NSLOG(netsurf, ERROR,
"local to utf8 encoding failed (%s)",
messages_get_errorcode(ret));
assert(ret != NSERROR_BAD_ENCODING);
free(buffer);
return true;
}
/* Done with buffer */
free(buffer);
/* Get new length */
size = strlen(utf8_buff);
/* Simulate a click over the input box, to place caret */
box_coords(text_box, &bx, &by);
textarea_mouse_action(text_box->gadget->data.text.ta,
BROWSER_MOUSE_PRESS_1, x - bx, y - by);
/* Paste the file as text */
textarea_drop_text(text_box->gadget->data.text.ta,
utf8_buff, size);
free(utf8_buff);
}
return true;
}
/**
* set debug status.
*
* \param c The content to debug
* \param op The debug operation type
*/
static nserror
html_debug(struct content *c, enum content_debug op)
{
html_redraw_debug = !html_redraw_debug;
return NSERROR_OK;
}
/**
* Dump debug info concerning the html_content
*
* \param c The content to debug
2014-08-04 03:36:14 +04:00
* \param f The file to dump to
* \param op The debug dump type
*/
2014-08-04 03:36:14 +04:00
static nserror
html_debug_dump(struct content *c, FILE *f, enum content_debug op)
{
2014-08-04 03:36:14 +04:00
html_content *htmlc = (html_content *)c;
dom_node *html;
dom_exception exc; /* returned by libdom functions */
nserror ret;
assert(htmlc != NULL);
2014-08-04 03:36:14 +04:00
if (op == CONTENT_DEBUG_RENDER) {
assert(htmlc->layout != NULL);
box_dump(f, htmlc->layout, 0, true);
ret = NSERROR_OK;
} else {
if (htmlc->document == NULL) {
NSLOG(netsurf, INFO, "No document to dump");
2014-08-04 03:36:14 +04:00
return NSERROR_DOM;
}
exc = dom_document_get_document_element(htmlc->document, (void *) &html);
if ((exc != DOM_NO_ERR) || (html == NULL)) {
NSLOG(netsurf, INFO, "Unable to obtain root node");
2014-08-04 03:36:14 +04:00
return NSERROR_DOM;
}
ret = libdom_dump_structure(html, f, 0);
NSLOG(netsurf, INFO, "DOM structure dump returning %d", ret);
2014-08-04 03:36:14 +04:00
dom_node_unref(html);
}
2014-08-04 03:36:14 +04:00
return ret;
}
#if ALWAYS_DUMP_FRAMESET
/**
* Print a frameset tree to stderr.
*/
2012-07-06 23:00:32 +04:00
static void
html_dump_frameset(struct content_html_frames *frame, unsigned int depth)
{
unsigned int i;
int row, col, index;
const char *unit[] = {"px", "%", "*"};
const char *scrolling[] = {"auto", "yes", "no"};
assert(frame);
fprintf(stderr, "%p ", frame);
fprintf(stderr, "(%i %i) ", frame->rows, frame->cols);
fprintf(stderr, "w%g%s ", frame->width.value, unit[frame->width.unit]);
fprintf(stderr, "h%g%s ", frame->height.value,unit[frame->height.unit]);
fprintf(stderr, "(margin w%i h%i) ",
frame->margin_width, frame->margin_height);
if (frame->name)
fprintf(stderr, "'%s' ", frame->name);
if (frame->url)
fprintf(stderr, "<%s> ", frame->url);
if (frame->no_resize)
fprintf(stderr, "noresize ");
fprintf(stderr, "(scrolling %s) ", scrolling[frame->scrolling]);
if (frame->border)
fprintf(stderr, "border %x ",
(unsigned int) frame->border_colour);
fprintf(stderr, "\n");
if (frame->children) {
for (row = 0; row != frame->rows; row++) {
for (col = 0; col != frame->cols; col++) {
for (i = 0; i != depth; i++)
fprintf(stderr, " ");
fprintf(stderr, "(%i %i): ", row, col);
index = (row * frame->cols) + col;
html_dump_frameset(&frame->children[index],
depth + 1);
}
}
}
}
#endif
/**
* Retrieve HTML document tree
*
* \param h HTML content to retrieve document tree from
* \return Pointer to document tree
*/
dom_document *html_get_document(hlcache_handle *h)
{
html_content *c = (html_content *) hlcache_handle_get_content(h);
assert(c != NULL);
return c->document;
}
/**
* Retrieve box tree
*
* \param h HTML content to retrieve tree from
* \return Pointer to box tree
*
* \todo This API must die, as must all use of the box tree outside of
* HTML content handler
*/
struct box *html_get_box_tree(hlcache_handle *h)
{
html_content *c = (html_content *) hlcache_handle_get_content(h);
assert(c != NULL);
return c->layout;
}
/**
* Retrieve the charset of an HTML document
*
* \param c Content to retrieve charset from
2014-11-12 19:21:23 +03:00
* \param op The content encoding operation to perform.
* \return Pointer to charset, or NULL
*/
static const char *html_encoding(const struct content *c, enum content_encoding_type op)
{
html_content *html = (html_content *) c;
static char enc_token[10] = "Encoding0";
assert(html != NULL);
if (op == CONTENT_ENCODING_SOURCE) {
enc_token[8] = '0' + html->encoding_source;
return messages_get(enc_token);
}
return html->encoding;
}
/**
* Retrieve framesets used in an HTML document
*
* \param h Content to inspect
* \return Pointer to framesets, or NULL if none
*/
struct content_html_frames *html_get_frameset(hlcache_handle *h)
{
html_content *c = (html_content *) hlcache_handle_get_content(h);
assert(c != NULL);
return c->frameset;
}
/**
* Retrieve iframes used in an HTML document
*
* \param h Content to inspect
* \return Pointer to iframes, or NULL if none
*/
struct content_html_iframe *html_get_iframe(hlcache_handle *h)
{
html_content *c = (html_content *) hlcache_handle_get_content(h);
assert(c != NULL);
return c->iframe;
}
/**
* Retrieve an HTML content's base URL
*
* \param h Content to retrieve base target from
* \return Pointer to URL
*/
nsurl *html_get_base_url(hlcache_handle *h)
{
html_content *c = (html_content *) hlcache_handle_get_content(h);
assert(c != NULL);
return c->base_url;
}
/**
* Retrieve an HTML content's base target
*
* \param h Content to retrieve base target from
* \return Pointer to target, or NULL if none
*/
const char *html_get_base_target(hlcache_handle *h)
{
html_content *c = (html_content *) hlcache_handle_get_content(h);
assert(c != NULL);
return c->base_target;
}
/**
* Retrieve layout coordinates of box with given id
*
* \param h HTML document to search
* \param frag_id String containing an element id
* \param x Updated to global x coord iff id found
* \param y Updated to global y coord iff id found
* \return true iff id found
*/
bool html_get_id_offset(hlcache_handle *h, lwc_string *frag_id, int *x, int *y)
{
struct box *pos;
struct box *layout;
if (content_get_type(h) != CONTENT_HTML)
return false;
layout = html_get_box_tree(h);
if ((pos = box_find_by_id(layout, frag_id)) != 0) {
box_coords(pos, x, y);
return true;
}
return false;
}
bool html_exec(struct content *c, const char *src, size_t srclen)
{
html_content *htmlc = (html_content *)c;
bool result = false;
dom_exception err;
dom_html_body_element *body_node;
dom_string *dom_src;
dom_text *text_node;
dom_node *spare_node;
dom_html_script_element *script_node;
if (htmlc->document == NULL) {
NSLOG(netsurf, DEEPDEBUG, "Unable to exec, no document");
goto out_no_string;
}
err = dom_string_create((const uint8_t *)src, srclen, &dom_src);
if (err != DOM_NO_ERR) {
NSLOG(netsurf, DEEPDEBUG, "Unable to exec, could not create string");
goto out_no_string;
}
err = dom_html_document_get_body(htmlc->document, &body_node);
if (err != DOM_NO_ERR) {
NSLOG(netsurf, DEEPDEBUG, "Unable to retrieve body element");
goto out_no_body;
}
err = dom_document_create_text_node(htmlc->document, dom_src, &text_node);
if (err != DOM_NO_ERR) {
NSLOG(netsurf, DEEPDEBUG, "Unable to exec, could not create text node");
goto out_no_text_node;
}
err = dom_document_create_element(htmlc->document, corestring_dom_SCRIPT, &script_node);
if (err != DOM_NO_ERR) {
NSLOG(netsurf, DEEPDEBUG, "Unable to exec, could not create script node");
goto out_no_script_node;
}
err = dom_node_append_child(script_node, text_node, &spare_node);
if (err != DOM_NO_ERR) {
NSLOG(netsurf, DEEPDEBUG, "Unable to exec, could not insert code node into script node");
goto out_unparented;
}
dom_node_unref(spare_node); /* We do not need the spare ref at all */
err = dom_node_append_child(body_node, script_node, &spare_node);
if (err != DOM_NO_ERR) {
NSLOG(netsurf, DEEPDEBUG, "Unable to exec, could not insert script node into document body");
goto out_unparented;
}
dom_node_unref(spare_node); /* Again no need for the spare ref */
/* We successfully inserted the node into the DOM */
result = true;
/* Now we unwind, starting by removing the script from wherever it
* ended up parented
*/
err = dom_node_get_parent_node(script_node, &spare_node);
if (err == DOM_NO_ERR && spare_node != NULL) {
dom_node *second_spare;
err = dom_node_remove_child(spare_node, script_node, &second_spare);
if (err == DOM_NO_ERR) {
dom_node_unref(second_spare);
}
dom_node_unref(spare_node);
}
out_unparented:
dom_node_unref(script_node);
out_no_script_node:
dom_node_unref(text_node);
out_no_text_node:
dom_node_unref(body_node);
out_no_body:
dom_string_unref(dom_src);
out_no_string:
return result;
}
/* See \ref content_saw_insecure_objects */
static bool
html_saw_insecure_objects(struct content *c)
{
html_content *htmlc = (html_content *)c;
struct content_html_object *obj = htmlc->object_list;
/* Check through the object list */
while (obj != NULL) {
if (obj->content != NULL) {
if (content_saw_insecure_objects(obj->content))
return true;
}
obj = obj->next;
}
/* Now check the script list */
if (html_saw_insecure_scripts(htmlc)) {
return true;
}
/* Now check stylesheets */
if (html_css_saw_insecure_stylesheets(htmlc)) {
return true;
}
return false;
}
/**
* Compute the type of a content
*
* \return CONTENT_HTML
*/
static content_type html_content_type(void)
{
return CONTENT_HTML;
}
static void html_fini(void)
{
html_css_fini();
}
/**
* Finds all occurrences of a given string in an html box
*
* \param pattern the string pattern to search for
* \param p_len pattern length
* \param cur pointer to the current box
* \param case_sens whether to perform a case sensitive search
* \param context The search context to add the entry to.
* \return true on success, false on memory allocation failure
*/
static nserror
find_occurrences_html_box(const char *pattern,
int p_len,
struct box *cur,
bool case_sens,
struct textsearch_context *context)
{
struct box *a;
nserror res = NSERROR_OK;
/* ignore this box, if there's no visible text */
if (!cur->object && cur->text) {
const char *text = cur->text;
unsigned length = cur->length;
while (length > 0) {
unsigned match_length;
unsigned match_offset;
const char *new_text;
const char *pos;
pos = content_textsearch_find_pattern(text,
length,
pattern,
p_len,
case_sens,
&match_length);
if (!pos)
break;
/* found string in box => add to list */
match_offset = pos - cur->text;
res = content_textsearch_add_match(context,
cur->byte_offset + match_offset,
cur->byte_offset + match_offset + match_length,
cur,
cur);
if (res != NSERROR_OK) {
return res;
}
new_text = pos + match_length;
length -= (new_text - text);
text = new_text;
}
}
/* and recurse */
for (a = cur->children; a; a = a->next) {
res = find_occurrences_html_box(pattern,
p_len,
a,
case_sens,
context);
if (res != NSERROR_OK) {
return res;
}
}
return res;
}
/**
* Finds all occurrences of a given string in the html box tree
*
* \param pattern the string pattern to search for
* \param p_len pattern length
* \param c The content to search
* \param csens whether to perform a case sensitive search
* \param context The search context to add the entry to.
* \return true on success, false on memory allocation failure
*/
static nserror
html_textsearch_find(struct content *c,
struct textsearch_context *context,
const char *pattern,
int p_len,
bool csens)
{
html_content *html = (html_content *)c;
if (html->layout == NULL) {
return NSERROR_INVALID;
}
return find_occurrences_html_box(pattern,
p_len,
html->layout,
csens,
context);
}
static nserror
html_textsearch_bounds(struct content *c,
unsigned start_idx,
unsigned end_idx,
struct box *start_box,
struct box *end_box,
struct rect *bounds)
{
/* get box position and jump to it */
box_coords(start_box, &bounds->x0, &bounds->y0);
/* \todo: move x0 in by correct idx */
box_coords(end_box, &bounds->x1, &bounds->y1);
/* \todo: move x1 in by correct idx */
bounds->x1 += end_box->width;
bounds->y1 += end_box->height;
return NSERROR_OK;
}
/**
* create a selection object suitable for this content
*/
static nserror
html_create_selection(struct content *c, struct selection **sel_out)
{
html_content *html = (html_content *)c;
struct selection *sel;
sel = selection_create(c, true);
if (sel == NULL) {
return NSERROR_NOMEM;
}
selection_init(sel, html->layout, &html->len_ctx);
*sel_out = sel;
return NSERROR_OK;
}
/**
* HTML content handler function table
*/
static const content_handler html_content_handler = {
.fini = html_fini,
.create = html_create,
.process_data = html_process_data,
.data_complete = html_convert,
.reformat = html_reformat,
.destroy = html_destroy,
.stop = html_stop,
.mouse_track = html_mouse_track,
.mouse_action = html_mouse_action,
.keypress = html_keypress,
.redraw = html_redraw,
.open = html_open,
.close = html_close,
.get_selection = html_get_selection,
.clear_selection = html_clear_selection,
.get_contextual_content = html_get_contextual_content,
.scroll_at_point = html_scroll_at_point,
.drop_file_at_point = html_drop_file_at_point,
.debug_dump = html_debug_dump,
.debug = html_debug,
.clone = html_clone,
.get_encoding = html_encoding,
.type = html_content_type,
.exec = html_exec,
.saw_insecure_objects = html_saw_insecure_objects,
.textsearch_find = html_textsearch_find,
.textsearch_bounds = html_textsearch_bounds,
.create_selection = html_create_selection,
.no_share = true,
};
/* exported function documented in html/html.h */
nserror html_init(void)
{
uint32_t i;
nserror error;
error = html_css_init();
if (error != NSERROR_OK)
goto error;
for (i = 0; i < NOF_ELEMENTS(html_types); i++) {
error = content_factory_register_handler(html_types[i],
&html_content_handler);
if (error != NSERROR_OK)
goto error;
}
return NSERROR_OK;
error:
html_fini();
return error;
}