From 19b45fb494358838be8b3175fde9e3ab55ef5bfa Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 4 May 2019 12:18:21 +0100 Subject: Support script insertion after conversion has begun Signed-off-by: Daniel Silverstone --- content/handlers/html/html.c | 111 ++++++++++++++++++++++++++++++++-- content/handlers/html/html_internal.h | 4 +- content/handlers/html/html_script.c | 33 +++++++--- 3 files changed, 135 insertions(+), 13 deletions(-) (limited to 'content/handlers/html') diff --git a/content/handlers/html/html.c b/content/handlers/html/html.c index 62f625f6b..30af90f97 100644 --- a/content/handlers/html/html.c +++ b/content/handlers/html/html.c @@ -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. diff --git a/content/handlers/html/html_internal.h b/content/handlers/html/html_internal.h index 77354c369..a4ae1da57 100644 --- a/content/handlers/html/html_internal.h +++ b/content/handlers/html/html_internal.h @@ -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. diff --git a/content/handlers/html/html_script.c b/content/handlers/html/html_script.c index 80992b9dd..b404d2edf 100644 --- a/content/handlers/html/html_script.c +++ b/content/handlers/html/html_script.c @@ -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; -- cgit v1.2.3