From 07d445a5ca3201314b47fe46fd24629701c28670 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sat, 24 Mar 2012 17:11:17 +0000 Subject: conversion to libdom work in progress with FIXME blocks svn path=/trunk/netsurf/; revision=13606 --- render/html.c | 2722 +++++++++++++++++++++++++++++------------------------ render/imagemap.c | 22 +- render/imagemap.h | 4 +- 3 files changed, 1484 insertions(+), 1264 deletions(-) diff --git a/render/html.c b/render/html.c index 6449eb315..8002b4280 100644 --- a/render/html.c +++ b/render/html.c @@ -27,6 +27,7 @@ #include #include #include + #include "utils/config.h" #include "content/content_protected.h" #include "content/fetch.h" @@ -60,83 +61,6 @@ #define ALWAYS_DUMP_FRAMESET 0 #define ALWAYS_DUMP_BOX 0 -static void html_fini(void); -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 nserror html_create_html_data(html_content *c, - const http_parameter *params); -static bool html_process_data(struct content *c, const char *data, - unsigned int size); -static bool html_convert(struct content *c); -static void html_reformat(struct content *c, int width, int height); -static void html_destroy(struct content *c); -static void html_stop(struct content *c); -static void html_open(struct content *c, struct browser_window *bw, - struct content *page, struct box *box, - struct object_params *params); -static void html_close(struct content *c); -struct selection *html_get_selection(struct content *c); -static void html_get_contextual_content(struct content *c, - int x, int y, struct contextual_content *data); -static bool html_scroll_at_point(struct content *c, - int x, int y, int scrx, int scry); -static bool html_drop_file_at_point(struct content *c, - int x, int y, char *file); -struct search_context *html_get_search(struct content *c); -static nserror html_clone(const struct content *old, struct content **newc); -static content_type html_content_type(void); - -static void html_finish_conversion(html_content *c); -static void html_box_convert_done(html_content *c, bool success); -static nserror html_convert_css_callback(hlcache_handle *css, - const hlcache_event *event, void *pw); -static bool html_meta_refresh(html_content *c, xmlNode *head); -static bool html_head(html_content *c, xmlNode *head); -static bool html_find_stylesheets(html_content *c, xmlNode *html); -static bool html_process_style_element(html_content *c, unsigned int *index, - xmlNode *style); -static void html_inline_style_done(struct content_css_data *css, void *pw); -static bool html_replace_object(struct content_html_object *object, - nsurl *url); -static nserror html_object_callback(hlcache_handle *object, - const hlcache_event *event, void *pw); -static void html_object_done(struct box *box, hlcache_handle *object, - bool background); -static void html_object_failed(struct box *box, html_content *content, - bool background); -static void html_object_refresh(void *p); -static void html_destroy_objects(html_content *html); -static void html_destroy_frameset(struct content_html_frames *frameset); -static void html_destroy_iframe(struct content_html_iframe *iframe); -#if ALWAYS_DUMP_FRAMESET -static void html_dump_frameset(struct content_html_frames *frame, - unsigned int depth); -#endif - -static const content_handler html_content_handler = { - .fini = html_fini, - .create = html_create, - .process_data = html_process_data, - .data_complete = html_convert, - .reformat = html_reformat, - .destroy = html_destroy, - .stop = html_stop, - .mouse_track = html_mouse_track, - .mouse_action = html_mouse_action, - .redraw = html_redraw, - .open = html_open, - .close = html_close, - .get_selection = html_get_selection, - .get_contextual_content = html_get_contextual_content, - .scroll_at_point = html_scroll_at_point, - .drop_file_at_point = html_drop_file_at_point, - .clone = html_clone, - .type = html_content_type, - .no_share = true, -}; - static const char empty_document[] = "" @@ -155,6 +79,10 @@ static const char *html_types[] = { "text/html" }; +/* forward declared functions */ +static void html_object_refresh(void *p); + +/* pre-interned character set */ static lwc_string *html_charset; static nsurl *html_default_stylesheet_url; @@ -162,119 +90,22 @@ static nsurl *html_adblock_stylesheet_url; static nsurl *html_quirks_stylesheet_url; static nsurl *html_user_stylesheet_url; -nserror html_init(void) -{ - uint32_t i; - lwc_error lerror; - nserror error; - - lerror = lwc_intern_string("charset", SLEN("charset"), &html_charset); - if (lerror != lwc_error_ok) { - error = NSERROR_NOMEM; - goto error; - } - - error = nsurl_create("resource:default.css", - &html_default_stylesheet_url); - if (error != NSERROR_OK) - goto error; - - error = nsurl_create("resource:adblock.css", - &html_adblock_stylesheet_url); - if (error != NSERROR_OK) - goto error; - - error = nsurl_create("resource:quirks.css", - &html_quirks_stylesheet_url); - if (error != NSERROR_OK) - goto error; - - error = nsurl_create("resource:user.css", - &html_user_stylesheet_url); - if (error != NSERROR_OK) - goto error; - - for (i = 0; i < NOF_ELEMENTS(html_types); i++) { - error = content_factory_register_handler(html_types[i], - &html_content_handler); - if (error != NSERROR_OK) - goto error; - } - - return NSERROR_OK; - -error: - html_fini(); - - return error; -} - -void html_fini(void) -{ - if (html_user_stylesheet_url != NULL) { - nsurl_unref(html_user_stylesheet_url); - html_user_stylesheet_url = NULL; - } - - if (html_quirks_stylesheet_url != NULL) { - nsurl_unref(html_quirks_stylesheet_url); - html_quirks_stylesheet_url = NULL; - } - - if (html_adblock_stylesheet_url != NULL) { - nsurl_unref(html_adblock_stylesheet_url); - html_adblock_stylesheet_url = NULL; - } - - if (html_default_stylesheet_url != NULL) { - nsurl_unref(html_default_stylesheet_url); - html_default_stylesheet_url = NULL; - } - - if (html_charset != NULL) { - lwc_string_unref(html_charset); - html_charset = NULL; - } -} - -/** - * Create a CONTENT_HTML. - * - * The content_html_data structure is initialized and the HTML parser is - * created. - */ - -nserror html_create(const content_handler *handler, - lwc_string *imime_type, const http_parameter *params, - llcache_handle *llcache, const char *fallback_charset, - bool quirks, struct content **c) -{ - html_content *html; - nserror error; - - html = talloc_zero(0, 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) { - talloc_free(html); - return error; - } - - error = html_create_html_data(html, params); - if (error != NSERROR_OK) { - talloc_free(html); - return error; - } - - *c = (struct content *) html; - - return NSERROR_OK; -} - -nserror html_create_html_data(html_content *c, const http_parameter *params) +/* pre-interned dom strings */ +static dom_string *html_dom_string_html; +static dom_string *html_dom_string_head; +static dom_string *html_dom_string_rel; +static dom_string *html_dom_string_href; +static dom_string *html_dom_string_hreflang; +static dom_string *html_dom_string_type; +static dom_string *html_dom_string_media; +static dom_string *html_dom_string_sizes; +static dom_string *html_dom_string_title; +static dom_string *html_dom_string_base; +static dom_string *html_dom_string_link; + + +static nserror +html_create_html_data(html_content *c, const http_parameter *params) { lwc_string *charset; union content_msg_data msg_data; @@ -367,12 +198,55 @@ error: return nerror; } +/** + * Create a CONTENT_HTML. + * + * The content_html_data structure is initialized and the HTML parser is + * created. + */ + +static nserror +html_create(const content_handler *handler, + lwc_string *imime_type, + const http_parameter *params, + llcache_handle *llcache, + const char *fallback_charset, + bool quirks, + struct content **c) +{ + html_content *html; + nserror error; + + html = talloc_zero(0, 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) { + talloc_free(html); + return error; + } + + error = html_create_html_data(html, params); + if (error != NSERROR_OK) { + talloc_free(html); + return error; + } + + *c = (struct content *) html; + + return NSERROR_OK; +} + + /** * Process data for CONTENT_HTML. */ -bool html_process_data(struct content *c, const char *data, unsigned int size) +static bool +html_process_data(struct content *c, const char *data, unsigned int size) { html_content *html = (html_content *) c; binding_error err; @@ -460,546 +334,283 @@ encoding_change: } } -/** - * Convert a CONTENT_HTML for display. - * - * The following steps are carried out in order: - * - * - parsing to an XML tree is completed - * - stylesheets are fetched - * - the XML tree is converted to a box tree and object fetches are started - * - * On exit, the content status will be either CONTENT_STATUS_DONE if the - * document is completely loaded or CONTENT_STATUS_READY if objects are still - * being fetched. - */ - -bool html_convert(struct content *c) +/** process link node */ +static bool html_process_link(html_content *c, dom_node *node) { - html_content *htmlc = (html_content *) c; - binding_error err; - xmlNode *html, *head; - union content_msg_data msg_data; - unsigned long size; - struct form *f; - - /* finish parsing */ - content__get_source_data(c, &size); - if (size == 0) { - /* Destroy current binding */ - binding_destroy_tree(htmlc->parser_binding); - - /* Also, any existing encoding information, - * as it's not guaranteed to match the error page. - */ - talloc_free(htmlc->encoding); - htmlc->encoding = NULL; + struct content_rfc5988_link link; /* the link added to the content */ + dom_exception exc; /* returned by libdom functions */ + dom_string *atr_string; + nserror error; - /* Create new binding, using default charset */ - err = binding_create_tree(c, NULL, &htmlc->parser_binding); - if (err != BINDING_OK) { - union content_msg_data msg_data; + memset(&link, 0, sizeof(struct content_rfc5988_link)); - if (err == BINDING_BADENCODING) { - LOG(("Bad encoding: %s", htmlc->encoding - ? htmlc->encoding : "")); - msg_data.error = messages_get("ParsingFail"); - } else - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } + /* check that the relation exists - w3c spec says must be present */ + exc = dom_element_get_attribute(node, html_dom_string_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; + } + + /* check that the href exists - w3c spec says must be present */ + exc = dom_element_get_attribute(node, html_dom_string_href, &atr_string); + if ((exc != DOM_NO_ERR) || (atr_string == NULL)) { + lwc_string_unref(link.rel); + return false; + } - /* Process the error page */ - if (html_process_data(c, (char *) empty_document, - SLEN(empty_document)) == false) - 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; } - err = binding_parse_completed(htmlc->parser_binding); - if (err != BINDING_OK) { - union content_msg_data msg_data; + /* look for optional properties -- we don't care if internment fails */ - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + exc = dom_element_get_attribute(node, html_dom_string_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); + } - return false; + exc = dom_element_get_attribute(node, html_dom_string_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); } - htmlc->document = binding_get_document(htmlc->parser_binding, - &htmlc->quirks); - /*xmlDebugDumpDocument(stderr, htmlc->document);*/ + exc = dom_element_get_attribute(node, html_dom_string_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); + } - if (htmlc->document == NULL) { - LOG(("Parsing failed")); - msg_data.error = messages_get("ParsingFail"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; + exc = dom_element_get_attribute(node, html_dom_string_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 (htmlc->encoding == NULL) { - const char *encoding = binding_get_encoding( - htmlc->parser_binding, - &htmlc->encoding_source); + /* add to content */ + content__add_rfc5988_link(&c->base, &link); - htmlc->encoding = talloc_strdup(c, encoding); - if (htmlc->encoding == NULL) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } - } + 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); - /* Give up processing if we've been aborted */ - if (htmlc->aborted) { - msg_data.error = messages_get("Stopped"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } + nsurl_unref(link.href); + lwc_string_unref(link.rel); - /* locate html and head elements */ - html = xmlDocGetRootElement(htmlc->document); - if (html == NULL || strcmp((const char *) html->name, "html") != 0) { - LOG(("html element not found")); - msg_data.error = messages_get("ParsingFail"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return true; +} + +/** 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)) { return false; } - for (head = html->children; - head != NULL && head->type != XML_ELEMENT_NODE; - head = head->next) - ; - if (head && strcmp((const char *) head->name, "head") != 0) { - head = NULL; - LOG(("head element not found")); - } - if (head != NULL) { - if (html_head(htmlc, head) == false) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } + title_str = squash_whitespace(dom_string_data(title)); + dom_string_unref(title); - /* handle meta refresh */ - if (html_meta_refresh(htmlc, head) == false) - return false; + if (title_str == NULL) { + return false; } - /* Retrieve forms from parser */ - htmlc->forms = binding_get_forms(htmlc->parser_binding); - for (f = htmlc->forms; f != NULL; f = f->prev) { - char *action; - url_func_result res; - - /* Make all actions absolute */ - if (f->action == NULL || f->action[0] == '\0') { - /* HTML5 4.10.22.3 step 11 */ - res = url_join(nsurl_access(content_get_url(c)), - nsurl_access(htmlc->base_url), &action); - } else { - res = url_join(f->action, nsurl_access(htmlc->base_url), - &action); - } + success = content__set_title(&c->base, title_str); - if (res != URL_FUNC_OK) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - return false; - } + free(title_str); - free(f->action); - f->action = action; + return success; +} - /* Ensure each form has a document encoding */ - if (f->document_charset == NULL) { - f->document_charset = strdup(htmlc->encoding); - if (f->document_charset == NULL) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(c, CONTENT_MSG_ERROR, - msg_data); +static bool html_process_base(html_content *c, dom_node *node) +{ +#ifdef FIXME + char *href = (char *) xmlGetProp(node, (const xmlChar *) "href"); + if (href) { + nsurl *url; + nserror error; + error = nsurl_create(href, &url); + if (error == NSERROR_OK) { + if (c->base_url != NULL) + nsurl_unref(c->base_url); + c->base_url = url; + } + xmlFree(href); + } + + /* don't use the central values to ease freeing later on */ + if ((s = xmlGetProp(node, (const xmlChar *) "target"))) { + if ((!strcasecmp((const char *) s, "_blank")) || + (!strcasecmp((const char *) s, + "_top")) || + (!strcasecmp((const char *) s, + "_parent")) || + (!strcasecmp((const char *) s, + "_self")) || + ('a' <= s[0] && s[0] <= 'z') || + ('A' <= s[0] && s[0] <= 'Z')) { /* [6.16] */ + c->base_target = talloc_strdup(c, + (const char *) s); + if (!c->base_target) { + xmlFree(s); return false; } } + xmlFree(s); } - - /* get stylesheets */ - if (html_find_stylesheets(htmlc, html) == false) - return false; - - return true; + #endif + return false; } /** - * Complete conversion of an HTML document - * - * \param c Content to convert + * 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. */ -void html_finish_conversion(html_content *c) -{ - union content_msg_data msg_data; - xmlNode *html; - uint32_t i; - css_error error; - - /* Bail out if we've been aborted */ - if (c->aborted) { - msg_data.error = messages_get("Stopped"); - content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); - content_set_error(&c->base); - return; - } - - html = xmlDocGetRootElement(c->document); - assert(html != NULL); - - /* check that the base stylesheet loaded; layout fails without it */ - if (c->stylesheets[STYLESHEET_BASE].data.external == NULL) { - msg_data.error = "Base stylesheet failed to load"; - content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); - content_set_error(&c->base); - return; - } - /* Create selection context */ - error = css_select_ctx_create(ns_realloc, c, &c->select_ctx); - if (error != CSS_OK) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); - content_set_error(&c->base); - return; +static bool 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; + + exc = dom_node_get_first_child(head, &node); + if (exc != DOM_NO_ERR) { + return false; } - /* Add sheets to it */ - for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) { - const struct html_stylesheet *hsheet = &c->stylesheets[i]; - css_stylesheet *sheet; - css_origin origin = CSS_ORIGIN_AUTHOR; + while (node != NULL) { + exc = dom_node_get_node_type(head, &node_type); - if (i < STYLESHEET_USER) - origin = CSS_ORIGIN_UA; - else if (i < STYLESHEET_START) - origin = CSS_ORIGIN_USER; + if ((exc == DOM_NO_ERR) && (node_type == DOM_ELEMENT_NODE)) { + exc = dom_node_get_node_name(head, &node_name); - if (hsheet->type == HTML_STYLESHEET_EXTERNAL && - hsheet->data.external != NULL) { - sheet = nscss_get_stylesheet(hsheet->data.external); - } else if (hsheet->type == HTML_STYLESHEET_INTERNAL) { - sheet = hsheet->data.internal->sheet; - } else { - sheet = NULL; + if ((exc == DOM_NO_ERR) || (node_name != NULL)) { + if (!dom_string_caseless_isequal(node_name, + html_dom_string_title)) { + html_process_title(c, node); + } else if (!dom_string_caseless_isequal(node_name, + html_dom_string_base)) { + html_process_base(c, node); + } else if (!dom_string_caseless_isequal(node_name, + html_dom_string_link)) { + html_process_link(c, node); + } + } } - if (sheet != NULL) { - error = css_select_ctx_append_sheet( - c->select_ctx, sheet, - origin, CSS_MEDIA_SCREEN); - if (error != CSS_OK) { - msg_data.error = messages_get("NoMemory"); - content_broadcast(&c->base, CONTENT_MSG_ERROR, - msg_data); - content_set_error(&c->base); - return; - } + /* 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; } } - /* convert xml tree to box tree */ - LOG(("XML to box (%p)", c)); - content_set_status(&c->base, messages_get("Processing")); - content_broadcast(&c->base, CONTENT_MSG_STATUS, msg_data); - if (xml_to_box(html, c, html_box_convert_done) == false) { - html_destroy_objects(c); - msg_data.error = messages_get("NoMemory"); - content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); - content_set_error(&c->base); - return; - } + return true; } /** - * Perform post-box-creation conversion of a document + * Search for meta refresh * - * \param c HTML content to complete conversion of - * \param success Whether box tree construction was successful + * 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) */ -void html_box_convert_done(html_content *c, bool success) + +static bool html_meta_refresh(html_content *c, dom_node *head) { +#ifdef FIXME + dom_node *n; + xmlChar *equiv, *content; union content_msg_data msg_data; - xmlNode *html; + char *url, *end, *refresh = NULL, quote = 0; + nsurl *nsurl; + nserror error; - LOG(("Done XML to box (%p)", c)); + for (n = head == NULL ? NULL : head->children; n; n = n->next) { + if (n->type != XML_ELEMENT_NODE) + continue; - /* Clean up and report error if unsuccessful or aborted */ - if (success == false || c->aborted) { - html_destroy_objects(c); - if (success == false) - msg_data.error = messages_get("NoMemory"); - else - msg_data.error = messages_get("Stopped"); - content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); - content_set_error(&c->base); - return; - } + /* Recurse into noscript elements */ + if (strcmp((const char *) n->name, "noscript") == 0) { + if (html_meta_refresh(c, n) == false) { + /* Some error occurred */ + return false; + } else if (c->base.refresh) { + /* Meta refresh found - stop */ + return true; + } + } - html = xmlDocGetRootElement(c->document); - assert(html != NULL); + if (strcmp((const char *) n->name, "meta") != 0) { + continue; + } -#if ALWAYS_DUMP_BOX - box_dump(stderr, c->layout->children, 0); -#endif -#if ALWAYS_DUMP_FRAMESET - if (c->frameset) - html_dump_frameset(c->frameset, 0); -#endif + equiv = xmlGetProp(n, (const xmlChar *) "http-equiv"); + if (equiv == NULL) + continue; - /* extract image maps - can't do this sensibly in xml_to_box */ - if (imagemap_extract(html, c) == false) { - LOG(("imagemap extraction failed")); - html_destroy_objects(c); - msg_data.error = messages_get("NoMemory"); - content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); - content_set_error(&c->base); - return; - } - /*imagemap_dump(c);*/ + if (strcasecmp((const char *) equiv, "refresh") != 0) { + xmlFree(equiv); + continue; + } - /* Destroy the parser binding */ - binding_destroy_tree(c->parser_binding); - c->parser_binding = NULL; + xmlFree(equiv); - content_set_ready(&c->base); + content = xmlGetProp(n, (const xmlChar *) "content"); + if (content == NULL) + continue; - if (c->base.active == 0) - content_set_done(&c->base); + end = (char *) content + strlen((const char *) content); - html_set_status(c, ""); -} - - -/** process link node */ -static bool html_process_link(html_content *c, xmlNode *node) -{ - struct content_rfc5988_link link; - char *xmlstr; - nserror error; - lwc_string *rel; - nsurl *href; - - /* check that the relation exists - w3c spec says must be present */ - xmlstr = (char *)xmlGetProp(node, (const xmlChar *)"rel"); - if (xmlstr == NULL) { - return false; - } - if (lwc_intern_string(xmlstr, strlen(xmlstr), &rel) != lwc_error_ok) { - xmlFree(xmlstr); - return false; - } - xmlFree(xmlstr); - - /* check that the href exists - w3c spec says must be present */ - xmlstr = (char *)xmlGetProp(node, (const xmlChar *) "href"); - if (xmlstr == NULL) { - return false; - } - error = nsurl_join(c->base_url, xmlstr, &href); - xmlFree(xmlstr); - if (error != NSERROR_OK) { - lwc_string_unref(rel); - return false; - } - - memset(&link, 0, sizeof(struct content_rfc5988_link)); - - link.rel = rel; - link.href = href; - - /* look for optional properties -- we don't care if internment fails */ - xmlstr = (char *)xmlGetProp(node, (const xmlChar *) "hreflang"); - if (xmlstr != NULL) { - lwc_intern_string(xmlstr, strlen(xmlstr), &link.hreflang); - xmlFree(xmlstr); - } - - xmlstr = (char *) xmlGetProp(node, (const xmlChar *) "type"); - if (xmlstr != NULL) { - lwc_intern_string(xmlstr, strlen(xmlstr), &link.type); - xmlFree(xmlstr); - } - - xmlstr = (char *) xmlGetProp(node, (const xmlChar *) "media"); - if (xmlstr != NULL) { - lwc_intern_string(xmlstr, strlen(xmlstr), &link.media); - xmlFree(xmlstr); - } - - xmlstr = (char *) xmlGetProp(node, (const xmlChar *) "sizes"); - if (xmlstr != NULL) { - lwc_intern_string(xmlstr, strlen(xmlstr), &link.sizes); - xmlFree(xmlstr); - } - - /* add to content */ - content__add_rfc5988_link(&c->base, &link); - - 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); - - nsurl_unref(link.href); - lwc_string_unref(link.rel); - - return true; -} - -/** - * 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. - */ - -bool html_head(html_content *c, xmlNode *head) -{ - xmlNode *node; - xmlChar *s; - - for (node = head->children; node != 0; node = node->next) { - if (node->type != XML_ELEMENT_NODE) - continue; - - if (c->base.title == NULL && strcmp((const char *) node->name, - "title") == 0) { - xmlChar *title = xmlNodeGetContent(node); - char *title2; - if (!title) - return false; - title2 = squash_whitespace((const char *) title); - xmlFree(title); - if (!title2) - return false; - if (content__set_title(&c->base, title2) == false) { - free(title2); - return false; - } - - free(title2); - - } else if (strcmp((const char *) node->name, "base") == 0) { - char *href = (char *) xmlGetProp(node, - (const xmlChar *) "href"); - if (href) { - nsurl *url; - nserror error; - error = nsurl_create(href, &url); - if (error == NSERROR_OK) { - if (c->base_url != NULL) - nsurl_unref(c->base_url); - c->base_url = url; - } - xmlFree(href); - } - /* don't use the central values to ease freeing later on */ - if ((s = xmlGetProp(node, (const xmlChar *) "target"))) { - if ((!strcasecmp((const char *) s, "_blank")) || - (!strcasecmp((const char *) s, - "_top")) || - (!strcasecmp((const char *) s, - "_parent")) || - (!strcasecmp((const char *) s, - "_self")) || - ('a' <= s[0] && s[0] <= 'z') || - ('A' <= s[0] && s[0] <= 'Z')) { /* [6.16] */ - c->base_target = talloc_strdup(c, - (const char *) s); - if (!c->base_target) { - xmlFree(s); - return false; - } - } - xmlFree(s); - } - } else if (strcmp((const char *) node->name, "link") == 0) { - html_process_link(c, node); - } - } - return true; -} - - -/** - * 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) - */ - -bool html_meta_refresh(html_content *c, xmlNode *head) -{ - xmlNode *n; - xmlChar *equiv, *content; - union content_msg_data msg_data; - char *url, *end, *refresh = NULL, quote = 0; - nsurl *nsurl; - nserror error; - - for (n = head == NULL ? NULL : head->children; n; n = n->next) { - if (n->type != XML_ELEMENT_NODE) - continue; - - /* Recurse into noscript elements */ - if (strcmp((const char *) n->name, "noscript") == 0) { - if (html_meta_refresh(c, n) == false) { - /* Some error occurred */ - return false; - } else if (c->base.refresh) { - /* Meta refresh found - stop */ - return true; - } - } - - if (strcmp((const char *) n->name, "meta") != 0) { - continue; - } - - equiv = xmlGetProp(n, (const xmlChar *) "http-equiv"); - if (equiv == NULL) - continue; - - if (strcasecmp((const char *) equiv, "refresh") != 0) { - xmlFree(equiv); - continue; - } - - xmlFree(equiv); - - content = xmlGetProp(n, (const xmlChar *) "content"); - if (content == NULL) - continue; - - end = (char *) content + strlen((const char *) 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] - */ + /* 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 = (char *) content; @@ -1134,218 +745,544 @@ bool html_meta_refresh(html_content *c, xmlNode *head) content_broadcast(&c->base, CONTENT_MSG_REFRESH, msg_data); } - +#endif return true; } - /** - * Process inline stylesheets and fetch linked stylesheets. - * - * Uses STYLE and LINK elements inside and outside HEAD - * - * \param c content structure - * \param html xml node of html element - * \return true on success, false if an error occurred + * Update a box whose content has completed rendering. */ -bool html_find_stylesheets(html_content *c, xmlNode *html) +static void +html_object_done(struct box *box, + hlcache_handle *object, + bool background) { - content_type accept = CONTENT_CSS; - xmlNode *node; - char *rel, *type, *media, *href; - 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; + struct box *b; - 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; + if (background) { + box->background = object; + return; + } - c->base.active++; + box->object = object; - 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; + if (!(box->flags & REPLACE_DIM)) { + /* invalidate parent min, max widths */ + for (b = box; b; b = b->parent) + b->max_width = UNKNOWN_MAX_WIDTH; - c->base.active++; + /* delete any clones of this box */ + while (box->next && (box->next->flags & CLONE)) { + /* box_free_box(box->next); */ + box->next = box->next->next; + } } +} - 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; +/** + * 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 + */ - c->base.active++; - } +static void +html_object_failed(struct box *box, html_content *content, bool background) +{ + /* Nothing to do */ + return; +} - 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; +/** + * Callback for hlcache_handle_retrieve() for objects. + */ - c->base.active++; +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; - node = html; + assert(c->base.status != CONTENT_STATUS_ERROR); - /* depth-first search the tree for link elements */ - while (node) { - if (node->children) { /* 1. children */ - node = node->children; - } else if (node->next) { /* 2. siblings */ - node = node->next; - } else { /* 3. ancestor siblings */ - while (node && !node->next) - node = node->parent; - if (!node) + box = o->box; + + 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, + box->object_params); + break; + + case CONTENT_MSG_READY: + if (content_get_type(object) == CONTENT_HTML) { + 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--; + html_object_done(box, object, o->background); + + if (c->base.status != CONTENT_STATUS_LOADING && + box->flags & REPLACE_DIM) { + union content_msg_data data; + + if (!box_visible(box)) break; - node = node->next; + + 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; + + content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); } + break; - assert(node); + case CONTENT_MSG_ERROR: + hlcache_handle_release(object); - if (node->type != XML_ELEMENT_NODE) - continue; + o->content = NULL; - if (strcmp((const char *) node->name, "link") == 0) { - /* rel= */ - if ((rel = (char *) xmlGetProp(node, - (const xmlChar *) "rel")) == NULL) - continue; - if (strcasestr(rel, "stylesheet") == 0) { - xmlFree(rel); - continue; - } else if (strcasestr(rel, "alternate")) { - /* Ignore alternate stylesheets */ - xmlFree(rel); - continue; - } - xmlFree(rel); + c->base.active--; - /* type='text/css' or not present */ - if ((type = (char *) xmlGetProp(node, - (const xmlChar *) "type")) != NULL) { - if (strcmp(type, "text/css") != 0) { - xmlFree(type); - continue; - } - xmlFree(type); + content_add_error(&c->base, "?", 0); + html_set_status(c, event->data.error); + content_broadcast(&c->base, CONTENT_MSG_STATUS, event->data); + html_object_failed(box, c, o->background); + break; + + case CONTENT_MSG_STATUS: + html_set_status(c, content_get_status_message(object)); + /* content_broadcast(&c->base, CONTENT_MSG_STATUS, 0); */ + break; + + case CONTENT_MSG_REFORMAT: + break; + + case CONTENT_MSG_REDRAW: + if (c->base.status != CONTENT_STATUS_LOADING) { + union content_msg_data data = event->data; + + 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; } - /* media contains 'screen' or 'all' or not present */ - if ((media = (char *) xmlGetProp(node, - (const xmlChar *) "media")) != NULL) { - if (strcasestr(media, "screen") == NULL && - strcasestr(media, "all") == - NULL) { - xmlFree(media); - continue; - } - xmlFree(media); + 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; + + case CONTENT_MSG_REFRESH: + if (content_get_type(object) == CONTENT_HTML) { + /* only for HTML objects */ + schedule(event->data.delay * 100, + html_object_refresh, o); + } + + break; + + case CONTENT_MSG_LINK: + /* Don't care about favicons */ + break; + + default: + assert(0); + } + + 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 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 NSERROR_OK; +} + +/** + * 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 + */ + +static bool html_replace_object(struct content_html_object *object, nsurl *url) +{ + html_content *c; + hlcache_child_context child; + html_content *page; + nserror error; + + assert(object != NULL); + + c = (html_content *) object->parent; + + child.charset = c->encoding; + child.quirks = c->base.quirks; + + if (object->content != NULL) { + /* remove existing object */ + if (content_get_status(object->content) != CONTENT_STATUS_DONE) + c->base.active--; + + hlcache_handle_release(object->content); + object->content = NULL; + + object->box->object = NULL; + } + + /* 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++; + page->base.status = CONTENT_STATUS_READY; + } + + return true; +} + +/** + * schedule() callback for object refresh + */ + +static void html_object_refresh(void *p) +{ + struct content_html_object *object = p; + nsurl *refresh_url; + + assert(content_get_type(object->content) == CONTENT_HTML); + + refresh_url = content_get_refresh_url(object->content); + + /* Ignore if refresh URL has gone + * (may happen if fetch errored) */ + if (refresh_url == NULL) + return; + + content_invalidate_reuse_data(object->content); + + if (!html_replace_object(object, refresh_url)) { + /** \todo handle memory exhaustion */ + } +} + + + + + + +static void html_destroy_objects(html_content *html) +{ + while (html->object_list != NULL) { + struct content_html_object *victim = html->object_list; + + if (victim->content != NULL) { + LOG(("object %p", victim->content)); + + if (content_get_type(victim->content) == CONTENT_HTML) + schedule_remove(html_object_refresh, victim); + + hlcache_handle_release(victim->content); + } + + html->object_list = victim->next; + talloc_free(victim); + } +} + +/** + * Perform post-box-creation conversion of a document + * + * \param c HTML content to complete conversion of + * \param success Whether box tree construction was successful + */ +static void html_box_convert_done(html_content *c, bool success) +{ + union content_msg_data msg_data; + dom_exception exc; /* returned by libdom functions */ + dom_node *html; + + LOG(("Done XML to box (%p)", c)); + + /* Clean up and report error if unsuccessful or aborted */ + if ((success == false) || c->aborted) { + html_destroy_objects(c); + if (success == false) + msg_data.error = messages_get("NoMemory"); + else + msg_data.error = messages_get("Stopped"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } + +#if ALWAYS_DUMP_BOX + box_dump(stderr, c->layout->children, 0); +#endif +#if ALWAYS_DUMP_FRAMESET + if (c->frameset) + html_dump_frameset(c->frameset, 0); +#endif + + exc = dom_document_get_document_element(c->document, &html); + if ((exc != DOM_NO_ERR) || (html == NULL)) { + LOG(("error retrieving html element from dom")); + msg_data.error = messages_get("ParsingFail"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } + + /* extract image maps - can't do this sensibly in xml_to_box */ + if (imagemap_extract(html, c) == false) { + LOG(("imagemap extraction failed")); + html_destroy_objects(c); + msg_data.error = messages_get("NoMemory"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } + /*imagemap_dump(c);*/ + + /* Destroy the parser binding */ + binding_destroy_tree(c->parser_binding); + c->parser_binding = NULL; + + content_set_ready(&c->base); + + if (c->base.active == 0) + content_set_done(&c->base); + + html_set_status(c, ""); +} + +/** + * Complete conversion of an HTML document + * + * \param c Content to convert + */ +static void html_finish_conversion(html_content *c) +{ + union content_msg_data msg_data; + dom_exception exc; /* returned by libdom functions */ + dom_node *html; + uint32_t i; + css_error error; + + /* Bail out if we've been aborted */ + if (c->aborted) { + msg_data.error = messages_get("Stopped"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } + + /* check that the base stylesheet loaded; layout fails without it */ + if (c->stylesheets[STYLESHEET_BASE].data.external == NULL) { + msg_data.error = "Base stylesheet failed to load"; + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } + + /* Create selection context */ + error = css_select_ctx_create(ns_realloc, c, &c->select_ctx); + if (error != CSS_OK) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } + + /* Add sheets to it */ + for (i = STYLESHEET_BASE; i != c->stylesheet_count; i++) { + const struct html_stylesheet *hsheet = &c->stylesheets[i]; + css_stylesheet *sheet; + css_origin origin = CSS_ORIGIN_AUTHOR; + + if (i < STYLESHEET_USER) + origin = CSS_ORIGIN_UA; + else if (i < STYLESHEET_START) + origin = CSS_ORIGIN_USER; + + if (hsheet->type == HTML_STYLESHEET_EXTERNAL && + hsheet->data.external != NULL) { + sheet = nscss_get_stylesheet(hsheet->data.external); + } else if (hsheet->type == HTML_STYLESHEET_INTERNAL) { + sheet = hsheet->data.internal->sheet; + } else { + sheet = NULL; + } + + if (sheet != NULL) { + error = css_select_ctx_append_sheet( + c->select_ctx, sheet, + origin, CSS_MEDIA_SCREEN); + if (error != CSS_OK) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, + msg_data); + content_set_error(&c->base); + return; } + } + } - /* href='...' */ - if ((href = (char *) xmlGetProp(node, - (const xmlChar *) "href")) == NULL) - continue; + /* convert xml tree to box tree */ + LOG(("XML to box (%p)", c)); + content_set_status(&c->base, messages_get("Processing")); + content_broadcast(&c->base, CONTENT_MSG_STATUS, msg_data); + + exc = dom_document_get_document_element(c->document, &html); + if ((exc != DOM_NO_ERR) || (html == NULL)) { + LOG(("error retrieving html element from dom")); + msg_data.error = messages_get("ParsingFail"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } + + if (xml_to_box(html, c, html_box_convert_done) == false) { + html_destroy_objects(c); + msg_data.error = messages_get("NoMemory"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); + content_set_error(&c->base); + return; + } +} - /* TODO: only the first preferred stylesheets (ie. - * those with a title attribute) should be loaded - * (see HTML4 14.3) */ +/** + * Callback for fetchcache() for linked stylesheets. + */ - ns_error = nsurl_join(c->base_url, href, &joined); - if (ns_error != NSERROR_OK) { - xmlFree(href); - goto no_memory; - } - xmlFree(href); +static nserror +html_convert_css_callback(hlcache_handle *css, + const hlcache_event *event, + void *pw) +{ + html_content *parent = pw; + unsigned int i; + struct html_stylesheet *s; - LOG(("linked stylesheet %i '%s'", i, - nsurl_access(joined))); + /* 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; + } - /* start fetch */ - stylesheets = talloc_realloc(c, - c->stylesheets, - struct html_stylesheet, i + 1); - if (stylesheets == NULL) { - nsurl_unref(joined); - goto no_memory; - } + assert(i != parent->stylesheet_count); - 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); + switch (event->type) { + case CONTENT_MSG_LOADING: + break; - nsurl_unref(joined); + case CONTENT_MSG_READY: + break; - if (ns_error != NSERROR_OK) - goto no_memory; + case CONTENT_MSG_DONE: + LOG(("got stylesheet '%s'", + nsurl_access(hlcache_handle_get_url(css)))); + parent->base.active--; + break; - c->base.active++; + 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--; + content_add_error(&parent->base, "?", 0); + break; - i++; - } else if (strcmp((const char *) node->name, "style") == 0) { - if (!html_process_style_element(c, &i, node)) - return false; - } - } + case CONTENT_MSG_STATUS: + html_set_status(parent, content_get_status_message(css)); + content_broadcast(&parent->base, CONTENT_MSG_STATUS, + event->data); + break; - assert(c->stylesheet_count == i); + default: + assert(0); + } - return true; + if (parent->base.active == 0) + html_finish_conversion(parent); -no_memory: - msg_data.error = messages_get("NoMemory"); - content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); - return false; + return NSERROR_OK; } - /** * Process an inline stylesheet in the document. * @@ -1356,10 +1293,13 @@ no_memory: * \return true on success, false if an error occurred */ -bool html_process_style_element(html_content *c, unsigned int *index, - xmlNode *style) +static bool +html_process_style_element(html_content *c, + unsigned int *index, + dom_node *style) { - xmlNode *child; +#ifdef FIXME + dom_node *child; char *type, *media, *data; union content_msg_data msg_data; struct html_stylesheet *stylesheets; @@ -1450,437 +1390,526 @@ bool html_process_style_element(html_content *c, unsigned int *index, no_memory: msg_data.error = messages_get("NoMemory"); content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); +#endif return false; } /** - * Handle notification of inline style completion + * Process inline stylesheets and fetch linked stylesheets. * - * \param css Inline style object - * \param pw Private data - */ -void html_inline_style_done(struct content_css_data *css, void *pw) -{ - html_content *html = pw; - - if (--html->base.active == 0) - html_finish_conversion(html); -} - -/** - * Callback for fetchcache() for linked stylesheets. + * Uses STYLE and LINK elements inside and outside HEAD + * + * \param c content structure + * \param html xml node of html element + * \return true on success, false if an error occurred */ -nserror html_convert_css_callback(hlcache_handle *css, - const hlcache_event *event, void *pw) +static bool html_find_stylesheets(html_content *c, dom_node *html) { - html_content *parent = pw; - unsigned int i; - struct html_stylesheet *s; - - /* 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; - } - - assert(i != parent->stylesheet_count); - - switch (event->type) { - case CONTENT_MSG_LOADING: - break; - - case CONTENT_MSG_READY: - break; - - case CONTENT_MSG_DONE: - LOG(("got stylesheet '%s'", - nsurl_access(hlcache_handle_get_url(css)))); - parent->base.active--; - break; - - 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--; - content_add_error(&parent->base, "?", 0); - break; - - case CONTENT_MSG_STATUS: - html_set_status(parent, content_get_status_message(css)); - content_broadcast(&parent->base, CONTENT_MSG_STATUS, - event->data); - break; - - default: - assert(0); - } - - if (parent->base.active == 0) - html_finish_conversion(parent); +#ifdef FIXME + content_type accept = CONTENT_CSS; + dom_node *node; + char *rel, *type, *media, *href; + unsigned int i = STYLESHEET_START; + union content_msg_data msg_data; + struct html_stylesheet *stylesheets; + hlcache_child_context child; + nserror ns_error; + nsurl *joined; - return NSERROR_OK; -} + 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; -/** - * Start a fetch for an object required by a page. - * - * \param c content of type CONTENT_HTML - * \param url URL of object to fetch (copied) - * \param box box that will contain the object - * \param permitted_types bitmap of acceptable types - * \param available_width estimate of width of object - * \param available_height estimate of height of object - * \param background this is a background image - * \return true on success, false on memory exhaustion - */ + c->base.active = 0; -bool html_fetch_object(html_content *c, nsurl *url, struct box *box, - content_type permitted_types, - int available_width, int available_height, - bool background) -{ - struct content_html_object *object; - hlcache_child_context child; - nserror error; + 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; - /* If we've already been aborted, don't bother attempting the fetch */ - if (c->aborted) - return true; + c->base.active++; - child.charset = c->encoding; - child.quirks = c->base.quirks; + 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; - object = talloc(c, struct content_html_object); - if (object == NULL) { - return false; + c->base.active++; } - object->parent = (struct content *) c; - object->next = NULL; - object->content = NULL; - object->box = box; - object->permitted_types = permitted_types; - object->background = background; - - 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) { - talloc_free(object); - return error != 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, accept, + &c->stylesheets[STYLESHEET_ADBLOCK]. + data.external); + if (ns_error != NSERROR_OK) + goto no_memory; + + c->base.active++; } - /* add to content object list */ - object->next = c->object_list; - c->object_list = object; + 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->num_objects++; c->base.active++; - return true; -} + node = html; -/** - * 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 - */ + /* depth-first search the tree for link elements */ + while (node) { + if (node->children) { /* 1. children */ + node = node->children; + } else if (node->next) { /* 2. siblings */ + node = node->next; + } else { /* 3. ancestor siblings */ + while (node && !node->next) + node = node->parent; + if (!node) + break; + node = node->next; + } -bool html_replace_object(struct content_html_object *object, nsurl *url) -{ - html_content *c; - hlcache_child_context child; - html_content *page; - nserror error; + assert(node); - assert(object != NULL); + if (node->type != XML_ELEMENT_NODE) + continue; - c = (html_content *) object->parent; + if (strcmp((const char *) node->name, "link") == 0) { + /* rel= */ + if ((rel = (char *) xmlGetProp(node, + (const xmlChar *) "rel")) == NULL) + continue; + if (strcasestr(rel, "stylesheet") == 0) { + xmlFree(rel); + continue; + } else if (strcasestr(rel, "alternate")) { + /* Ignore alternate stylesheets */ + xmlFree(rel); + continue; + } + xmlFree(rel); - child.charset = c->encoding; - child.quirks = c->base.quirks; + /* type='text/css' or not present */ + if ((type = (char *) xmlGetProp(node, + (const xmlChar *) "type")) != NULL) { + if (strcmp(type, "text/css") != 0) { + xmlFree(type); + continue; + } + xmlFree(type); + } - if (object->content != NULL) { - /* remove existing object */ - if (content_get_status(object->content) != CONTENT_STATUS_DONE) - c->base.active--; + /* media contains 'screen' or 'all' or not present */ + if ((media = (char *) xmlGetProp(node, + (const xmlChar *) "media")) != NULL) { + if (strcasestr(media, "screen") == NULL && + strcasestr(media, "all") == + NULL) { + xmlFree(media); + continue; + } + xmlFree(media); + } - hlcache_handle_release(object->content); - object->content = NULL; + /* href='...' */ + if ((href = (char *) xmlGetProp(node, + (const xmlChar *) "href")) == NULL) + continue; - object->box->object = NULL; - } + /* TODO: only the first preferred stylesheets (ie. + * those with a title attribute) should be loaded + * (see HTML4 14.3) */ - /* 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); + ns_error = nsurl_join(c->base_url, href, &joined); + if (ns_error != NSERROR_OK) { + xmlFree(href); + goto no_memory; + } + xmlFree(href); - if (error != NSERROR_OK) - return false; + LOG(("linked stylesheet %i '%s'", i, + nsurl_access(joined))); - for (page = c; page != NULL; page = page->page) { - page->base.active++; - page->base.status = CONTENT_STATUS_READY; + /* start fetch */ + stylesheets = talloc_realloc(c, + c->stylesheets, + struct html_stylesheet, i + 1); + if (stylesheets == NULL) { + nsurl_unref(joined); + goto no_memory; + } + + 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); + + nsurl_unref(joined); + + if (ns_error != NSERROR_OK) + goto no_memory; + + c->base.active++; + + i++; + } else if (strcmp((const char *) node->name, "style") == 0) { + if (!html_process_style_element(c, &i, node)) + return false; + } } + assert(c->stylesheet_count == i); + return true; -} +no_memory: + msg_data.error = messages_get("NoMemory"); + content_broadcast(&c->base, CONTENT_MSG_ERROR, msg_data); +#endif + return false; +} /** - * Callback for hlcache_handle_retrieve() for objects. + * Convert a CONTENT_HTML for display. + * + * The following steps are carried out in order: + * + * - parsing to an XML tree is completed + * - stylesheets are fetched + * - the XML tree is converted to a box tree and object fetches are started + * + * On exit, the content status will be either CONTENT_STATUS_DONE if the + * document is completely loaded or CONTENT_STATUS_READY if objects are still + * being fetched. */ -nserror html_object_callback(hlcache_handle *object, - const hlcache_event *event, void *pw) +static bool html_convert(struct content *c) { - struct content_html_object *o = pw; - html_content *c = (html_content *) o->parent; - int x, y; - struct box *box; + html_content *htmlc = (html_content *) c; + binding_error err; + dom_node *html, *head; + union content_msg_data msg_data; + unsigned long size; + struct form *f; + dom_exception exc; /* returned by libdom functions */ + dom_string *node_name = NULL; - assert(c->base.status != CONTENT_STATUS_ERROR); + /* finish parsing */ + content__get_source_data(c, &size); + if (size == 0) { + /* Destroy current binding */ + binding_destroy_tree(htmlc->parser_binding); - box = o->box; + /* Also, any existing encoding information, + * as it's not guaranteed to match the error page. + */ + talloc_free(htmlc->encoding); + htmlc->encoding = NULL; - 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, - box->object_params); - break; + /* Create new binding, using default charset */ + err = binding_create_tree(c, NULL, &htmlc->parser_binding); + if (err != BINDING_OK) { + union content_msg_data msg_data; - case CONTENT_MSG_READY: - if (content_get_type(object) == CONTENT_HTML) { - 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); + if (err == BINDING_BADENCODING) { + LOG(("Bad encoding: %s", htmlc->encoding + ? htmlc->encoding : "")); + msg_data.error = messages_get("ParsingFail"); + } else + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; } - break; - case CONTENT_MSG_DONE: - c->base.active--; - html_object_done(box, object, o->background); + /* Process the error page */ + if (html_process_data(c, (char *) empty_document, + SLEN(empty_document)) == false) + return false; + } - if (c->base.status != CONTENT_STATUS_LOADING && - box->flags & REPLACE_DIM) { - union content_msg_data data; + err = binding_parse_completed(htmlc->parser_binding); + if (err != BINDING_OK) { + union content_msg_data msg_data; - if (!box_visible(box)) - break; + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); - box_coords(box, &x, &y); + return false; + } - 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; + htmlc->document = binding_get_document(htmlc->parser_binding, + &htmlc->quirks); + /*xmlDebugDumpDocument(stderr, htmlc->document);*/ + + if (htmlc->document == NULL) { + LOG(("Parsing failed")); + msg_data.error = messages_get("ParsingFail"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; + } + + if (htmlc->encoding == NULL) { + const char *encoding = binding_get_encoding( + htmlc->parser_binding, + &htmlc->encoding_source); - content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); + htmlc->encoding = talloc_strdup(c, encoding); + if (htmlc->encoding == NULL) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; } - break; + } - case CONTENT_MSG_ERROR: - hlcache_handle_release(object); + /* Give up processing if we've been aborted */ + if (htmlc->aborted) { + msg_data.error = messages_get("Stopped"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; + } - o->content = NULL; + /* locate html and head elements */ + exc = dom_document_get_document_element(htmlc->document, &html); + if ((exc != DOM_NO_ERR) || (html == NULL)) { + LOG(("error retrieving html element from dom")); + msg_data.error = messages_get("ParsingFail"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; + } - c->base.active--; + exc = dom_node_get_node_name(html, &node_name); + if ((exc != DOM_NO_ERR) || + (node_name == NULL) || + (!dom_string_caseless_isequal(node_name, html_dom_string_html))) { + LOG(("html element not found")); + msg_data.error = messages_get("ParsingFail"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; + } + dom_string_unref(node_name); - content_add_error(&c->base, "?", 0); - html_set_status(c, event->data.error); - content_broadcast(&c->base, CONTENT_MSG_STATUS, event->data); - html_object_failed(box, c, o->background); - break; + exc = dom_node_get_first_child(html, &head); + if ((exc != DOM_NO_ERR) || (head == NULL)) { + head = NULL; + LOG(("head element not found")); + } else { + dom_node_type node_type; + dom_node *next_node; - case CONTENT_MSG_STATUS: - html_set_status(c, content_get_status_message(object)); - /* content_broadcast(&c->base, CONTENT_MSG_STATUS, 0); */ - break; + /* find first node thats a element */ + do { + exc = dom_node_get_node_type(head, &node_type); - case CONTENT_MSG_REFORMAT: - break; + if ((exc != DOM_NO_ERR) || + (node_type == DOM_ELEMENT_NODE)) + break; - case CONTENT_MSG_REDRAW: - if (c->base.status != CONTENT_STATUS_LOADING) { - union content_msg_data data = event->data; + exc = dom_node_get_next_sibling(head, &next_node); + dom_node_unref(head); + if (exc == DOM_NO_ERR) { + head = next_node; + } else { + head = NULL; + } + } while (head != NULL); + + if (head != NULL) { + exc = dom_node_get_node_name(head, &node_name); + if ((exc == DOM_NO_ERR) || (node_name != NULL)) { + if (!dom_string_caseless_isequal(node_name, + html_dom_string_head)) { + dom_node_unref(head); + LOG(("head element not found")); + head = NULL; + } + dom_string_unref(node_name); + } + } + } - if (!box_visible(box)) - break; + if (head != NULL) { + if (html_head(htmlc, head) == false) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; + } - box_coords(box, &x, &y); + /* handle meta refresh */ + if (html_meta_refresh(htmlc, head) == false) + return false; + } - 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; - } + /* Retrieve forms from parser */ + htmlc->forms = binding_get_forms(htmlc->parser_binding); + for (f = htmlc->forms; f != NULL; f = f->prev) { + char *action; + url_func_result res; - 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]; + /* Make all actions absolute */ + if (f->action == NULL || f->action[0] == '\0') { + /* HTML5 4.10.22.3 step 11 */ + res = url_join(nsurl_access(content_get_url(c)), + nsurl_access(htmlc->base_url), &action); + } else { + res = url_join(f->action, nsurl_access(htmlc->base_url), + &action); + } - content_broadcast(&c->base, CONTENT_MSG_REDRAW, data); + if (res != URL_FUNC_OK) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; } - 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); + free(f->action); + f->action = action; + + /* Ensure each form has a document encoding */ + if (f->document_charset == NULL) { + f->document_charset = strdup(htmlc->encoding); + if (f->document_charset == NULL) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, + msg_data); + return false; + } } + } - break; + /* get stylesheets */ + if (html_find_stylesheets(htmlc, html) == false) + return false; - case CONTENT_MSG_LINK: - /* Don't care about favicons */ - break; + return true; +} - default: - assert(0); - } - 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 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); - } +/** + * 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 NSERROR_OK; + if (--html->base.active == 0) + html_finish_conversion(html); } + /** - * Update a box whose content has completed rendering. + * Start a fetch for an object required by a page. + * + * \param c content of type CONTENT_HTML + * \param url URL of object to fetch (copied) + * \param box box that will contain the object + * \param permitted_types bitmap of acceptable types + * \param available_width estimate of width of object + * \param available_height estimate of height of object + * \param background this is a background image + * \return true on success, false on memory exhaustion */ -void html_object_done(struct box *box, hlcache_handle *object, - bool background) +bool html_fetch_object(html_content *c, nsurl *url, struct box *box, + content_type permitted_types, + int available_width, int available_height, + bool background) { - struct box *b; + struct content_html_object *object; + hlcache_child_context child; + nserror error; - if (background) { - box->background = object; - return; - } + /* If we've already been aborted, don't bother attempting the fetch */ + if (c->aborted) + return true; - box->object = object; + child.charset = c->encoding; + child.quirks = c->base.quirks; - if (!(box->flags & REPLACE_DIM)) { - /* invalidate parent min, max widths */ - for (b = box; b; b = b->parent) - b->max_width = UNKNOWN_MAX_WIDTH; + object = talloc(c, struct content_html_object); + if (object == NULL) { + return false; + } - /* delete any clones of this box */ - while (box->next && (box->next->flags & CLONE)) { - /* box_free_box(box->next); */ - box->next = box->next->next; - } + object->parent = (struct content *) c; + object->next = NULL; + object->content = NULL; + object->box = box; + object->permitted_types = permitted_types; + object->background = background; + + 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) { + talloc_free(object); + return error != NSERROR_NOMEM; } -} + /* add to content object list */ + object->next = c->object_list; + c->object_list = object; -/** - * 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 - */ + c->num_objects++; + c->base.active++; -void html_object_failed(struct box *box, html_content *content, - bool background) -{ - /* Nothing to do */ - return; + return true; } -/** - * schedule() callback for object refresh - */ -void html_object_refresh(void *p) -{ - struct content_html_object *object = p; - nsurl *refresh_url; - assert(content_get_type(object->content) == CONTENT_HTML); - refresh_url = content_get_refresh_url(object->content); - /* Ignore if refresh URL has gone - * (may happen if fetch errored) */ - if (refresh_url == NULL) - return; - content_invalidate_reuse_data(object->content); - if (!html_replace_object(object, refresh_url)) { - /** \todo handle memory exhaustion */ - } -} + + /** * Stop loading a CONTENT_HTML. */ -void html_stop(struct content *c) +static void html_stop(struct content *c) { html_content *htmlc = (html_content *) c; struct content_html_object *object; @@ -1937,7 +1966,7 @@ void html_stop(struct content *c) * Reformat a CONTENT_HTML to a new width. */ -void html_reformat(struct content *c, int width, int height) +static void html_reformat(struct content *c, int width, int height) { html_content *htmlc = (html_content *) c; struct box *layout; @@ -2008,12 +2037,57 @@ void html__redraw_a_box(struct content *c, struct box *box) box->padding[TOP] + box->height + box->padding[BOTTOM]); } +static void html_destroy_frameset(struct content_html_frames *frameset) +{ + int i; + + if (frameset->name) { + talloc_free(frameset->name); + frameset->name = NULL; + } + if (frameset->url) { + talloc_free(frameset->url); + frameset->url = NULL; + } + if (frameset->children) { + for (i = 0; i < (frameset->rows * frameset->cols); i++) { + if (frameset->children[i].name) { + talloc_free(frameset->children[i].name); + frameset->children[i].name = NULL; + } + if (frameset->children[i].url) { + nsurl_unref(frameset->children[i].url); + frameset->children[i].url = NULL; + } + if (frameset->children[i].children) + html_destroy_frameset(&frameset->children[i]); + } + talloc_free(frameset->children); + frameset->children = NULL; + } +} + +static void html_destroy_iframe(struct content_html_iframe *iframe) +{ + struct content_html_iframe *next; + next = iframe; + while ((iframe = next) != NULL) { + next = iframe->next; + if (iframe->name) + talloc_free(iframe->name); + if (iframe->url) { + nsurl_unref(iframe->url); + iframe->url = NULL; + } + talloc_free(iframe); + } +} /** * Destroy a CONTENT_HTML and free all resources it owns. */ -void html_destroy(struct content *c) +static void html_destroy(struct content *c) { html_content *html = (html_content *) c; unsigned int i; @@ -2090,72 +2164,8 @@ void html_destroy(struct content *c) html_destroy_objects(html); } -void html_destroy_objects(html_content *html) -{ - while (html->object_list != NULL) { - struct content_html_object *victim = html->object_list; - - if (victim->content != NULL) { - LOG(("object %p", victim->content)); - - if (content_get_type(victim->content) == CONTENT_HTML) - schedule_remove(html_object_refresh, victim); - - hlcache_handle_release(victim->content); - } - - html->object_list = victim->next; - talloc_free(victim); - } -} - -void html_destroy_frameset(struct content_html_frames *frameset) -{ - int i; - - if (frameset->name) { - talloc_free(frameset->name); - frameset->name = NULL; - } - if (frameset->url) { - talloc_free(frameset->url); - frameset->url = NULL; - } - if (frameset->children) { - for (i = 0; i < (frameset->rows * frameset->cols); i++) { - if (frameset->children[i].name) { - talloc_free(frameset->children[i].name); - frameset->children[i].name = NULL; - } - if (frameset->children[i].url) { - nsurl_unref(frameset->children[i].url); - frameset->children[i].url = NULL; - } - if (frameset->children[i].children) - html_destroy_frameset(&frameset->children[i]); - } - talloc_free(frameset->children); - frameset->children = NULL; - } -} - -void html_destroy_iframe(struct content_html_iframe *iframe) -{ - struct content_html_iframe *next; - next = iframe; - while ((iframe = next) != NULL) { - next = iframe->next; - if (iframe->name) - talloc_free(iframe->name); - if (iframe->url) { - nsurl_unref(iframe->url); - iframe->url = NULL; - } - talloc_free(iframe); - } -} -nserror html_clone(const struct content *old, struct content **newc) +static nserror html_clone(const struct content *old, struct content **newc) { /** \todo Clone HTML specifics */ @@ -2181,9 +2191,12 @@ void html_set_status(html_content *c, const char *extra) * Handle a window containing a CONTENT_HTML being opened. */ -void html_open(struct content *c, struct browser_window *bw, - struct content *page, struct box *box, - struct object_params *params) +static void +html_open(struct content *c, + struct browser_window *bw, + struct content *page, + struct box *box, + struct object_params *params) { html_content *html = (html_content *) c; struct content_html_object *object, *next; @@ -2216,7 +2229,7 @@ void html_open(struct content *c, struct browser_window *bw, * Handle a window containing a CONTENT_HTML being closed. */ -void html_close(struct content *c) +static void html_close(struct content *c) { html_content *html = (html_content *) c; struct content_html_object *object, *next; @@ -2247,7 +2260,7 @@ void html_close(struct content *c) * Return an HTML content's selection context */ -struct selection *html_get_selection(struct content *c) +static struct selection *html_get_selection(struct content *c) { html_content *html = (html_content *) c; @@ -2265,8 +2278,11 @@ struct selection *html_get_selection(struct content *c) * \param data pointer to contextual_content struct. Its fields are updated * with pointers to any relevent content, or set to NULL if none. */ -void html_get_contextual_content(struct content *c, - int x, int y, struct contextual_content *data) +static void +html_get_contextual_content(struct content *c, + int x, + int y, + struct contextual_content *data) { html_content *html = (html_content *) c; @@ -2313,7 +2329,8 @@ void html_get_contextual_content(struct content *c, * \param scry y-coordinate of point of interest * \return true iff scroll was consumed by something in the content */ -bool html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry) +static bool +html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry) { html_content *html = (html_content *) c; @@ -2362,7 +2379,7 @@ bool html_scroll_at_point(struct content *c, int x, int y, int scrx, int scry) * \param file path to file to be dropped * \return true iff file drop has been handled */ -bool html_drop_file_at_point(struct content *c, int x, int y, char *file) +static bool html_drop_file_at_point(struct content *c, int x, int y, char *file) { html_content *html = (html_content *) c; @@ -2536,7 +2553,7 @@ void html_set_search(struct content *c, struct search_context *s) * \return content's search context, or NULL if none */ -struct search_context *html_get_search(struct content *c) +static struct search_context *html_get_search(struct content *c) { html_content *html = (html_content *) c; @@ -2549,8 +2566,8 @@ struct search_context *html_get_search(struct content *c) * Print a frameset tree to stderr. */ -void html_dump_frameset(struct content_html_frames *frame, - unsigned int depth) +static void +html_dump_frameset(struct content_html_frames *frame, unsigned int depth) { unsigned int i; int row, col, index; @@ -2789,11 +2806,211 @@ bool html_get_id_offset(hlcache_handle *h, lwc_string *frag_id, int *x, int *y) * * \return CONTENT_HTML */ -content_type html_content_type(void) +static content_type html_content_type(void) { return CONTENT_HTML; } + +static void html_fini(void) +{ + if (html_dom_string_title != NULL) { + dom_string_unref(html_dom_string_title); + html_dom_string_title = NULL; + } + + if (html_dom_string_base != NULL) { + dom_string_unref(html_dom_string_base); + html_dom_string_base = NULL; + } + + if (html_dom_string_link != NULL) { + dom_string_unref(html_dom_string_link); + html_dom_string_link = NULL; + } + + if (html_dom_string_rel != NULL) { + dom_string_unref(html_dom_string_rel); + html_dom_string_rel = NULL; + } + + if (html_dom_string_href != NULL) { + dom_string_unref(html_dom_string_href); + html_dom_string_href = NULL; + } + + if (html_dom_string_hreflang != NULL) { + dom_string_unref(html_dom_string_hreflang); + html_dom_string_hreflang = NULL; + } + + if (html_dom_string_type != NULL) { + dom_string_unref(html_dom_string_type); + html_dom_string_type = NULL; + } + + if (html_dom_string_media != NULL) { + dom_string_unref(html_dom_string_media); + html_dom_string_media = NULL; + } + + if (html_dom_string_sizes != NULL) { + dom_string_unref(html_dom_string_sizes); + html_dom_string_sizes = NULL; + } + + if (html_dom_string_head != NULL) { + dom_string_unref(html_dom_string_head); + html_dom_string_head = NULL; + } + + if (html_dom_string_html != NULL) { + dom_string_unref(html_dom_string_html); + html_dom_string_html = NULL; + } + + if (html_user_stylesheet_url != NULL) { + nsurl_unref(html_user_stylesheet_url); + html_user_stylesheet_url = NULL; + } + + if (html_quirks_stylesheet_url != NULL) { + nsurl_unref(html_quirks_stylesheet_url); + html_quirks_stylesheet_url = NULL; + } + + if (html_adblock_stylesheet_url != NULL) { + nsurl_unref(html_adblock_stylesheet_url); + html_adblock_stylesheet_url = NULL; + } + + if (html_default_stylesheet_url != NULL) { + nsurl_unref(html_default_stylesheet_url); + html_default_stylesheet_url = NULL; + } + + if (html_charset != NULL) { + lwc_string_unref(html_charset); + html_charset = NULL; + } +} + +static const content_handler html_content_handler = { + .fini = html_fini, + .create = html_create, + .process_data = html_process_data, + .data_complete = html_convert, + .reformat = html_reformat, + .destroy = html_destroy, + .stop = html_stop, + .mouse_track = html_mouse_track, + .mouse_action = html_mouse_action, + .redraw = html_redraw, + .open = html_open, + .close = html_close, + .get_selection = html_get_selection, + .get_contextual_content = html_get_contextual_content, + .scroll_at_point = html_scroll_at_point, + .drop_file_at_point = html_drop_file_at_point, + .clone = html_clone, + .type = html_content_type, + .no_share = true, +}; + +nserror html_init(void) +{ + uint32_t i; + lwc_error lerror; + nserror error; + dom_exception exc; /* returned by libdom functions */ + + lerror = lwc_intern_string("charset", SLEN("charset"), &html_charset); + if (lerror != lwc_error_ok) { + error = NSERROR_NOMEM; + goto error; + } + + error = nsurl_create("resource:default.css", + &html_default_stylesheet_url); + if (error != NSERROR_OK) + goto error; + + error = nsurl_create("resource:adblock.css", + &html_adblock_stylesheet_url); + if (error != NSERROR_OK) + goto error; + + error = nsurl_create("resource:quirks.css", + &html_quirks_stylesheet_url); + if (error != NSERROR_OK) + goto error; + + error = nsurl_create("resource:user.css", + &html_user_stylesheet_url); + if (error != NSERROR_OK) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"html", + 4, + &html_dom_string_html); + if ((exc != DOM_NO_ERR) || (html_dom_string_html == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"head", 4, &html_dom_string_head); + if ((exc != DOM_NO_ERR) || (html_dom_string_head == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"rel", 3, &html_dom_string_rel); + if ((exc != DOM_NO_ERR) || (html_dom_string_rel == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"href", 4, &html_dom_string_href); + if ((exc != DOM_NO_ERR) || (html_dom_string_href == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"hreflang", 8, &html_dom_string_hreflang); + if ((exc != DOM_NO_ERR) || (html_dom_string_hreflang == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"type", 4, &html_dom_string_type); + if ((exc != DOM_NO_ERR) || (html_dom_string_type == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"media", 5, &html_dom_string_media); + if ((exc != DOM_NO_ERR) || (html_dom_string_media == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"sizes", 5, &html_dom_string_sizes); + if ((exc != DOM_NO_ERR) || (html_dom_string_sizes == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"title", 5, &html_dom_string_title); + if ((exc != DOM_NO_ERR) || (html_dom_string_title == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"base", 4, &html_dom_string_base); + if ((exc != DOM_NO_ERR) || (html_dom_string_base == NULL)) + goto error; + + exc = dom_string_create_interned((const uint8_t *)"link", 4, &html_dom_string_link); + if ((exc != DOM_NO_ERR) || (html_dom_string_link == NULL)) + goto error; + + for (i = 0; i < NOF_ELEMENTS(html_types); i++) { + error = content_factory_register_handler(html_types[i], + &html_content_handler); + if (error != NSERROR_OK) + goto error; + } + + return NSERROR_OK; + +error: + html_fini(); + + return error; +} + /** * Get the browser window containing an HTML content * @@ -2809,4 +3026,3 @@ struct browser_window *html_get_browser_window(struct content *c) return html->bw; } - diff --git a/render/imagemap.c b/render/imagemap.c index cf91f31d3..fd6399790 100644 --- a/render/imagemap.c +++ b/render/imagemap.c @@ -24,6 +24,7 @@ #include #include #include + #include "content/content_protected.h" #include "content/hlcache.h" #include "render/box.h" @@ -75,9 +76,9 @@ struct imagemap { static bool imagemap_add(html_content *c, const char *key, struct mapentry *list); static bool imagemap_create(html_content *c); -static bool imagemap_extract_map(xmlNode *node, html_content *c, +static bool imagemap_extract_map(dom_node *node, html_content *c, struct mapentry **entry); -static bool imagemap_addtolist(xmlNode *n, nsurl *base_url, +static bool imagemap_addtolist(dom_node *n, nsurl *base_url, struct mapentry **entry); static void imagemap_freelist(struct mapentry *list); static unsigned int imagemap_hash(const char *key); @@ -246,9 +247,10 @@ void imagemap_dump(html_content *c) * \param c The containing content * \return false on memory exhaustion, true otherwise */ -bool imagemap_extract(xmlNode *node, html_content *c) +bool imagemap_extract(dom_node *node, html_content *c) { - xmlNode *this_node; +#ifdef FIXME + dom_node *this_node; struct mapentry *entry = NULL; char *name; @@ -292,7 +294,7 @@ bool imagemap_extract(xmlNode *node, html_content *c) if (imagemap_extract(this_node, c) == false) return false; } - +#endif return true; } @@ -304,9 +306,10 @@ bool imagemap_extract(xmlNode *node, html_content *c) * \param entry List of map entries * \return false on memory exhaustion, true otherwise */ -bool imagemap_extract_map(xmlNode *node, html_content *c, +bool imagemap_extract_map(dom_node *node, html_content *c, struct mapentry **entry) { +#ifdef FIXME xmlNode *this_node; assert(c != NULL); @@ -331,7 +334,7 @@ bool imagemap_extract_map(xmlNode *node, html_content *c, if (imagemap_extract_map(this_node, c, entry) == false) return false; } - +#endif return true; } @@ -343,9 +346,10 @@ bool imagemap_extract_map(xmlNode *node, html_content *c, * \param entry Pointer to list of entries * \return false on memory exhaustion, true otherwise */ -bool imagemap_addtolist(xmlNode *n, nsurl *base_url, +bool imagemap_addtolist(dom_node *n, nsurl *base_url, struct mapentry **entry) { +#ifdef FIXME char *shape, *coords = NULL, *href, *target = NULL; struct mapentry *new_map, *temp; @@ -588,7 +592,7 @@ bool imagemap_addtolist(xmlNode *n, nsurl *base_url, xmlFree(shape); if (coords) xmlFree(coords); - +#endif return true; } diff --git a/render/imagemap.h b/render/imagemap.h index 7877e5e04..56bee6b10 100644 --- a/render/imagemap.h +++ b/render/imagemap.h @@ -19,7 +19,7 @@ #ifndef _NETSURF_RENDER_IMAGEMAP_H_ #define _NETSURF_RENDER_IMAGEMAP_H_ -#include +#include #include "utils/nsurl.h" @@ -28,7 +28,7 @@ struct hlcache_handle; void imagemap_destroy(struct html_content *c); void imagemap_dump(struct html_content *c); -bool imagemap_extract(xmlNode *node, struct html_content *c); +bool imagemap_extract(dom_node *node, struct html_content *c); nsurl *imagemap_get(struct html_content *c, const char *key, unsigned long x, unsigned long y, -- cgit v1.2.3