diff options
author | Vincent Sanders <vince@netsurf-browser.org> | 2012-06-19 09:35:51 +0000 |
---|---|---|
committer | Vincent Sanders <vince@netsurf-browser.org> | 2012-06-19 09:35:51 +0000 |
commit | 49effe103a260a60949d3c95494ae81b031799d9 (patch) | |
tree | 5a205757088c1f1d978eea067a7bf186568c582a | |
parent | a229a35767760c71c94ae66567f7b172c5b8c356 (diff) | |
download | netsurf-49effe103a260a60949d3c95494ae81b031799d9.tar.gz |
restructure javascript binding layout to be more explicit
fix html content so it correctly loads all the script tags
svn path=/trunk/netsurf/; revision=13968
-rw-r--r-- | Makefile.sources | 4 | ||||
-rw-r--r-- | desktop/options_main.h | 8 | ||||
-rw-r--r-- | javascript/jsapi.c (renamed from javascript/js.c) | 6 | ||||
-rw-r--r-- | javascript/jsapi.h (renamed from javascript/global.h) | 8 | ||||
-rw-r--r-- | javascript/jsapi/global.c (renamed from javascript/global.c) | 4 | ||||
-rw-r--r-- | javascript/none.c (renamed from javascript/nojs.c) | 0 | ||||
-rw-r--r-- | render/html.c | 765 | ||||
-rw-r--r-- | render/html.h | 20 | ||||
-rw-r--r-- | render/html_internal.h | 10 |
9 files changed, 589 insertions, 236 deletions
diff --git a/Makefile.sources b/Makefile.sources index 8806071c1..50c8a9b50 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -29,9 +29,9 @@ S_DESKTOP := cookies.c history_global_core.c hotlist.c knockout.c \ # Javascript sources ifeq ($(NETSURF_USE_JS),YES) -S_JAVASCRIPT += js.c global.c +S_JAVASCRIPT += jsapi.c jsapi/global.c else -S_JAVASCRIPT += nojs.c +S_JAVASCRIPT += none.c endif # S_COMMON are sources common to all builds diff --git a/desktop/options_main.h b/desktop/options_main.h index d56d3a446..d44c26505 100644 --- a/desktop/options_main.h +++ b/desktop/options_main.h @@ -70,7 +70,7 @@ int disc_cache_age; \ /** Whether to block advertisements */ \ bool block_ads; \ - /** Disable website tracking, see \ + /** Disable website tracking, see \ * http://www.w3.org/Submission/2011/SUBM-web-tracking-protection-20110224/#dnt-uas */ \ bool do_not_track; \ /** Minimum GIF animation delay */ \ @@ -83,6 +83,8 @@ bool background_images; \ /** Whether to animate images */ \ bool animate_images; \ + /** Whether to execute javascript */ \ + bool enable_javascript; \ /** How many days to retain URL data for */ \ int expire_url; \ /** Default font family */ \ @@ -262,7 +264,8 @@ .max_cached_fetch_handles = 6, \ .suppress_curl_debug = true, \ .target_blank = true, \ - .button_2_tab = true + .button_2_tab = true, \ + .enable_javascript = true #define NSOPTION_MAIN_SYS_COLOUR_DEFAULTS \ .sys_colour_ActiveBorder = 0x00000000, \ @@ -320,6 +323,7 @@ { "foreground_images", OPTION_BOOL, &nsoptions.foreground_images }, \ { "background_images", OPTION_BOOL, &nsoptions.background_images }, \ { "animate_images", OPTION_BOOL, &nsoptions.animate_images }, \ + { "enable_javasctipt", OPTION_BOOL, &nsoptions.enable_javascript}, \ { "expire_url", OPTION_INTEGER, &nsoptions.expire_url }, \ { "font_default", OPTION_INTEGER, &nsoptions.font_default }, \ { "ca_bundle", OPTION_STRING, &nsoptions.ca_bundle }, \ diff --git a/javascript/js.c b/javascript/jsapi.c index 833e01785..6f5b510ef 100644 --- a/javascript/js.c +++ b/javascript/jsapi.c @@ -19,7 +19,7 @@ #include "mozjs/jsapi.h" #include "content/content.h" -#include "javascript/global.h" +#include "javascript/jsapi.h" #include "javascript/js.h" #include "utils/log.h" @@ -50,7 +50,7 @@ void js_finalise(void) /* The error reporter callback. */ static void js_reportError(JSContext *cx, const char *message, JSErrorReport *report) { - LOG(("%s:%u:%s\n", + LOG(("%s:%u:%s", report->filename ? report->filename : "<no filename>", (unsigned int) report->lineno, message)); @@ -131,7 +131,7 @@ jsobject *js_newcompartment(jscontext *ctx, struct content* c) JS_SetContextPrivate(cx, c); /* private pointer to content */ - js_new_globalfunc(cx, global); + jsapi_new_globalfunc(cx, global); /* Populate the global object with the standard globals, like Object and Array. */ diff --git a/javascript/global.h b/javascript/jsapi.h index 329c82c8d..ce2051148 100644 --- a/javascript/global.h +++ b/javascript/jsapi.h @@ -17,12 +17,12 @@ */ /** \file - * spidermonkey engine global functions. + * spidermonkey jsapi bindings. */ -#ifndef _NETSURF_JAVASCRIPT_GLOBAL_H_ -#define _NETSURF_JAVASCRIPT_GLOBAL_H_ +#ifndef _NETSURF_JAVASCRIPT_JSAPI_H_ +#define _NETSURF_JAVASCRIPT_JSAPI_H_ -bool js_new_globalfunc(JSContext *cx, JSObject *global); +bool jsapi_new_globalfunc(JSContext *cx, JSObject *global); #endif diff --git a/javascript/global.c b/javascript/jsapi/global.c index c2033d4fc..b86400428 100644 --- a/javascript/global.c +++ b/javascript/jsapi/global.c @@ -19,7 +19,7 @@ #include "mozjs/jsapi.h" #include "content/content.h" -#include "javascript/global.h" +#include "javascript/jsapi.h" #include "utils/log.h" static JSBool jsalert(JSContext *cx, uintN argc, jsval *vp) @@ -54,7 +54,7 @@ static JSFunctionSpec global_functions[] = JS_FS_END }; -bool js_new_globalfunc(JSContext *cx, JSObject *global) +bool jsapi_new_globalfunc(JSContext *cx, JSObject *global) { return JS_DefineFunctions(cx, global, global_functions); } diff --git a/javascript/nojs.c b/javascript/none.c index ec0ccc0f8..ec0ccc0f8 100644 --- a/javascript/nojs.c +++ b/javascript/none.c diff --git a/render/html.c b/render/html.c index e7ebbd54c..a3b5e01a2 100644 --- a/render/html.c +++ b/render/html.c @@ -93,6 +93,7 @@ static dom_string *html_dom_string_title; static dom_string *html_dom_string_base; static dom_string *html_dom_string_link; static dom_string *html_dom_string_script; +static dom_string *html_dom_string_src; dom_string *html_dom_string_target; static dom_string *html_dom_string__parent; static dom_string *html_dom_string__self; @@ -147,6 +148,8 @@ html_create_html_data(html_content *c, const http_parameter *params) c->box = NULL; c->font_func = &nsfont; c->scrollbar = NULL; + c->scripts_count = 0; + c->scripts = NULL; c->jscontext = NULL; if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) { @@ -346,39 +349,6 @@ encoding_change: } } -/** process script node */ -static bool html_process_script(html_content *c, dom_node *node) -{ - dom_exception exc; /* returned by libdom functions */ - dom_string *script; - - bool success = true; - - /* ensure javascript context is available */ - if (c->jscontext == NULL) { - union content_msg_data msg_data; - - msg_data.jscontext = &c->jscontext; - content_broadcast(&c->base, CONTENT_MSG_GETCTX, msg_data); - LOG(("javascript context %p ", c->jscontext)); - if (c->jscontext == NULL) { - return false; - } - } - - exc = dom_node_get_text_content(node, &script); - if ((exc != DOM_NO_ERR) || (script == NULL)) { - return false; - } - - js_exec(c->jscontext, - dom_string_data(script), - dom_string_byte_length(script)) ; - - dom_string_unref(script); - - return success; -} /** process link node */ static bool html_process_link(html_content *c, dom_node *node) @@ -581,10 +551,7 @@ static bool html_head(html_content *c, dom_node *head) } else if (dom_string_caseless_isequal(node_name, html_dom_string_link)) { html_process_link(c, node); - } else if (dom_string_caseless_isequal(node_name, - html_dom_string_script)) { - html_process_script(c, node); - } + } } } @@ -958,6 +925,8 @@ html_object_callback(hlcache_handle *object, case CONTENT_MSG_DONE: c->base.active--; + LOG(("%d fetches active", c->base.active)); + html_object_done(box, object, o->background); if (c->base.status != CONTENT_STATUS_LOADING && @@ -985,6 +954,7 @@ html_object_callback(hlcache_handle *object, o->content = NULL; c->base.active--; + LOG(("%d fetches active", c->base.active)); content_add_error(&c->base, "?", 0); html_set_status(c, event->data.error); @@ -1107,8 +1077,10 @@ static bool html_replace_object(struct content_html_object *object, nsurl *url) if (object->content != NULL) { /* remove existing object */ - if (content_get_status(object->content) != CONTENT_STATUS_DONE) + if (content_get_status(object->content) != CONTENT_STATUS_DONE) { c->base.active--; + LOG(("%d fetches active", c->base.active)); + } hlcache_handle_release(object->content); object->content = NULL; @@ -1128,6 +1100,8 @@ static bool html_replace_object(struct content_html_object *object, nsurl *url) for (page = c; page != NULL; page = page->page) { page->base.active++; + LOG(("%d fetches active", c->base.active)); + page->base.status = CONTENT_STATUS_READY; } @@ -1375,9 +1349,10 @@ html_convert_css_callback(hlcache_handle *css, break; case CONTENT_MSG_DONE: - LOG(("got stylesheet '%s'", + LOG(("done stylesheet slot %d '%s'", i, nsurl_access(hlcache_handle_get_url(css)))); parent->base.active--; + LOG(("%d fetches active", parent->base.active)); break; case CONTENT_MSG_ERROR: @@ -1387,6 +1362,7 @@ html_convert_css_callback(hlcache_handle *css, hlcache_handle_release(css); s->data.external = NULL; parent->base.active--; + LOG(("%d fetches active", parent->base.active)); content_add_error(&parent->base, "?", 0); break; @@ -1542,11 +1518,13 @@ html_process_style_element(html_content *c, } c->base.active++; + LOG(("%d fetches active", c->base.active)); /* Convert the content -- manually, as we want the result */ if (nscss_convert_css_data(sheet) != CSS_OK) { /* conversion failed */ c->base.active--; + LOG(("%d fetches active", c->base.active)); nscss_destroy_css_data(sheet); talloc_free(sheet); sheet = NULL; @@ -1564,95 +1542,22 @@ no_memory: return false; } -/** - * Process inline stylesheets and fetch linked stylesheets. - * - * Uses STYLE and LINK elements inside and outside HEAD + +/* depth-first walk the dom calling callback for each element * - * \param c content structure - * \param html xml node of html element - * \return true on success, false if an error occurred + * @param root the dom node to use as the root of the tree walk + * @return true if all nodes were examined, false if the callback terminated + * the walk early. */ - -static bool html_find_stylesheets(html_content *c, dom_node *html) +static bool +html_treewalk_dom(dom_node *root, + bool (*callback)(dom_node *node, dom_string *name, void *ctx), + void *ctx) { - content_type accept = CONTENT_CSS; dom_node *node; - unsigned int i = STYLESHEET_START; - union content_msg_data msg_data; - struct html_stylesheet *stylesheets; - hlcache_child_context child; - nserror ns_error; - nsurl *joined; - - child.charset = c->encoding; - child.quirks = c->base.quirks; - - /* stylesheet 0 is the base style sheet, - * stylesheet 1 is the quirks mode style sheet, - * stylesheet 2 is the adblocking stylesheet, - * stylesheet 3 is the user stylesheet */ - c->stylesheets = talloc_array(c, struct html_stylesheet, - STYLESHEET_START); - if (c->stylesheets == NULL) - goto no_memory; - c->stylesheets[STYLESHEET_BASE].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_BASE].data.external = NULL; - c->stylesheets[STYLESHEET_QUIRKS].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_QUIRKS].data.external = NULL; - c->stylesheets[STYLESHEET_ADBLOCK].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_ADBLOCK].data.external = NULL; - c->stylesheets[STYLESHEET_USER].type = HTML_STYLESHEET_EXTERNAL; - c->stylesheets[STYLESHEET_USER].data.external = NULL; - c->stylesheet_count = STYLESHEET_START; - - c->base.active = 0; - - ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, accept, - &c->stylesheets[STYLESHEET_BASE].data.external); - if (ns_error != NSERROR_OK) - goto no_memory; - - c->base.active++; - - if (c->quirks == BINDING_QUIRKS_MODE_FULL) { - ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url, - 0, content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, accept, - &c->stylesheets[STYLESHEET_QUIRKS]. - data.external); - if (ns_error != NSERROR_OK) - goto no_memory; - - c->base.active++; - } + bool result = true;; - if (nsoption_bool(block_ads)) { - ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url, - 0, content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, accept, - &c->stylesheets[STYLESHEET_ADBLOCK]. - data.external); - if (ns_error != NSERROR_OK) - goto no_memory; - - c->base.active++; - } - - ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, accept, - &c->stylesheets[STYLESHEET_USER].data.external); - if (ns_error != NSERROR_OK) - goto no_memory; - - c->base.active++; - - /* depth-first search the tree for link elements */ - - node = dom_node_ref(html); /* tree root */ + node = dom_node_ref(root); /* tree root */ while (node != NULL) { dom_node *next = NULL; @@ -1723,126 +1628,525 @@ static bool html_find_stylesheets(html_content *c, dom_node *html) assert(node != NULL); exc = dom_node_get_node_type(node, &type); - if (exc != DOM_NO_ERR || type != DOM_ELEMENT_NODE) + if ((exc != DOM_NO_ERR) || (type != DOM_ELEMENT_NODE)) continue; exc = dom_node_get_node_name(node, &name); if (exc != DOM_NO_ERR) continue; - if (strcmp(dom_string_data(name), "link") == 0) { - dom_string *rel, *type_attr, *media, *href; + result = callback(node, name, ctx); - dom_string_unref(name); + dom_string_unref(name); - /* rel=<space separated list, including 'stylesheet'> */ - exc = dom_element_get_attribute(node, - html_dom_string_rel, &rel); - if (exc != DOM_NO_ERR || rel == NULL) - continue; + if (result == false) { + break; /* callback caused early termination */ + } + + } + return result; +} - if (strcasestr(dom_string_data(rel), - "stylesheet") == 0) { - dom_string_unref(rel); - continue; - } else if (strcasestr(dom_string_data(rel), - "alternate") != 0) { - /* Ignore alternate stylesheets */ - dom_string_unref(rel); - continue; - } - dom_string_unref(rel); - - /* type='text/css' or not present */ - exc = dom_element_get_attribute(node, - html_dom_string_type, &type_attr); - if (exc == DOM_NO_ERR && type_attr != NULL) { - if (strcmp(dom_string_data(type_attr), - "text/css") != 0) { - dom_string_unref(type_attr); - continue; - } - dom_string_unref(type_attr); - } +/* attempt to progress script execution + * + * execute scripts using algorithm found in: + * http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#the-script-element + * + */ +static bool html_scripts_exec(html_content *c) +{ + unsigned int i; + struct html_script *s; - /* media contains 'screen' or 'all' or not present */ - exc = dom_element_get_attribute(node, - html_dom_string_media, &media); - if (exc == DOM_NO_ERR && media != NULL) { - if (strcasestr(dom_string_data(media), - "screen") == NULL && - strcasestr( - dom_string_data(media), - "all") == NULL) { - dom_string_unref(media); - continue; + if (c->jscontext == NULL) + return false; + + for (i = 0, s = c->scripts; i != c->scripts_count; i++, s++) { + if (s->already_started) { + continue; + } + + assert((s->type == HTML_SCRIPT_EXTERNAL) || + (s->type == HTML_SCRIPT_INTERNAL)); + + if (s->type == HTML_SCRIPT_EXTERNAL) { + if (s->data.external == NULL) + continue; /* script not present - skip */ + + if (content_get_status(s->data.external) == CONTENT_STATUS_ERROR) + continue; /* script content errored - skip */ + + if (content_get_status(s->data.external) == CONTENT_STATUS_DONE) { + /* external script is now available + * execute it and continue + */ + const char *data; + unsigned long size; + data = content_get_source_data(s->data.external, &size ); + js_exec(c->jscontext, data, size); + s->already_started = true; + + } else { + /* script not yet available */ + if (!s->defer && !s->async) { + break; /* not deferable or async */ } - dom_string_unref(media); } + } else { + js_exec(c->jscontext, + dom_string_data(s->data.internal), + dom_string_byte_length(s->data.internal)); + s->already_started = true; + } + } - /* href='...' */ - exc = dom_element_get_attribute(node, - html_dom_string_href, &href); - if (exc != DOM_NO_ERR || href == NULL) - continue; + return true; +} - /* TODO: only the first preferred stylesheets (ie. - * those with a title attribute) should be loaded - * (see HTML4 14.3) */ +/* create new html script entry */ +static struct html_script *html_process_new_script(html_content *c, enum html_script_type type) +{ + struct html_script *nscript; + /* add space for new script entry */ + nscript = realloc(c->scripts, + sizeof(struct html_script) * (c->scripts_count + 1)); + if (nscript == NULL) { + return NULL; + } - ns_error = nsurl_join(c->base_url, - dom_string_data(href), &joined); - if (ns_error != NSERROR_OK) { - dom_string_unref(href); - goto no_memory; - } - dom_string_unref(href); - - LOG(("linked stylesheet %i '%s'", i, - nsurl_access(joined))); - - /* start fetch */ - stylesheets = talloc_realloc(c, - c->stylesheets, - struct html_stylesheet, i + 1); - if (stylesheets == NULL) { - nsurl_unref(joined); - goto no_memory; - } + c->scripts = nscript; - c->stylesheets = stylesheets; - c->stylesheet_count++; - c->stylesheets[i].type = HTML_STYLESHEET_EXTERNAL; - ns_error = hlcache_handle_retrieve(joined, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, - accept, - &c->stylesheets[i].data.external); + /* increment script entry count */ + nscript = &c->scripts[c->scripts_count]; + c->scripts_count++; - nsurl_unref(joined); + nscript->already_started = false; + nscript->parser_inserted = false; + nscript->force_async = true; + nscript->ready_exec = false; + nscript->async = false; + nscript->defer = false; - if (ns_error != NSERROR_OK) - goto no_memory; + nscript->type = type; - c->base.active++; + return nscript; +} - i++; - } else if (strcmp(dom_string_data(name), "style") == 0) { - dom_string_unref(name); +/** + * Callback for fetchcache() for linked stylesheets. + */ - if (!html_process_style_element(c, &i, node)) - return false; - } else { - dom_string_unref(name); +static nserror +html_convert_script_callback(hlcache_handle *script, + const hlcache_event *event, + void *pw) +{ + html_content *parent = pw; + unsigned int i; + struct html_script *s; + + /* Find sheet */ + for (i = 0, s = parent->scripts; i != parent->scripts_count; i++, s++) { + if (s->type == HTML_SCRIPT_EXTERNAL && + s->data.external == script) + break; + } + + assert(i != parent->scripts_count); + + switch (event->type) { + case CONTENT_MSG_LOADING: + break; + + case CONTENT_MSG_READY: + break; + + case CONTENT_MSG_DONE: + LOG(("script %d done '%s'", i, + nsurl_access(hlcache_handle_get_url(script)))); + parent->base.active--; + LOG(("%d fetches active", parent->base.active)); + + /* script finished loading so try and continue execution */ + html_scripts_exec(parent); + break; + + case CONTENT_MSG_ERROR: + LOG(("script %s failed: %s", + nsurl_access(hlcache_handle_get_url(script)), + event->data.error)); + hlcache_handle_release(script); + s->data.external = NULL; + parent->base.active--; + LOG(("%d fetches active", parent->base.active)); + content_add_error(&parent->base, "?", 0); + + /* script failed loading so try and continue execution */ + html_scripts_exec(parent); + + break; + + case CONTENT_MSG_STATUS: + html_set_status(parent, content_get_status_message(script)); + content_broadcast(&parent->base, CONTENT_MSG_STATUS, + event->data); + break; + + default: + assert(0); + } + + if (parent->base.active == 0) + html_finish_conversion(parent); + + return NSERROR_OK; +} + +/** process script node + * + * + */ +static bool +html_process_script(dom_node *node, dom_string *name, void *ctx) +{ + html_content *c = (html_content *)ctx; + dom_exception exc; /* returned by libdom functions */ + dom_string *src, *script; + struct html_script *nscript; + union content_msg_data msg_data; + + if (!dom_string_isequal(name, html_dom_string_script)) + return true; /* was not a script tag, carry on the walk */ + + /* ensure javascript context is available */ + if (c->jscontext == NULL) { + union content_msg_data msg_data; + + msg_data.jscontext = &c->jscontext; + content_broadcast(&c->base, CONTENT_MSG_GETCTX, msg_data); + LOG(("javascript context %p ", c->jscontext)); + if (c->jscontext == NULL) { + /* no context and it could not be created, abort */ + return false; } } - assert(c->stylesheet_count == i); + exc = dom_element_get_attribute(node, html_dom_string_src, &src); + if (exc != DOM_NO_ERR || src == NULL) { + /* does not appear to be a src so script is inline content */ + exc = dom_node_get_text_content(node, &script); + if ((exc != DOM_NO_ERR) || (script == NULL)) { + return true; /* no contents, skip */ + } + + nscript = html_process_new_script(c, HTML_STYLESHEET_INTERNAL); + if (nscript == NULL) { + dom_string_unref(script); + goto html_process_script_no_memory; + } + + nscript->data.internal = script; + + /* type */ + /* charset (encoding) */ + } else { + /* script with a src tag */ + nserror ns_error; + nsurl *joined; + hlcache_child_context child; + + child.charset = c->encoding; + child.quirks = c->base.quirks; + + nscript = html_process_new_script(c, HTML_STYLESHEET_EXTERNAL); + if (nscript == NULL) { + dom_string_unref(src); + goto html_process_script_no_memory; + } + + /* type */ + /* charset (encoding) */ + + ns_error = nsurl_join(c->base_url, dom_string_data(src), &joined); + dom_string_unref(src); + if (ns_error != NSERROR_OK) { + goto html_process_script_no_memory; + } + + LOG(("script %i '%s'", c->scripts_count, nsurl_access(joined))); + + ns_error = hlcache_handle_retrieve(joined, + 0, + content_get_url(&c->base), + NULL, + html_convert_script_callback, + c, + &child, + CONTENT_ANY, + &nscript->data.external); + + nsurl_unref(joined); + + if (ns_error != NSERROR_OK) + goto html_process_script_no_memory; + + c->base.active++; /* ensure base content knows the fetch is active */ + LOG(("%d fetches active", c->base.active)); + + } + + return true; + +html_process_script_no_memory: + msg_data.error = messages_get("NoMemory"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + return false; /* out of memory, abort walk */ +} + +/** + * Process inline script and fetch linked scripts. + * + * + * + * \param c content structure + * \param html dom node of html element + * \return true on success, false if an error occurred + */ +static bool html_find_scripts(html_content *c, dom_node *html) +{ + return html_treewalk_dom(html, html_process_script, c); +} + +struct find_stylesheet_ctx { + unsigned int count; + hlcache_child_context child; + html_content *c; +}; + +/** callback to process stylesheet elements + */ +static bool +html_process_stylesheet(dom_node *node, dom_string *name, void *vctx) +{ + struct find_stylesheet_ctx *ctx = (struct find_stylesheet_ctx *)vctx; + dom_string *rel, *type_attr, *media, *href; + struct html_stylesheet *stylesheets; + nsurl *joined; + union content_msg_data msg_data; + dom_exception exc; + nserror ns_error; + + /* deal with style nodes */ + if (strcmp(dom_string_data(name), "style") == 0) { + if (!html_process_style_element(ctx->c, &ctx->count, node)) + return false; + return true; + } + + /* if it is not a link node skip it */ + if (strcmp(dom_string_data(name), "link") != 0) { + return true; + } + + /* rel=<space separated list, including 'stylesheet'> */ + exc = dom_element_get_attribute(node, + html_dom_string_rel, &rel); + if (exc != DOM_NO_ERR || rel == NULL) + return true; + + if (strcasestr(dom_string_data(rel), "stylesheet") == 0) { + dom_string_unref(rel); + return true; + } else if (strcasestr(dom_string_data(rel), "alternate") != 0) { + /* Ignore alternate stylesheets */ + dom_string_unref(rel); + return true; + } + dom_string_unref(rel); + + /* type='text/css' or not present */ + exc = dom_element_get_attribute(node, html_dom_string_type, &type_attr); + if (exc == DOM_NO_ERR && type_attr != NULL) { + if (strcmp(dom_string_data(type_attr), "text/css") != 0) { + dom_string_unref(type_attr); + return true; + } + dom_string_unref(type_attr); + } + + /* media contains 'screen' or 'all' or not present */ + exc = dom_element_get_attribute(node, html_dom_string_media, &media); + if (exc == DOM_NO_ERR && media != NULL) { + if (strcasestr(dom_string_data(media), "screen") == NULL && + strcasestr(dom_string_data(media), "all") == NULL) { + dom_string_unref(media); + return true; + } + dom_string_unref(media); + } + + /* href='...' */ + exc = dom_element_get_attribute(node, html_dom_string_href, &href); + if (exc != DOM_NO_ERR || href == NULL) + return true; + + /* TODO: only the first preferred stylesheets (ie. + * those with a title attribute) should be loaded + * (see HTML4 14.3) */ + + ns_error = nsurl_join(ctx->c->base_url, dom_string_data(href), &joined); + if (ns_error != NSERROR_OK) { + dom_string_unref(href); + goto no_memory; + } + dom_string_unref(href); + + LOG(("linked stylesheet %i '%s'", ctx->count, nsurl_access(joined))); + + /* start fetch */ + stylesheets = talloc_realloc(ctx->c, + ctx->c->stylesheets, + struct html_stylesheet, + ctx->count + 1); + if (stylesheets == NULL) { + nsurl_unref(joined); + goto no_memory; + } + + ctx->c->stylesheets = stylesheets; + ctx->c->stylesheet_count++; + ctx->c->stylesheets[ctx->count].type = HTML_STYLESHEET_EXTERNAL; + ns_error = hlcache_handle_retrieve(joined, + 0, + content_get_url(&ctx->c->base), + NULL, + html_convert_css_callback, + ctx->c, + &ctx->child, + CONTENT_CSS, + &ctx->c->stylesheets[ctx->count].data.external); + + nsurl_unref(joined); + + if (ns_error != NSERROR_OK) + goto no_memory; + + ctx->c->base.active++; + LOG(("%d fetches active", ctx->c->base.active)); + + ctx->count++; return true; no_memory: msg_data.error = messages_get("NoMemory"); + content_broadcast(&ctx->c->base, CONTENT_MSG_ERROR, msg_data); + return false; + +} + + +/** + * Process inline stylesheets and fetch linked stylesheets. + * + * Uses STYLE and LINK elements inside and outside HEAD + * + * \param c content structure + * \param html dom node of html element + * \return true on success, false if an error occurred + */ + +static bool html_find_stylesheets(html_content *c, dom_node *html) +{ + content_type accept = CONTENT_CSS; + union content_msg_data msg_data; + nserror ns_error; + bool result; + struct find_stylesheet_ctx ctx; + + /* setup context */ + ctx.c = c; + ctx.count = STYLESHEET_START; + + ctx.child.charset = c->encoding; + ctx.child.quirks = c->base.quirks; + + /* stylesheet 0 is the base style sheet, + * stylesheet 1 is the quirks mode style sheet, + * stylesheet 2 is the adblocking stylesheet, + * stylesheet 3 is the user stylesheet */ + c->stylesheets = talloc_array(c, struct html_stylesheet, + STYLESHEET_START); + if (c->stylesheets == NULL) + goto html_find_stylesheets_no_memory; + + c->stylesheets[STYLESHEET_BASE].type = HTML_STYLESHEET_EXTERNAL; + c->stylesheets[STYLESHEET_BASE].data.external = NULL; + c->stylesheets[STYLESHEET_QUIRKS].type = HTML_STYLESHEET_EXTERNAL; + c->stylesheets[STYLESHEET_QUIRKS].data.external = NULL; + c->stylesheets[STYLESHEET_ADBLOCK].type = HTML_STYLESHEET_EXTERNAL; + c->stylesheets[STYLESHEET_ADBLOCK].data.external = NULL; + c->stylesheets[STYLESHEET_USER].type = HTML_STYLESHEET_EXTERNAL; + c->stylesheets[STYLESHEET_USER].data.external = NULL; + c->stylesheet_count = STYLESHEET_START; + + ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0, + content_get_url(&c->base), NULL, + html_convert_css_callback, c, &ctx.child, accept, + &c->stylesheets[STYLESHEET_BASE].data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; + + c->base.active++; + LOG(("%d fetches active", c->base.active)); + + if (c->quirks == BINDING_QUIRKS_MODE_FULL) { + ns_error = hlcache_handle_retrieve(html_quirks_stylesheet_url, + 0, content_get_url(&c->base), NULL, + html_convert_css_callback, c, &ctx.child, accept, + &c->stylesheets[STYLESHEET_QUIRKS]. + data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; + + c->base.active++; + LOG(("%d fetches active", c->base.active)); + + } + + if (nsoption_bool(block_ads)) { + ns_error = hlcache_handle_retrieve(html_adblock_stylesheet_url, + 0, content_get_url(&c->base), NULL, + html_convert_css_callback, c, &ctx.child, accept, + &c->stylesheets[STYLESHEET_ADBLOCK]. + data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; + + c->base.active++; + LOG(("%d fetches active", c->base.active)); + + } + + ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0, + content_get_url(&c->base), NULL, + html_convert_css_callback, c, &ctx.child, accept, + &c->stylesheets[STYLESHEET_USER].data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; + + c->base.active++; + LOG(("%d fetches active", c->base.active)); + + + result = html_treewalk_dom(html, html_process_stylesheet, &ctx); + + assert(c->stylesheet_count == ctx.count); + + return result; + +html_find_stylesheets_no_memory: + msg_data.error = messages_get("NoMemory"); content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); return false; } @@ -2024,6 +2328,14 @@ static bool html_convert(struct content *c) } } + /* find script tags */ + if (nsoption_bool(enable_javascript)) { + /* @todo this ought to be done during parse */ + html_find_scripts(htmlc, html); + /* run as far as we can */ + html_scripts_run(htmlc); + } + /* get stylesheets */ if (html_find_stylesheets(htmlc, html) == false) return false; @@ -2091,6 +2403,7 @@ bool html_fetch_object(html_content *c, nsurl *url, struct box *box, c->num_objects++; c->base.active++; + LOG(("%d fetches active", c->base.active)); return true; } @@ -2099,12 +2412,6 @@ bool html_fetch_object(html_content *c, nsurl *url, struct box *box, - - - - - - /** * Stop loading a CONTENT_HTML. */ @@ -2141,6 +2448,8 @@ static void html_stop(struct content *c) object->content = NULL; c->active--; + LOG(("%d fetches active", c->active)); + } } @@ -2360,6 +2669,20 @@ static void html_destroy(struct content *c) } } + /* Free scripts */ + for (i = 0; i != html->scripts_count; i++) { + if (html->scripts[i].type == HTML_SCRIPT_EXTERNAL && + html->scripts[i].data.external != NULL) { + hlcache_handle_release( + html->scripts[i].data.external); + } else if (html->scripts[i].type == + HTML_SCRIPT_INTERNAL && + html->scripts[i].data.internal != NULL) { + dom_string_unref(html->scripts[i].data.internal); + } + } + free(html->scripts); + /* Free objects */ html_destroy_objects(html); } @@ -3034,6 +3357,7 @@ static void html_fini(void) HTML_DOM_STRING_UNREF(sizes); HTML_DOM_STRING_UNREF(title); HTML_DOM_STRING_UNREF(base); + HTML_DOM_STRING_UNREF(src); HTML_DOM_STRING_UNREF(script); HTML_DOM_STRING_UNREF(link); HTML_DOM_STRING_UNREF(target); @@ -3164,6 +3488,7 @@ nserror html_init(void) HTML_DOM_STRING_INTERN(base); HTML_DOM_STRING_INTERN(link); HTML_DOM_STRING_INTERN(script); + HTML_DOM_STRING_INTERN(src); HTML_DOM_STRING_INTERN(target); HTML_DOM_STRING_INTERN(_blank); HTML_DOM_STRING_INTERN(_self); diff --git a/render/html.h b/render/html.h index 7d2ef82cf..f0ab47d51 100644 --- a/render/html.h +++ b/render/html.h @@ -58,6 +58,26 @@ struct html_stylesheet { } data; /**< Sheet data */ }; +/** + * Container for scripts used by an HTML document + */ +struct html_script { + /** Type of script */ + enum html_script_type { HTML_SCRIPT_EXTERNAL, HTML_SCRIPT_INTERNAL } type; + union { + struct hlcache_handle *external; + struct dom_string *internal; + } data; /**< Script data */ + struct dom_string *script_type; + struct dom_string *encoding; + bool already_started; + bool parser_inserted; + bool force_async; + bool ready_exec; + bool async; + bool defer; +}; + /** An object (<img>, <object>, etc.) in a CONTENT_HTML document. */ struct content_html_object { diff --git a/render/html_internal.h b/render/html_internal.h index 3d9dea389..da3d686bd 100644 --- a/render/html_internal.h +++ b/render/html_internal.h @@ -58,6 +58,13 @@ typedef struct html_content { /** Font callback table */ const struct font_functions *font_func; + /** Number of entries in scripts */ + unsigned int scripts_count; + /** Scripts */ + struct html_script *scripts; + /** javascript context */ + struct jscontext *jscontext; + /** Number of entries in stylesheet_content. */ unsigned int stylesheet_count; /** Stylesheets. Each may be NULL. */ @@ -105,9 +112,6 @@ typedef struct html_content { /** Context for free text search, or NULL if none */ struct search_context *search; - /** javascript context */ - struct jscontext *jscontext; - } html_content; |