From 5be20a0d6e8eb264d3d0d212413e218b8eeb7536 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Tue, 19 Feb 2013 23:51:16 +0000 Subject: change stylesheet fetching to be generated from the default dom events --- render/form.c | 4 + render/html.c | 2509 +++++++++++++++++++++++++++++---------------------------- 2 files changed, 1277 insertions(+), 1236 deletions(-) (limited to 'render') diff --git a/render/form.c b/render/form.c index 0c0eef2cb..46d5b28f6 100644 --- a/render/form.c +++ b/render/form.c @@ -212,6 +212,10 @@ struct form_control *form_new_control(void *node, form_control_type type) */ void form_add_control(struct form *form, struct form_control *control) { + if (form == NULL) { + return; + } + control->form = form; if (form->controls != NULL) { diff --git a/render/html.c b/render/html.c index 054663573..421b5c5ef 100644 --- a/render/html.c +++ b/render/html.c @@ -311,1593 +311,1632 @@ void html_finish_conversion(html_content *c) dom_node_unref(html); } - +/** + * Callback for fetchcache() for linked stylesheets. + */ static nserror -html_create_html_data(html_content *c, const http_parameter *params) +html_convert_css_callback(hlcache_handle *css, + const hlcache_event *event, + void *pw) { - lwc_string *charset; - nserror nerror; - dom_hubbub_parser_params parse_params; - dom_hubbub_error error; - - c->parser = NULL; - 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->bctx = NULL; - c->layout = NULL; - c->background_colour = NS_TRANSPARENT; - c->stylesheet_count = 0; - c->stylesheets = NULL; - c->select_ctx = NULL; - 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 = &nsfont; - c->drag_type = HTML_DRAG_NONE; - c->drag_owner.no_owner = true; - c->scripts_count = 0; - c->scripts = NULL; - c->jscontext = NULL; - - c->base.active = 1; /* The html content itself is active */ + html_content *parent = pw; + unsigned int i; + struct html_stylesheet *s; - if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) { - return NSERROR_NOMEM; + /* Find sheet */ + for (i = 0, s = parent->stylesheets; + i != parent->stylesheet_count; i++, s++) { + if (s->type == HTML_STYLESHEET_EXTERNAL && + s->data.external == css) + break; } - selection_prepare(&c->sel, (struct content *)c, true); - - nerror = http_parameter_list_find_item(params, html_charset, &charset); - if (nerror == NSERROR_OK) { - c->encoding = strdup(lwc_string_data(charset)); - - lwc_string_unref(charset); + assert(i != parent->stylesheet_count); - if (c->encoding == NULL) { - lwc_string_unref(c->universal); - c->universal = NULL; - return NSERROR_NOMEM; + switch (event->type) { + case CONTENT_MSG_LOADING: + break; - } - c->encoding_source = DOM_HUBBUB_ENCODING_SOURCE_HEADER; - } + case CONTENT_MSG_READY: + break; - /* Create the parser binding */ - parse_params.enc = c->encoding; - parse_params.fix_enc = true; - parse_params.enable_script = nsoption_bool(enable_javascript); - parse_params.msg = NULL; - parse_params.script = html_process_script; - parse_params.ctx = c; - parse_params.daf = NULL; + case CONTENT_MSG_DONE: + 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; - 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; + case CONTENT_MSG_ERROR: + LOG(("stylesheet %s failed: %s", + nsurl_access(hlcache_handle_get_url(css)), + event->data.error)); + 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; - parse_params.enc = c->encoding; + case CONTENT_MSG_STATUS: + if (event->data.explicit_status_text == NULL) { + /* Object content's status text updated */ + html_set_status(parent, + content_get_status_message(css)); + content_broadcast(&parent->base, CONTENT_MSG_STATUS, + event->data); + } else { + /* Object content wants to set explicit message */ + content_broadcast(&parent->base, CONTENT_MSG_STATUS, + event->data); + } + break; - error = dom_hubbub_parser_create(&parse_params, - &c->parser, - &c->document); + default: + assert(0); } - 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); - } + if (parent->base.active == 0) + html_finish_conversion(parent); return NSERROR_OK; - } /** - * Create a CONTENT_HTML. + * Initialise core stylesheets * - * The content_html_data structure is initialized and the HTML parser is - * created. + * \param c content structure + * \return true on success, false if an error occurred */ -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) +static bool html_init_stylesheets(html_content *c) { - html_content *html; - nserror error; - - html = calloc(1, sizeof(html_content)); - if (html == NULL) - return NSERROR_NOMEM; + nserror ns_error; + hlcache_child_context child; - error = content__init(&html->base, handler, imime_type, params, - llcache, fallback_charset, quirks); - if (error != NSERROR_OK) { - free(html); - return error; + if (c->stylesheets != NULL) { + return true; /* already initialised */ } - error = html_create_html_data(html, params); - if (error != NSERROR_OK) { - content_broadcast_errorcode(&html->base, error); - free(html); - return error; + /* stylesheet 0 is the base style sheet, + * stylesheet 1 is the quirks mode style sheet, + * stylesheet 2 is the adblocking stylesheet, + * stylesheet 3 is the user stylesheet */ + c->stylesheets = calloc(STYLESHEET_START, sizeof(struct html_stylesheet)); + if (c->stylesheets == NULL) { + ns_error = NSERROR_NOMEM; + goto html_find_stylesheets_no_memory; } - *c = (struct content *) html; + 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; - return NSERROR_OK; -} + child.charset = c->encoding; + child.quirks = c->base.quirks; + ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0, + content_get_url(&c->base), NULL, + html_convert_css_callback, c, &child, CONTENT_CSS, + &c->stylesheets[STYLESHEET_BASE].data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; + c->base.active++; + LOG(("%d fetches active", c->base.active)); -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 char *source_data; - unsigned long source_size; + if (c->quirks == DOM_DOCUMENT_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, + CONTENT_CSS, + &c->stylesheets[STYLESHEET_QUIRKS].data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; - /* Retrieve new encoding */ - encoding = dom_hubbub_parser_get_encoding(html->parser, - &html->encoding_source); - if (encoding == NULL) { - return NSERROR_NOMEM; - } + c->base.active++; + LOG(("%d fetches active", c->base.active)); - if (html->encoding != NULL) { - free(html->encoding); } - html->encoding = strdup(encoding); - if (html->encoding == NULL) { - return NSERROR_NOMEM; - } + 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, CONTENT_CSS, + &c->stylesheets[STYLESHEET_ADBLOCK]. + data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; - /* Destroy binding */ - dom_hubbub_parser_destroy(html->parser); - html->parser = NULL; + c->base.active++; + LOG(("%d fetches active", c->base.active)); - if (html->document != NULL) { - dom_node_unref(html->document); } - parse_params.enc = html->encoding; - parse_params.fix_enc = true; - parse_params.enable_script = nsoption_bool(enable_javascript); - parse_params.msg = NULL; - parse_params.script = html_process_script; - parse_params.ctx = html; - parse_params.daf = NULL; - - /* 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) { - return NSERROR_NOMEM; - } - parse_params.enc = html->encoding; - - error = dom_hubbub_parser_create(&parse_params, - &html->parser, - &html->document); + ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0, + content_get_url(&c->base), NULL, + html_convert_css_callback, c, &child, CONTENT_CSS, + &c->stylesheets[STYLESHEET_USER].data.external); + if (ns_error != NSERROR_OK) + goto html_find_stylesheets_no_memory; - if (error != DOM_HUBBUB_OK) { - return libdom_hubbub_error_to_nserror(error); - } + c->base.active++; + LOG(("%d fetches active", c->base.active)); - } + return true; - source_data = content__get_source_data(c, &source_size); +html_find_stylesheets_no_memory: + content_broadcast_errorcode(&c->base, ns_error); + return false; +} - /* 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, - (const uint8_t *)source_data, - source_size); +/** + * Handle notification of inline style completion + * + * \param css Inline style object + * \param pw Private data + */ +static void html_inline_style_done(struct content_css_data *css, void *pw) +{ + html_content *html = pw; - return libdom_hubbub_error_to_nserror(error); + if (--html->base.active == 0) + html_finish_conversion(html); } - /** - * Process data for CONTENT_HTML. + * Process an inline stylesheet in the document. + * + * \param c content structure + * \param style xml node of style element + * \return true on success, false if an error occurred */ -static bool -html_process_data(struct content *c, const char *data, unsigned int size) +static bool html_process_style_element(html_content *c, dom_node *style) { - 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); + dom_node *child, *next; + dom_string *val; + dom_exception exc; + struct html_stylesheet *stylesheets; + struct content_css_data *sheet; + nserror error; - /* deal with encoding change */ - if (err == NSERROR_ENCODING_CHANGE) { - err = html_process_encoding_change(c, data, size); + /* ensure sheets are initialised */ + if (html_init_stylesheets(c) == false) { + return false; } - /* broadcast the error if necessary */ - if (err != NSERROR_OK) { - content_broadcast_errorcode(c, err); - return false; + /* type='text/css', or not present (invalid but common) */ + exc = dom_element_get_attribute(style, corestring_dom_type, &val); + if (exc == DOM_NO_ERR && val != NULL) { + if (!dom_string_caseless_lwc_isequal(val, + corestring_lwc_text_css)) { + dom_string_unref(val); + return true; + } + dom_string_unref(val); } - return true; -} + /* media contains 'screen' or 'all' or not present */ + exc = dom_element_get_attribute(style, corestring_dom_media, &val); + if (exc == DOM_NO_ERR && val != NULL) { + if (strcasestr(dom_string_data(val), "screen") == NULL && + strcasestr(dom_string_data(val), + "all") == NULL) { + dom_string_unref(val); + return true; + } + dom_string_unref(val); + } + /* Extend array */ + stylesheets = realloc(c->stylesheets, + sizeof(struct html_stylesheet) * (c->stylesheet_count + 1)); + if (stylesheets == NULL) + goto no_memory; -/** process link node */ -static bool html_process_link(html_content *c, dom_node *node) -{ - struct content_rfc5988_link link; /* the link added to the content */ - dom_exception exc; /* returned by libdom functions */ - dom_string *atr_string; - nserror error; + c->stylesheets = stylesheets; - memset(&link, 0, sizeof(struct content_rfc5988_link)); + c->stylesheets[c->stylesheet_count].type = HTML_STYLESHEET_INTERNAL; + c->stylesheets[c->stylesheet_count].data.internal = NULL; - /* check that the relation exists - w3c spec says must be present */ - exc = dom_element_get_attribute(node, corestring_dom_rel, &atr_string); - if ((exc != DOM_NO_ERR) || (atr_string == NULL)) { - return false; - } - /* get a lwc string containing the link relation */ - exc = dom_string_intern(atr_string, &link.rel); - dom_string_unref(atr_string); - if (exc != DOM_NO_ERR) { - return false; + /* create stylesheet */ + sheet = calloc(1, sizeof(struct content_css_data)); + if (sheet == NULL) { + goto no_memory; } - /* check that the href exists - w3c spec says must be present */ - exc = dom_element_get_attribute(node, corestring_dom_href, &atr_string); - if ((exc != DOM_NO_ERR) || (atr_string == NULL)) { - lwc_string_unref(link.rel); + error = nscss_create_css_data(sheet, + nsurl_access(c->base_url), NULL, c->quirks, + html_inline_style_done, c); + if (error != NSERROR_OK) { + free(sheet); + content_broadcast_errorcode(&c->base, error); return false; } - /* get nsurl */ - error = nsurl_join(c->base_url, dom_string_data(atr_string), - &link.href); - dom_string_unref(atr_string); - if (error != NSERROR_OK) { - lwc_string_unref(link.rel); - return false; + /* can't just use xmlNodeGetContent(style), because that won't + * give the content of comments which may be used to 'hide' + * the content */ + exc = dom_node_get_first_child(style, &child); + if (exc != DOM_NO_ERR) { + nscss_destroy_css_data(sheet); + free(sheet); + goto no_memory; } - /* look for optional properties -- we don't care if internment fails */ + while (child != NULL) { + dom_string *data; - exc = dom_element_get_attribute(node, - corestring_dom_hreflang, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the href lang */ - exc = dom_string_intern(atr_string, &link.hreflang); - dom_string_unref(atr_string); - } + exc = dom_node_get_text_content(child, &data); + if (exc != DOM_NO_ERR) { + dom_node_unref(child); + nscss_destroy_css_data(sheet); + free(sheet); + goto no_memory; + } - exc = dom_element_get_attribute(node, - corestring_dom_type, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the type */ - exc = dom_string_intern(atr_string, &link.type); - dom_string_unref(atr_string); - } + if (nscss_process_css_data(sheet, dom_string_data(data), + dom_string_byte_length(data)) == false) { + dom_string_unref(data); + dom_node_unref(child); + nscss_destroy_css_data(sheet); + free(sheet); + goto no_memory; + } - exc = dom_element_get_attribute(node, - corestring_dom_media, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the media */ - exc = dom_string_intern(atr_string, &link.media); - dom_string_unref(atr_string); - } + dom_string_unref(data); - exc = dom_element_get_attribute(node, - corestring_dom_sizes, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* get a lwc string containing the sizes */ - exc = dom_string_intern(atr_string, &link.sizes); - dom_string_unref(atr_string); + exc = dom_node_get_next_sibling(child, &next); + if (exc != DOM_NO_ERR) { + dom_node_unref(child); + nscss_destroy_css_data(sheet); + free(sheet); + goto no_memory; + } + + dom_node_unref(child); + child = next; } - /* add to content */ - content__add_rfc5988_link(&c->base, &link); + c->base.active++; + LOG(("%d fetches active", c->base.active)); - if (link.sizes != NULL) - lwc_string_unref(link.sizes); - if (link.media != NULL) - lwc_string_unref(link.media); - if (link.type != NULL) - lwc_string_unref(link.type); - if (link.hreflang != NULL) - lwc_string_unref(link.hreflang); + /* 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); + free(sheet); + sheet = NULL; + } - nsurl_unref(link.href); - lwc_string_unref(link.rel); + /* Update index */ + c->stylesheets[c->stylesheet_count].data.internal = sheet; + c->stylesheet_count++; return true; + +no_memory: + content_broadcast_errorcode(&c->base, NSERROR_NOMEM); + return false; } -/** process title node */ -static bool html_process_title(html_content *c, dom_node *node) -{ - dom_exception exc; /* returned by libdom functions */ - dom_string *title; - char *title_str; - bool success; - if (c->base.title != NULL) - return true; - exc = dom_node_get_text_content(node, &title); - if ((exc != DOM_NO_ERR) || (title == NULL)) { +static bool html_process_stylesheet_link(html_content *htmlc, dom_node *node) +{ + dom_string *rel, *type_attr, *media, *href; + struct html_stylesheet *stylesheets; + nsurl *joined; + dom_exception exc; + nserror ns_error; + hlcache_child_context child; + + /* ensure sheets are initialised */ + if (html_init_stylesheets(htmlc) == false) { return false; } - title_str = squash_whitespace(dom_string_data(title)); - dom_string_unref(title); + /* rel= */ + exc = dom_element_get_attribute(node, corestring_dom_rel, &rel); + if (exc != DOM_NO_ERR || rel == NULL) + return true; - if (title_str == NULL) { - return false; + 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); - success = content__set_title(&c->base, title_str); - - free(title_str); + /* type='text/css' or not present */ + exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr); + if (exc == DOM_NO_ERR && type_attr != NULL) { + if (!dom_string_caseless_lwc_isequal(type_attr, + corestring_lwc_text_css)) { + dom_string_unref(type_attr); + return true; + } + dom_string_unref(type_attr); + } - return success; -} + /* media contains 'screen' or 'all' or not present */ + exc = dom_element_get_attribute(node, corestring_dom_media, &media); + if (exc == DOM_NO_ERR && media != NULL) { + if (strcasestr(dom_string_data(media), "screen") == NULL && + strcasestr(dom_string_data(media), "all") == NULL) { + dom_string_unref(media); + return true; + } + dom_string_unref(media); + } -static bool html_process_base(html_content *c, dom_node *node) -{ - dom_exception exc; /* returned by libdom functions */ - dom_string *atr_string; + /* href='...' */ + exc = dom_element_get_attribute(node, corestring_dom_href, &href); + if (exc != DOM_NO_ERR || href == NULL) + return true; - /* get href attribute if present */ - exc = dom_element_get_attribute(node, - corestring_dom_href, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - nsurl *url; - nserror error; + /* TODO: only the first preferred stylesheets (ie. + * those with a title attribute) should be loaded + * (see HTML4 14.3) */ - /* get url from string */ - error = nsurl_create(dom_string_data(atr_string), &url); - dom_string_unref(atr_string); - if (error == NSERROR_OK) { - if (c->base_url != NULL) - nsurl_unref(c->base_url); - c->base_url = url; - } + ns_error = nsurl_join(htmlc->base_url, dom_string_data(href), &joined); + if (ns_error != NSERROR_OK) { + dom_string_unref(href); + goto no_memory; } + dom_string_unref(href); + LOG(("linked stylesheet %i '%s'", htmlc->stylesheet_count, nsurl_access(joined))); - /* get target attribute if present and not already set */ - if (c->base_target != NULL) { - return true; + /* extend stylesheets array to allow for new sheet */ + stylesheets = realloc(htmlc->stylesheets, + sizeof(struct html_stylesheet) * (htmlc->stylesheet_count + 1)); + if (stylesheets == NULL) { + nsurl_unref(joined); + ns_error = NSERROR_NOMEM; + goto no_memory; } - exc = dom_element_get_attribute(node, - corestring_dom_target, &atr_string); - if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { - /* Validation rules from the HTML5 spec for the base element: - * The target must be one of _blank, _self, _parent, or - * _top or any identifier which does not begin with an - * underscore - */ - if (*dom_string_data(atr_string) != '_' || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__blank) || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__self) || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__parent) || - dom_string_caseless_lwc_isequal(atr_string, - corestring_lwc__top)) { - c->base_target = strdup(dom_string_data(atr_string)); - } - dom_string_unref(atr_string); - } + htmlc->stylesheets = stylesheets; + htmlc->stylesheets[htmlc->stylesheet_count].type = HTML_STYLESHEET_EXTERNAL; - return true; -} + /* start fetch */ + child.charset = htmlc->encoding; + child.quirks = htmlc->base.quirks; -/** - * Process elements in . - * - * \param c content structure - * \param head xml node of head element - * \return true on success, false on memory exhaustion - * - * The title and base href are extracted if present. - */ + ns_error = hlcache_handle_retrieve(joined, + 0, + content_get_url(&htmlc->base), + NULL, + html_convert_css_callback, + htmlc, + &child, + CONTENT_CSS, + &htmlc->stylesheets[htmlc->stylesheet_count].data.external); -static nserror html_head(html_content *c, dom_node *head) -{ - dom_node *node; - dom_exception exc; /* returned by libdom functions */ - dom_string *node_name; - dom_node_type node_type; - dom_node *next_node; + nsurl_unref(joined); - exc = dom_node_get_first_child(head, &node); - if (exc != DOM_NO_ERR) { - return NSERROR_DOM; - } + if (ns_error != NSERROR_OK) + goto no_memory; - while (node != NULL) { - exc = dom_node_get_node_type(node, &node_type); + htmlc->stylesheet_count++; - if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) { - exc = dom_node_get_node_name(node, &node_name); + htmlc->base.active++; + LOG(("%d fetches active", htmlc->base.active)); - if ((exc == DOM_NO_ERR) && (node_name != NULL)) { - if (dom_string_caseless_lwc_isequal( - node_name, - corestring_lwc_title)) { - html_process_title(c, node); - } else if (dom_string_caseless_lwc_isequal( - node_name, - corestring_lwc_base)) { - html_process_base(c, node); - } else if (dom_string_caseless_lwc_isequal( - node_name, - corestring_lwc_link)) { - html_process_link(c, node); + return true; + +no_memory: + content_broadcast_errorcode(&htmlc->base, ns_error); + return false; +} + +/* callback for DOMNodeInserted end type */ +static void +dom_default_action_DOMNodeInserted_cb(struct dom_event *evt, void *pw) +{ + dom_event_target *node; + dom_node_type type; + dom_string *name; + dom_exception exc; + html_content *htmlc = pw; + + 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 inserted */ + exc = dom_node_get_node_name(node, &name); + if ((exc == DOM_NO_ERR) && (name != NULL)) { + /* LOG(("element htmlc:%p name:%s", htmlc, dom_string_data(name))); */ + if (dom_string_caseless_isequal(name, corestring_dom_style)) { + html_process_style_element(htmlc, (dom_node *)node); + } else if (dom_string_caseless_isequal(name, corestring_dom_link)) { + html_process_stylesheet_link(htmlc, (dom_node *)node); } } - if (node_name != NULL) { - dom_string_unref(node_name); - } - } - - /* move to next node */ - exc = dom_node_get_next_sibling(node, &next_node); - dom_node_unref(node); - if (exc == DOM_NO_ERR) { - node = next_node; - } else { - node = NULL; } } - - return NSERROR_OK; } -static nserror html_meta_refresh_process_element(html_content *c, dom_node *n) +/* callback function selector + * + * selects a callback function for libdom to call based on the type and phase. + * dom_default_action_phase from events/document_event.h + * + * @return callback function pointer or NULL for none + */ +static dom_default_action_callback +dom_event_fetcher(dom_string *type, + dom_default_action_phase phase, + void **pw) { - union content_msg_data msg_data; - const char *url, *end, *refresh = NULL; - char *new_url; - char quote = '\0'; - dom_string *equiv, *content; - dom_exception exc; - nsurl *nsurl; - nserror error = NSERROR_OK; + //LOG(("type:%s", dom_string_data(type))); - exc = dom_element_get_attribute(n, corestring_dom_http_equiv, &equiv); - if (exc != DOM_NO_ERR) { - return NSERROR_DOM; + if ((phase == DOM_DEFAULT_ACTION_END) && + dom_string_isequal(type, corestring_dom_DOMNodeInserted)) { + return dom_default_action_DOMNodeInserted_cb; } + return NULL; +} - if (equiv == NULL) { - return NSERROR_OK; - } +static nserror +html_create_html_data(html_content *c, const http_parameter *params) +{ + lwc_string *charset; + nserror nerror; + dom_hubbub_parser_params parse_params; + dom_hubbub_error error; - if (!dom_string_caseless_lwc_isequal(equiv, corestring_lwc_refresh)) { - dom_string_unref(equiv); - return NSERROR_OK; - } + c->parser = NULL; + 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->bctx = NULL; + c->layout = NULL; + c->background_colour = NS_TRANSPARENT; + c->stylesheet_count = 0; + c->stylesheets = NULL; + c->select_ctx = NULL; + 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 = &nsfont; + c->drag_type = HTML_DRAG_NONE; + c->drag_owner.no_owner = true; + c->scripts_count = 0; + c->scripts = NULL; + c->jscontext = NULL; - dom_string_unref(equiv); + c->base.active = 1; /* The html content itself is active */ - exc = dom_element_get_attribute(n, corestring_dom_content, &content); - if (exc != DOM_NO_ERR) { - return NSERROR_DOM; + if (lwc_intern_string("*", SLEN("*"), &c->universal) != lwc_error_ok) { + return NSERROR_NOMEM; } - if (content == NULL) { - return NSERROR_OK; - } + selection_prepare(&c->sel, (struct content *)c, true); - end = dom_string_data(content) + dom_string_byte_length(content); + nerror = http_parameter_list_find_item(params, html_charset, &charset); + if (nerror == NSERROR_OK) { + c->encoding = strdup(lwc_string_data(charset)); - /* content := *LWS intpart fracpart? *LWS [';' *LWS *1url *LWS] - * intpart := 1*DIGIT - * fracpart := 1*('.' | DIGIT) - * url := "url" *LWS '=' *LWS (url-nq | url-sq | url-dq) - * url-nq := *urlchar - * url-sq := "'" *(urlchar | '"') "'" - * url-dq := '"' *(urlchar | "'") '"' - * urlchar := [#x9#x21#x23-#x26#x28-#x7E] | nonascii - * nonascii := [#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF] - */ + lwc_string_unref(charset); - url = dom_string_data(content); + if (c->encoding == NULL) { + lwc_string_unref(c->universal); + c->universal = NULL; + return NSERROR_NOMEM; - /* *LWS */ - while (url < end && isspace(*url)) { - url++; + } + c->encoding_source = DOM_HUBBUB_ENCODING_SOURCE_HEADER; } - /* intpart */ - if (url == end || (*url < '0' || '9' < *url)) { - /* Empty content, or invalid timeval */ - dom_string_unref(content); - return NSERROR_OK; - } + /* Create the parser binding */ + parse_params.enc = c->encoding; + parse_params.fix_enc = true; + parse_params.enable_script = nsoption_bool(enable_javascript); + parse_params.msg = NULL; + parse_params.script = html_process_script; + parse_params.ctx = c; + parse_params.daf = dom_event_fetcher; - msg_data.delay = (int) strtol(url, &new_url, 10); - /* a very small delay and self-referencing URL can cause a loop - * that grinds machines to a halt. To prevent this we set a - * minimum refresh delay of 1s. */ - if (msg_data.delay < 1) { - msg_data.delay = 1; - } + 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; - url = new_url; + parse_params.enc = c->encoding; - /* fracpart? (ignored, as delay is integer only) */ - while (url < end && (('0' <= *url && *url <= '9') || - *url == '.')) { - url++; + error = dom_hubbub_parser_create(&parse_params, + &c->parser, + &c->document); } - /* *LWS */ - while (url < end && isspace(*url)) { - url++; - } + if (error != DOM_HUBBUB_OK) { + nsurl_unref(c->base_url); + c->base_url = NULL; - /* ';' */ - if (url < end && *url == ';') - url++; + lwc_string_unref(c->universal); + c->universal = NULL; - /* *LWS */ - while (url < end && isspace(*url)) { - url++; + return libdom_hubbub_error_to_nserror(error); } - if (url == end) { - /* Just delay specified, so refresh current page */ - dom_string_unref(content); + return NSERROR_OK; - c->base.refresh = nsurl_ref( - content_get_url(&c->base)); +} - content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data); +/** + * Create a CONTENT_HTML. + * + * The content_html_data structure is initialized and the HTML parser is + * created. + */ - return NSERROR_OK; - } +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; - /* "url" */ - if (url <= end - 3) { - if (strncasecmp(url, "url", 3) == 0) { - url += 3; - } else { - /* Unexpected input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; - } - } else { - /* Insufficient input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; + html = calloc(1, sizeof(html_content)); + if (html == NULL) + return NSERROR_NOMEM; + + error = content__init(&html->base, handler, imime_type, params, + llcache, fallback_charset, quirks); + if (error != NSERROR_OK) { + free(html); + return error; } - /* *LWS */ - while (url < end && isspace(*url)) { - url++; + error = html_create_html_data(html, params); + if (error != NSERROR_OK) { + content_broadcast_errorcode(&html->base, error); + free(html); + return error; } - /* '=' */ - if (url < end) { - if (*url == '=') { - url++; - } else { - /* Unexpected input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; - } - } else { - /* Insufficient input, ignore this header */ - dom_string_unref(content); - return NSERROR_OK; + *c = (struct content *) html; + + 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 char *source_data; + unsigned long source_size; + + /* Retrieve new encoding */ + encoding = dom_hubbub_parser_get_encoding(html->parser, + &html->encoding_source); + if (encoding == NULL) { + return NSERROR_NOMEM; } - /* *LWS */ - while (url < end && isspace(*url)) { - url++; + if (html->encoding != NULL) { + free(html->encoding); } - /* '"' or "'" */ - if (url < end && (*url == '"' || *url == '\'')) { - quote = *url; - url++; + html->encoding = strdup(encoding); + if (html->encoding == NULL) { + return NSERROR_NOMEM; } - /* Start of URL */ - refresh = url; + /* Destroy binding */ + dom_hubbub_parser_destroy(html->parser); + html->parser = NULL; - if (quote != 0) { - /* url-sq | url-dq */ - while (url < end && *url != quote) - url++; - } else { - /* url-nq */ - while (url < end && !isspace(*url)) - url++; + if (html->document != NULL) { + dom_node_unref(html->document); } - /* '"' or "'" or *LWS (we don't care) */ - if (url > refresh) { - /* There's a URL */ - new_url = strndup(refresh, url - refresh); - if (new_url == NULL) { - dom_string_unref(content); + parse_params.enc = html->encoding; + parse_params.fix_enc = true; + parse_params.enable_script = nsoption_bool(enable_javascript); + parse_params.msg = NULL; + parse_params.script = html_process_script; + parse_params.ctx = html; + parse_params.daf = 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) { return NSERROR_NOMEM; } + parse_params.enc = html->encoding; - error = nsurl_join(c->base_url, new_url, &nsurl); - if (error == NSERROR_OK) { - /* broadcast valid refresh url */ - - c->base.refresh = nsurl; + error = dom_hubbub_parser_create(&parse_params, + &html->parser, + &html->document); - content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data); + if (error != DOM_HUBBUB_OK) { + return libdom_hubbub_error_to_nserror(error); } - free(new_url); - } - dom_string_unref(content); + source_data = content__get_source_data(c, &source_size); + + /* 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, + (const uint8_t *)source_data, + source_size); + + return libdom_hubbub_error_to_nserror(error); +} - return error; -} /** - * Search for meta refresh - * - * http://wp.netscape.com/assist/net_sites/pushpull.html - * - * \param c content structure - * \param head xml node of head element - * \return true on success, false otherwise (error reported) + * Process data for CONTENT_HTML. */ -static nserror html_meta_refresh(html_content *c, dom_node *head) +static bool +html_process_data(struct content *c, const char *data, unsigned int size) { - dom_node *n, *next; - dom_exception exc; - nserror ns_error = NSERROR_OK; + html_content *html = (html_content *) c; + dom_hubbub_error dom_ret; + nserror err = NSERROR_OK; /* assume its all going to be ok */ - if (head == NULL) { - return ns_error; + 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); } - exc = dom_node_get_first_child(head, &n); - if (exc != DOM_NO_ERR) { - return NSERROR_DOM; + /* broadcast the error if necessary */ + if (err != NSERROR_OK) { + content_broadcast_errorcode(c, err); + return false; } - while (n != NULL) { - dom_node_type type; + return true; +} - exc = dom_node_get_node_type(n, &type); - if (exc != DOM_NO_ERR) { - dom_node_unref(n); - return NSERROR_DOM; - } - if (type == DOM_ELEMENT_NODE) { - dom_string *name; +/** process link node */ +static bool html_process_link(html_content *c, dom_node *node) +{ + struct content_rfc5988_link link; /* the link added to the content */ + dom_exception exc; /* returned by libdom functions */ + dom_string *atr_string; + nserror error; - exc = dom_node_get_node_name(n, &name); - if (exc != DOM_NO_ERR) { - dom_node_unref(n); - return NSERROR_DOM; - } + memset(&link, 0, sizeof(struct content_rfc5988_link)); - /* Recurse into noscript elements */ - if (dom_string_caseless_lwc_isequal(name, corestring_lwc_noscript)) { - ns_error = html_meta_refresh(c, n); - if (ns_error != NSERROR_OK) { - /* Some error occurred */ - dom_string_unref(name); - dom_node_unref(n); - return ns_error; - } else if (c->base.refresh != NULL) { - /* Meta refresh found - stop */ - dom_string_unref(name); - dom_node_unref(n); - return NSERROR_OK; - } - } else if (dom_string_caseless_lwc_isequal(name, corestring_lwc_meta)) { - ns_error = html_meta_refresh_process_element(c, n); - if (ns_error != NSERROR_OK) { - /* Some error occurred */ - dom_string_unref(name); - dom_node_unref(n); - return ns_error; - } else if (c->base.refresh != NULL) { - /* Meta refresh found - stop */ - dom_string_unref(name); - dom_node_unref(n); - return NSERROR_OK; - } - } - dom_string_unref(name); - } + /* check that the relation exists - w3c spec says must be present */ + exc = dom_element_get_attribute(node, corestring_dom_rel, &atr_string); + if ((exc != DOM_NO_ERR) || (atr_string == NULL)) { + return false; + } + /* get a lwc string containing the link relation */ + exc = dom_string_intern(atr_string, &link.rel); + dom_string_unref(atr_string); + if (exc != DOM_NO_ERR) { + return false; + } - exc = dom_node_get_next_sibling(n, &next); - if (exc != DOM_NO_ERR) { - dom_node_unref(n); - return NSERROR_DOM; - } + /* check that the href exists - w3c spec says must be present */ + exc = dom_element_get_attribute(node, corestring_dom_href, &atr_string); + if ((exc != DOM_NO_ERR) || (atr_string == NULL)) { + lwc_string_unref(link.rel); + return false; + } - dom_node_unref(n); - n = next; + /* get nsurl */ + error = nsurl_join(c->base_url, dom_string_data(atr_string), + &link.href); + dom_string_unref(atr_string); + if (error != NSERROR_OK) { + lwc_string_unref(link.rel); + return false; } - return ns_error; -} + /* look for optional properties -- we don't care if internment fails */ -/** - * Update a box whose content has completed rendering. - */ + exc = dom_element_get_attribute(node, + corestring_dom_hreflang, &atr_string); + if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { + /* get a lwc string containing the href lang */ + exc = dom_string_intern(atr_string, &link.hreflang); + dom_string_unref(atr_string); + } -static void -html_object_done(struct box *box, - hlcache_handle *object, - bool background) -{ - struct box *b; + exc = dom_element_get_attribute(node, + corestring_dom_type, &atr_string); + if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { + /* get a lwc string containing the type */ + exc = dom_string_intern(atr_string, &link.type); + dom_string_unref(atr_string); + } - if (background) { - box->background = object; - return; + exc = dom_element_get_attribute(node, + corestring_dom_media, &atr_string); + if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { + /* get a lwc string containing the media */ + exc = dom_string_intern(atr_string, &link.media); + dom_string_unref(atr_string); } - box->object = object; + exc = dom_element_get_attribute(node, + corestring_dom_sizes, &atr_string); + if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { + /* get a lwc string containing the sizes */ + exc = dom_string_intern(atr_string, &link.sizes); + dom_string_unref(atr_string); + } - if (!(box->flags & REPLACE_DIM)) { - /* invalidate parent min, max widths */ - for (b = box; b; b = b->parent) - b->max_width = UNKNOWN_MAX_WIDTH; + /* add to content */ + content__add_rfc5988_link(&c->base, &link); - /* delete any clones of this box */ - while (box->next && (box->next->flags & CLONE)) { - /* box_free_box(box->next); */ - box->next = box->next->next; - } - } -} + if (link.sizes != NULL) + lwc_string_unref(link.sizes); + if (link.media != NULL) + lwc_string_unref(link.media); + if (link.type != NULL) + lwc_string_unref(link.type); + if (link.hreflang != NULL) + lwc_string_unref(link.hreflang); -/** - * Handle object fetching or loading failure. - * - * \param box box containing object which failed to load - * \param content document of type CONTENT_HTML - * \param background the object was the background image for the box - */ + nsurl_unref(link.href); + lwc_string_unref(link.rel); -static void -html_object_failed(struct box *box, html_content *content, bool background) -{ - /* Nothing to do */ - return; + return true; } -/** - * Callback for hlcache_handle_retrieve() for objects. - */ - -static nserror -html_object_callback(hlcache_handle *object, - const hlcache_event *event, - void *pw) +/** process title node */ +static bool html_process_title(html_content *c, dom_node *node) { - struct content_html_object *o = pw; - html_content *c = (html_content *) o->parent; - int x, y; - struct box *box; + dom_exception exc; /* returned by libdom functions */ + dom_string *title; + char *title_str; + bool success; - assert(c->base.status != CONTENT_STATUS_ERROR); + if (c->base.title != NULL) + return true; - box = o->box; + exc = dom_node_get_text_content(node, &title); + if ((exc != DOM_NO_ERR) || (title == NULL)) { + return false; + } - switch (event->type) { - case CONTENT_MSG_LOADING: - if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL) - content_open(object, - c->bw, &c->base, - box->object_params); - break; + title_str = squash_whitespace(dom_string_data(title)); + dom_string_unref(title); - case CONTENT_MSG_READY: - if (content_can_reformat(object)) { - /* TODO: avoid knowledge of box internals here */ - content_reformat(object, false, - box->max_width != UNKNOWN_MAX_WIDTH ? - box->width : 0, - box->max_width != UNKNOWN_MAX_WIDTH ? - box->height : 0); + if (title_str == NULL) { + return false; + } - /* Adjust parent content for new object size */ - html_object_done(box, object, o->background); - if (c->base.status == CONTENT_STATUS_READY || - c->base.status == CONTENT_STATUS_DONE) - content__reformat(&c->base, false, - c->base.available_width, - c->base.height); - } - break; - - case CONTENT_MSG_DONE: - c->base.active--; - LOG(("%d fetches active", c->base.active)); - - html_object_done(box, object, o->background); + success = content__set_title(&c->base, title_str); - if (c->base.status != CONTENT_STATUS_LOADING && - box->flags & REPLACE_DIM) { - union content_msg_data data; + free(title_str); - if (!box_visible(box)) - break; + return success; +} - box_coords(box, &x, &y); +static bool html_process_base(html_content *c, dom_node *node) +{ + dom_exception exc; /* returned by libdom functions */ + dom_string *atr_string; - data.redraw.x = x + box->padding[LEFT]; - data.redraw.y = y + box->padding[TOP]; - data.redraw.width = box->width; - data.redraw.height = box->height; - data.redraw.full_redraw = true; + /* get href attribute if present */ + exc = dom_element_get_attribute(node, + corestring_dom_href, &atr_string); + if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { + nsurl *url; + nserror error; - content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); + /* get url from string */ + error = nsurl_create(dom_string_data(atr_string), &url); + dom_string_unref(atr_string); + if (error == NSERROR_OK) { + if (c->base_url != NULL) + nsurl_unref(c->base_url); + c->base_url = url; } - break; - - case CONTENT_MSG_ERROR: - hlcache_handle_release(object); - - o->content = NULL; + } - c->base.active--; - LOG(("%d fetches active", c->base.active)); - content_add_error(&c->base, "?", 0); - html_object_failed(box, c, o->background); - break; + /* get target attribute if present and not already set */ + if (c->base_target != NULL) { + return true; + } - case CONTENT_MSG_STATUS: - if (event->data.explicit_status_text == NULL) { - /* Object content's status text updated */ - union content_msg_data data; - data.explicit_status_text = - content_get_status_message(object); - html_set_status(c, data.explicit_status_text); - content_broadcast(&c->base, CONTENT_MSG_STATUS, data); - } else { - /* Object content wants to set explicit message */ - content_broadcast(&c->base, CONTENT_MSG_STATUS, - event->data); + exc = dom_element_get_attribute(node, + corestring_dom_target, &atr_string); + if ((exc == DOM_NO_ERR) && (atr_string != NULL)) { + /* Validation rules from the HTML5 spec for the base element: + * The target must be one of _blank, _self, _parent, or + * _top or any identifier which does not begin with an + * underscore + */ + if (*dom_string_data(atr_string) != '_' || + dom_string_caseless_lwc_isequal(atr_string, + corestring_lwc__blank) || + dom_string_caseless_lwc_isequal(atr_string, + corestring_lwc__self) || + dom_string_caseless_lwc_isequal(atr_string, + corestring_lwc__parent) || + dom_string_caseless_lwc_isequal(atr_string, + corestring_lwc__top)) { + c->base_target = strdup(dom_string_data(atr_string)); } - break; + dom_string_unref(atr_string); + } - case CONTENT_MSG_REFORMAT: - break; + return true; +} - case CONTENT_MSG_REDRAW: - if (c->base.status != CONTENT_STATUS_LOADING) { - union content_msg_data data = event->data; +/** + * Process elements in . + * + * \param c content structure + * \param head xml node of head element + * \return true on success, false on memory exhaustion + * + * The title and base href are extracted if present. + */ - if (!box_visible(box)) - break; +static nserror html_head(html_content *c, dom_node *head) +{ + dom_node *node; + dom_exception exc; /* returned by libdom functions */ + dom_string *node_name; + dom_node_type node_type; + dom_node *next_node; - box_coords(box, &x, &y); + exc = dom_node_get_first_child(head, &node); + if (exc != DOM_NO_ERR) { + return NSERROR_DOM; + } - if (hlcache_handle_get_content(object) == - event->data.redraw.object) { - data.redraw.x = data.redraw.x * - box->width / content_get_width(object); - data.redraw.y = data.redraw.y * - box->height / - content_get_height(object); - data.redraw.width = data.redraw.width * - box->width / content_get_width(object); - data.redraw.height = data.redraw.height * - box->height / - content_get_height(object); - data.redraw.object_width = box->width; - data.redraw.object_height = box->height; - } + while (node != NULL) { + exc = dom_node_get_node_type(node, &node_type); - data.redraw.x += x + box->padding[LEFT]; - data.redraw.y += y + box->padding[TOP]; - data.redraw.object_x += x + box->padding[LEFT]; - data.redraw.object_y += y + box->padding[TOP]; + if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) { + exc = dom_node_get_node_name(node, &node_name); - content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); + if ((exc == DOM_NO_ERR) && (node_name != NULL)) { + if (dom_string_caseless_lwc_isequal( + node_name, + corestring_lwc_title)) { + html_process_title(c, node); + } else if (dom_string_caseless_lwc_isequal( + node_name, + corestring_lwc_base)) { + html_process_base(c, node); + } else if (dom_string_caseless_lwc_isequal( + node_name, + corestring_lwc_link)) { + html_process_link(c, node); + } + } + if (node_name != NULL) { + dom_string_unref(node_name); + } } - break; - case CONTENT_MSG_REFRESH: - if (content_get_type(object) == CONTENT_HTML) { - /* only for HTML objects */ - schedule(event->data.delay * 100, - html_object_refresh, o); + /* move to next node */ + exc = dom_node_get_next_sibling(node, &next_node); + dom_node_unref(node); + if (exc == DOM_NO_ERR) { + node = next_node; + } else { + node = NULL; } + } - break; - - case CONTENT_MSG_LINK: - /* Don't care about favicons that aren't on top level content */ - break; + return NSERROR_OK; +} - case CONTENT_MSG_GETCTX: - *(event->data.jscontext) = NULL; - break; +static nserror html_meta_refresh_process_element(html_content *c, dom_node *n) +{ + union content_msg_data msg_data; + const char *url, *end, *refresh = NULL; + char *new_url; + char quote = '\0'; + dom_string *equiv, *content; + dom_exception exc; + nsurl *nsurl; + nserror error = NSERROR_OK; - case CONTENT_MSG_SCROLL: - if (box->scroll_x != NULL) - scrollbar_set(box->scroll_x, event->data.scroll.x0, - false); - if (box->scroll_y != NULL) - scrollbar_set(box->scroll_y, event->data.scroll.y0, - false); - break; + exc = dom_element_get_attribute(n, corestring_dom_http_equiv, &equiv); + if (exc != DOM_NO_ERR) { + return NSERROR_DOM; + } - case CONTENT_MSG_DRAGSAVE: - { - union content_msg_data msg_data; - if (event->data.dragsave.content == NULL) - msg_data.dragsave.content = object; - else - msg_data.dragsave.content = - event->data.dragsave.content; + if (equiv == NULL) { + return NSERROR_OK; + } - content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data); + if (!dom_string_caseless_lwc_isequal(equiv, corestring_lwc_refresh)) { + dom_string_unref(equiv); + return NSERROR_OK; } - break; - case CONTENT_MSG_SAVELINK: - case CONTENT_MSG_POINTER: - /* These messages are for browser window layer. - * we're not interested, so pass them on. */ - content_broadcast(&c->base, event->type, event->data); - break; + dom_string_unref(equiv); - case CONTENT_MSG_DRAG: - { - html_drag_type drag_type = HTML_DRAG_NONE; - union html_drag_owner drag_owner; - drag_owner.content = box; + exc = dom_element_get_attribute(n, corestring_dom_content, &content); + if (exc != DOM_NO_ERR) { + return NSERROR_DOM; + } - switch (event->data.drag.type) { - case CONTENT_DRAG_NONE: - drag_type = HTML_DRAG_NONE; - drag_owner.no_owner = true; - break; - case CONTENT_DRAG_SCROLL: - drag_type = HTML_DRAG_CONTENT_SCROLL; - break; - case CONTENT_DRAG_SELECTION: - drag_type = HTML_DRAG_CONTENT_SELECTION; - break; - } - html_set_drag_type(c, drag_type, drag_owner, - event->data.drag.rect); + if (content == NULL) { + return NSERROR_OK; } - break; - default: - assert(0); + end = dom_string_data(content) + dom_string_byte_length(content); + + /* content := *LWS intpart fracpart? *LWS [';' *LWS *1url *LWS] + * intpart := 1*DIGIT + * fracpart := 1*('.' | DIGIT) + * url := "url" *LWS '=' *LWS (url-nq | url-sq | url-dq) + * url-nq := *urlchar + * url-sq := "'" *(urlchar | '"') "'" + * url-dq := '"' *(urlchar | "'") '"' + * urlchar := [#x9#x21#x23-#x26#x28-#x7E] | nonascii + * nonascii := [#x80-#xD7FF#xE000-#xFFFD#x10000-#x10FFFF] + */ + + url = dom_string_data(content); + + /* *LWS */ + while (url < end && isspace(*url)) { + url++; } - if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 && - (event->type == CONTENT_MSG_LOADING || - event->type == CONTENT_MSG_DONE || - event->type == CONTENT_MSG_ERROR)) { - /* all objects have arrived */ - content__reformat(&c->base, false, c->base.available_width, - c->base.height); - html_set_status(c, ""); - content_set_done(&c->base); + /* intpart */ + if (url == end || (*url < '0' || '9' < *url)) { + /* Empty content, or invalid timeval */ + dom_string_unref(content); + return NSERROR_OK; } - /* If 1) the configuration option to reflow pages while objects are - * fetched is set - * 2) an object is newly fetched & converted, - * 3) the box's dimensions need to change due to being replaced - * 4) the object's parent HTML is ready for reformat, - * 5) the time since the previous reformat is more than the - * configured minimum time between reformats - * then reformat the page to display newly fetched objects */ - else if (nsoption_bool(incremental_reflow) && - event->type == CONTENT_MSG_DONE && - !(box->flags & REPLACE_DIM) && - (c->base.status == CONTENT_STATUS_READY || - c->base.status == CONTENT_STATUS_DONE) && - (wallclock() > c->base.reformat_time)) { - content__reformat(&c->base, false, c->base.available_width, - c->base.height); + msg_data.delay = (int) strtol(url, &new_url, 10); + /* a very small delay and self-referencing URL can cause a loop + * that grinds machines to a halt. To prevent this we set a + * minimum refresh delay of 1s. */ + if (msg_data.delay < 1) { + msg_data.delay = 1; } - return NSERROR_OK; -} + url = new_url; -/** - * Start a fetch for an object required by a page, replacing an existing object. - * - * \param object Object to replace - * \param url URL of object to fetch (copied) - * \return true on success, false on memory exhaustion - */ + /* fracpart? (ignored, as delay is integer only) */ + while (url < end && (('0' <= *url && *url <= '9') || + *url == '.')) { + url++; + } -static bool html_replace_object(struct content_html_object *object, nsurl *url) -{ - html_content *c; - hlcache_child_context child; - html_content *page; - nserror error; + /* *LWS */ + while (url < end && isspace(*url)) { + url++; + } - assert(object != NULL); + /* ';' */ + if (url < end && *url == ';') + url++; - c = (html_content *) object->parent; + /* *LWS */ + while (url < end && isspace(*url)) { + url++; + } - child.charset = c->encoding; - child.quirks = c->base.quirks; + if (url == end) { + /* Just delay specified, so refresh current page */ + dom_string_unref(content); - if (object->content != NULL) { - /* remove existing object */ - if (content_get_status(object->content) != CONTENT_STATUS_DONE) { - c->base.active--; - LOG(("%d fetches active", c->base.active)); - } + c->base.refresh = nsurl_ref( + content_get_url(&c->base)); - hlcache_handle_release(object->content); - object->content = NULL; + content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data); - object->box->object = NULL; + return NSERROR_OK; } - /* initialise fetch */ - error = hlcache_handle_retrieve(url, HLCACHE_RETRIEVE_SNIFF_TYPE, - content_get_url(&c->base), NULL, - html_object_callback, object, &child, - object->permitted_types, - &object->content); + /* "url" */ + if (url <= end - 3) { + if (strncasecmp(url, "url", 3) == 0) { + url += 3; + } else { + /* Unexpected input, ignore this header */ + dom_string_unref(content); + return NSERROR_OK; + } + } else { + /* Insufficient input, ignore this header */ + dom_string_unref(content); + return NSERROR_OK; + } - if (error != NSERROR_OK) - return false; + /* *LWS */ + while (url < end && isspace(*url)) { + url++; + } + + /* '=' */ + if (url < end) { + if (*url == '=') { + url++; + } else { + /* Unexpected input, ignore this header */ + dom_string_unref(content); + return NSERROR_OK; + } + } else { + /* Insufficient input, ignore this header */ + dom_string_unref(content); + return NSERROR_OK; + } - for (page = c; page != NULL; page = page->page) { - page->base.active++; - LOG(("%d fetches active", c->base.active)); + /* *LWS */ + while (url < end && isspace(*url)) { + url++; + } - page->base.status = CONTENT_STATUS_READY; + /* '"' or "'" */ + if (url < end && (*url == '"' || *url == '\'')) { + quote = *url; + url++; } - return true; -} + /* Start of URL */ + refresh = url; -/** - * schedule() callback for object refresh - */ + if (quote != 0) { + /* url-sq | url-dq */ + while (url < end && *url != quote) + url++; + } else { + /* url-nq */ + while (url < end && !isspace(*url)) + url++; + } -static void html_object_refresh(void *p) -{ - struct content_html_object *object = p; - nsurl *refresh_url; + /* '"' or "'" or *LWS (we don't care) */ + if (url > refresh) { + /* There's a URL */ + new_url = strndup(refresh, url - refresh); + if (new_url == NULL) { + dom_string_unref(content); + return NSERROR_NOMEM; + } - assert(content_get_type(object->content) == CONTENT_HTML); + error = nsurl_join(c->base_url, new_url, &nsurl); + if (error == NSERROR_OK) { + /* broadcast valid refresh url */ - refresh_url = content_get_refresh_url(object->content); + c->base.refresh = nsurl; - /* Ignore if refresh URL has gone - * (may happen if fetch errored) */ - if (refresh_url == NULL) - return; + content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data); + } - content_invalidate_reuse_data(object->content); + free(new_url); - if (!html_replace_object(object, refresh_url)) { - /** \todo handle memory exhaustion */ } -} - - + dom_string_unref(content); + return error; +} /** - * Callback for fetchcache() for linked stylesheets. + * Search for meta refresh + * + * http://wp.netscape.com/assist/net_sites/pushpull.html + * + * \param c content structure + * \param head xml node of head element + * \return true on success, false otherwise (error reported) */ -static nserror -html_convert_css_callback(hlcache_handle *css, - const hlcache_event *event, - void *pw) +static nserror html_meta_refresh(html_content *c, dom_node *head) { - html_content *parent = pw; - unsigned int i; - struct html_stylesheet *s; + dom_node *n, *next; + dom_exception exc; + nserror ns_error = NSERROR_OK; - /* Find sheet */ - for (i = 0, s = parent->stylesheets; - i != parent->stylesheet_count; i++, s++) { - if (s->type == HTML_STYLESHEET_EXTERNAL && - s->data.external == css) - break; + if (head == NULL) { + return ns_error; } - assert(i != parent->stylesheet_count); + exc = dom_node_get_first_child(head, &n); + if (exc != DOM_NO_ERR) { + return NSERROR_DOM; + } - switch (event->type) { - case CONTENT_MSG_LOADING: - break; + while (n != NULL) { + dom_node_type type; - case CONTENT_MSG_READY: - break; + exc = dom_node_get_node_type(n, &type); + if (exc != DOM_NO_ERR) { + dom_node_unref(n); + return NSERROR_DOM; + } - case CONTENT_MSG_DONE: - 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; + if (type == DOM_ELEMENT_NODE) { + dom_string *name; - case CONTENT_MSG_ERROR: - LOG(("stylesheet %s failed: %s", - nsurl_access(hlcache_handle_get_url(css)), - event->data.error)); - 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; + exc = dom_node_get_node_name(n, &name); + if (exc != DOM_NO_ERR) { + dom_node_unref(n); + return NSERROR_DOM; + } - case CONTENT_MSG_STATUS: - if (event->data.explicit_status_text == NULL) { - /* Object content's status text updated */ - html_set_status(parent, - content_get_status_message(css)); - content_broadcast(&parent->base, CONTENT_MSG_STATUS, - event->data); - } else { - /* Object content wants to set explicit message */ - content_broadcast(&parent->base, CONTENT_MSG_STATUS, - event->data); + /* Recurse into noscript elements */ + if (dom_string_caseless_lwc_isequal(name, corestring_lwc_noscript)) { + ns_error = html_meta_refresh(c, n); + if (ns_error != NSERROR_OK) { + /* Some error occurred */ + dom_string_unref(name); + dom_node_unref(n); + return ns_error; + } else if (c->base.refresh != NULL) { + /* Meta refresh found - stop */ + dom_string_unref(name); + dom_node_unref(n); + return NSERROR_OK; + } + } else if (dom_string_caseless_lwc_isequal(name, corestring_lwc_meta)) { + ns_error = html_meta_refresh_process_element(c, n); + if (ns_error != NSERROR_OK) { + /* Some error occurred */ + dom_string_unref(name); + dom_node_unref(n); + return ns_error; + } else if (c->base.refresh != NULL) { + /* Meta refresh found - stop */ + dom_string_unref(name); + dom_node_unref(n); + return NSERROR_OK; + } + } + dom_string_unref(name); } - break; - - default: - assert(0); - } - - if (parent->base.active == 0) - html_finish_conversion(parent); - return NSERROR_OK; -} + exc = dom_node_get_next_sibling(n, &next); + if (exc != DOM_NO_ERR) { + dom_node_unref(n); + return NSERROR_DOM; + } -/** - * Handle notification of inline style completion - * - * \param css Inline style object - * \param pw Private data - */ -static void html_inline_style_done(struct content_css_data *css, void *pw) -{ - html_content *html = pw; + dom_node_unref(n); + n = next; + } - if (--html->base.active == 0) - html_finish_conversion(html); + return ns_error; } /** - * Process an inline stylesheet in the document. - * - * \param c content structure - * \param style xml node of style element - * \return true on success, false if an error occurred + * Update a box whose content has completed rendering. */ -static bool html_process_style_element(html_content *c, dom_node *style) +static void +html_object_done(struct box *box, + hlcache_handle *object, + bool background) { - dom_node *child, *next; - dom_string *val; - dom_exception exc; - struct html_stylesheet *stylesheets; - struct content_css_data *sheet; - nserror error; - - /* type='text/css', or not present (invalid but common) */ - exc = dom_element_get_attribute(style, corestring_dom_type, &val); - if (exc == DOM_NO_ERR && val != NULL) { - if (!dom_string_caseless_lwc_isequal(val, - corestring_lwc_text_css)) { - dom_string_unref(val); - return true; - } - dom_string_unref(val); - } - - /* media contains 'screen' or 'all' or not present */ - exc = dom_element_get_attribute(style, corestring_dom_media, &val); - if (exc == DOM_NO_ERR && val != NULL) { - if (strcasestr(dom_string_data(val), "screen") == NULL && - strcasestr(dom_string_data(val), - "all") == NULL) { - dom_string_unref(val); - return true; - } - dom_string_unref(val); - } + struct box *b; - /* Extend array */ - stylesheets = realloc(c->stylesheets, - sizeof(struct html_stylesheet) * (c->stylesheet_count + 1)); - if (stylesheets == NULL) - goto no_memory; + if (background) { + box->background = object; + return; + } - c->stylesheets = stylesheets; + box->object = object; - c->stylesheets[c->stylesheet_count].type = HTML_STYLESHEET_INTERNAL; - c->stylesheets[c->stylesheet_count].data.internal = NULL; + if (!(box->flags & REPLACE_DIM)) { + /* invalidate parent min, max widths */ + for (b = box; b; b = b->parent) + b->max_width = UNKNOWN_MAX_WIDTH; - /* create stylesheet */ - sheet = calloc(1, sizeof(struct content_css_data)); - if (sheet == NULL) { - goto no_memory; + /* delete any clones of this box */ + while (box->next && (box->next->flags & CLONE)) { + /* box_free_box(box->next); */ + box->next = box->next->next; + } } +} - error = nscss_create_css_data(sheet, - nsurl_access(c->base_url), NULL, c->quirks, - html_inline_style_done, c); - if (error != NSERROR_OK) { - free(sheet); - content_broadcast_errorcode(&c->base, error); - return false; - } +/** + * Handle object fetching or loading failure. + * + * \param box box containing object which failed to load + * \param content document of type CONTENT_HTML + * \param background the object was the background image for the box + */ - /* can't just use xmlNodeGetContent(style), because that won't - * give the content of comments which may be used to 'hide' - * the content */ - exc = dom_node_get_first_child(style, &child); - if (exc != DOM_NO_ERR) { - nscss_destroy_css_data(sheet); - free(sheet); - goto no_memory; - } +static void +html_object_failed(struct box *box, html_content *content, bool background) +{ + /* Nothing to do */ + return; +} - while (child != NULL) { - dom_string *data; +/** + * Callback for hlcache_handle_retrieve() for objects. + */ - exc = dom_node_get_text_content(child, &data); - if (exc != DOM_NO_ERR) { - dom_node_unref(child); - nscss_destroy_css_data(sheet); - free(sheet); - goto no_memory; - } +static nserror +html_object_callback(hlcache_handle *object, + const hlcache_event *event, + void *pw) +{ + struct content_html_object *o = pw; + html_content *c = (html_content *) o->parent; + int x, y; + struct box *box; - if (nscss_process_css_data(sheet, dom_string_data(data), - dom_string_byte_length(data)) == false) { - dom_string_unref(data); - dom_node_unref(child); - nscss_destroy_css_data(sheet); - free(sheet); - goto no_memory; - } + assert(c->base.status != CONTENT_STATUS_ERROR); - dom_string_unref(data); + box = o->box; - exc = dom_node_get_next_sibling(child, &next); - if (exc != DOM_NO_ERR) { - dom_node_unref(child); - nscss_destroy_css_data(sheet); - free(sheet); - goto no_memory; - } + switch (event->type) { + case CONTENT_MSG_LOADING: + if (c->base.status != CONTENT_STATUS_LOADING && c->bw != NULL) + content_open(object, + c->bw, &c->base, + box->object_params); + break; - dom_node_unref(child); - child = next; - } + case CONTENT_MSG_READY: + if (content_can_reformat(object)) { + /* TODO: avoid knowledge of box internals here */ + content_reformat(object, false, + box->max_width != UNKNOWN_MAX_WIDTH ? + box->width : 0, + box->max_width != UNKNOWN_MAX_WIDTH ? + box->height : 0); - c->base.active++; - LOG(("%d fetches active", c->base.active)); + /* Adjust parent content for new object size */ + html_object_done(box, object, o->background); + if (c->base.status == CONTENT_STATUS_READY || + c->base.status == CONTENT_STATUS_DONE) + content__reformat(&c->base, false, + c->base.available_width, + c->base.height); + } + break; - /* Convert the content -- manually, as we want the result */ - if (nscss_convert_css_data(sheet) != CSS_OK) { - /* conversion failed */ + case CONTENT_MSG_DONE: c->base.active--; LOG(("%d fetches active", c->base.active)); - nscss_destroy_css_data(sheet); - free(sheet); - sheet = NULL; - } - /* Update index */ - c->stylesheets[c->stylesheet_count].data.internal = sheet; - c->stylesheet_count++; + html_object_done(box, object, o->background); - return true; + if (c->base.status != CONTENT_STATUS_LOADING && + box->flags & REPLACE_DIM) { + union content_msg_data data; -no_memory: - content_broadcast_errorcode(&c->base, NSERROR_NOMEM); - return false; -} + if (!box_visible(box)) + break; + box_coords(box, &x, &y); + data.redraw.x = x + box->padding[LEFT]; + data.redraw.y = y + box->padding[TOP]; + data.redraw.width = box->width; + data.redraw.height = box->height; + data.redraw.full_redraw = true; -static bool html_process_stylesheet_link(html_content *htmlc, dom_node *node) -{ - dom_string *rel, *type_attr, *media, *href; - struct html_stylesheet *stylesheets; - nsurl *joined; - dom_exception exc; - nserror ns_error; - hlcache_child_context child; + content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); + } + break; - /* rel= */ - exc = dom_element_get_attribute(node, corestring_dom_rel, &rel); - if (exc != DOM_NO_ERR || rel == NULL) - return true; + case CONTENT_MSG_ERROR: + hlcache_handle_release(object); - 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); + o->content = NULL; - /* type='text/css' or not present */ - exc = dom_element_get_attribute(node, corestring_dom_type, &type_attr); - if (exc == DOM_NO_ERR && type_attr != NULL) { - if (!dom_string_caseless_lwc_isequal(type_attr, - corestring_lwc_text_css)) { - dom_string_unref(type_attr); - return true; - } - dom_string_unref(type_attr); - } + c->base.active--; + LOG(("%d fetches active", c->base.active)); - /* media contains 'screen' or 'all' or not present */ - exc = dom_element_get_attribute(node, corestring_dom_media, &media); - if (exc == DOM_NO_ERR && media != NULL) { - if (strcasestr(dom_string_data(media), "screen") == NULL && - strcasestr(dom_string_data(media), "all") == NULL) { - dom_string_unref(media); - return true; + content_add_error(&c->base, "?", 0); + html_object_failed(box, c, o->background); + break; + + case CONTENT_MSG_STATUS: + if (event->data.explicit_status_text == NULL) { + /* Object content's status text updated */ + union content_msg_data data; + data.explicit_status_text = + content_get_status_message(object); + html_set_status(c, data.explicit_status_text); + content_broadcast(&c->base, CONTENT_MSG_STATUS, data); + } else { + /* Object content wants to set explicit message */ + content_broadcast(&c->base, CONTENT_MSG_STATUS, + event->data); } - dom_string_unref(media); - } + break; - /* href='...' */ - exc = dom_element_get_attribute(node, corestring_dom_href, &href); - if (exc != DOM_NO_ERR || href == NULL) - return true; + case CONTENT_MSG_REFORMAT: + break; - /* TODO: only the first preferred stylesheets (ie. - * those with a title attribute) should be loaded - * (see HTML4 14.3) */ + case CONTENT_MSG_REDRAW: + if (c->base.status != CONTENT_STATUS_LOADING) { + union content_msg_data data = event->data; - ns_error = nsurl_join(htmlc->base_url, dom_string_data(href), &joined); - if (ns_error != NSERROR_OK) { - dom_string_unref(href); - goto no_memory; - } - dom_string_unref(href); + if (!box_visible(box)) + break; + + box_coords(box, &x, &y); + + if (hlcache_handle_get_content(object) == + event->data.redraw.object) { + data.redraw.x = data.redraw.x * + box->width / content_get_width(object); + data.redraw.y = data.redraw.y * + box->height / + content_get_height(object); + data.redraw.width = data.redraw.width * + box->width / content_get_width(object); + data.redraw.height = data.redraw.height * + box->height / + content_get_height(object); + data.redraw.object_width = box->width; + data.redraw.object_height = box->height; + } + + data.redraw.x += x + box->padding[LEFT]; + data.redraw.y += y + box->padding[TOP]; + data.redraw.object_x += x + box->padding[LEFT]; + data.redraw.object_y += y + box->padding[TOP]; + + content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); + } + break; - LOG(("linked stylesheet %i '%s'", htmlc->stylesheet_count, nsurl_access(joined))); + case CONTENT_MSG_REFRESH: + if (content_get_type(object) == CONTENT_HTML) { + /* only for HTML objects */ + schedule(event->data.delay * 100, + html_object_refresh, o); + } - /* extend stylesheets array to allow for new sheet */ - stylesheets = realloc(htmlc->stylesheets, - sizeof(struct html_stylesheet) * (htmlc->stylesheet_count + 1)); - if (stylesheets == NULL) { - nsurl_unref(joined); - ns_error = NSERROR_NOMEM; - goto no_memory; - } + break; - htmlc->stylesheets = stylesheets; - htmlc->stylesheets[htmlc->stylesheet_count].type = HTML_STYLESHEET_EXTERNAL; + case CONTENT_MSG_LINK: + /* Don't care about favicons that aren't on top level content */ + break; - /* start fetch */ - child.charset = htmlc->encoding; - child.quirks = htmlc->base.quirks; + case CONTENT_MSG_GETCTX: + *(event->data.jscontext) = NULL; + break; - ns_error = hlcache_handle_retrieve(joined, - 0, - content_get_url(&htmlc->base), - NULL, - html_convert_css_callback, - htmlc, - &child, - CONTENT_CSS, - &htmlc->stylesheets[htmlc->stylesheet_count].data.external); + case CONTENT_MSG_SCROLL: + if (box->scroll_x != NULL) + scrollbar_set(box->scroll_x, event->data.scroll.x0, + false); + if (box->scroll_y != NULL) + scrollbar_set(box->scroll_y, event->data.scroll.y0, + false); + break; - nsurl_unref(joined); + case CONTENT_MSG_DRAGSAVE: + { + union content_msg_data msg_data; + if (event->data.dragsave.content == NULL) + msg_data.dragsave.content = object; + else + msg_data.dragsave.content = + event->data.dragsave.content; - if (ns_error != NSERROR_OK) - goto no_memory; + content_broadcast(&c->base, CONTENT_MSG_DRAGSAVE, msg_data); + } + break; - htmlc->base.active++; - LOG(("%d fetches active", htmlc->base.active)); - htmlc->stylesheet_count++; + case CONTENT_MSG_SAVELINK: + case CONTENT_MSG_POINTER: + /* These messages are for browser window layer. + * we're not interested, so pass them on. */ + content_broadcast(&c->base, event->type, event->data); + break; - return true; + case CONTENT_MSG_DRAG: + { + html_drag_type drag_type = HTML_DRAG_NONE; + union html_drag_owner drag_owner; + drag_owner.content = box; -no_memory: - content_broadcast_errorcode(&htmlc->base, ns_error); - return false; -} + switch (event->data.drag.type) { + case CONTENT_DRAG_NONE: + drag_type = HTML_DRAG_NONE; + drag_owner.no_owner = true; + break; + case CONTENT_DRAG_SCROLL: + drag_type = HTML_DRAG_CONTENT_SCROLL; + break; + case CONTENT_DRAG_SELECTION: + drag_type = HTML_DRAG_CONTENT_SELECTION; + break; + } + html_set_drag_type(c, drag_type, drag_owner, + event->data.drag.rect); + } + break; -/** callback to process stylesheet elements - */ -static bool -html_process_stylesheet(dom_node *node, dom_string *name, void *vctx) -{ - html_content *htmlc = vctx; + default: + assert(0); + } - /* deal with style nodes */ - if (dom_string_caseless_lwc_isequal(name, corestring_lwc_style)) { - if (!html_process_style_element(htmlc, node)) - return false; - return true; + if (c->base.status == CONTENT_STATUS_READY && c->base.active == 0 && + (event->type == CONTENT_MSG_LOADING || + event->type == CONTENT_MSG_DONE || + event->type == CONTENT_MSG_ERROR)) { + /* all objects have arrived */ + content__reformat(&c->base, false, c->base.available_width, + c->base.height); + html_set_status(c, ""); + content_set_done(&c->base); } - /* if it is not a link node skip it */ - if (dom_string_caseless_lwc_isequal(name, corestring_lwc_link)) { - return html_process_stylesheet_link(htmlc, node); + /* If 1) the configuration option to reflow pages while objects are + * fetched is set + * 2) an object is newly fetched & converted, + * 3) the box's dimensions need to change due to being replaced + * 4) the object's parent HTML is ready for reformat, + * 5) the time since the previous reformat is more than the + * configured minimum time between reformats + * then reformat the page to display newly fetched objects */ + else if (nsoption_bool(incremental_reflow) && + event->type == CONTENT_MSG_DONE && + !(box->flags & REPLACE_DIM) && + (c->base.status == CONTENT_STATUS_READY || + c->base.status == CONTENT_STATUS_DONE) && + (wallclock() > c->base.reformat_time)) { + content__reformat(&c->base, false, c->base.available_width, + c->base.height); } - return true; + return NSERROR_OK; } /** - * Process inline stylesheets and fetch linked stylesheets. - * - * Uses STYLE and LINK elements inside and outside HEAD + * Start a fetch for an object required by a page, replacing an existing object. * - * \param c content structure - * \param html dom node of html element - * \return true on success, false if an error occurred + * \param object Object to replace + * \param url URL of object to fetch (copied) + * \return true on success, false on memory exhaustion */ -static bool html_find_stylesheets(html_content *c, dom_node *html) +static bool html_replace_object(struct content_html_object *object, nsurl *url) { - nserror ns_error; - bool result; + html_content *c; hlcache_child_context child; + html_content *page; + nserror error; - /* stylesheet 0 is the base style sheet, - * stylesheet 1 is the quirks mode style sheet, - * stylesheet 2 is the adblocking stylesheet, - * stylesheet 3 is the user stylesheet */ - c->stylesheets = calloc(STYLESHEET_START, sizeof(struct html_stylesheet)); - if (c->stylesheets == NULL) { - ns_error = NSERROR_NOMEM; - goto html_find_stylesheets_no_memory; - } + assert(object != NULL); - 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 = (html_content *) object->parent; child.charset = c->encoding; child.quirks = c->base.quirks; - ns_error = hlcache_handle_retrieve(html_default_stylesheet_url, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_BASE].data.external); - if (ns_error != NSERROR_OK) - goto html_find_stylesheets_no_memory; + if (object->content != NULL) { + /* remove existing object */ + if (content_get_status(object->content) != CONTENT_STATUS_DONE) { + c->base.active--; + LOG(("%d fetches active", c->base.active)); + } - c->base.active++; - LOG(("%d fetches active", c->base.active)); + hlcache_handle_release(object->content); + object->content = NULL; - if (c->quirks == DOM_DOCUMENT_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, - CONTENT_CSS, - &c->stylesheets[STYLESHEET_QUIRKS].data.external); - if (ns_error != NSERROR_OK) - goto html_find_stylesheets_no_memory; + object->box->object = NULL; + } - c->base.active++; + /* initialise fetch */ + error = hlcache_handle_retrieve(url, HLCACHE_RETRIEVE_SNIFF_TYPE, + content_get_url(&c->base), NULL, + html_object_callback, object, &child, + object->permitted_types, + &object->content); + + if (error != NSERROR_OK) + return false; + + for (page = c; page != NULL; page = page->page) { + page->base.active++; LOG(("%d fetches active", c->base.active)); + page->base.status = CONTENT_STATUS_READY; } - 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, CONTENT_CSS, - &c->stylesheets[STYLESHEET_ADBLOCK]. - data.external); - if (ns_error != NSERROR_OK) - goto html_find_stylesheets_no_memory; + return true; +} - c->base.active++; - LOG(("%d fetches active", c->base.active)); +/** + * schedule() callback for object refresh + */ - } +static void html_object_refresh(void *p) +{ + struct content_html_object *object = p; + nsurl *refresh_url; - ns_error = hlcache_handle_retrieve(html_user_stylesheet_url, 0, - content_get_url(&c->base), NULL, - html_convert_css_callback, c, &child, CONTENT_CSS, - &c->stylesheets[STYLESHEET_USER].data.external); - if (ns_error != NSERROR_OK) - goto html_find_stylesheets_no_memory; + assert(content_get_type(object->content) == CONTENT_HTML); - c->base.active++; - LOG(("%d fetches active", c->base.active)); + refresh_url = content_get_refresh_url(object->content); - result = libdom_treewalk(html, html_process_stylesheet, c); + /* Ignore if refresh URL has gone + * (may happen if fetch errored) */ + if (refresh_url == NULL) + return; - return result; + content_invalidate_reuse_data(object->content); -html_find_stylesheets_no_memory: - content_broadcast_errorcode(&c->base, ns_error); - return false; + if (!html_replace_object(object, refresh_url)) { + /** \todo handle memory exhaustion */ + } } + + + + + + + + + /** * Convert a CONTENT_HTML for display. * @@ -2086,15 +2125,13 @@ html_begin_conversion(html_content *htmlc) } dom_node_unref(head); + dom_node_unref(html); - /* get stylesheets */ - if (html_find_stylesheets(htmlc, html) == false) { - dom_node_unref(html); + /* ensure stylesheets are initialised */ + if (html_init_stylesheets(htmlc) == false) { return false; } - dom_node_unref(html); - if (htmlc->base.active == 0) { html_finish_conversion(htmlc); } -- cgit v1.2.3