mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-12-19 10:42:36 +03:00
Support script insertion after conversion has begun
Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
This commit is contained in:
parent
90cabaf8c8
commit
19b45fb494
@ -645,6 +645,56 @@ void html_finish_conversion(html_content *htmlc)
|
||||
dom_node_unref(html);
|
||||
}
|
||||
|
||||
/* handler for a SCRIPT which has been added to a tree */
|
||||
static void
|
||||
dom_SCRIPT_showed_up(html_content *htmlc, dom_html_script_element *script)
|
||||
{
|
||||
dom_exception exc;
|
||||
dom_html_script_element_flags flags;
|
||||
dom_hubbub_error res;
|
||||
bool within;
|
||||
|
||||
if (!htmlc->enable_scripting) {
|
||||
NSLOG(netsurf, INFO, "Encountered a script, but scripting is off, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLOG(netsurf, DEEPDEBUG, "Encountered a script, node %p showed up", script);
|
||||
|
||||
exc = dom_html_script_element_get_flags(script, &flags);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
NSLOG(netsurf, DEEPDEBUG, "Unable to retrieve flags, giving up");
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & DOM_HTML_SCRIPT_ELEMENT_FLAG_PARSER_INSERTED) {
|
||||
NSLOG(netsurf, DEBUG, "Script was parser inserted, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
exc = dom_node_contains(htmlc->document, script, &within);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
NSLOG(netsurf, DEBUG, "Unable to determine if script was within document, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!within) {
|
||||
NSLOG(netsurf, DEBUG, "Script was not within the document, ignoring for now");
|
||||
return;
|
||||
}
|
||||
|
||||
res = html_process_script(htmlc, (dom_node *) script);
|
||||
if (res == DOM_HUBBUB_OK) {
|
||||
NSLOG(netsurf, DEEPDEBUG, "Inserted script has finished running");
|
||||
} else {
|
||||
if (res == (DOM_HUBBUB_HUBBUB_ERR | HUBBUB_PAUSED)) {
|
||||
NSLOG(netsurf, DEEPDEBUG, "Inserted script has launced asynchronously");
|
||||
} else {
|
||||
NSLOG(netsurf, DEEPDEBUG, "Failure starting script");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* callback for DOMNodeInserted end type */
|
||||
static void
|
||||
dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw)
|
||||
@ -693,6 +743,9 @@ dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw)
|
||||
case DOM_HTML_ELEMENT_TYPE_STYLE:
|
||||
html_css_process_style(htmlc, (dom_node *) node);
|
||||
break;
|
||||
case DOM_HTML_ELEMENT_TYPE_SCRIPT:
|
||||
dom_SCRIPT_showed_up(htmlc, (dom_html_script_element *) node);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -720,7 +773,39 @@ dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw)
|
||||
}
|
||||
}
|
||||
|
||||
/* callback for DOMNodeInserted end type */
|
||||
/* callback for DOMNodeInsertedIntoDocument end type */
|
||||
static void
|
||||
dom_default_action_DOMNodeInsertedIntoDocument_cb(struct dom_event *evt, void *pw)
|
||||
{
|
||||
html_content *htmlc = pw;
|
||||
dom_event_target *node;
|
||||
dom_node_type type;
|
||||
dom_exception exc;
|
||||
|
||||
exc = dom_event_get_target(evt, &node);
|
||||
if ((exc == DOM_NO_ERR) && (node != NULL)) {
|
||||
exc = dom_node_get_node_type(node, &type);
|
||||
if ((exc == DOM_NO_ERR) && (type == DOM_ELEMENT_NODE)) {
|
||||
/* an element node has been modified */
|
||||
dom_html_element_type tag_type;
|
||||
|
||||
exc = dom_html_element_get_tag_type(node, &tag_type);
|
||||
if (exc != DOM_NO_ERR) {
|
||||
tag_type = DOM_HTML_ELEMENT_TYPE__UNKNOWN;
|
||||
}
|
||||
|
||||
switch (tag_type) {
|
||||
case DOM_HTML_ELEMENT_TYPE_SCRIPT:
|
||||
dom_SCRIPT_showed_up(htmlc, (dom_html_script_element *) node);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
dom_node_unref(node);
|
||||
}
|
||||
}
|
||||
|
||||
/* callback for DOMSubtreeModified end type */
|
||||
static void
|
||||
dom_default_action_DOMSubtreeModified_cb(struct dom_event *evt, void *pw)
|
||||
{
|
||||
@ -787,11 +872,13 @@ dom_event_fetcher(dom_string *type,
|
||||
dom_default_action_phase phase,
|
||||
void **pw)
|
||||
{
|
||||
NSLOG(netsurf, DEEPDEBUG, "type:%s", dom_string_data(type));
|
||||
NSLOG(netsurf, DEEPDEBUG, "phase:%d type:%s", phase, dom_string_data(type));
|
||||
|
||||
if (phase == DOM_DEFAULT_ACTION_END) {
|
||||
if (dom_string_isequal(type, corestring_dom_DOMNodeInserted)) {
|
||||
return dom_default_action_DOMNodeInserted_cb;
|
||||
} else if (dom_string_isequal(type, corestring_dom_DOMNodeInsertedIntoDocument)) {
|
||||
return dom_default_action_DOMNodeInsertedIntoDocument_cb;
|
||||
} else if (dom_string_isequal(type, corestring_dom_DOMSubtreeModified)) {
|
||||
return dom_default_action_DOMSubtreeModified_cb;
|
||||
}
|
||||
@ -847,6 +934,7 @@ html_create_html_data(html_content *c, const http_parameter *params)
|
||||
|
||||
c->parser = NULL;
|
||||
c->parse_completed = false;
|
||||
c->conversion_begun = false;
|
||||
c->document = NULL;
|
||||
c->quirks = DOM_DOCUMENT_QUIRKS_MODE_NONE;
|
||||
c->encoding = NULL;
|
||||
@ -948,6 +1036,7 @@ html_create_html_data(html_content *c, const http_parameter *params)
|
||||
(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;
|
||||
|
||||
@ -1185,14 +1274,21 @@ bool html_can_begin_conversion(html_content *htmlc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Cannot begin conversion if we already have */
|
||||
if (htmlc->conversion_begun)
|
||||
return false;
|
||||
|
||||
/* Cannot begin conversion if we're still fetching stuff */
|
||||
if (htmlc->base.active != 0)
|
||||
return false;
|
||||
|
||||
for (i = 0; i != htmlc->stylesheet_count; i++) {
|
||||
/* Cannot begin conversion if the stylesheets are modified */
|
||||
if (htmlc->stylesheets[i].modified)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* All is good, begin */
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1206,6 +1302,10 @@ html_begin_conversion(html_content *htmlc)
|
||||
dom_string *node_name = NULL;
|
||||
dom_hubbub_error error;
|
||||
|
||||
if (htmlc->conversion_begun)
|
||||
/* Conversion already began, so we are okay */
|
||||
return true;
|
||||
|
||||
/* 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
|
||||
@ -1247,8 +1347,11 @@ html_begin_conversion(html_content *htmlc)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* complete script execution */
|
||||
html_script_exec(htmlc);
|
||||
/* Conversion begins proper at this point */
|
||||
htmlc->conversion_begun = true;
|
||||
|
||||
/* complete script execution, including deferred scripts */
|
||||
html_script_exec(htmlc, true);
|
||||
|
||||
/* fire a simple event that bubbles named DOMContentLoaded at
|
||||
* the Document.
|
||||
|
@ -98,6 +98,7 @@ typedef struct html_content {
|
||||
|
||||
dom_hubbub_parser *parser; /**< Parser object handle */
|
||||
bool parse_completed; /**< Whether the parse has been completed */
|
||||
bool conversion_begun; /**< Whether or not the conversion has begun */
|
||||
|
||||
/** Document tree */
|
||||
dom_document *document;
|
||||
@ -313,9 +314,10 @@ dom_hubbub_error html_process_script(void *ctx, dom_node *node);
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#the-script-element
|
||||
*
|
||||
* \param htmlc html content.
|
||||
* \param allow_defer allow deferred execution, if not, only async scripts.
|
||||
* \return NSERROR_OK error code.
|
||||
*/
|
||||
nserror html_script_exec(html_content *htmlc);
|
||||
nserror html_script_exec(html_content *htmlc, bool allow_defer);
|
||||
|
||||
/**
|
||||
* Free all script resources and references for a html content.
|
||||
|
@ -55,7 +55,7 @@ static script_handler_t *select_script_handler(content_type ctype)
|
||||
|
||||
|
||||
/* exported internal interface documented in html/html_internal.h */
|
||||
nserror html_script_exec(html_content *c)
|
||||
nserror html_script_exec(html_content *c, bool allow_defer)
|
||||
{
|
||||
unsigned int i;
|
||||
struct html_script *s;
|
||||
@ -71,7 +71,7 @@ nserror html_script_exec(html_content *c)
|
||||
}
|
||||
|
||||
if ((s->type == HTML_SCRIPT_ASYNC) ||
|
||||
(s->type == HTML_SCRIPT_DEFER)) {
|
||||
(allow_defer && (s->type == HTML_SCRIPT_DEFER))) {
|
||||
/* ensure script content is present */
|
||||
if (s->data.handle == NULL)
|
||||
continue;
|
||||
@ -200,6 +200,13 @@ convert_script_async_cb(hlcache_handle *script,
|
||||
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) {
|
||||
html_script_exec(parent, false);
|
||||
}
|
||||
|
||||
return NSERROR_OK;
|
||||
}
|
||||
|
||||
@ -304,9 +311,11 @@ convert_script_sync_cb(hlcache_handle *script,
|
||||
}
|
||||
|
||||
/* continue parse */
|
||||
err = dom_hubbub_parser_pause(parent->parser, false);
|
||||
if (err != DOM_HUBBUB_OK) {
|
||||
NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
|
||||
if (parent->parser != NULL) {
|
||||
err = dom_hubbub_parser_pause(parent->parser, false);
|
||||
if (err != DOM_HUBBUB_OK) {
|
||||
NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@ -328,9 +337,11 @@ convert_script_sync_cb(hlcache_handle *script,
|
||||
s->already_started = true;
|
||||
|
||||
/* continue parse */
|
||||
err = dom_hubbub_parser_pause(parent->parser, false);
|
||||
if (err != DOM_HUBBUB_OK) {
|
||||
NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
|
||||
if (parent->parser != NULL) {
|
||||
err = dom_hubbub_parser_pause(parent->parser, false);
|
||||
if (err != DOM_HUBBUB_OK) {
|
||||
NSLOG(netsurf, INFO, "unpause returned 0x%x", err);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@ -402,6 +413,12 @@ exec_src_script(html_content *c,
|
||||
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;
|
||||
|
@ -427,6 +427,7 @@ method Node::appendChild()
|
||||
dom_exception err;
|
||||
dom_node *spare;
|
||||
|
||||
NSLOG(dukky, DEEPDEBUG, "About to append %p to %p", other->node, priv->node);
|
||||
err = dom_node_append_child(priv->node, other->node, &spare);
|
||||
if (err != DOM_NO_ERR) return 0;
|
||||
dukky_push_node(ctx, spare);
|
||||
|
1
test/js/inserted-script-async.js
Normal file
1
test/js/inserted-script-async.js
Normal file
@ -0,0 +1 @@
|
||||
console.log("External asynchronous dynamism!");
|
1
test/js/inserted-script-defer.js
Normal file
1
test/js/inserted-script-defer.js
Normal file
@ -0,0 +1 @@
|
||||
console.log("External deferred dynamism!");
|
39
test/js/inserted-script.html
Normal file
39
test/js/inserted-script.html
Normal file
@ -0,0 +1,39 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Inserted script test</title>
|
||||
<script>
|
||||
/* After one second, insert an inline script element */
|
||||
setTimeout(function() {
|
||||
var div = document.createElement("DIV");
|
||||
var script = document.createElement("SCRIPT");
|
||||
var textnode = document.createTextNode("console.log(\"Dynamism\");");
|
||||
script.appendChild(textnode);
|
||||
div.appendChild(script);
|
||||
document.body.appendChild(div);
|
||||
}, 1000);
|
||||
/* After two seconds, insert a script element for immediate fetch */
|
||||
setTimeout(function() {
|
||||
var script = document.createElement("SCRIPT");
|
||||
script.setAttribute("src", "inserted-script.js");
|
||||
document.body.appendChild(script);
|
||||
}, 2000);
|
||||
/* After three seconds, insert a script element for async fetch */
|
||||
setTimeout(function() {
|
||||
var script = document.createElement("SCRIPT");
|
||||
script.setAttribute("src", "inserted-script-async.js");
|
||||
script.setAttribute("async", "");
|
||||
document.body.appendChild(script);
|
||||
}, 3000);
|
||||
/* After four seconds, insert a script element for deferred fetch */
|
||||
setTimeout(function() {
|
||||
var script = document.createElement("SCRIPT");
|
||||
script.setAttribute("src", "inserted-script-defer.js");
|
||||
script.setAttribute("defer", "");
|
||||
document.body.appendChild(script);
|
||||
}, 4000);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Check the log
|
||||
</body>
|
||||
</html>
|
1
test/js/inserted-script.js
Normal file
1
test/js/inserted-script.js
Normal file
@ -0,0 +1 @@
|
||||
console.log("External dynamism!");
|
Loading…
Reference in New Issue
Block a user